From 7d0f493f19607774fdccb1a1ea06fdd96a3d9698 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Mon, 14 Apr 2014 21:33:46 -0400 Subject: [PATCH] Add TAP tests for client programs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Pavel Stěhule Reviewed-by: Erik Rijkers --- GNUmakefile.in | 4 +- configure | 47 +++++ configure.in | 5 + doc/src/sgml/installation.sgml | 3 +- doc/src/sgml/regress.sgml | 28 +++ src/Makefile.global.in | 16 ++ src/bin/initdb/.gitignore | 2 + src/bin/initdb/Makefile | 7 + src/bin/initdb/t/001_initdb.pl | 37 ++++ src/bin/pg_basebackup/.gitignore | 2 + src/bin/pg_basebackup/Makefile | 6 + src/bin/pg_basebackup/t/010_pg_basebackup.pl | 90 +++++++++ src/bin/pg_basebackup/t/020_pg_receivexlog.pl | 8 + src/bin/pg_config/.gitignore | 1 + src/bin/pg_config/Makefile | 6 + src/bin/pg_config/t/001_pg_config.pl | 12 ++ src/bin/pg_controldata/.gitignore | 1 + src/bin/pg_controldata/Makefile | 6 + .../pg_controldata/t/001_pg_controldata.pl | 14 ++ src/bin/pg_ctl/.gitignore | 1 + src/bin/pg_ctl/Makefile | 6 + src/bin/pg_ctl/t/001_start_stop.pl | 25 +++ src/bin/pg_ctl/t/002_status.pl | 19 ++ src/bin/scripts/.gitignore | 2 + src/bin/scripts/Makefile | 7 + src/bin/scripts/t/010_clusterdb.pl | 18 ++ src/bin/scripts/t/011_clusterdb_all.pl | 9 + src/bin/scripts/t/020_createdb.pl | 16 ++ src/bin/scripts/t/030_createlang.pl | 18 ++ src/bin/scripts/t/040_createuser.pl | 26 +++ src/bin/scripts/t/050_dropdb.pl | 16 ++ src/bin/scripts/t/060_droplang.pl | 15 ++ src/bin/scripts/t/070_dropuser.pl | 16 ++ src/bin/scripts/t/080_pg_isready.pl | 15 ++ src/bin/scripts/t/090_reindexdb.pl | 21 ++ src/bin/scripts/t/091_reindexdb_all.pl | 11 ++ src/bin/scripts/t/100_vacuumdb.pl | 17 ++ src/bin/scripts/t/101_vacuumdb_all.pl | 9 + src/test/perl/TestLib.pm | 186 ++++++++++++++++++ 39 files changed, 745 insertions(+), 3 deletions(-) create mode 100644 src/bin/initdb/t/001_initdb.pl create mode 100644 src/bin/pg_basebackup/t/010_pg_basebackup.pl create mode 100644 src/bin/pg_basebackup/t/020_pg_receivexlog.pl create mode 100644 src/bin/pg_config/t/001_pg_config.pl create mode 100644 src/bin/pg_controldata/t/001_pg_controldata.pl create mode 100644 src/bin/pg_ctl/t/001_start_stop.pl create mode 100644 src/bin/pg_ctl/t/002_status.pl create mode 100644 src/bin/scripts/t/010_clusterdb.pl create mode 100644 src/bin/scripts/t/011_clusterdb_all.pl create mode 100644 src/bin/scripts/t/020_createdb.pl create mode 100644 src/bin/scripts/t/030_createlang.pl create mode 100644 src/bin/scripts/t/040_createuser.pl create mode 100644 src/bin/scripts/t/050_dropdb.pl create mode 100644 src/bin/scripts/t/060_droplang.pl create mode 100644 src/bin/scripts/t/070_dropuser.pl create mode 100644 src/bin/scripts/t/080_pg_isready.pl create mode 100644 src/bin/scripts/t/090_reindexdb.pl create mode 100644 src/bin/scripts/t/091_reindexdb_all.pl create mode 100644 src/bin/scripts/t/100_vacuumdb.pl create mode 100644 src/bin/scripts/t/101_vacuumdb_all.pl create mode 100644 src/test/perl/TestLib.pm diff --git a/GNUmakefile.in b/GNUmakefile.in index a573880d79..69e08249c1 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -66,9 +66,9 @@ check check-tests: all check check-tests installcheck installcheck-parallel installcheck-tests: $(MAKE) -C src/test/regress $@ -$(call recurse,check-world,src/test src/pl src/interfaces/ecpg contrib,check) +$(call recurse,check-world,src/test src/pl src/interfaces/ecpg contrib src/bin,check) -$(call recurse,installcheck-world,src/test src/pl src/interfaces/ecpg contrib,installcheck) +$(call recurse,installcheck-world,src/test src/pl src/interfaces/ecpg contrib src/bin,installcheck) GNUmakefile: GNUmakefile.in $(top_builddir)/config.status ./config.status $@ diff --git a/configure b/configure index 122ace7e39..e0dbdfec10 100755 --- a/configure +++ b/configure @@ -627,6 +627,7 @@ ac_includes_default="\ ac_subst_vars='LTLIBOBJS vpath_build +PROVE OSX XSLTPROC COLLATEINDEX @@ -14350,6 +14351,52 @@ fi done +# +# Check for test tools +# +for ac_prog in prove +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_PROVE+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PROVE"; then + ac_cv_prog_PROVE="$PROVE" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_PROVE="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PROVE=$ac_cv_prog_PROVE +if test -n "$PROVE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROVE" >&5 +$as_echo "$PROVE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$PROVE" && break +done + + # Thread testing # We have to run the thread test near the end so we have all our symbols diff --git a/configure.in b/configure.in index 0f3e0ccdd0..fc9c52f83d 100644 --- a/configure.in +++ b/configure.in @@ -1817,6 +1817,11 @@ PGAC_PATH_COLLATEINDEX AC_CHECK_PROGS(XSLTPROC, xsltproc) AC_CHECK_PROGS(OSX, [osx sgml2xml sx]) +# +# Check for test tools +# +AC_CHECK_PROGS(PROVE, prove) + # Thread testing # We have to run the thread test near the end so we have all our symbols diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml index efaaad80b4..32160bf33f 100644 --- a/doc/src/sgml/installation.sgml +++ b/doc/src/sgml/installation.sgml @@ -333,7 +333,8 @@ su - postgres Perl 5.8 or later is needed to build from a Git checkout, or if you changed the input files for any of the build steps that use Perl scripts. If building on Windows you will need - Perl in any case. + Perl in any case. Perl is + also required to run some test suites. diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index 16b36211f3..aee049a3d5 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -204,6 +204,12 @@ make installcheck-world located in src/test/isolation. + + + Tests of client programs under src/bin. See + also . + + @@ -660,6 +666,28 @@ float8:out:i.86-.*-openbsd=float8-small-is-zero.out + + TAP Tests + + + The client program tests under src/bin use the Perl + TAP tools and are run by prove. You can pass + command-line options to prove by setting + the make variable PROVE_FLAGS, for example: + +make -C src/bin check PROVE_FLAGS='--reverse' + + The default is --verbose. See the manual page + of prove for more information. + + + + The tests written in Perl require the Perl + module IPC::Run, otherwise most tests will be skipped. + This module is available from CPAN or an operating system package. + + + Test Coverage Examination diff --git a/src/Makefile.global.in b/src/Makefile.global.in index cdddf492f4..7a7b0899fd 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -292,6 +292,22 @@ XGETTEXT = @XGETTEXT@ GZIP = gzip BZIP2 = bzip2 +# Testing + +PROVE = @PROVE@ +PG_PROVE_FLAGS = --ext='.pl' -I $(top_srcdir)/src/test/perl/ +PROVE_FLAGS = --verbose + +define prove_installcheck +PATH="$(bindir):$$PATH" PGPORT='6$(DEF_PGPORT)' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) +endef + +define prove_check +$(MKDIR_P) tmp_check/log +$(MAKE) -C $(top_builddir) DESTDIR=$(CURDIR)/tmp_check/install install >$(CURDIR)/tmp_check/log/install.log 2>&1 +PATH="$(CURDIR)/tmp_check/install$(bindir):$$PATH" PGPORT='6$(DEF_PGPORT)' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) +endef + # Installation. install_bin = @install_bin@ diff --git a/src/bin/initdb/.gitignore b/src/bin/initdb/.gitignore index 0f74727d8f..71a899ffb8 100644 --- a/src/bin/initdb/.gitignore +++ b/src/bin/initdb/.gitignore @@ -2,3 +2,5 @@ /localtime.c /initdb + +/tmp_check/ diff --git a/src/bin/initdb/Makefile b/src/bin/initdb/Makefile index 1ece7acf44..fb7d142487 100644 --- a/src/bin/initdb/Makefile +++ b/src/bin/initdb/Makefile @@ -57,3 +57,10 @@ clean distclean maintainer-clean: # ensure that changes in datadir propagate into object file initdb.o: initdb.c $(top_builddir)/src/Makefile.global + + +check: all + $(prove_check) + +installcheck: + $(prove_installcheck) diff --git a/src/bin/initdb/t/001_initdb.pl b/src/bin/initdb/t/001_initdb.pl new file mode 100644 index 0000000000..ad6d74cc0d --- /dev/null +++ b/src/bin/initdb/t/001_initdb.pl @@ -0,0 +1,37 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 14; + +my $tempdir = TestLib::tempdir; + +program_help_ok('initdb'); +program_version_ok('initdb'); +program_options_handling_ok('initdb'); + +command_ok(['initdb', "$tempdir/data"], 'basic initdb'); +command_fails(['initdb', "$tempdir/data"], 'existing data directory'); +command_ok(['initdb', '-N', "$tempdir/data2"], 'nosync'); +command_ok(['initdb', '-S', "$tempdir/data2"], 'sync only'); +command_fails(['initdb', '-S', "$tempdir/data3"], 'sync missing data directory'); +mkdir "$tempdir/data4" or BAIL_OUT($!); +command_ok(['initdb', "$tempdir/data4"], 'existing empty data directory'); + +system_or_bail "rm -rf $tempdir/*"; + +command_ok(['initdb', "$tempdir/data", '-X', "$tempdir/pgxlog"], 'separate xlog directory'); + +system_or_bail "rm -rf $tempdir/*"; +command_fails(['initdb', "$tempdir/data", '-X', 'pgxlog'], 'relative xlog directory not allowed'); + +system_or_bail "rm -rf $tempdir/*"; +mkdir "$tempdir/pgxlog"; +command_ok(['initdb', "$tempdir/data", '-X', "$tempdir/pgxlog"], 'existing empty xlog directory'); + +system_or_bail "rm -rf $tempdir/*"; +mkdir "$tempdir/pgxlog"; +mkdir "$tempdir/pgxlog/lost+found"; +command_fails(['initdb', "$tempdir/data", '-X', "$tempdir/pgxlog"], 'existing nonempty xlog directory'); + +system_or_bail "rm -rf $tempdir/*"; +command_ok(['initdb', "$tempdir/data", '-T', 'german'], 'select default dictionary'); diff --git a/src/bin/pg_basebackup/.gitignore b/src/bin/pg_basebackup/.gitignore index 7abea15a3f..36a2f12d61 100644 --- a/src/bin/pg_basebackup/.gitignore +++ b/src/bin/pg_basebackup/.gitignore @@ -1,3 +1,5 @@ /pg_basebackup /pg_receivexlog /pg_recvlogical + +/tmp_check/ diff --git a/src/bin/pg_basebackup/Makefile b/src/bin/pg_basebackup/Makefile index 346560eeab..37ee6b0288 100644 --- a/src/bin/pg_basebackup/Makefile +++ b/src/bin/pg_basebackup/Makefile @@ -48,3 +48,9 @@ clean distclean maintainer-clean: rm -f pg_basebackup$(X) pg_receivexlog$(X) pg_recvlogical$(X) \ pg_basebackup.o pg_receivexlog.o pg_recvlogical.o \ $(OBJS) + +check: all + $(prove_check) + +installcheck: + $(prove_installcheck) diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.pl new file mode 100644 index 0000000000..38f4692955 --- /dev/null +++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl @@ -0,0 +1,90 @@ +use strict; +use warnings; +use Cwd; +use TestLib; +use Test::More tests => 28; + +program_help_ok('pg_basebackup'); +program_version_ok('pg_basebackup'); +program_options_handling_ok('pg_basebackup'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +command_fails(['pg_basebackup'], 'pg_basebackup needs target directory specified'); +command_fails(['pg_basebackup', '-D', "$tempdir/backup"], 'pg_basebackup fails because of hba'); + +open HBA, ">>$tempdir/pgdata/pg_hba.conf"; +print HBA "local replication all trust\n"; +print HBA "host replication all 127.0.0.1/32 trust\n"; +print HBA "host replication all ::1/128 trust\n"; +close HBA; +system_or_bail 'pg_ctl', '-s', '-D', "$tempdir/pgdata", 'reload'; + +command_fails(['pg_basebackup', '-D', "$tempdir/backup"], 'pg_basebackup fails because of WAL configuration'); + +open CONF, ">>$tempdir/pgdata/postgresql.conf"; +print CONF "max_wal_senders = 10\n"; +print CONF "wal_level = archive\n"; +close CONF; +restart_test_server; + +command_ok(['pg_basebackup', '-D', "$tempdir/backup"], 'pg_basebackup runs'); +ok(-f "$tempdir/backup/PG_VERSION", 'backup was created'); + +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'], 'tar format'); +ok(-f "$tempdir/tarbackup/base.tar", 'backup tar was created'); + +mkdir "$tempdir/tblspc1"; +psql 'postgres', "CREATE TABLESPACE tblspc1 LOCATION '$tempdir/tblspc1';"; +psql 'postgres', "CREATE TABLE test1 (a int) TABLESPACE tblspc1;"; +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(['pg_basebackup', '-D', "$tempdir/backup1", '-Fp'], + 'plain format with tablespaces fails without tablespace mapping'); + +command_ok(['pg_basebackup', '-D', "$tempdir/backup1", '-Fp', + "-T$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; +ok((grep { -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 '$tempdir/tbl=spc2';"; +command_ok(['pg_basebackup', '-D', "$tempdir/backup3", '-Fp', + "-T$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;"; + +command_fails(['pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', + "-T=/foo"], + '-T with empty old directory fails'); +command_fails(['pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', + "-T/foo="], + '-T with empty new directory fails'); +command_fails(['pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', + "-T/foo=/bar=/baz"], + '-T with multiple = fails'); +command_fails(['pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', + "-Tfoo=/bar"], + '-T with old directory not absolute fails'); +command_fails(['pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', + "-T/foo=bar"], + '-T with new directory not absolute fails'); +command_fails(['pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', + "-Tfoo"], + '-T with invalid format fails'); diff --git a/src/bin/pg_basebackup/t/020_pg_receivexlog.pl b/src/bin/pg_basebackup/t/020_pg_receivexlog.pl new file mode 100644 index 0000000000..700ae045a1 --- /dev/null +++ b/src/bin/pg_basebackup/t/020_pg_receivexlog.pl @@ -0,0 +1,8 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 3; + +program_help_ok('pg_receivexlog'); +program_version_ok('pg_receivexlog'); +program_options_handling_ok('pg_receivexlog'); diff --git a/src/bin/pg_config/.gitignore b/src/bin/pg_config/.gitignore index 169bc766de..cc422470d9 100644 --- a/src/bin/pg_config/.gitignore +++ b/src/bin/pg_config/.gitignore @@ -1 +1,2 @@ /pg_config +/tmp_check/ diff --git a/src/bin/pg_config/Makefile b/src/bin/pg_config/Makefile index 578a2a52c8..579dce62b7 100644 --- a/src/bin/pg_config/Makefile +++ b/src/bin/pg_config/Makefile @@ -47,3 +47,9 @@ uninstall: clean distclean maintainer-clean: rm -f pg_config$(X) $(OBJS) + +check: all + $(prove_check) + +installcheck: + $(prove_installcheck) diff --git a/src/bin/pg_config/t/001_pg_config.pl b/src/bin/pg_config/t/001_pg_config.pl new file mode 100644 index 0000000000..200f394fd7 --- /dev/null +++ b/src/bin/pg_config/t/001_pg_config.pl @@ -0,0 +1,12 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 7; + +program_help_ok('pg_config'); +program_version_ok('pg_config'); +program_options_handling_ok('pg_config'); +command_like(['pg_config', '--bindir'], qr/bin/, 'pg_config single option'); # XXX might be wrong +command_like(['pg_config', '--bindir', '--libdir'], qr/bin.*\n.*lib/, 'pg_config two options'); +command_like(['pg_config', '--libdir', '--bindir'], qr/lib.*\n.*bin/, 'pg_config two options different order'); +command_like(['pg_config'], qr/.*\n.*\n.*/, 'pg_config without options prints many lines'); diff --git a/src/bin/pg_controldata/.gitignore b/src/bin/pg_controldata/.gitignore index eab0c28a8b..051d71d8f3 100644 --- a/src/bin/pg_controldata/.gitignore +++ b/src/bin/pg_controldata/.gitignore @@ -1 +1,2 @@ /pg_controldata +/tmp_check/ diff --git a/src/bin/pg_controldata/Makefile b/src/bin/pg_controldata/Makefile index 8b5f340d36..5abe76cc0d 100644 --- a/src/bin/pg_controldata/Makefile +++ b/src/bin/pg_controldata/Makefile @@ -33,3 +33,9 @@ uninstall: clean distclean maintainer-clean: rm -f pg_controldata$(X) $(OBJS) + +check: all + $(prove_check) + +installcheck: + $(prove_installcheck) diff --git a/src/bin/pg_controldata/t/001_pg_controldata.pl b/src/bin/pg_controldata/t/001_pg_controldata.pl new file mode 100644 index 0000000000..ca89d87535 --- /dev/null +++ b/src/bin/pg_controldata/t/001_pg_controldata.pl @@ -0,0 +1,14 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 6; + +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'); +system_or_bail "initdb -D $tempdir/data -A trust >/dev/null"; +command_like(['pg_controldata', "$tempdir/data"], qr/checkpoint/, 'pg_controldata produces output'); diff --git a/src/bin/pg_ctl/.gitignore b/src/bin/pg_ctl/.gitignore index c90c1030ef..73ab4ed9f5 100644 --- a/src/bin/pg_ctl/.gitignore +++ b/src/bin/pg_ctl/.gitignore @@ -1 +1,2 @@ /pg_ctl +/tmp_check/ diff --git a/src/bin/pg_ctl/Makefile b/src/bin/pg_ctl/Makefile index cbc1638118..a9a0366ef5 100644 --- a/src/bin/pg_ctl/Makefile +++ b/src/bin/pg_ctl/Makefile @@ -36,3 +36,9 @@ uninstall: clean distclean maintainer-clean: rm -f pg_ctl$(X) $(OBJS) + +check: all + $(prove_check) + +installcheck: + $(prove_installcheck) diff --git a/src/bin/pg_ctl/t/001_start_stop.pl b/src/bin/pg_ctl/t/001_start_stop.pl new file mode 100644 index 0000000000..49c1b1a66a --- /dev/null +++ b/src/bin/pg_ctl/t/001_start_stop.pl @@ -0,0 +1,25 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 10; + +my $tempdir = TestLib::tempdir; + +program_help_ok('pg_ctl'); +program_version_ok('pg_ctl'); +program_options_handling_ok('pg_ctl'); + +command_ok(['pg_ctl', 'initdb', '-D', "$tempdir/data"], 'pg_ctl initdb'); +open CONF, ">>$tempdir/data/postgresql.conf"; +print CONF "listen_addresses = ''\n"; +print CONF "unix_socket_directories = '$tempdir'\n"; +close CONF; +command_ok(['pg_ctl', 'start', '-D', "$tempdir/data", '-w'], 'pg_ctl start -w'); +command_ok(['pg_ctl', 'start', '-D', "$tempdir/data", '-w'], 'second pg_ctl start succeeds'); +command_ok(['pg_ctl', 'stop', '-D', "$tempdir/data", '-w', '-m', 'fast'], 'pg_ctl stop -w'); +command_fails(['pg_ctl', 'stop', '-D', "$tempdir/data", '-w', '-m', 'fast'], 'second pg_ctl stop fails'); + +command_ok(['pg_ctl', 'restart', '-D', "$tempdir/data", '-w', '-m', 'fast'], 'pg_ctl restart with server not running'); +command_ok(['pg_ctl', 'restart', '-D', "$tempdir/data", '-w', '-m', 'fast'], 'pg_ctl restart with server running'); + +system_or_bail 'pg_ctl', '-s', 'stop', '-D', "$tempdir/data", '-m', 'fast'; diff --git a/src/bin/pg_ctl/t/002_status.pl b/src/bin/pg_ctl/t/002_status.pl new file mode 100644 index 0000000000..e41c22fd5b --- /dev/null +++ b/src/bin/pg_ctl/t/002_status.pl @@ -0,0 +1,19 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 2; + +my $tempdir = TestLib::tempdir; + +system_or_bail "initdb -D $tempdir/data -A trust >/dev/null"; +open CONF, ">>$tempdir/data/postgresql.conf"; +print CONF "listen_addresses = ''\n"; +print CONF "unix_socket_directories = '$tempdir'\n"; +close CONF; + +command_exit_is(['pg_ctl', 'status', '-D', "$tempdir/data"], 3, 'pg_ctl status with server not running'); + +system_or_bail 'pg_ctl', '-s', '-l', "$tempdir/logfile", '-D', "$tempdir/data", '-w', 'start'; +command_exit_is(['pg_ctl', 'status', '-D', "$tempdir/data"], 0, 'pg_ctl status with server running'); + +system_or_bail 'pg_ctl', '-s', 'stop', '-D', "$tempdir/data", '-m', 'fast'; diff --git a/src/bin/scripts/.gitignore b/src/bin/scripts/.gitignore index 0b9b786da0..1056b2870c 100644 --- a/src/bin/scripts/.gitignore +++ b/src/bin/scripts/.gitignore @@ -14,3 +14,5 @@ /kwlookup.c /mbprint.c /print.c + +/tmp_check/ diff --git a/src/bin/scripts/Makefile b/src/bin/scripts/Makefile index b5d9207b29..de0e11f714 100644 --- a/src/bin/scripts/Makefile +++ b/src/bin/scripts/Makefile @@ -68,3 +68,10 @@ clean distclean maintainer-clean: rm -f $(addsuffix $(X), $(PROGRAMS)) $(addsuffix .o, $(PROGRAMS)) rm -f common.o dumputils.o kwlookup.o keywords.o print.o mbprint.o $(WIN32RES) rm -f dumputils.c print.c mbprint.c kwlookup.c keywords.c + + +check: all + $(prove_check) + +installcheck: + $(prove_installcheck) diff --git a/src/bin/scripts/t/010_clusterdb.pl b/src/bin/scripts/t/010_clusterdb.pl new file mode 100644 index 0000000000..371b2dd2d3 --- /dev/null +++ b/src/bin/scripts/t/010_clusterdb.pl @@ -0,0 +1,18 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 6; + +program_help_ok('clusterdb'); +program_version_ok('clusterdb'); +program_options_handling_ok('clusterdb'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +issues_sql_like(['clusterdb', 'postgres'], qr/statement: CLUSTER;/, 'SQL CLUSTER run'); + +command_fails(['clusterdb', '-t', 'nonexistent', 'postgres'], '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', 'postgres', '-t', 'test1'], qr/statement: CLUSTER test1;/, 'cluster specific table'); diff --git a/src/bin/scripts/t/011_clusterdb_all.pl b/src/bin/scripts/t/011_clusterdb_all.pl new file mode 100644 index 0000000000..304c4befa5 --- /dev/null +++ b/src/bin/scripts/t/011_clusterdb_all.pl @@ -0,0 +1,9 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 1; + +my $tempdir = tempdir; +start_test_server $tempdir; + +issues_sql_like(['clusterdb', '-a'], qr/statement: CLUSTER.*statement: CLUSTER/s, 'cluster all databases'); diff --git a/src/bin/scripts/t/020_createdb.pl b/src/bin/scripts/t/020_createdb.pl new file mode 100644 index 0000000000..8b82a2bd96 --- /dev/null +++ b/src/bin/scripts/t/020_createdb.pl @@ -0,0 +1,16 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 6; + +program_help_ok('createdb'); +program_version_ok('createdb'); +program_options_handling_ok('createdb'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +issues_sql_like(['createdb', 'foobar1'], qr/statement: CREATE DATABASE foobar1/, 'SQL CREATE DATABASE run'); +issues_sql_like(['createdb', 'foobar2', '-l', 'C', '-E', 'LATIN1', '-T', 'template0'], qr/statement: CREATE DATABASE foobar2 ENCODING 'LATIN1'/, 'create database with encoding'); + +command_fails(['createdb', 'foobar1'], 'fails if database already exists'); diff --git a/src/bin/scripts/t/030_createlang.pl b/src/bin/scripts/t/030_createlang.pl new file mode 100644 index 0000000000..9a87f4c89c --- /dev/null +++ b/src/bin/scripts/t/030_createlang.pl @@ -0,0 +1,18 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 6; + +program_help_ok('createlang'); +program_version_ok('createlang'); +program_options_handling_ok('createlang'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +command_fails(['createlang', 'plpgsql', 'postgres'], 'fails if language already exists'); + +psql 'postgres', 'DROP EXTENSION plpgsql'; +issues_sql_like(['createlang', 'plpgsql', 'postgres'], qr/statement: CREATE EXTENSION "plpgsql"/, 'SQL CREATE EXTENSION run'); + +command_like(['createlang', '--list', 'postgres'], qr/plpgsql/, 'list output'); diff --git a/src/bin/scripts/t/040_createuser.pl b/src/bin/scripts/t/040_createuser.pl new file mode 100644 index 0000000000..922873ab37 --- /dev/null +++ b/src/bin/scripts/t/040_createuser.pl @@ -0,0 +1,26 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 8; + +program_help_ok('createuser'); +program_version_ok('createuser'); +program_options_handling_ok('createuser'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +issues_sql_like(['createuser', 'user1'], + qr/statement: CREATE ROLE user1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN;/, + 'SQL CREATE USER run'); +issues_sql_like(['createuser', '-L', 'role1'], + qr/statement: CREATE ROLE role1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN;/, + 'create a non-login role'); +issues_sql_like(['createuser', '-r', 'user2'], + qr/statement: CREATE ROLE user2 NOSUPERUSER NOCREATEDB CREATEROLE INHERIT LOGIN;/, + 'create a CREATEROLE user'); +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'); diff --git a/src/bin/scripts/t/050_dropdb.pl b/src/bin/scripts/t/050_dropdb.pl new file mode 100644 index 0000000000..3662dd01c1 --- /dev/null +++ b/src/bin/scripts/t/050_dropdb.pl @@ -0,0 +1,16 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 5; + +program_help_ok('dropdb'); +program_version_ok('dropdb'); +program_options_handling_ok('dropdb'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +psql 'postgres', 'CREATE DATABASE foobar1'; +issues_sql_like(['dropdb', 'foobar1'], qr/statement: DROP DATABASE foobar1/, 'SQL DROP DATABASE run'); + +command_fails(['dropdb', 'nonexistent'], 'fails with nonexistent database'); diff --git a/src/bin/scripts/t/060_droplang.pl b/src/bin/scripts/t/060_droplang.pl new file mode 100644 index 0000000000..47cb48f117 --- /dev/null +++ b/src/bin/scripts/t/060_droplang.pl @@ -0,0 +1,15 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 5; + +program_help_ok('droplang'); +program_version_ok('droplang'); +program_options_handling_ok('droplang'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +issues_sql_like(['droplang', 'plpgsql', 'postgres'], qr/statement: DROP EXTENSION "plpgsql"/, 'SQL DROP EXTENSION run'); + +command_fails(['droplang', 'nonexistent', 'postgres'], 'fails with nonexistent language'); diff --git a/src/bin/scripts/t/070_dropuser.pl b/src/bin/scripts/t/070_dropuser.pl new file mode 100644 index 0000000000..495636ae84 --- /dev/null +++ b/src/bin/scripts/t/070_dropuser.pl @@ -0,0 +1,16 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 5; + +program_help_ok('dropuser'); +program_version_ok('dropuser'); +program_options_handling_ok('dropuser'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +psql 'postgres', 'CREATE ROLE foobar1'; +issues_sql_like(['dropuser', 'foobar1'], qr/statement: DROP ROLE foobar1/, 'SQL DROP ROLE run'); + +command_fails(['dropuser', 'nonexistent'], 'fails with nonexistent user'); diff --git a/src/bin/scripts/t/080_pg_isready.pl b/src/bin/scripts/t/080_pg_isready.pl new file mode 100644 index 0000000000..03c3657153 --- /dev/null +++ b/src/bin/scripts/t/080_pg_isready.pl @@ -0,0 +1,15 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 5; + +program_help_ok('pg_isready'); +program_version_ok('pg_isready'); +program_options_handling_ok('pg_isready'); + +command_fails(['pg_isready'], 'fails with no server running'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +command_ok(['pg_isready'], 'succeeds with server running'); diff --git a/src/bin/scripts/t/090_reindexdb.pl b/src/bin/scripts/t/090_reindexdb.pl new file mode 100644 index 0000000000..18756e86aa --- /dev/null +++ b/src/bin/scripts/t/090_reindexdb.pl @@ -0,0 +1,21 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 7; + +program_help_ok('reindexdb'); +program_version_ok('reindexdb'); +program_options_handling_ok('reindexdb'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +$ENV{PGOPTIONS} = '--client-min-messages=WARNING'; + +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(['reindexdb', 'postgres', '-t', 'test1'], qr/statement: REINDEX TABLE test1;/, 'reindex specific table'); +issues_sql_like(['reindexdb', 'postgres', '-i', 'test1x'], qr/statement: REINDEX INDEX test1x;/, 'reindex specific index'); + +issues_sql_like(['reindexdb', 'postgres', '-s'], qr/statement: REINDEX SYSTEM postgres;/, 'reindex system tables'); diff --git a/src/bin/scripts/t/091_reindexdb_all.pl b/src/bin/scripts/t/091_reindexdb_all.pl new file mode 100644 index 0000000000..eee8ba8ed9 --- /dev/null +++ b/src/bin/scripts/t/091_reindexdb_all.pl @@ -0,0 +1,11 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 1; + +my $tempdir = tempdir; +start_test_server $tempdir; + +$ENV{PGOPTIONS} = '--client-min-messages=WARNING'; + +issues_sql_like(['reindexdb', '-a'], qr/statement: REINDEX.*statement: REINDEX/s, 'reindex all databases'); diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl new file mode 100644 index 0000000000..39f1cec8a1 --- /dev/null +++ b/src/bin/scripts/t/100_vacuumdb.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 8; + +program_help_ok('vacuumdb'); +program_version_ok('vacuumdb'); +program_options_handling_ok('vacuumdb'); + +my $tempdir = tempdir; +start_test_server $tempdir; + +issues_sql_like(['vacuumdb', 'postgres'], qr/statement: VACUUM;/, 'SQL VACUUM run'); +issues_sql_like(['vacuumdb', '-f', 'postgres'], qr/statement: VACUUM \(FULL\);/, 'vacuumdb -f'); +issues_sql_like(['vacuumdb', '-F', 'postgres'], qr/statement: VACUUM \(FREEZE\);/, 'vacuumdb -F'); +issues_sql_like(['vacuumdb', '-z', 'postgres'], qr/statement: VACUUM \(ANALYZE\);/, 'vacuumdb -z'); +issues_sql_like(['vacuumdb', '-Z', 'postgres'], qr/statement: ANALYZE;/, 'vacuumdb -z'); diff --git a/src/bin/scripts/t/101_vacuumdb_all.pl b/src/bin/scripts/t/101_vacuumdb_all.pl new file mode 100644 index 0000000000..b5779bcf12 --- /dev/null +++ b/src/bin/scripts/t/101_vacuumdb_all.pl @@ -0,0 +1,9 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 1; + +my $tempdir = tempdir; +start_test_server $tempdir; + +issues_sql_like(['vacuumdb', '-a'], qr/statement: VACUUM.*statement: VACUUM/s, 'vacuum all databases'); diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm new file mode 100644 index 0000000000..7c378e0fa9 --- /dev/null +++ b/src/test/perl/TestLib.pm @@ -0,0 +1,186 @@ +package TestLib; + +use strict; +use warnings; + +use Exporter 'import'; +our @EXPORT = qw( + tempdir + start_test_server + restart_test_server + psql + system_or_bail + + command_ok + command_fails + command_exit_is + program_help_ok + program_version_ok + program_options_handling_ok + command_like + issues_sql_like +); + +use Cwd; +use File::Spec; +use File::Temp (); +use Test::More; +BEGIN { + eval { + require IPC::Run; + import IPC::Run qw(run start); + 1; + } or do { + plan skip_all => "IPC::Run not available"; + } +} + +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}) { + $ENV{PGPORT} = 65432; +} + +$ENV{PGPORT} = int($ENV{PGPORT}) % 65536; + + +# +# Helper functions +# + + +sub tempdir { + return File::Temp::tempdir('testXXXX', DIR => cwd(), CLEANUP => 1); +} + +my ($test_server_datadir, $test_server_logfile); + +sub start_test_server { + my ($tempdir) = @_; + my $ret; + + system "initdb -D $tempdir/pgdata -A trust -N >/dev/null"; + $ret = system 'pg_ctl', '-D', "$tempdir/pgdata", '-s', '-w', '-l', "$tempdir/logfile", '-o', "--fsync=off -k $tempdir --listen-addresses='' --log-statement=all", 'start'; + + if ($ret != 0) { + system('cat', "$tempdir/logfile"); + BAIL_OUT("pg_ctl failed"); + } + + $ENV{PGHOST} = $tempdir; + $test_server_datadir = "$tempdir/pgdata"; + $test_server_logfile = "$tempdir/logfile"; +} + +sub restart_test_server { + system 'pg_ctl', '-s', '-D', $test_server_datadir, '-w', '-l', $test_server_logfile, 'restart'; +} + +END { + if ($test_server_datadir) { + system 'pg_ctl', '-D', $test_server_datadir, '-s', '-w', '-m', 'immediate', 'stop'; + } +} + +sub psql { + my ($dbname, $sql) = @_; + run ['psql', '-X', '-q', '-d', $dbname, '-f', '-'], '<', \$sql or die; +} + +sub system_or_bail { + system(@_) == 0 or BAIL_OUT("system @_ failed: $?"); +} + + +# +# Test functions +# + + +sub command_ok { + my ($cmd, $test_name) = @_; + my $result = run $cmd, '>', File::Spec->devnull(), '2>', File::Spec->devnull(); + ok($result, $test_name); +} + +sub command_fails { + my ($cmd, $test_name) = @_; + my $result = run $cmd, '>', File::Spec->devnull(), '2>', File::Spec->devnull(); + ok(!$result, $test_name); +} + +sub command_exit_is { + my ($cmd, $expected, $test_name) = @_; + my $h = start $cmd, '>', File::Spec->devnull(), '2>', File::Spec->devnull(); + $h->finish(); + is($h->result(0), $expected, $test_name); +} + +sub program_help_ok { + my ($cmd) = @_; + subtest "$cmd --help" => sub { + plan tests => 3; + my ($stdout, $stderr); + my $result = 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"); + }; +} + +sub program_version_ok { + my ($cmd) = @_; + subtest "$cmd --version" => sub { + plan tests => 3; + my ($stdout, $stderr); + my $result = 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"); + }; +} + +sub program_options_handling_ok { + my ($cmd) = @_; + subtest "$cmd options handling" => sub { + plan tests => 2; + my ($stdout, $stderr); + my $result = 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"); + }; +} + +sub command_like { + my ($cmd, $expected_stdout, $test_name) = @_; + subtest $test_name => sub { + plan tests => 3; + my ($stdout, $stderr); + my $result = 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) = @_; + subtest $test_name => sub { + plan tests => 2; + my ($stdout, $stderr); + truncate $test_server_logfile, 0; + my $result = run $cmd, '>', \$stdout, '2>', \$stderr; + ok($result, "@$cmd exit code 0"); + my $log = `cat $test_server_logfile`; + like($log, $expected_sql, "$test_name: SQL found in server log"); + }; +} + +1;