]> granicus.if.org Git - postgresql/commitdiff
Refactor Perl test code
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 2 Dec 2015 21:46:16 +0000 (18:46 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 2 Dec 2015 21:46:16 +0000 (18:46 -0300)
The original code was a bit clunky; make it more amenable for further
reuse by creating a new Perl package PostgresNode, which is an
object-oriented representation of a single server, with some support
routines such as init, start, stop, psql.  This serves as a better basis
on which to build further test code, and enables writing tests that use
more than one server without too much complication.

This commit modifies a lot of the existing test files, mostly to remove
explicit calls to system commands (pg_ctl) replacing them with method
calls of a PostgresNode object.  The result is quite a bit more
straightforward.

Also move some initialization code to BEGIN and INIT blocks instead of
having it straight in as top-level code.

This commit also introduces package RecursiveCopy so that we can copy
whole directories without having to depend on packages that may not be
present on vanilla Perl 5.8 installations.

I also ran perltidy on the modified files, which changes some code sites
that are not otherwise touched by this patch.  I tried to avoid this,
but it ended up being more trouble than it's worth.

Authors: Michael Paquier, Álvaro Herrera
Review: Noah Misch

27 files changed:
src/bin/initdb/t/001_initdb.pl
src/bin/pg_basebackup/t/010_pg_basebackup.pl
src/bin/pg_controldata/t/001_pg_controldata.pl
src/bin/pg_ctl/t/001_start_stop.pl
src/bin/pg_ctl/t/002_status.pl
src/bin/pg_rewind/RewindTest.pm
src/bin/pg_rewind/t/003_extrafiles.pl
src/bin/pg_rewind/t/004_pg_xlog_symlink.pl
src/bin/scripts/t/010_clusterdb.pl
src/bin/scripts/t/011_clusterdb_all.pl
src/bin/scripts/t/020_createdb.pl
src/bin/scripts/t/030_createlang.pl
src/bin/scripts/t/040_createuser.pl
src/bin/scripts/t/050_dropdb.pl
src/bin/scripts/t/060_droplang.pl
src/bin/scripts/t/070_dropuser.pl
src/bin/scripts/t/080_pg_isready.pl
src/bin/scripts/t/090_reindexdb.pl
src/bin/scripts/t/091_reindexdb_all.pl
src/bin/scripts/t/100_vacuumdb.pl
src/bin/scripts/t/101_vacuumdb_all.pl
src/bin/scripts/t/102_vacuumdb_stages.pl
src/test/perl/PostgresNode.pm [new file with mode: 0644]
src/test/perl/RecursiveCopy.pm [new file with mode: 0644]
src/test/perl/TestLib.pm
src/test/ssl/ServerSetup.pm
src/test/ssl/t/001_ssltests.pl

index 299dcf5b3b98ee343343f974882cc81a6be12235..f64186d6373a5148fdee570b1458044f5864fa17 100644 (file)
@@ -4,6 +4,7 @@
 
 use strict;
 use warnings;
+use PostgresNode;
 use TestLib;
 use Test::More tests => 14;
 
index dc96bbf2067dc2a2a7ad9fd634b9cb0a77835f50..3e491a82e4a928dd32a0dd230034ecccdfe26f05 100644 (file)
@@ -2,6 +2,7 @@ use strict;
 use warnings;
 use Cwd;
 use Config;
+use PostgresNode;
 use TestLib;
 use Test::More tests => 51;
 
@@ -9,12 +10,18 @@ program_help_ok('pg_basebackup');
 program_version_ok('pg_basebackup');
 program_options_handling_ok('pg_basebackup');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $tempdir = TestLib::tempdir;
 
-command_fails(['pg_basebackup'],
+my $node = get_new_node();
+
+# Initialize node without replication settings
+$node->init(hba_permit_replication => 0);
+$node->start;
+my $pgdata = $node->data_dir;
+
+$node->command_fails(['pg_basebackup'],
        'pg_basebackup needs target directory specified');
-command_fails(
+$node->command_fails(
        [ 'pg_basebackup', '-D', "$tempdir/backup" ],
        'pg_basebackup fails because of hba');
 
@@ -26,160 +33,191 @@ if (open BADCHARS, ">>$tempdir/pgdata/FOO\xe0\xe0\xe0BAR")
        close BADCHARS;
 }
 
-configure_hba_for_replication "$tempdir/pgdata";
-system_or_bail 'pg_ctl', '-D', "$tempdir/pgdata", 'reload';
+$node->set_replication_conf();
+system_or_bail 'pg_ctl', '-D', $pgdata, 'reload';
 
-command_fails(
+$node->command_fails(
        [ 'pg_basebackup', '-D', "$tempdir/backup" ],
        'pg_basebackup fails because of WAL configuration');
 
-open CONF, ">>$tempdir/pgdata/postgresql.conf";
+open CONF, ">>$pgdata/postgresql.conf";
 print CONF "max_replication_slots = 10\n";
 print CONF "max_wal_senders = 10\n";
 print CONF "wal_level = archive\n";
 close CONF;
-restart_test_server;
+$node->restart;
 
-command_ok([ 'pg_basebackup', '-D', "$tempdir/backup" ],
+$node->command_ok([ 'pg_basebackup', '-D', "$tempdir/backup" ],
        'pg_basebackup runs');
 ok(-f "$tempdir/backup/PG_VERSION", 'backup was created');
 
-is_deeply([sort(slurp_dir("$tempdir/backup/pg_xlog/"))],
-                 [sort qw(. .. archive_status)],
-                 'no WAL files copied');
+is_deeply(
+       [ sort(slurp_dir("$tempdir/backup/pg_xlog/")) ],
+       [ sort qw(. .. archive_status) ],
+       'no WAL files copied');
 
-command_ok(
+$node->command_ok(
        [   'pg_basebackup', '-D', "$tempdir/backup2", '--xlogdir',
                "$tempdir/xlog2" ],
        'separate xlog directory');
 ok(-f "$tempdir/backup2/PG_VERSION", 'backup was created');
 ok(-d "$tempdir/xlog2/",             'xlog directory was created');
 
-command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup", '-Ft' ],
+$node->command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup", '-Ft' ],
        'tar format');
 ok(-f "$tempdir/tarbackup/base.tar", 'backup tar was created');
 
-command_fails(
+$node->command_fails(
        [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T=/foo" ],
        '-T with empty old directory fails');
-command_fails(
+$node->command_fails(
        [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=" ],
        '-T with empty new directory fails');
-command_fails(
+$node->command_fails(
        [   'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp',
                "-T/foo=/bar=/baz" ],
        '-T with multiple = fails');
-command_fails(
+$node->command_fails(
        [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo=/bar" ],
        '-T with old directory not absolute fails');
-command_fails(
+$node->command_fails(
        [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=bar" ],
        '-T with new directory not absolute fails');
-command_fails(
+$node->command_fails(
        [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo" ],
        '-T with invalid format fails');
 
 # Tar format doesn't support filenames longer than 100 bytes.
 my $superlongname = "superlongname_" . ("x" x 100);
-my $superlongpath = "$tempdir/pgdata/$superlongname";
+my $superlongpath = "$pgdata/$superlongname";
 
 open FILE, ">$superlongpath" or die "unable to create file $superlongpath";
 close FILE;
-command_fails([ 'pg_basebackup', '-D', "$tempdir/tarbackup_l1", '-Ft' ],
+$node->command_fails(
+       [ 'pg_basebackup', '-D', "$tempdir/tarbackup_l1", '-Ft' ],
        'pg_basebackup tar with long name fails');
-unlink "$tempdir/pgdata/$superlongname";
+unlink "$pgdata/$superlongname";
 
 # The following tests test symlinks. Windows doesn't have symlinks, so
 # skip on Windows.
-SKIP: {
-    skip "symlinks not supported on Windows", 10 if ($windows_os);
+SKIP:
+{
+       skip "symlinks not supported on Windows", 10 if ($windows_os);
 
        # Create a temporary directory in the system location and symlink it
        # to our physical temp location.  That way we can use shorter names
        # for the tablespace directories, which hopefully won't run afoul of
        # the 99 character length limit.
-       my $shorter_tempdir = tempdir_short . "/tempdir";
+       my $shorter_tempdir = TestLib::tempdir_short . "/tempdir";
        symlink "$tempdir", $shorter_tempdir;
 
        mkdir "$tempdir/tblspc1";
-       psql 'postgres',
-       "CREATE TABLESPACE tblspc1 LOCATION '$shorter_tempdir/tblspc1';";
-       psql 'postgres', "CREATE TABLE test1 (a int) TABLESPACE tblspc1;";
-       command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup2", '-Ft' ],
-                          'tar format with tablespaces');
+       $node->psql('postgres',
+               "CREATE TABLESPACE tblspc1 LOCATION '$shorter_tempdir/tblspc1';");
+       $node->psql('postgres', "CREATE TABLE test1 (a int) TABLESPACE tblspc1;");
+       $node->command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup2", '-Ft' ],
+               'tar format with tablespaces');
        ok(-f "$tempdir/tarbackup2/base.tar", 'backup tar was created');
        my @tblspc_tars = glob "$tempdir/tarbackup2/[0-9]*.tar";
        is(scalar(@tblspc_tars), 1, 'one tablespace tar was created');
 
-       command_fails(
+       $node->command_fails(
                [ 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp' ],
                'plain format with tablespaces fails without tablespace mapping');
 
-       command_ok(
+       $node->command_ok(
                [   'pg_basebackup', '-D', "$tempdir/backup1", '-Fp',
                        "-T$shorter_tempdir/tblspc1=$tempdir/tbackup/tblspc1" ],
                'plain format with tablespaces succeeds with tablespace mapping');
        ok(-d "$tempdir/tbackup/tblspc1", 'tablespace was relocated');
-       opendir(my $dh, "$tempdir/pgdata/pg_tblspc") or die;
+       opendir(my $dh, "$pgdata/pg_tblspc") or die;
        ok( (   grep {
-               -l "$tempdir/backup1/pg_tblspc/$_"
-                       and readlink "$tempdir/backup1/pg_tblspc/$_" eq
-                       "$tempdir/tbackup/tblspc1"
-                       } readdir($dh)),
+                               -l "$tempdir/backup1/pg_tblspc/$_"
+                                 and readlink "$tempdir/backup1/pg_tblspc/$_" eq
+                                 "$tempdir/tbackup/tblspc1"
+                         } readdir($dh)),
                "tablespace symlink was updated");
        closedir $dh;
 
        mkdir "$tempdir/tbl=spc2";
-       psql 'postgres', "DROP TABLE test1;";
-       psql 'postgres', "DROP TABLESPACE tblspc1;";
-       psql 'postgres',
-       "CREATE TABLESPACE tblspc2 LOCATION '$shorter_tempdir/tbl=spc2';";
-       command_ok(
+       $node->psql('postgres', "DROP TABLE test1;");
+       $node->psql('postgres', "DROP TABLESPACE tblspc1;");
+       $node->psql('postgres',
+               "CREATE TABLESPACE tblspc2 LOCATION '$shorter_tempdir/tbl=spc2';");
+       $node->command_ok(
                [   'pg_basebackup', '-D', "$tempdir/backup3", '-Fp',
                        "-T$shorter_tempdir/tbl\\=spc2=$tempdir/tbackup/tbl\\=spc2" ],
                'mapping tablespace with = sign in path');
-       ok(-d "$tempdir/tbackup/tbl=spc2", 'tablespace with = sign was relocated');
-       psql 'postgres', "DROP TABLESPACE tblspc2;";
+       ok(-d "$tempdir/tbackup/tbl=spc2",
+               'tablespace with = sign was relocated');
+       $node->psql('postgres', "DROP TABLESPACE tblspc2;");
 
        mkdir "$tempdir/$superlongname";
-       psql 'postgres',
-       "CREATE TABLESPACE tblspc3 LOCATION '$tempdir/$superlongname';";
-       command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup_l3", '-Ft' ],
-                          'pg_basebackup tar with long symlink target');
-       psql 'postgres', "DROP TABLESPACE tblspc3;";
+       $node->psql('postgres',
+               "CREATE TABLESPACE tblspc3 LOCATION '$tempdir/$superlongname';");
+       $node->command_ok(
+               [ 'pg_basebackup', '-D', "$tempdir/tarbackup_l3", '-Ft' ],
+               'pg_basebackup tar with long symlink target');
+       $node->psql('postgres', "DROP TABLESPACE tblspc3;");
 }
 
-command_ok([ 'pg_basebackup', '-D', "$tempdir/backupR", '-R' ],
+$node->command_ok([ 'pg_basebackup', '-D', "$tempdir/backupR", '-R' ],
        'pg_basebackup -R runs');
 ok(-f "$tempdir/backupR/recovery.conf", 'recovery.conf was created');
 my $recovery_conf = slurp_file "$tempdir/backupR/recovery.conf";
+
 # using a character class for the final "'" here works around an apparent
 # bug in several version of the Msys DTK perl
-like($recovery_conf, qr/^standby_mode = 'on[']$/m, 'recovery.conf sets standby_mode');
-like($recovery_conf, qr/^primary_conninfo = '.*port=$ENV{PGPORT}.*'$/m, 'recovery.conf sets primary_conninfo');
-
-command_ok([ 'pg_basebackup', '-D', "$tempdir/backupxf", '-X', 'fetch' ],
+like(
+       $recovery_conf,
+       qr/^standby_mode = 'on[']$/m,
+       'recovery.conf sets standby_mode');
+like(
+       $recovery_conf,
+       qr/^primary_conninfo = '.*port=$ENV{PGPORT}.*'$/m,
+       'recovery.conf sets primary_conninfo');
+
+$node->command_ok(
+       [ 'pg_basebackup', '-D', "$tempdir/backupxf", '-X', 'fetch' ],
        'pg_basebackup -X fetch runs');
-ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxf/pg_xlog")), 'WAL files copied');
-command_ok([ 'pg_basebackup', '-D', "$tempdir/backupxs", '-X', 'stream' ],
+ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxf/pg_xlog")),
+       'WAL files copied');
+$node->command_ok(
+       [ 'pg_basebackup', '-D', "$tempdir/backupxs", '-X', 'stream' ],
        'pg_basebackup -X stream runs');
-ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxf/pg_xlog")), 'WAL files copied');
+ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxf/pg_xlog")),
+       'WAL files copied');
 
-command_fails([ 'pg_basebackup', '-D', "$tempdir/fail", '-S', 'slot1' ],
+$node->command_fails(
+       [ 'pg_basebackup', '-D', "$tempdir/fail", '-S', 'slot1' ],
        'pg_basebackup with replication slot fails without -X stream');
-command_fails([ 'pg_basebackup', '-D', "$tempdir/backupxs_sl_fail", '-X', 'stream', '-S', 'slot1' ],
+$node->command_fails(
+       [   'pg_basebackup',             '-D',
+               "$tempdir/backupxs_sl_fail", '-X',
+               'stream',                    '-S',
+               'slot1' ],
        'pg_basebackup fails with nonexistent replication slot');
 
-psql 'postgres', q{SELECT * FROM pg_create_physical_replication_slot('slot1')};
-my $lsn = psql 'postgres', q{SELECT restart_lsn FROM pg_replication_slots WHERE slot_name = 'slot1'};
+$node->psql('postgres',
+       q{SELECT * FROM pg_create_physical_replication_slot('slot1')});
+my $lsn = $node->psql('postgres',
+       q{SELECT restart_lsn FROM pg_replication_slots WHERE slot_name = 'slot1'}
+);
 is($lsn, '', 'restart LSN of new slot is null');
-command_ok([ 'pg_basebackup', '-D', "$tempdir/backupxs_sl", '-X', 'stream', '-S', 'slot1' ],
+$node->command_ok(
+       [   'pg_basebackup', '-D', "$tempdir/backupxs_sl", '-X',
+               'stream',        '-S', 'slot1' ],
        'pg_basebackup -X stream with replication slot runs');
-$lsn = psql 'postgres', q{SELECT restart_lsn FROM pg_replication_slots WHERE slot_name = 'slot1'};
+$lsn = $node->psql('postgres',
+       q{SELECT restart_lsn FROM pg_replication_slots WHERE slot_name = 'slot1'}
+);
 like($lsn, qr!^0/[0-9A-Z]{7,8}$!, 'restart LSN of slot has advanced');
 
-command_ok([ 'pg_basebackup', '-D', "$tempdir/backupxs_sl_R", '-X', 'stream', '-S', 'slot1', '-R' ],
+$node->command_ok(
+       [   'pg_basebackup', '-D', "$tempdir/backupxs_sl_R", '-X',
+               'stream',        '-S', 'slot1',                  '-R' ],
        'pg_basebackup with replication slot and -R runs');
-like(slurp_file("$tempdir/backupxs_sl_R/recovery.conf"),
-        qr/^primary_slot_name = 'slot1'$/m,
-        'recovery.conf sets primary_slot_name');
+like(
+       slurp_file("$tempdir/backupxs_sl_R/recovery.conf"),
+       qr/^primary_slot_name = 'slot1'$/m,
+       'recovery.conf sets primary_slot_name');
index e2b0d420a23c8d8575b610026733c179a5574ebb..ae45f41339ce1a7e86664cea71baaeedba969317 100644 (file)
@@ -1,16 +1,19 @@
 use strict;
 use warnings;
+use PostgresNode;
 use TestLib;
 use Test::More tests => 13;
 
-my $tempdir = TestLib::tempdir;
-
 program_help_ok('pg_controldata');
 program_version_ok('pg_controldata');
 program_options_handling_ok('pg_controldata');
 command_fails(['pg_controldata'], 'pg_controldata without arguments fails');
 command_fails([ 'pg_controldata', 'nonexistent' ],
        'pg_controldata with nonexistent directory fails');
-standard_initdb "$tempdir/data";
-command_like([ 'pg_controldata', "$tempdir/data" ],
+
+my $node = get_new_node();
+$node->init;
+$node->start;
+
+command_like([ 'pg_controldata', $node->data_dir ],
        qr/checkpoint/, 'pg_controldata produces output');
index f57abcef1dc150c135c1fb187a3fb6faf030585f..cbe99d79ad510e5f2af35b46cbae61111f198a82 100644 (file)
@@ -1,6 +1,8 @@
 use strict;
 use warnings;
+
 use Config;
+use PostgresNode;
 use TestLib;
 use Test::More tests => 17;
 
@@ -16,13 +18,11 @@ command_exit_is([ 'pg_ctl', 'start', '-D', "$tempdir/nonexistent" ],
 
 command_ok([ 'pg_ctl', 'initdb', '-D', "$tempdir/data", '-o', '-N' ],
        'pg_ctl initdb');
-command_ok(
-       [ $ENV{PG_REGRESS}, '--config-auth',
-               "$tempdir/data" ],
+command_ok([ $ENV{PG_REGRESS}, '--config-auth', "$tempdir/data" ],
        'configure authentication');
 open CONF, ">>$tempdir/data/postgresql.conf";
 print CONF "fsync = off\n";
-if (! $windows_os)
+if (!$windows_os)
 {
        print CONF "listen_addresses = ''\n";
        print CONF "unix_socket_directories = '$tempdir_short'\n";
@@ -34,6 +34,7 @@ else
 close CONF;
 command_ok([ 'pg_ctl', 'start', '-D', "$tempdir/data", '-w' ],
        'pg_ctl start -w');
+
 # sleep here is because Windows builds can't check postmaster.pid exactly,
 # so they may mistake a pre-existing postmaster.pid for one created by the
 # postmaster they start.  Waiting more than the 2 seconds slop time allowed
index 31f7c722f1ffdb8ca0dcf62eae38b70c0249de68..f1c131bd26f2626a4185696c89147412f87e12e9 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 3;
 
@@ -9,14 +11,15 @@ my $tempdir_short = TestLib::tempdir_short;
 command_exit_is([ 'pg_ctl', 'status', '-D', "$tempdir/nonexistent" ],
        4, 'pg_ctl status with nonexistent directory');
 
-standard_initdb "$tempdir/data";
+my $node = get_new_node();
+$node->init;
 
-command_exit_is([ 'pg_ctl', 'status', '-D', "$tempdir/data" ],
+command_exit_is([ 'pg_ctl', 'status', '-D', $node->data_dir ],
        3, 'pg_ctl status with server not running');
 
 system_or_bail 'pg_ctl', '-l', "$tempdir/logfile", '-D',
-  "$tempdir/data", '-w', 'start';
-command_exit_is([ 'pg_ctl', 'status', '-D', "$tempdir/data" ],
+  $node->data_dir, '-w', 'start';
+command_exit_is([ 'pg_ctl', 'status', '-D', $node->data_dir ],
        0, 'pg_ctl status with server running');
 
-system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/data", '-m', 'fast';
+system_or_bail 'pg_ctl', 'stop', '-D', $node->data_dir, '-m', 'fast';
index a4c17371dcf2159963c7a5063dd6e491a238df11..c1c7d1fa19f625fa4c2e815f9a63dc3dbeaa4bf5 100644 (file)
@@ -9,22 +9,20 @@ package RewindTest;
 # To run a test, the test script (in t/ subdirectory) calls the functions
 # in this module. These functions should be called in this sequence:
 #
-# 1. init_rewind_test - sets up log file etc.
+# 1. setup_cluster - creates a PostgreSQL cluster that runs as the master
 #
-# 2. setup_cluster - creates a PostgreSQL cluster that runs as the master
+# 2. start_master - starts the master server
 #
-# 3. start_master - starts the master server
-#
-# 4. create_standby - runs pg_basebackup to initialize a standby server, and
+# 3. create_standby - runs pg_basebackup to initialize a standby server, and
 #    sets it up to follow the master.
 #
-# 5. promote_standby - runs "pg_ctl promote" to promote the standby server.
+# 4. promote_standby - runs "pg_ctl promote" to promote the standby server.
 # The old master keeps running.
 #
-# 6. run_pg_rewind - stops the old master (if it's still running) and runs
+# 5. run_pg_rewind - stops the old master (if it's still running) and runs
 # pg_rewind to synchronize it with the now-promoted standby server.
 #
-# 7. clean_rewind_test - stops both servers used in the test, if they're
+# 6. clean_rewind_test - stops both servers used in the test, if they're
 # still running.
 #
 # The test script can use the helper functions master_psql and standby_psql
@@ -37,27 +35,23 @@ package RewindTest;
 use strict;
 use warnings;
 
-use TestLib;
-use Test::More;
-
 use Config;
+use Exporter 'import';
 use File::Copy;
 use File::Path qw(rmtree);
-use IPC::Run qw(run start);
+use IPC::Run qw(run);
+use PostgresNode;
+use TestLib;
+use Test::More;
 
-use Exporter 'import';
 our @EXPORT = qw(
-  $connstr_master
-  $connstr_standby
-  $test_master_datadir
-  $test_standby_datadir
+  $node_master
+  $node_standby
 
-  append_to_file
   master_psql
   standby_psql
   check_query
 
-  init_rewind_test
   setup_cluster
   start_master
   create_standby
@@ -66,32 +60,24 @@ our @EXPORT = qw(
   clean_rewind_test
 );
 
-our $test_master_datadir  = "$tmp_check/data_master";
-our $test_standby_datadir = "$tmp_check/data_standby";
-
-# Define non-conflicting ports for both nodes.
-my $port_master  = $ENV{PGPORT};
-my $port_standby = $port_master + 1;
-
-my $connstr_master  = "port=$port_master";
-my $connstr_standby = "port=$port_standby";
-
-$ENV{PGDATABASE} = "postgres";
+# Our nodes.
+our $node_master;
+our $node_standby;
 
 sub master_psql
 {
        my $cmd = shift;
 
-       system_or_bail 'psql', '-q', '--no-psqlrc', '-d', $connstr_master,
-         '-c', "$cmd";
+       system_or_bail 'psql', '-q', '--no-psqlrc', '-d',
+         $node_master->connstr('postgres'), '-c', "$cmd";
 }
 
 sub standby_psql
 {
        my $cmd = shift;
 
-       system_or_bail 'psql', '-q', '--no-psqlrc', '-d', $connstr_standby,
-         '-c', "$cmd";
+       system_or_bail 'psql', '-q', '--no-psqlrc', '-d',
+         $node_standby->connstr('postgres'), '-c', "$cmd";
 }
 
 # Run a query against the master, and check that the output matches what's
@@ -103,8 +89,9 @@ sub check_query
 
        # we want just the output, no formatting
        my $result = run [
-               'psql',          '-q', '-A', '-t', '--no-psqlrc', '-d',
-               $connstr_master, '-c', $query ],
+               'psql', '-q', '-A', '-t', '--no-psqlrc', '-d',
+               $node_master->connstr('postgres'),
+               '-c', $query ],
          '>', \$stdout, '2>', \$stderr;
 
        # We don't use ok() for the exit code and stderr, because we want this
@@ -125,56 +112,16 @@ sub check_query
        }
 }
 
-# Run a query once a second, until it returns 't' (i.e. SQL boolean true).
-sub poll_query_until
-{
-       my ($query, $connstr) = @_;
-
-       my $max_attempts = 30;
-       my $attempts     = 0;
-       my ($stdout, $stderr);
-
-       while ($attempts < $max_attempts)
-       {
-               my $cmd = [ 'psql', '-At', '-c', "$query", '-d', "$connstr" ];
-               my $result = run $cmd, '>', \$stdout, '2>', \$stderr;
-
-               chomp($stdout);
-               $stdout =~ s/\r//g if $Config{osname} eq 'msys';
-               if ($stdout eq "t")
-               {
-                       return 1;
-               }
-
-               # Wait a second before retrying.
-               sleep 1;
-               $attempts++;
-       }
-
-       # The query result didn't change in 30 seconds. Give up. Print the stderr
-       # from the last attempt, hopefully that's useful for debugging.
-       diag $stderr;
-       return 0;
-}
-
-sub append_to_file
-{
-       my ($filename, $str) = @_;
-
-       open my $fh, ">>", $filename or die "could not open file $filename";
-       print $fh $str;
-       close $fh;
-}
-
 sub setup_cluster
 {
+
        # Initialize master, data checksums are mandatory
-       rmtree($test_master_datadir);
-       standard_initdb($test_master_datadir);
+       $node_master = get_new_node();
+       $node_master->init;
 
        # Custom parameters for master's postgresql.conf
-       append_to_file(
-               "$test_master_datadir/postgresql.conf", qq(
+       $node_master->append_conf(
+               "postgresql.conf", qq(
 wal_level = hot_standby
 max_wal_senders = 2
 wal_keep_segments = 20
@@ -185,17 +132,11 @@ hot_standby = on
 autovacuum = off
 max_connections = 10
 ));
-
-       # Accept replication connections on master
-       configure_hba_for_replication $test_master_datadir;
 }
 
 sub start_master
 {
-       system_or_bail('pg_ctl' , '-w',
-                                  '-D' , $test_master_datadir,
-                                  '-l',  "$log_path/master.log",
-                                  "-o", "-p $port_master", 'start');
+       $node_master->start;
 
        #### Now run the test-specific parts to initialize the master before setting
        # up standby
@@ -203,24 +144,20 @@ sub start_master
 
 sub create_standby
 {
+       $node_standby = get_new_node();
+       $node_master->backup('my_backup');
+       $node_standby->init_from_backup($node_master, 'my_backup');
+       my $connstr_master = $node_master->connstr('postgres');
 
-       # Set up standby with necessary parameter
-       rmtree $test_standby_datadir;
-
-       # Base backup is taken with xlog files included
-       system_or_bail('pg_basebackup', '-D', $test_standby_datadir,
-                                  '-p', $port_master, '-x');
-       append_to_file(
-               "$test_standby_datadir/recovery.conf", qq(
+       $node_standby->append_conf(
+               "recovery.conf", qq(
 primary_conninfo='$connstr_master application_name=rewind_standby'
 standby_mode=on
 recovery_target_timeline='latest'
 ));
 
        # Start standby
-       system_or_bail('pg_ctl', '-w', '-D', $test_standby_datadir,
-                                  '-l', "$log_path/standby.log",
-                                  '-o', "-p $port_standby", 'start');
+       $node_standby->start;
 
        # The standby may have WAL to apply before it matches the primary.  That
        # is fine, because no test examines the standby before promotion.
@@ -234,14 +171,15 @@ sub promote_standby
        # Wait for the standby to receive and write all WAL.
        my $wal_received_query =
 "SELECT pg_current_xlog_location() = write_location FROM pg_stat_replication WHERE application_name = 'rewind_standby';";
-       poll_query_until($wal_received_query, $connstr_master)
+       $node_master->poll_query_until('postgres', $wal_received_query)
          or die "Timed out while waiting for standby to receive and write WAL";
 
        # Now promote slave and insert some new data on master, this will put
        # the master out-of-sync with the standby. Wait until the standby is
        # out of recovery mode, and is ready to accept read-write connections.
-       system_or_bail('pg_ctl', '-w', '-D', $test_standby_datadir, 'promote');
-       poll_query_until("SELECT NOT pg_is_in_recovery()", $connstr_standby)
+       system_or_bail('pg_ctl', '-w', '-D', $node_standby->data_dir, 'promote');
+       $node_standby->poll_query_until('postgres',
+               "SELECT NOT pg_is_in_recovery()")
          or die "Timed out while waiting for promotion of standby";
 
        # Force a checkpoint after the promotion. pg_rewind looks at the control
@@ -255,10 +193,14 @@ sub promote_standby
 
 sub run_pg_rewind
 {
-       my $test_mode = shift;
+       my $test_mode       = shift;
+       my $master_pgdata   = $node_master->data_dir;
+       my $standby_pgdata  = $node_standby->data_dir;
+       my $standby_connstr = $node_standby->connstr('postgres');
+       my $tmp_folder      = TestLib::tempdir;
 
        # Stop the master and be ready to perform the rewind
-       system_or_bail('pg_ctl', '-D', $test_master_datadir, '-m', 'fast', 'stop');
+       $node_master->stop;
 
        # At this point, the rewind processing is ready to run.
        # We now have a very simple scenario with a few diverged WAL record.
@@ -267,31 +209,33 @@ sub run_pg_rewind
 
        # Keep a temporary postgresql.conf for master node or it would be
        # overwritten during the rewind.
-       copy("$test_master_datadir/postgresql.conf",
-                "$tmp_check/master-postgresql.conf.tmp");
+       copy(
+               "$master_pgdata/postgresql.conf",
+               "$tmp_folder/master-postgresql.conf.tmp");
 
        # Now run pg_rewind
        if ($test_mode eq "local")
        {
+
                # Do rewind using a local pgdata as source
                # Stop the master and be ready to perform the rewind
-               system_or_bail('pg_ctl', '-D', $test_standby_datadir,
-                                          '-m', 'fast', 'stop');
-               command_ok(['pg_rewind',
-                                       "--debug",
-                                       "--source-pgdata=$test_standby_datadir",
-                                       "--target-pgdata=$test_master_datadir"],
-                                  'pg_rewind local');
+               $node_standby->stop;
+               command_ok(
+                       [   'pg_rewind',
+                               "--debug",
+                               "--source-pgdata=$standby_pgdata",
+                               "--target-pgdata=$master_pgdata" ],
+                       'pg_rewind local');
        }
        elsif ($test_mode eq "remote")
        {
+
                # Do rewind using a remote connection as source
-               command_ok(['pg_rewind',
-                                       "--debug",
-                                       "--source-server",
-                                       "port=$port_standby dbname=postgres",
-                                       "--target-pgdata=$test_master_datadir"],
-                                  'pg_rewind remote');
+               command_ok(
+                       [   'pg_rewind',       "--debug",
+                               "--source-server", $standby_connstr,
+                               "--target-pgdata=$master_pgdata" ],
+                       'pg_rewind remote');
        }
        else
        {
@@ -301,21 +245,21 @@ sub run_pg_rewind
        }
 
        # Now move back postgresql.conf with old settings
-       move("$tmp_check/master-postgresql.conf.tmp",
-                "$test_master_datadir/postgresql.conf");
+       move(
+               "$tmp_folder/master-postgresql.conf.tmp",
+               "$master_pgdata/postgresql.conf");
 
        # Plug-in rewound node to the now-promoted standby node
-       append_to_file(
-               "$test_master_datadir/recovery.conf", qq(
+       my $port_standby = $node_standby->port;
+       $node_master->append_conf(
+               'recovery.conf', qq(
 primary_conninfo='port=$port_standby'
 standby_mode=on
 recovery_target_timeline='latest'
 ));
 
        # Restart the master to check that rewind went correctly
-       system_or_bail('pg_ctl', '-w', '-D', $test_master_datadir,
-                                  '-l', "$log_path/master.log",
-                                  '-o', "-p $port_master", 'start');
+       $node_master->start;
 
        #### Now run the test-specific parts to check the result
 }
@@ -323,22 +267,8 @@ recovery_target_timeline='latest'
 # Clean up after the test. Stop both servers, if they're still running.
 sub clean_rewind_test
 {
-       if ($test_master_datadir)
-       {
-               system
-                 'pg_ctl', '-D', $test_master_datadir, '-m', 'immediate', 'stop';
-       }
-       if ($test_standby_datadir)
-       {
-               system
-                 'pg_ctl', '-D', $test_standby_datadir, '-m', 'immediate', 'stop';
-       }
+       $node_master->teardown_node  if defined $node_master;
+       $node_standby->teardown_node if defined $node_standby;
 }
 
-# Stop the test servers, just in case they're still running.
-END
-{
-       my $save_rc = $?;
-       clean_rewind_test();
-       $? = $save_rc;
-}
+1;
index d317f53186bf6362192f40febdd9be90babca104..cedde1409bc9c278264dcb85a298174997637221 100644 (file)
@@ -17,7 +17,7 @@ sub run_test
        RewindTest::setup_cluster();
        RewindTest::start_master();
 
-       my $test_master_datadir = $RewindTest::test_master_datadir;
+       my $test_master_datadir = $node_master->data_dir;
 
        # Create a subdir and files that will be present in both
        mkdir "$test_master_datadir/tst_both_dir";
@@ -30,6 +30,7 @@ sub run_test
        RewindTest::create_standby();
 
        # Create different subdirs and files in master and standby
+       my $test_standby_datadir = $node_standby->data_dir;
 
        mkdir "$test_standby_datadir/tst_standby_dir";
        append_to_file "$test_standby_datadir/tst_standby_dir/standby_file1",
index c5f72e2e3cde4807c5f1e1d6df5974f6419a750b..bdcab5688b9b7c3b825a208bdc3be6d635c7fe8d 100644 (file)
@@ -23,11 +23,13 @@ sub run_test
 {
        my $test_mode = shift;
 
-       my $master_xlogdir = "$tmp_check/xlog_master";
+       my $master_xlogdir = "${TestLib::tmp_check}/xlog_master";
 
        rmtree($master_xlogdir);
        RewindTest::setup_cluster();
 
+       my $test_master_datadir = $node_master->data_dir;
+
        # turn pg_xlog into a symlink
        print("moving $test_master_datadir/pg_xlog to $master_xlogdir\n");
        move("$test_master_datadir/pg_xlog", $master_xlogdir) or die;
index dc0d78a27d374edecf3682c3e19ae16ec8721fdd..5131b35d82b460b05d6d993658c3be13c66dec93 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 13;
 
@@ -7,20 +9,22 @@ program_help_ok('clusterdb');
 program_version_ok('clusterdb');
 program_options_handling_ok('clusterdb');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-issues_sql_like(
-       [ 'clusterdb', 'postgres' ],
+$node->issues_sql_like(
+       ['clusterdb'],
        qr/statement: CLUSTER;/,
        'SQL CLUSTER run');
 
-command_fails([ 'clusterdb', '-t', 'nonexistent', 'postgres' ],
+$node->command_fails([ 'clusterdb', '-t', 'nonexistent' ],
        'fails with nonexistent table');
 
-psql 'postgres',
-'CREATE TABLE test1 (a int); CREATE INDEX test1x ON test1 (a); CLUSTER test1 USING test1x';
-issues_sql_like(
-       [ 'clusterdb', '-t', 'test1', 'postgres' ],
+$node->psql('postgres',
+'CREATE TABLE test1 (a int); CREATE INDEX test1x ON test1 (a); CLUSTER test1 USING test1x'
+);
+$node->issues_sql_like(
+       [ 'clusterdb', '-t', 'test1' ],
        qr/statement: CLUSTER test1;/,
        'cluster specific table');
index 7769f70bb153ed7be4a4ded9550b6b04204783c8..15cd30cf4a1f22f96ae366b69640002120d5dacc 100644 (file)
@@ -1,12 +1,19 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 2;
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
+
+# clusterdb -a is not compatible with -d, hence enforce environment variable
+# correctly.
+$ENV{PGDATABASE} = 'postgres';
 
-issues_sql_like(
+$node->issues_sql_like(
        [ 'clusterdb', '-a' ],
        qr/statement: CLUSTER.*statement: CLUSTER/s,
        'cluster all databases');
index a44283c9458e90e5df6802e5e4024d274c656c29..e0cf860058577e5df30da3b6fc2a8a03ef0088d4 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 13;
 
@@ -7,16 +9,18 @@ program_help_ok('createdb');
 program_version_ok('createdb');
 program_options_handling_ok('createdb');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-issues_sql_like(
+$node->issues_sql_like(
        [ 'createdb', 'foobar1' ],
        qr/statement: CREATE DATABASE foobar1/,
        'SQL CREATE DATABASE run');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'createdb', '-l', 'C', '-E', 'LATIN1', '-T', 'template0', 'foobar2' ],
        qr/statement: CREATE DATABASE foobar2 ENCODING 'LATIN1'/,
        'create database with encoding');
 
-command_fails([ 'createdb', 'foobar1' ], 'fails if database already exists');
+$node->command_fails([ 'createdb', 'foobar1' ],
+       'fails if database already exists');
index 7ff0a3ed38d2ce43533275401d3e295284c17fad..4097f03da99790800bf7a099f405bff34d6857e6 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 14;
 
@@ -7,18 +9,17 @@ program_help_ok('createlang');
 program_version_ok('createlang');
 program_options_handling_ok('createlang');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-command_fails(
-       [ 'createlang', 'plpgsql', 'postgres' ],
+$node->command_fails([ 'createlang', 'plpgsql' ],
        'fails if language already exists');
 
-psql 'postgres', 'DROP EXTENSION plpgsql';
-issues_sql_like(
-       [ 'createlang', 'plpgsql', 'postgres' ],
+$node->psql('postgres', 'DROP EXTENSION plpgsql');
+$node->issues_sql_like(
+       [ 'createlang', 'plpgsql' ],
        qr/statement: CREATE EXTENSION "plpgsql"/,
        'SQL CREATE EXTENSION run');
 
-command_like([ 'createlang', '--list', 'postgres' ],
-       qr/plpgsql/, 'list output');
+$node->command_like([ 'createlang', '--list' ], qr/plpgsql/, 'list output');
index 4d44e14b7cf2d4a51e0ef78d700667fad471d68b..fcada6338c6a1cc41b68c79cda5c66ec9920f237 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 17;
 
@@ -7,24 +9,26 @@ program_help_ok('createuser');
 program_version_ok('createuser');
 program_options_handling_ok('createuser');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-issues_sql_like(
+$node->issues_sql_like(
        [ 'createuser', 'user1' ],
 qr/statement: CREATE ROLE user1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN;/,
        'SQL CREATE USER run');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'createuser', '-L', 'role1' ],
 qr/statement: CREATE ROLE role1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN;/,
        'create a non-login role');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'createuser', '-r', 'user2' ],
 qr/statement: CREATE ROLE user2 NOSUPERUSER NOCREATEDB CREATEROLE INHERIT LOGIN;/,
        'create a CREATEROLE user');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'createuser', '-s', 'user3' ],
 qr/statement: CREATE ROLE user3 SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN;/,
        'create a superuser');
 
-command_fails([ 'createuser', 'user1' ], 'fails if role already exists');
+$node->command_fails([ 'createuser', 'user1' ],
+       'fails if role already exists');
index 3065e5051df77b8f20196c69be86985c61a2c2db..2adc80a03f7234c78c9b3e3b1efb4a22ea1591f4 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 11;
 
@@ -7,13 +9,15 @@ program_help_ok('dropdb');
 program_version_ok('dropdb');
 program_options_handling_ok('dropdb');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-psql 'postgres', 'CREATE DATABASE foobar1';
-issues_sql_like(
+$node->psql('postgres', 'CREATE DATABASE foobar1');
+$node->issues_sql_like(
        [ 'dropdb', 'foobar1' ],
        qr/statement: DROP DATABASE foobar1/,
        'SQL DROP DATABASE run');
 
-command_fails([ 'dropdb', 'nonexistent' ], 'fails with nonexistent database');
+$node->command_fails([ 'dropdb', 'nonexistent' ],
+       'fails with nonexistent database');
index 6a21d7e33d8cea1da7d260661ac5b7e013a67da9..722804747ede21ad5dcaf38ec3750e14eb9c012c 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 11;
 
@@ -7,14 +9,15 @@ program_help_ok('droplang');
 program_version_ok('droplang');
 program_options_handling_ok('droplang');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-issues_sql_like(
+$node->issues_sql_like(
        [ 'droplang', 'plpgsql', 'postgres' ],
        qr/statement: DROP EXTENSION "plpgsql"/,
        'SQL DROP EXTENSION run');
 
-command_fails(
+$node->command_fails(
        [ 'droplang', 'nonexistent', 'postgres' ],
        'fails with nonexistent language');
index bbb3b7922a46620b21fcb24daab92540b74d689f..0849f77ed672fcfbed5bcb1d1c87aff7109675f4 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 11;
 
@@ -7,13 +9,15 @@ program_help_ok('dropuser');
 program_version_ok('dropuser');
 program_options_handling_ok('dropuser');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-psql 'postgres', 'CREATE ROLE foobar1';
-issues_sql_like(
+$node->psql('postgres', 'CREATE ROLE foobar1');
+$node->issues_sql_like(
        [ 'dropuser', 'foobar1' ],
        qr/statement: DROP ROLE foobar1/,
        'SQL DROP ROLE run');
 
-command_fails([ 'dropuser', 'nonexistent' ], 'fails with nonexistent user');
+$node->command_fails([ 'dropuser', 'nonexistent' ],
+       'fails with nonexistent user');
index f432505d5cd390a549351244a0c6665ea8864aae..8f3f25cc3643863fa9b262b5d72c82d72df09236 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 10;
 
@@ -9,7 +11,8 @@ program_options_handling_ok('pg_isready');
 
 command_fails(['pg_isready'], 'fails with no server running');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-command_ok(['pg_isready'], 'succeeds with server running');
+$node->command_ok(['pg_isready'], 'succeeds with server running');
index 42628c25e2b9dc00ea4ea182dc97a1f2b8ff67b7..fd4eac347e1647cb7792f75b29323d8448f8f912 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 20;
 
@@ -7,35 +9,36 @@ program_help_ok('reindexdb');
 program_version_ok('reindexdb');
 program_options_handling_ok('reindexdb');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
 $ENV{PGOPTIONS} = '--client-min-messages=WARNING';
 
-issues_sql_like(
+$node->issues_sql_like(
        [ 'reindexdb', 'postgres' ],
        qr/statement: REINDEX DATABASE postgres;/,
        'SQL REINDEX run');
 
-psql 'postgres',
-  'CREATE TABLE test1 (a int); CREATE INDEX test1x ON test1 (a);';
-issues_sql_like(
+$node->psql('postgres',
+       'CREATE TABLE test1 (a int); CREATE INDEX test1x ON test1 (a);');
+$node->issues_sql_like(
        [ 'reindexdb', '-t', 'test1', 'postgres' ],
        qr/statement: REINDEX TABLE test1;/,
        'reindex specific table');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'reindexdb', '-i', 'test1x', 'postgres' ],
        qr/statement: REINDEX INDEX test1x;/,
        'reindex specific index');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'reindexdb', '-S', 'pg_catalog', 'postgres' ],
        qr/statement: REINDEX SCHEMA pg_catalog;/,
        'reindex specific schema');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'reindexdb', '-s', 'postgres' ],
        qr/statement: REINDEX SYSTEM postgres;/,
        'reindex system tables');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'reindexdb', '-v', '-t', 'test1', 'postgres' ],
        qr/statement: REINDEX \(VERBOSE\) TABLE test1;/,
        'reindex with verbose output');
index ffadf29bc65679467fa2f6a8a9a99a38ca69c123..d47b18b98926a468cc089ca40d330a31b1232ec3 100644 (file)
@@ -1,14 +1,16 @@
 use strict;
 use warnings;
-use TestLib;
+
+use PostgresNode;
 use Test::More tests => 2;
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
 $ENV{PGOPTIONS} = '--client-min-messages=WARNING';
 
-issues_sql_like(
+$node->issues_sql_like(
        [ 'reindexdb', '-a' ],
        qr/statement: REINDEX.*statement: REINDEX/s,
        'reindex all databases');
index ac160ba8374502c095caa8540acf1f10a942bc06..387d2b41e2309ee80334e329910221d06692bd42 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+
+use PostgresNode;
 use TestLib;
 use Test::More tests => 18;
 
@@ -7,26 +9,27 @@ program_help_ok('vacuumdb');
 program_version_ok('vacuumdb');
 program_options_handling_ok('vacuumdb');
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-issues_sql_like(
+$node->issues_sql_like(
        [ 'vacuumdb', 'postgres' ],
        qr/statement: VACUUM;/,
        'SQL VACUUM run');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'vacuumdb', '-f', 'postgres' ],
        qr/statement: VACUUM \(FULL\);/,
        'vacuumdb -f');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'vacuumdb', '-F', 'postgres' ],
        qr/statement: VACUUM \(FREEZE\);/,
        'vacuumdb -F');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'vacuumdb', '-z', 'postgres' ],
        qr/statement: VACUUM \(ANALYZE\);/,
        'vacuumdb -z');
-issues_sql_like(
+$node->issues_sql_like(
        [ 'vacuumdb', '-Z', 'postgres' ],
        qr/statement: ANALYZE;/,
        'vacuumdb -Z');
index e90f321d1eb92fb805a64159876bc28af8a14393..8f1536f44feb97496c773327691707c004317988 100644 (file)
@@ -1,12 +1,14 @@
 use strict;
 use warnings;
-use TestLib;
+
+use PostgresNode;
 use Test::More tests => 2;
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-issues_sql_like(
+$node->issues_sql_like(
        [ 'vacuumdb', '-a' ],
        qr/statement: VACUUM.*statement: VACUUM/s,
        'vacuum all databases');
index 57b980ec6a56b3d3711ddc64d6a456d11246da48..4cb5b64877d07ae772975f1249ebc9e2977ffd4a 100644 (file)
@@ -1,12 +1,14 @@
 use strict;
 use warnings;
-use TestLib;
+
+use PostgresNode;
 use Test::More tests => 4;
 
-my $tempdir = tempdir;
-start_test_server $tempdir;
+my $node = get_new_node();
+$node->init;
+$node->start;
 
-issues_sql_like(
+$node->issues_sql_like(
        [ 'vacuumdb', '--analyze-in-stages', 'postgres' ],
 qr/.*statement:\ SET\ default_statistics_target=1;\ SET\ vacuum_cost_delay=0;
                    .*statement:\ ANALYZE.*
@@ -16,8 +18,7 @@ qr/.*statement:\ SET\ default_statistics_target=1;\ SET\ vacuum_cost_delay=0;
                    .*statement:\ ANALYZE/sx,
        'analyze three times');
 
-
-issues_sql_like(
+$node->issues_sql_like(
        [ 'vacuumdb', '--analyze-in-stages', '--all' ],
 qr/.*statement:\ SET\ default_statistics_target=1;\ SET\ vacuum_cost_delay=0;
                    .*statement:\ ANALYZE.*
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
new file mode 100644 (file)
index 0000000..aa7a00c
--- /dev/null
@@ -0,0 +1,470 @@
+# PostgresNode, class representing a data directory and postmaster.
+#
+# This contains a basic set of routines able to work on a PostgreSQL node,
+# allowing to start, stop, backup and initialize it with various options.
+# The set of nodes managed by a given test is also managed by this module.
+
+package PostgresNode;
+
+use strict;
+use warnings;
+
+use Config;
+use Cwd;
+use Exporter 'import';
+use File::Basename;
+use File::Spec;
+use File::Temp ();
+use IPC::Run;
+use PostgresNode;
+use RecursiveCopy;
+use Test::More;
+use TestLib ();
+
+our @EXPORT = qw(
+  get_new_node
+);
+
+our ($test_pghost, $last_port_assigned, @all_nodes);
+
+BEGIN
+{
+
+       # PGHOST is set once and for all through a single series of tests when
+       # this module is loaded.
+       $test_pghost =
+         $TestLib::windows_os ? "127.0.0.1" : TestLib::tempdir_short;
+       $ENV{PGHOST}     = $test_pghost;
+       $ENV{PGDATABASE} = 'postgres';
+
+       # Tracking of last port value assigned to accelerate free port lookup.
+       # XXX: Should this use PG_VERSION_NUM?
+       $last_port_assigned = 90600 % 16384 + 49152;
+
+       # Node tracking
+       @all_nodes = ();
+}
+
+sub new
+{
+       my $class  = shift;
+       my $pghost = shift;
+       my $pgport = shift;
+       my $self   = {
+               _port     => $pgport,
+               _host     => $pghost,
+               _basedir  => TestLib::tempdir,
+               _applname => "node_$pgport",
+               _logfile  => "$TestLib::log_path/node_$pgport.log" };
+
+       bless $self, $class;
+       $self->dump_info;
+
+       return $self;
+}
+
+sub port
+{
+       my ($self) = @_;
+       return $self->{_port};
+}
+
+sub host
+{
+       my ($self) = @_;
+       return $self->{_host};
+}
+
+sub basedir
+{
+       my ($self) = @_;
+       return $self->{_basedir};
+}
+
+sub applname
+{
+       my ($self) = @_;
+       return $self->{_applname};
+}
+
+sub logfile
+{
+       my ($self) = @_;
+       return $self->{_logfile};
+}
+
+sub connstr
+{
+       my ($self, $dbname) = @_;
+       my $pgport = $self->port;
+       my $pghost = $self->host;
+       if (!defined($dbname))
+       {
+               return "port=$pgport host=$pghost";
+       }
+       return "port=$pgport host=$pghost dbname=$dbname";
+}
+
+sub data_dir
+{
+       my ($self) = @_;
+       my $res = $self->basedir;
+       return "$res/pgdata";
+}
+
+sub archive_dir
+{
+       my ($self) = @_;
+       my $basedir = $self->basedir;
+       return "$basedir/archives";
+}
+
+sub backup_dir
+{
+       my ($self) = @_;
+       my $basedir = $self->basedir;
+       return "$basedir/backup";
+}
+
+# Dump node information
+sub dump_info
+{
+       my ($self) = @_;
+       print "Data directory: " . $self->data_dir . "\n";
+       print "Backup directory: " . $self->backup_dir . "\n";
+       print "Archive directory: " . $self->archive_dir . "\n";
+       print "Connection string: " . $self->connstr . "\n";
+       print "Application name: " . $self->applname . "\n";
+       print "Log file: " . $self->logfile . "\n";
+}
+
+sub set_replication_conf
+{
+       my ($self) = @_;
+       my $pgdata = $self->data_dir;
+
+       open my $hba, ">>$pgdata/pg_hba.conf";
+       print $hba "\n# Allow replication (set up by PostgresNode.pm)\n";
+       if (!$TestLib::windows_os)
+       {
+               print $hba "local replication all trust\n";
+       }
+       else
+       {
+               print $hba
+"host replication all 127.0.0.1/32 sspi include_realm=1 map=regress\n";
+       }
+       close $hba;
+}
+
+# Initialize a new cluster for testing.
+#
+# Authentication is set up so that only the current OS user can access the
+# cluster. On Unix, we use Unix domain socket connections, with the socket in
+# a directory that's only accessible to the current user to ensure that.
+# On Windows, we use SSPI authentication to ensure the same (by pg_regress
+# --config-auth).
+sub init
+{
+       my ($self, %params) = @_;
+       my $port   = $self->port;
+       my $pgdata = $self->data_dir;
+       my $host   = $self->host;
+
+       $params{hba_permit_replication} = 1
+         if (!defined($params{hba_permit_replication}));
+
+       mkdir $self->backup_dir;
+       mkdir $self->archive_dir;
+
+       TestLib::system_or_bail('initdb', '-D', $pgdata, '-A', 'trust', '-N');
+       TestLib::system_or_bail($ENV{PG_REGRESS}, '--config-auth', $pgdata);
+
+       open my $conf, ">>$pgdata/postgresql.conf";
+       print $conf "\n# Added by PostgresNode.pm)\n";
+       print $conf "fsync = off\n";
+       print $conf "log_statement = all\n";
+       print $conf "port = $port\n";
+       if ($TestLib::windows_os)
+       {
+               print $conf "listen_addresses = '$host'\n";
+       }
+       else
+       {
+               print $conf "unix_socket_directories = '$host'\n";
+               print $conf "listen_addresses = ''\n";
+       }
+       close $conf;
+
+       $self->set_replication_conf if ($params{hba_permit_replication});
+}
+
+sub append_conf
+{
+       my ($self, $filename, $str) = @_;
+
+       my $conffile = $self->data_dir . '/' . $filename;
+
+       TestLib::append_to_file($conffile, $str);
+}
+
+sub backup
+{
+       my ($self, $backup_name) = @_;
+       my $backup_path = $self->backup_dir . '/' . $backup_name;
+       my $port        = $self->port;
+
+       print "# Taking backup $backup_name from node with port $port\n";
+       TestLib::system_or_bail("pg_basebackup -D $backup_path -p $port -x");
+       print "# Backup finished\n";
+}
+
+sub init_from_backup
+{
+       my ($self, $root_node, $backup_name) = @_;
+       my $backup_path = $root_node->backup_dir . '/' . $backup_name;
+       my $port        = $self->port;
+       my $root_port   = $root_node->port;
+
+       print
+"Initializing node $port from backup \"$backup_name\" of node $root_port\n";
+       die "Backup $backup_path does not exist" unless -d $backup_path;
+
+       mkdir $self->backup_dir;
+       mkdir $self->archive_dir;
+
+       my $data_path = $self->data_dir;
+       rmdir($data_path);
+       RecursiveCopy::copypath($backup_path, $data_path);
+       chmod(0700, $data_path);
+
+       # Base configuration for this node
+       $self->append_conf(
+               'postgresql.conf',
+               qq(
+port = $port
+));
+       $self->set_replication_conf;
+}
+
+sub start
+{
+       my ($self) = @_;
+       my $port   = $self->port;
+       my $pgdata = $self->data_dir;
+       print("### Starting test server in $pgdata\n");
+       my $ret = TestLib::system_log('pg_ctl', '-w', '-D', $self->data_dir, '-l',
+               $self->logfile, 'start');
+
+       if ($ret != 0)
+       {
+               print "# pg_ctl failed; logfile:\n";
+               print TestLib::slurp_file($self->logfile);
+               BAIL_OUT("pg_ctl failed");
+       }
+
+       $self->_update_pid;
+
+}
+
+sub stop
+{
+       my ($self, $mode) = @_;
+       my $port   = $self->port;
+       my $pgdata = $self->data_dir;
+       $mode = 'fast' if (!defined($mode));
+       print "### Stopping node in $pgdata with port $port using mode $mode\n";
+       TestLib::system_log('pg_ctl', '-D', $pgdata, '-m', $mode, 'stop');
+       $self->{_pid} = undef;
+       $self->_update_pid;
+}
+
+sub restart
+{
+       my ($self)  = @_;
+       my $port    = $self->port;
+       my $pgdata  = $self->data_dir;
+       my $logfile = $self->logfile;
+       TestLib::system_log('pg_ctl', '-D', $pgdata, '-w', '-l', $logfile,
+               'restart');
+       $self->_update_pid;
+}
+
+sub _update_pid
+{
+       my $self = shift;
+
+       # If we can open the PID file, read its first line and that's the PID we
+       # want.  If the file cannot be opened, presumably the server is not
+       # running; don't be noisy in that case.
+       open my $pidfile, $self->data_dir . "/postmaster.pid";
+       if (not defined $pidfile)
+       {
+               $self->{_pid} = undef;
+               print "# No postmaster PID\n";
+               return;
+       }
+
+       $self->{_pid} = <$pidfile>;
+       print "# Postmaster PID is $self->{_pid}\n";
+       close $pidfile;
+}
+
+#
+# Cluster management functions
+#
+
+# Build a new PostgresNode object, assigning a free port number.
+#
+# We also register the node, to avoid the port number from being reused
+# for another node even when this one is not active.
+sub get_new_node
+{
+       my $found = 0;
+       my $port  = $last_port_assigned;
+
+       while ($found == 0)
+       {
+               $port++;
+               print "# Checking for port $port\n";
+               my $devnull = $TestLib::windows_os ? "nul" : "/dev/null";
+               if (!TestLib::run_log([ 'pg_isready', '-p', $port ]))
+               {
+                       $found = 1;
+
+                       # Found a potential candidate port number.  Check first that it is
+                       # not included in the list of registered nodes.
+                       foreach my $node (@all_nodes)
+                       {
+                               $found = 0 if ($node->port == $port);
+                       }
+               }
+       }
+
+       print "# Found free port $port\n";
+
+       # Lock port number found by creating a new node
+       my $node = new PostgresNode($test_pghost, $port);
+
+       # Add node to list of nodes
+       push(@all_nodes, $node);
+
+       # And update port for next time
+       $last_port_assigned = $port;
+
+       return $node;
+}
+
+sub DESTROY
+{
+       my $self = shift;
+       return if not defined $self->{_pid};
+       print "# signalling QUIT to $self->{_pid}\n";
+       kill 'QUIT', $self->{_pid};
+}
+
+sub teardown_node
+{
+       my $self = shift;
+
+       $self->stop('immediate');
+}
+
+sub psql
+{
+       my ($self, $dbname, $sql) = @_;
+
+       my ($stdout, $stderr);
+       print("# Running SQL command: $sql\n");
+
+       IPC::Run::run [ 'psql', '-XAtq', '-d', $self->connstr($dbname), '-f',
+               '-' ], '<', \$sql, '>', \$stdout, '2>', \$stderr
+         or die;
+
+       if ($stderr ne "")
+       {
+               print "#### Begin standard error\n";
+               print $stderr;
+               print "#### End standard error\n";
+       }
+       chomp $stdout;
+       $stdout =~ s/\r//g if $Config{osname} eq 'msys';
+       return $stdout;
+}
+
+# Run a query once a second, until it returns 't' (i.e. SQL boolean true).
+sub poll_query_until
+{
+       my ($self, $dbname, $query) = @_;
+
+       my $max_attempts = 30;
+       my $attempts     = 0;
+       my ($stdout, $stderr);
+
+       while ($attempts < $max_attempts)
+       {
+               my $cmd =
+                 [ 'psql', '-At', '-c', $query, '-d', $self->connstr($dbname) ];
+               my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
+
+               chomp($stdout);
+               $stdout =~ s/\r//g if $Config{osname} eq 'msys';
+               if ($stdout eq "t")
+               {
+                       return 1;
+               }
+
+               # Wait a second before retrying.
+               sleep 1;
+               $attempts++;
+       }
+
+       # The query result didn't change in 30 seconds. Give up. Print the stderr
+       # from the last attempt, hopefully that's useful for debugging.
+       diag $stderr;
+       return 0;
+}
+
+sub command_ok
+{
+       my $self = shift;
+
+       local $ENV{PGPORT} = $self->port;
+
+       TestLib::command_ok(@_);
+}
+
+sub command_fails
+{
+       my $self = shift;
+
+       local $ENV{PGPORT} = $self->port;
+
+       TestLib::command_fails(@_);
+}
+
+sub command_like
+{
+       my $self = shift;
+
+       local $ENV{PGPORT} = $self->port;
+
+       TestLib::command_like(@_);
+}
+
+# Run a command on the node, then verify that $expected_sql appears in the
+# server log file.
+sub issues_sql_like
+{
+       my ($self, $cmd, $expected_sql, $test_name) = @_;
+
+       local $ENV{PGPORT} = $self->port;
+
+       truncate $self->logfile, 0;
+       my $result = TestLib::run_log($cmd);
+       ok($result, "@$cmd exit code 0");
+       my $log = TestLib::slurp_file($self->logfile);
+       like($log, $expected_sql, "$test_name: SQL found in server log");
+}
+
+1;
diff --git a/src/test/perl/RecursiveCopy.pm b/src/test/perl/RecursiveCopy.pm
new file mode 100644 (file)
index 0000000..9362aa8
--- /dev/null
@@ -0,0 +1,42 @@
+# RecursiveCopy, a simple recursive copy implementation
+package RecursiveCopy;
+
+use strict;
+use warnings;
+
+use File::Basename;
+use File::Copy;
+
+sub copypath
+{
+       my $srcpath  = shift;
+       my $destpath = shift;
+
+       die "Cannot operate on symlinks" if -l $srcpath or -l $destpath;
+
+       # This source path is a file, simply copy it to destination with the
+       # same name.
+       die "Destination path $destpath exists as file" if -f $destpath;
+       if (-f $srcpath)
+       {
+               copy($srcpath, $destpath)
+                 or die "copy $srcpath -> $destpath failed: $!";
+               return 1;
+       }
+
+       die "Destination needs to be a directory" unless -d $srcpath;
+       mkdir($destpath) or die "mkdir($destpath) failed: $!";
+
+       # Scan existing source directory and recursively copy everything.
+       opendir(my $directory, $srcpath) or die "could not opendir($srcpath): $!";
+       while (my $entry = readdir($directory))
+       {
+               next if ($entry eq '.' || $entry eq '..');
+               RecursiveCopy::copypath("$srcpath/$entry", "$destpath/$entry")
+                 or die "copypath $srcpath/$entry -> $destpath/$entry failed";
+       }
+       closedir($directory);
+       return 1;
+}
+
+1;
index 02533ebde538bae0a9b7938cb7358f378f9406d3..af46dc8c7a24060433596a50bed38502a1662ec9 100644 (file)
@@ -1,3 +1,10 @@
+# TestLib, low-level routines and actions regression tests.
+#
+# This module contains a set of routines dedicated to environment setup for
+# a PostgreSQL regression test tun, and includes some low-level routines
+# aimed at controlling command execution, logging and test functions. This
+# module should never depend on any other PostgreSQL regression test modules.
+
 package TestLib;
 
 use strict;
@@ -5,16 +12,17 @@ use warnings;
 
 use Config;
 use Exporter 'import';
+use File::Basename;
+use File::Spec;
+use File::Temp ();
+use IPC::Run;
+use SimpleTee;
+use Test::More;
+
 our @EXPORT = qw(
-  tempdir
-  tempdir_short
-  standard_initdb
-  configure_hba_for_replication
-  start_test_server
-  restart_test_server
-  psql
   slurp_dir
   slurp_file
+  append_to_file
   system_or_bail
   system_log
   run_log
@@ -26,88 +34,82 @@ our @EXPORT = qw(
   program_version_ok
   program_options_handling_ok
   command_like
-  issues_sql_like
 
-  $tmp_check
-  $log_path
   $windows_os
 );
 
-use Cwd;
-use File::Basename;
-use File::Spec;
-use File::Temp ();
-use IPC::Run qw(run start);
+our ($windows_os, $tmp_check, $log_path, $test_logfile);
 
-use SimpleTee;
-
-use Test::More;
-
-our $windows_os = $Config{osname} eq 'MSWin32' || $Config{osname} eq 'msys';
-
-# Open log file. For each test, the log file name uses the name of the
-# file launching this module, without the .pl suffix.
-our ($tmp_check, $log_path);
-$tmp_check = $ENV{TESTDIR} ? "$ENV{TESTDIR}/tmp_check" : "tmp_check";
-$log_path = "$tmp_check/log";
-mkdir $tmp_check;
-mkdir $log_path;
-my $test_logfile = basename($0);
-$test_logfile =~ s/\.[^.]+$//;
-$test_logfile = "$log_path/regress_log_$test_logfile";
-open TESTLOG, '>', $test_logfile or die "Cannot open STDOUT to logfile: $!";
-
-# Hijack STDOUT and STDERR to the log file
-open(ORIG_STDOUT, ">&STDOUT");
-open(ORIG_STDERR, ">&STDERR");
-open(STDOUT, ">&TESTLOG");
-open(STDERR, ">&TESTLOG");
-
-# The test output (ok ...) needs to be printed to the original STDOUT so
-# that the 'prove' program can parse it, and display it to the user in
-# real time. But also copy it to the log file, to provide more context
-# in the log.
-my $builder = Test::More->builder;
-my $fh = $builder->output;
-tie *$fh, "SimpleTee", *ORIG_STDOUT, *TESTLOG;
-$fh = $builder->failure_output;
-tie *$fh, "SimpleTee", *ORIG_STDERR, *TESTLOG;
-
-# Enable auto-flushing for all the file handles. Stderr and stdout are
-# redirected to the same file, and buffering causes the lines to appear
-# in the log in confusing order.
-autoflush STDOUT 1;
-autoflush STDERR 1;
-autoflush TESTLOG 1;
-
-# Set to untranslated messages, to be able to compare program output
-# with expected strings.
-delete $ENV{LANGUAGE};
-delete $ENV{LC_ALL};
-$ENV{LC_MESSAGES} = 'C';
-
-delete $ENV{PGCONNECT_TIMEOUT};
-delete $ENV{PGDATA};
-delete $ENV{PGDATABASE};
-delete $ENV{PGHOSTADDR};
-delete $ENV{PGREQUIRESSL};
-delete $ENV{PGSERVICE};
-delete $ENV{PGSSLMODE};
-delete $ENV{PGUSER};
-
-if (!$ENV{PGPORT})
+BEGIN
 {
-       $ENV{PGPORT} = 65432;
+
+       # Set to untranslated messages, to be able to compare program output
+       # with expected strings.
+       delete $ENV{LANGUAGE};
+       delete $ENV{LC_ALL};
+       $ENV{LC_MESSAGES} = 'C';
+
+       delete $ENV{PGCONNECT_TIMEOUT};
+       delete $ENV{PGDATA};
+       delete $ENV{PGDATABASE};
+       delete $ENV{PGHOSTADDR};
+       delete $ENV{PGREQUIRESSL};
+       delete $ENV{PGSERVICE};
+       delete $ENV{PGSSLMODE};
+       delete $ENV{PGUSER};
+       delete $ENV{PGPORT};
+       delete $ENV{PGHOST};
+
+       # Must be set early
+       $windows_os = $Config{osname} eq 'MSWin32' || $Config{osname} eq 'msys';
 }
 
-$ENV{PGPORT} = int($ENV{PGPORT}) % 65536;
+INIT
+{
 
+       # Determine output directories, and create them.  The base path is the
+       # TESTDIR environment variable, which is normally set by the invoking
+       # Makefile.
+       $tmp_check = $ENV{TESTDIR} ? "$ENV{TESTDIR}/tmp_check" : "tmp_check";
+       $log_path = "$tmp_check/log";
+
+       mkdir $tmp_check;
+       mkdir $log_path;
+
+       # Open the test log file, whose name depends on the test name.
+       $test_logfile = basename($0);
+       $test_logfile =~ s/\.[^.]+$//;
+       $test_logfile = "$log_path/regress_log_$test_logfile";
+       open TESTLOG, '>', $test_logfile
+         or die "could not open STDOUT to logfile \"$test_logfile\": $!";
+
+       # Hijack STDOUT and STDERR to the log file
+       open(ORIG_STDOUT, ">&STDOUT");
+       open(ORIG_STDERR, ">&STDERR");
+       open(STDOUT,      ">&TESTLOG");
+       open(STDERR,      ">&TESTLOG");
+
+       # The test output (ok ...) needs to be printed to the original STDOUT so
+       # that the 'prove' program can parse it, and display it to the user in
+       # real time. But also copy it to the log file, to provide more context
+       # in the log.
+       my $builder = Test::More->builder;
+       my $fh      = $builder->output;
+       tie *$fh, "SimpleTee", *ORIG_STDOUT, *TESTLOG;
+       $fh = $builder->failure_output;
+       tie *$fh, "SimpleTee", *ORIG_STDERR, *TESTLOG;
+
+       # Enable auto-flushing for all the file handles. Stderr and stdout are
+       # redirected to the same file, and buffering causes the lines to appear
+       # in the log in confusing order.
+       autoflush STDOUT 1;
+       autoflush STDERR 1;
+       autoflush TESTLOG 1;
+}
 
 #
 # Helper functions
 #
-
-
 sub tempdir
 {
        return File::Temp::tempdir(
@@ -124,117 +126,31 @@ sub tempdir_short
        return File::Temp::tempdir(CLEANUP => 1);
 }
 
-# Initialize a new cluster for testing.
-#
-# The PGHOST environment variable is set to connect to the new cluster.
-#
-# Authentication is set up so that only the current OS user can access the
-# cluster. On Unix, we use Unix domain socket connections, with the socket in
-# a directory that's only accessible to the current user to ensure that.
-# On Windows, we use SSPI authentication to ensure the same (by pg_regress
-# --config-auth).
-sub standard_initdb
-{
-       my $pgdata = shift;
-       system_or_bail('initdb', '-D', "$pgdata", '-A' , 'trust', '-N');
-       system_or_bail($ENV{PG_REGRESS}, '--config-auth', $pgdata);
-
-       my $tempdir_short = tempdir_short;
-
-       open CONF, ">>$pgdata/postgresql.conf";
-       print CONF "\n# Added by TestLib.pm)\n";
-       print CONF "fsync = off\n";
-       if ($windows_os)
-       {
-               print CONF "listen_addresses = '127.0.0.1'\n";
-       }
-       else
-       {
-               print CONF "unix_socket_directories = '$tempdir_short'\n";
-               print CONF "listen_addresses = ''\n";
-       }
-       close CONF;
-
-       $ENV{PGHOST}         = $windows_os ? "127.0.0.1" : $tempdir_short;
-}
-
-# Set up the cluster to allow replication connections, in the same way that
-# standard_initdb does for normal connections.
-sub configure_hba_for_replication
-{
-       my $pgdata = shift;
-
-       open HBA, ">>$pgdata/pg_hba.conf";
-       print HBA "\n# Allow replication (set up by TestLib.pm)\n";
-       if (! $windows_os)
-       {
-               print HBA "local replication all trust\n";
-       }
-       else
-       {
-               print HBA "host replication all 127.0.0.1/32 sspi include_realm=1 map=regress\n";
-       }
-       close HBA;
-}
-
-my ($test_server_datadir, $test_server_logfile);
-
-
-# Initialize a new cluster for testing in given directory, and start it.
-sub start_test_server
-{
-       my ($tempdir) = @_;
-       my $ret;
-
-       print("### Starting test server in $tempdir\n");
-       standard_initdb "$tempdir/pgdata";
-
-       $ret = system_log('pg_ctl', '-D', "$tempdir/pgdata", '-w', '-l',
-         "$log_path/postmaster.log", '-o', "--log-statement=all",
-         'start');
-
-       if ($ret != 0)
-       {
-               print "# pg_ctl failed; logfile:\n";
-               system('cat', "$log_path/postmaster.log");
-               BAIL_OUT("pg_ctl failed");
-       }
-
-       $test_server_datadir = "$tempdir/pgdata";
-       $test_server_logfile = "$log_path/postmaster.log";
-}
-
-sub restart_test_server
+sub system_log
 {
-       print("### Restarting test server\n");
-       system_log('pg_ctl', '-D', $test_server_datadir, '-w', '-l',
-         $test_server_logfile, 'restart');
+       print("# Running: " . join(" ", @_) . "\n");
+       return system(@_);
 }
 
-END
+sub system_or_bail
 {
-       if ($test_server_datadir)
+       if (system_log(@_) != 0)
        {
-               system_log('pg_ctl', '-D', $test_server_datadir, '-m',
-                 'immediate', 'stop');
+               BAIL_OUT("system $_[0] failed");
        }
 }
 
-sub psql
+sub run_log
 {
-       my ($dbname, $sql) = @_;
-       my ($stdout, $stderr);
-       print("# Running SQL command: $sql\n");
-       run [ 'psql', '-X', '-A', '-t', '-q', '-d', $dbname, '-f', '-' ], '<', \$sql, '>', \$stdout, '2>', \$stderr or die;
-       chomp $stdout;
-       $stdout =~ s/\r//g if $Config{osname} eq 'msys';
-       return $stdout;
+       print("# Running: " . join(" ", @{ $_[0] }) . "\n");
+       return run(@_);
 }
 
 sub slurp_dir
 {
        my ($dir) = @_;
-       opendir(my $dh, $dir) or die;
+       opendir(my $dh, $dir)
+         or die "could not opendir \"$dir\": $!";
        my @direntries = readdir $dh;
        closedir $dh;
        return @direntries;
@@ -249,32 +165,18 @@ sub slurp_file
        return $contents;
 }
 
-sub system_or_bail
-{
-       if (system_log(@_) != 0)
-       {
-               BAIL_OUT("system $_[0] failed: $?");
-       }
-}
-
-sub system_log
+sub append_to_file
 {
-       print("# Running: " . join(" ", @_) ."\n");
-       return system(@_);
-}
+       my ($filename, $str) = @_;
 
-sub run_log
-{
-       print("# Running: " . join(" ", @{$_[0]}) ."\n");
-       return run (@_);
+       open my $fh, ">>", $filename or die "could not open \"$filename\": $!";
+       print $fh $str;
+       close $fh;
 }
 
-
 #
 # Test functions
 #
-
-
 sub command_ok
 {
        my ($cmd, $test_name) = @_;
@@ -292,8 +194,8 @@ sub command_fails
 sub command_exit_is
 {
        my ($cmd, $expected, $test_name) = @_;
-       print("# Running: " . join(" ", @{$cmd}) ."\n");
-       my $h = start $cmd;
+       print("# Running: " . join(" ", @{$cmd}) . "\n");
+       my $h = IPC::Run::start $cmd;
        $h->finish();
 
        # On Windows, the exit status of the process is returned directly as the
@@ -303,8 +205,10 @@ sub command_exit_is
        # assuming the Unix convention, which will always return 0 on Windows as
        # long as the process was not terminated by an exception. To work around
        # that, use $h->full_result on Windows instead.
-       my $result = ($Config{osname} eq "MSWin32") ?
-               ($h->full_results)[0] : $h->result(0);
+       my $result =
+           ($Config{osname} eq "MSWin32")
+         ? ($h->full_results)[0]
+         : $h->result(0);
        is($result, $expected, $test_name);
 }
 
@@ -313,7 +217,8 @@ sub program_help_ok
        my ($cmd) = @_;
        my ($stdout, $stderr);
        print("# Running: $cmd --help\n");
-       my $result = run [ $cmd, '--help' ], '>', \$stdout, '2>', \$stderr;
+       my $result = IPC::Run::run [ $cmd, '--help' ], '>', \$stdout, '2>',
+         \$stderr;
        ok($result, "$cmd --help exit code 0");
        isnt($stdout, '', "$cmd --help goes to stdout");
        is($stderr, '', "$cmd --help nothing to stderr");
@@ -324,7 +229,8 @@ sub program_version_ok
        my ($cmd) = @_;
        my ($stdout, $stderr);
        print("# Running: $cmd --version\n");
-       my $result = run [ $cmd, '--version' ], '>', \$stdout, '2>', \$stderr;
+       my $result = IPC::Run::run [ $cmd, '--version' ], '>', \$stdout, '2>',
+         \$stderr;
        ok($result, "$cmd --version exit code 0");
        isnt($stdout, '', "$cmd --version goes to stdout");
        is($stderr, '', "$cmd --version nothing to stderr");
@@ -335,8 +241,9 @@ sub program_options_handling_ok
        my ($cmd) = @_;
        my ($stdout, $stderr);
        print("# Running: $cmd --not-a-valid-option\n");
-       my $result = run [ $cmd, '--not-a-valid-option' ], '>', \$stdout, '2>',
-         \$stderr;
+       my $result = IPC::Run::run [ $cmd, '--not-a-valid-option' ], '>',
+         \$stdout,
+         '2>', \$stderr;
        ok(!$result, "$cmd with invalid option nonzero exit code");
        isnt($stderr, '', "$cmd with invalid option prints error message");
 }
@@ -346,20 +253,10 @@ sub command_like
        my ($cmd, $expected_stdout, $test_name) = @_;
        my ($stdout, $stderr);
        print("# Running: " . join(" ", @{$cmd}) . "\n");
-       my $result = run $cmd, '>', \$stdout, '2>', \$stderr;
+       my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
        ok($result, "@$cmd exit code 0");
        is($stderr, '', "@$cmd no stderr");
        like($stdout, $expected_stdout, "$test_name: matches");
 }
 
-sub issues_sql_like
-{
-       my ($cmd, $expected_sql, $test_name) = @_;
-       truncate $test_server_logfile, 0;
-       my $result = run_log($cmd);
-       ok($result, "@$cmd exit code 0");
-       my $log = slurp_file($test_server_logfile);
-       like($log, $expected_sql, "$test_name: SQL found in server log");
-}
-
 1;
index a6c77b5c80a5003da8ca59af89734e77e2d7d315..4e93184eb0357f747c50d229c6e07dbfe63e93b4 100644 (file)
@@ -18,6 +18,7 @@ package ServerSetup;
 
 use strict;
 use warnings;
+use PostgresNode;
 use TestLib;
 use File::Basename;
 use File::Copy;
@@ -45,17 +46,19 @@ sub copy_files
 
 sub configure_test_server_for_ssl
 {
-       my $tempdir    = $_[0];
+       my $node       = $_[0];
        my $serverhost = $_[1];
 
+       my $pgdata = $node->data_dir;
+
        # Create test users and databases
-       psql 'postgres', "CREATE USER ssltestuser";
-       psql 'postgres', "CREATE USER anotheruser";
-       psql 'postgres', "CREATE DATABASE trustdb";
-       psql 'postgres', "CREATE DATABASE certdb";
+       $node->psql('postgres', "CREATE USER ssltestuser");
+       $node->psql('postgres', "CREATE USER anotheruser");
+       $node->psql('postgres', "CREATE DATABASE trustdb");
+       $node->psql('postgres', "CREATE DATABASE certdb");
 
        # enable logging etc.
-       open CONF, ">>$tempdir/pgdata/postgresql.conf";
+       open CONF, ">>$pgdata/postgresql.conf";
        print CONF "fsync=off\n";
        print CONF "log_connections=on\n";
        print CONF "log_hostname=on\n";
@@ -68,17 +71,17 @@ sub configure_test_server_for_ssl
        close CONF;
 
 # Copy all server certificates and keys, and client root cert, to the data dir
-       copy_files("ssl/server-*.crt", "$tempdir/pgdata");
-       copy_files("ssl/server-*.key", "$tempdir/pgdata");
-       chmod(0600, glob "$tempdir/pgdata/server-*.key") or die $!;
-       copy_files("ssl/root+client_ca.crt", "$tempdir/pgdata");
-       copy_files("ssl/root+client.crl",    "$tempdir/pgdata");
+       copy_files("ssl/server-*.crt", $pgdata);
+       copy_files("ssl/server-*.key", $pgdata);
+       chmod(0600, glob "$pgdata/server-*.key") or die $!;
+       copy_files("ssl/root+client_ca.crt", $pgdata);
+       copy_files("ssl/root+client.crl",    $pgdata);
 
   # Only accept SSL connections from localhost. Our tests don't depend on this
   # but seems best to keep it as narrow as possible for security reasons.
   #
   # When connecting to certdb, also check the client certificate.
-       open HBA, ">$tempdir/pgdata/pg_hba.conf";
+       open HBA, ">$pgdata/pg_hba.conf";
        print HBA
 "# TYPE  DATABASE        USER            ADDRESS                 METHOD\n";
        print HBA
@@ -96,12 +99,13 @@ sub configure_test_server_for_ssl
 # the server so that the configuration takes effect.
 sub switch_server_cert
 {
-       my $tempdir  = $_[0];
+       my $node     = $_[0];
        my $certfile = $_[1];
+       my $pgdata   = $node->data_dir;
 
        diag "Restarting server with certfile \"$certfile\"...";
 
-       open SSLCONF, ">$tempdir/pgdata/sslconfig.conf";
+       open SSLCONF, ">$pgdata/sslconfig.conf";
        print SSLCONF "ssl=on\n";
        print SSLCONF "ssl_ca_file='root+client_ca.crt'\n";
        print SSLCONF "ssl_cert_file='$certfile.crt'\n";
@@ -110,5 +114,5 @@ sub switch_server_cert
        close SSLCONF;
 
        # Stop and restart server to reload the new config.
-       restart_test_server();
+       $node->restart;
 }
index 0d6f339335d3bfe4b4cb49a8480b9a6d01348a16..92f16e4a11daf5bb4c32e17c5e6ddc1178a28a51 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use warnings;
+use PostgresNode;
+use TestLib;
 use TestLib;
 use Test::More tests => 38;
 use ServerSetup;
@@ -25,8 +27,6 @@ BEGIN
 # postgresql-ssl-regression.test.
 my $SERVERHOSTADDR = '127.0.0.1';
 
-my $tempdir = TestLib::tempdir;
-
 # Define a couple of helper functions to test connecting to the server.
 
 my $common_connstr;
@@ -74,10 +74,17 @@ chmod 0600, "ssl/client.key";
 
 #### Part 0. Set up the server.
 
-diag "setting up data directory in \"$tempdir\"...";
-start_test_server($tempdir);
-configure_test_server_for_ssl($tempdir, $SERVERHOSTADDR);
-switch_server_cert($tempdir, 'server-cn-only');
+diag "setting up data directory...";
+my $node = get_new_node();
+$node->init;
+
+# PGHOST is enforced here to set up the node, subsequent connections
+# will use a dedicated connection string.
+$ENV{PGHOST} = $node->host;
+$ENV{PGPORT} = $node->port;
+$node->start;
+configure_test_server_for_ssl($node, $SERVERHOSTADDR);
+switch_server_cert($node, 'server-cn-only');
 
 ### Part 1. Run client-side tests.
 ###
@@ -150,7 +157,7 @@ test_connect_ok("sslmode=verify-ca host=wronghost.test");
 test_connect_fails("sslmode=verify-full host=wronghost.test");
 
 # Test Subject Alternative Names.
-switch_server_cert($tempdir, 'server-multiple-alt-names');
+switch_server_cert($node, 'server-multiple-alt-names');
 
 diag "test hostname matching with X509 Subject Alternative Names";
 $common_connstr =
@@ -165,7 +172,7 @@ test_connect_fails("host=deep.subdomain.wildcard.pg-ssltest.test");
 
 # Test certificate with a single Subject Alternative Name. (this gives a
 # slightly different error message, that's all)
-switch_server_cert($tempdir, 'server-single-alt-name');
+switch_server_cert($node, 'server-single-alt-name');
 
 diag "test hostname matching with a single X509 Subject Alternative Name";
 $common_connstr =
@@ -178,7 +185,7 @@ test_connect_fails("host=deep.subdomain.wildcard.pg-ssltest.test");
 
 # Test server certificate with a CN and SANs. Per RFCs 2818 and 6125, the CN
 # should be ignored when the certificate has both.
-switch_server_cert($tempdir, 'server-cn-and-alt-names');
+switch_server_cert($node, 'server-cn-and-alt-names');
 
 diag "test certificate with both a CN and SANs";
 $common_connstr =
@@ -190,7 +197,7 @@ test_connect_fails("host=common-name.pg-ssltest.test");
 
 # Finally, test a server certificate that has no CN or SANs. Of course, that's
 # not a very sensible certificate, but libpq should handle it gracefully.
-switch_server_cert($tempdir, 'server-no-names');
+switch_server_cert($node, 'server-no-names');
 $common_connstr =
 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR";
 
@@ -199,7 +206,7 @@ test_connect_fails("sslmode=verify-full host=common-name.pg-ssltest.test");
 
 # Test that the CRL works
 diag "Testing client-side CRL";
-switch_server_cert($tempdir, 'server-revoked');
+switch_server_cert($node, 'server-revoked');
 
 $common_connstr =
 "user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
@@ -233,7 +240,3 @@ test_connect_fails(
 test_connect_fails(
 "user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked.key"
 );
-
-
-# All done! Save the log, before the temporary installation is deleted
-copy("$tempdir/client-log", "./client-log");