Contribution to Perldoc for TestLib module in Postgres

Started by Prajwal A Valmost 7 years ago17 messages
#1Prajwal A V
prajwal450@gmail.com

Hi Hackers,

I was going through the regression testing framework used in postgres. I
was trying to understand the custom functions written in perl for postgres.

I could not find the perldoc for TestLib module in src/test/perl and found
some difficultly in understanding what each function does while other
modules in the src/test folder had perldoc and it was easy to understand
the functions.

I would like to contribute for the perldoc for TestLib. I am looking for
your suggestions if this contribution is worth doing.

Regards,
Prajwal

#2Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Prajwal A V (#1)
Re: Contribution to Perldoc for TestLib module in Postgres

On 2019-Mar-19, Prajwal A V wrote:

I could not find the perldoc for TestLib module in src/test/perl and found
some difficultly in understanding what each function does while other
modules in the src/test folder had perldoc and it was easy to understand
the functions.

I would like to contribute for the perldoc for TestLib. I am looking for
your suggestions if this contribution is worth doing.

Yes, it is, please do.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#3Michael Paquier
michael@paquier.xyz
In reply to: Alvaro Herrera (#2)
Re: Contribution to Perldoc for TestLib module in Postgres

On Tue, Mar 19, 2019 at 09:05:29AM -0300, Alvaro Herrera wrote:

Yes, it is, please do.

+1.
--
Michael
#4Ramanarayana
raam.soft@gmail.com
In reply to: Michael Paquier (#3)
Re: Contribution to Perldoc for TestLib module in Postgres

Hi,
Can I take this up?

Regards,
Ram

#5Prajwal A V
prajwal450@gmail.com
In reply to: Ramanarayana (#4)
Re: Contribution to Perldoc for TestLib module in Postgres

Sure, please go ahead.

Regards,
Prajwal.

On Thu, 21 Mar 2019, 19:11 Ramanarayana, <raam.soft@gmail.com> wrote:

Show quoted text

Hi,
Can I take this up?

Regards,
Ram

#6Ramanarayana
raam.soft@gmail.com
In reply to: Prajwal A V (#5)
1 attachment(s)
Re: Contribution to Perldoc for TestLib module in Postgres

Hi,

Please find the first version of the patch for review. I was not sure what
some of the functions are used for and marked them with TODO.

Cheers
Ram 4.0

Attachments:

v1_perldoc_testlib.patchapplication/octet-stream; name=v1_perldoc_testlib.patchDownload
diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm
index ce59401..61018f1 100644
--- a/src/test/perl/TestLib.pm
+++ b/src/test/perl/TestLib.pm
@@ -1,9 +1,101 @@
-# TestLib, low-level routines and actions regression tests.
-#
-# This module contains a set of routines dedicated to environment setup for
-# a PostgreSQL regression test run 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.
+=pod
+
+=head1 NAME
+
+TestLib - class containing routines for environment setup, low-level routines for command execution, logging and test functions 
+
+=head1 SYNOPSIS
+
+  use TestLib;
+	
+  #Checks if all the tests passed or not
+  all_tests_passing()
+
+  #Creates a temporary directory
+  tempdir(prefix)
+	
+  #Creates a temporary directory outside the build tree for the Unix-domain socket
+  tempdir_short()
+
+  #Returns the real directory for a virtual path directory under msys
+  real_dir(dir)
+
+  #TODO
+  system_log()	
+
+  #TODO
+  system_or_bail()	
+
+  #TODO
+  run_log()	
+
+  #Returns the output after running a command
+  run_command(dir)	
+
+  #Generate a string made of the given range of ASCII characters
+  generate_ascii_string(from_char, to_char)	
+
+  #TODO
+  slurp_dir(dir)	
+
+  #TODO
+  slurp_file(filename)	
+
+  #Appends a string at the end of a given file
+  append_to_file(filename, str)	
+
+  #Check that all file/dir modes in a directory match the expected values
+  check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
+
+  #Change mode recursively on a directory
+  chmod_recursive(dir, dir_mode, file_mode)	
+
+  #Check presence of a given regexp within pg_config.h
+  check_pg_config(regexp)	
+
+  #Test function to check that the command runs successfully
+  command_ok(cmd, test_name)	
+
+  #Test function to check that the command fails
+  command_fails(cmd, test_name)	
+
+  #Test function to check that the command exit code matches with the expected exit code
+  command_exit_is(cmd, expected, test_name)	
+
+  #Test function to check that the command supports --help option 
+  program_help_ok(cmd)	
+
+  #Test function to check that the command supports --version option 
+  program_version_ok(cmd)	
+
+  #Test function to check that a command with invalid option returns non-zero exit code and error message
+  program_options_handling_ok(cmd)	
+
+  #Test function to check that the command runs successfully and the output 
+  matches with the given regular expression
+  command_like(cmd, expected_stdout, test_name)	
+
+  #TODO
+  command_like_safe(cmd, expected_stdout, test_name)	
+
+  #Test function to check that the command fails and the error message
+  matches with the given regular expression
+  command_fails_like(cmd, expected_stderr, test_name)	
+
+  #Test function to run a command and check its status and outputs
+  command_checks_all(cmd, expected_ret, out, err, test_name)	
+		
+
+=head1 DESCRIPTION
+
+Testlib module contains a set of routines dedicated to environment setup for
+a PostgreSQL regression test run 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.
+
+The IPC::Run module is required.
+
+=cut
 
 package TestLib;
 
@@ -134,6 +226,19 @@ END
 	$File::Temp::KEEP_ALL = 1 unless all_tests_passing();
 }
 
+
+=pod
+
+=head1 METHODS
+
+=over
+
+=item all_tests_passing()
+
+Returns 1 if all the tests pass. Otherwise returns 0
+
+=cut
+
 sub all_tests_passing
 {
 	my $fail_count = 0;
@@ -144,9 +249,15 @@ sub all_tests_passing
 	return 1;
 }
 
-#
-# Helper functions
-#
+=pod
+
+=item tempdir(prefix)
+
+Creates a temporary directory with name prefix_XXXX if prefix argument is passed. Otherwise a temporary directory with name tmp_test_XXXX is created. 
+XXXX represents four random characters.Temporary directory is created inside the folder represented by $tmp_check and it is deleted at the end of the tests.
+
+=cut
+
 sub tempdir
 {
 	my ($prefix) = @_;
@@ -157,17 +268,32 @@ sub tempdir
 		CLEANUP => 1);
 }
 
+
+=pod
+
+=item tempdir_short()
+
+Use a separate temp dir outside the build tree for the
+Unix-domain socket, to avoid file name length issues.
+
+=cut
+
 sub tempdir_short
 {
 
-	# Use a separate temp dir outside the build tree for the
-	# Unix-domain socket, to avoid file name length issues.
 	return File::Temp::tempdir(CLEANUP => 1);
 }
 
-# Return the real directory for a virtual path directory under msys.
-# The directory  must exist. If it's not an existing directory or we're
-# not under msys, return the input argument unchanged.
+=pod
+
+=item real_dir(dir)
+
+Return the real directory for a virtual path directory under msys.
+The directory  must exist. If it's not an existing directory or we're
+not under msys, return the input argument unchanged.
+
+=cut
+
 sub real_dir
 {
 	my $dir = "$_[0]";
@@ -183,12 +309,28 @@ sub real_dir
 	return $dir;
 }
 
+=pod
+
+=item system_log()
+
+TODO
+
+=cut
+
 sub system_log
 {
 	print("# Running: " . join(" ", @_) . "\n");
 	return system(@_);
 }
 
+=pod
+
+=item system_or_bail()
+
+TODO
+
+=cut
+
 sub system_or_bail
 {
 	if (system_log(@_) != 0)
@@ -198,12 +340,28 @@ sub system_or_bail
 	return;
 }
 
+=pod
+
+=item run_log()
+
+TODO
+
+=cut
+
 sub run_log
 {
 	print("# Running: " . join(" ", @{ $_[0] }) . "\n");
 	return IPC::Run::run(@_);
 }
 
+=pod
+
+=item run_command(cmd)
+
+Returns the output after running the command
+
+=cut
+
 sub run_command
 {
 	my ($cmd) = @_;
@@ -214,7 +372,14 @@ sub run_command
 	return ($stdout, $stderr);
 }
 
-# Generate a string made of the given range of ASCII characters
+=pod
+
+=item generate_ascii_string(from_char, to_char)
+
+Generate a string made of the given range of ASCII characters
+
+=cut
+
 sub generate_ascii_string
 {
 	my ($from_char, $to_char) = @_;
@@ -227,6 +392,14 @@ sub generate_ascii_string
 	return $res;
 }
 
+=pod
+
+=item slurp_dir(dir)
+
+TODO
+
+=cut
+
 sub slurp_dir
 {
 	my ($dir) = @_;
@@ -237,6 +410,14 @@ sub slurp_dir
 	return @direntries;
 }
 
+=pod
+
+=item slurp_file(filename)
+
+TODO
+
+=cut
+
 sub slurp_file
 {
 	my ($filename) = @_;
@@ -249,6 +430,15 @@ sub slurp_file
 	return $contents;
 }
 
+=pod
+
+=item append_to_file(filename, str)
+
+Append a string at the end of a given file
+
+=cut
+
+
 sub append_to_file
 {
 	my ($filename, $str) = @_;
@@ -259,8 +449,16 @@ sub append_to_file
 	return;
 }
 
-# Check that all file/dir modes in a directory match the expected values,
-# ignoring the mode of any specified files.
+
+=pod
+
+=item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
+
+Check that all file/dir modes in a directory match the expected values,
+ignoring the mode of any specified files.
+
+=cut
+
 sub check_mode_recursive
 {
 	my ($dir, $expected_dir_mode, $expected_file_mode, $ignore_list) = @_;
@@ -343,7 +541,14 @@ sub check_mode_recursive
 	return $result;
 }
 
-# Change mode recursively on a directory
+=pod
+
+=item chmod_recursive(dir, dir_mode, file_mode)
+
+Change mode recursively on a directory
+
+=cut
+
 sub chmod_recursive
 {
 	my ($dir, $dir_mode, $file_mode) = @_;
@@ -367,9 +572,17 @@ sub chmod_recursive
 	return;
 }
 
-# Check presence of a given regexp within pg_config.h for the installation
-# where tests are running, returning a match status result depending on
-# that.
+
+=pod
+
+=item check_pg_config(regexp)
+
+Check presence of a given regexp within pg_config.h for the installation
+where tests are running, returning a match status result depending on
+that.
+
+=cut
+
 sub check_pg_config
 {
 	my ($regexp) = @_;
@@ -385,9 +598,15 @@ sub check_pg_config
 	return $match;
 }
 
-#
-# Test functions
-#
+
+=pod
+
+=item command_ok(cmd, test_name)
+
+Test function to check that the command runs successfully
+
+=cut
+
 sub command_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -397,6 +616,14 @@ sub command_ok
 	return;
 }
 
+=pod
+
+=item command_fails(cmd, test_name)
+
+Test function to check that the command fails
+
+=cut
+
 sub command_fails
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -406,6 +633,14 @@ sub command_fails
 	return;
 }
 
+=pod
+
+=item command_exit_is(cmd, expected, test_name)
+
+Test function to check that the command exit code matches with the expected exit code.
+
+=cut
+
 sub command_exit_is
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -429,6 +664,14 @@ sub command_exit_is
 	return;
 }
 
+=pod
+
+=item program_help_ok(cmd)
+
+Test function to check that the command supports --help option.
+
+=cut
+
 sub program_help_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -443,6 +686,14 @@ sub program_help_ok
 	return;
 }
 
+=pod
+
+=item program_version_ok(cmd)
+
+Test function to check that the command supports --version option.
+
+=cut
+
 sub program_version_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -457,6 +708,14 @@ sub program_version_ok
 	return;
 }
 
+=pod
+
+=item program_options_handling_ok(cmd)
+
+Test function to check that a command with invalid option returns non-zero exit code and error message.
+
+=cut
+
 sub program_options_handling_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -471,6 +730,14 @@ sub program_options_handling_ok
 	return;
 }
 
+=pod
+
+=item command_like(cmd, expected_stdout, test_name)
+
+Test function to check that the command runs successfully and the output matches with the given regular expression.
+
+=cut
+
 sub command_like
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -484,6 +751,15 @@ sub command_like
 	return;
 }
 
+=pod
+
+=item command_like_safe(cmd, expected_stdout, test_name)
+
+TODO
+
+
+=cut
+
 sub command_like_safe
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -505,6 +781,14 @@ sub command_like_safe
 	return;
 }
 
+=pod
+
+=item command_fails_like(cmd, expected_stderr, test_name)
+
+Test function to check that the command fails and the error message matches with the given regular expression.
+
+=cut
+
 sub command_fails_like
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -517,13 +801,26 @@ sub command_fails_like
 	return;
 }
 
-# Run a command and check its status and outputs.
-# The 5 arguments are:
-# - cmd: ref to list for command, options and arguments to run
-# - ret: expected exit status
-# - out: ref to list of re to be checked against stdout (all must match)
-# - err: ref to list of re to be checked against stderr (all must match)
-# - test_name: name of test
+
+=pod
+
+=item command_checks_all(cmd, expected_ret, out, err, test_name)
+
+Run a command and check its status and outputs.
+The 5 arguments are:
+
+- cmd: ref to list for command, options and arguments to run
+
+- ret: expected exit status
+
+- out: ref to list of re to be checked against stdout (all must match)
+
+- err: ref to list of re to be checked against stderr (all must match)
+
+- test_name: name of test
+
+=cut
+
 sub command_checks_all
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -560,4 +857,10 @@ sub command_checks_all
 	return;
 }
 
+=pod
+
+=back
+
+=cut
+
 1;
#7Michael Paquier
michael@paquier.xyz
In reply to: Ramanarayana (#6)
Re: Contribution to Perldoc for TestLib module in Postgres

On Fri, Mar 22, 2019 at 04:59:58PM +0530, Ramanarayana wrote:

Please find the first version of the patch for review. I was not sure what
some of the functions are used for and marked them with TODO.

This is only adding some documentation to an internal perl module we
ship, so it is far from being a critical part and we could still get
that into v12, still there are many patches waiting for integration
into v12 and this has showed up very late. Could you please register
this patch to the commit fest once you have a patch you think is fit
for merging? Here is the next commit fest link:
https://commitfest.postgresql.org/23/
--
Michael

#8Ramanarayana
raam.soft@gmail.com
In reply to: Michael Paquier (#7)
1 attachment(s)
Re: Contribution to Perldoc for TestLib module in Postgres

Hi,
Please find the updated patch. Added to the commitfest as well
Regards,
Ram.

Attachments:

v2_perldoc_testlib.patch.patchapplication/octet-stream; name=v2_perldoc_testlib.patch.patchDownload
diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm
index a164cdb..fe11f9d 100644
--- a/src/test/perl/TestLib.pm
+++ b/src/test/perl/TestLib.pm
@@ -1,564 +1,868 @@
-# TestLib, low-level routines and actions regression tests.
-#
-# This module contains a set of routines dedicated to environment setup for
-# a PostgreSQL regression test run 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;
-use warnings;
-
-use Config;
-use Cwd;
-use Exporter 'import';
-use Fcntl qw(:mode);
-use File::Basename;
-use File::Find;
-use File::Spec;
-use File::stat qw(stat);
-use File::Temp ();
-use IPC::Run;
-use SimpleTee;
-
-# specify a recent enough version of Test::More to support the done_testing() function
-use Test::More 0.87;
-
-our @EXPORT = qw(
-  generate_ascii_string
-  slurp_dir
-  slurp_file
-  append_to_file
-  check_mode_recursive
-  chmod_recursive
-  check_pg_config
-  system_or_bail
-  system_log
-  run_log
-  run_command
-
-  command_ok
-  command_fails
-  command_exit_is
-  program_help_ok
-  program_version_ok
-  program_options_handling_ok
-  command_like
-  command_like_safe
-  command_fails_like
-  command_checks_all
-
-  $windows_os
-);
-
-our ($windows_os, $tmp_check, $log_path, $test_logfile);
-
-BEGIN
-{
-
-	# 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};
-	delete $ENV{PG_COLOR};
-
-	$ENV{PGAPPNAME} = basename($0);
-
-	# Must be set early
-	$windows_os = $Config{osname} eq 'MSWin32' || $Config{osname} eq 'msys';
-}
-
-INIT
-{
-
-	# Return EPIPE instead of killing the process with SIGPIPE.  An affected
-	# test may still fail, but it's more likely to report useful facts.
-	$SIG{PIPE} = 'IGNORE';
-
-	# 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 my $testlog, '>', $test_logfile
-	  or die "could not open STDOUT to logfile \"$test_logfile\": $!";
-
-	# Hijack STDOUT and STDERR to the log file
-	open(my $orig_stdout, '>&', \*STDOUT);
-	open(my $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;
-}
-
-END
-{
-
-	# Preserve temporary directory for this test on failure
-	$File::Temp::KEEP_ALL = 1 unless all_tests_passing();
-}
-
-sub all_tests_passing
-{
-	my $fail_count = 0;
-	foreach my $status (Test::More->builder->summary)
-	{
-		return 0 unless $status;
-	}
-	return 1;
-}
-
-#
-# Helper functions
-#
-sub tempdir
-{
-	my ($prefix) = @_;
-	$prefix = "tmp_test" unless defined $prefix;
-	return File::Temp::tempdir(
-		$prefix . '_XXXX',
-		DIR     => $tmp_check,
-		CLEANUP => 1);
-}
-
-sub tempdir_short
-{
-
-	# Use a separate temp dir outside the build tree for the
-	# Unix-domain socket, to avoid file name length issues.
-	return File::Temp::tempdir(CLEANUP => 1);
-}
-
-# Return the real directory for a virtual path directory under msys.
-# The directory  must exist. If it's not an existing directory or we're
-# not under msys, return the input argument unchanged.
-sub real_dir
-{
-	my $dir = "$_[0]";
-	return $dir unless -d $dir;
-	return $dir unless $Config{osname} eq 'msys';
-	my $here = cwd;
-	chdir $dir;
-
-	# this odd way of calling 'pwd -W' is the only way that seems to work.
-	$dir = qx{sh -c "pwd -W"};
-	chomp $dir;
-	chdir $here;
-	return $dir;
-}
-
-sub system_log
-{
-	print("# Running: " . join(" ", @_) . "\n");
-	return system(@_);
-}
-
-sub system_or_bail
-{
-	if (system_log(@_) != 0)
-	{
-		BAIL_OUT("system $_[0] failed");
-	}
-	return;
-}
-
-sub run_log
-{
-	print("# Running: " . join(" ", @{ $_[0] }) . "\n");
-	return IPC::Run::run(@_);
-}
-
-sub run_command
-{
-	my ($cmd) = @_;
-	my ($stdout, $stderr);
-	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
-	chomp($stdout);
-	chomp($stderr);
-	return ($stdout, $stderr);
-}
-
-# Generate a string made of the given range of ASCII characters
-sub generate_ascii_string
-{
-	my ($from_char, $to_char) = @_;
-	my $res;
-
-	for my $i ($from_char .. $to_char)
-	{
-		$res .= sprintf("%c", $i);
-	}
-	return $res;
-}
-
-sub slurp_dir
-{
-	my ($dir) = @_;
-	opendir(my $dh, $dir)
-	  or die "could not opendir \"$dir\": $!";
-	my @direntries = readdir $dh;
-	closedir $dh;
-	return @direntries;
-}
-
-sub slurp_file
-{
-	my ($filename) = @_;
-	local $/;
-	open(my $in, '<', $filename)
-	  or die "could not read \"$filename\": $!";
-	my $contents = <$in>;
-	close $in;
-	$contents =~ s/\r//g if $Config{osname} eq 'msys';
-	return $contents;
-}
-
-sub append_to_file
-{
-	my ($filename, $str) = @_;
-	open my $fh, ">>", $filename
-	  or die "could not write \"$filename\": $!";
-	print $fh $str;
-	close $fh;
-	return;
-}
-
-# Check that all file/dir modes in a directory match the expected values,
-# ignoring the mode of any specified files.
-sub check_mode_recursive
-{
-	my ($dir, $expected_dir_mode, $expected_file_mode, $ignore_list) = @_;
-
-	# Result defaults to true
-	my $result = 1;
-
-	find(
-		{
-			follow_fast => 1,
-			wanted      => sub {
-				# Is file in the ignore list?
-				foreach my $ignore ($ignore_list ? @{$ignore_list} : [])
-				{
-					if ("$dir/$ignore" eq $File::Find::name)
-					{
-						return;
-					}
-				}
-
-				# Allow ENOENT.  A running server can delete files, such as
-				# those in pg_stat.  Other stat() failures are fatal.
-				my $file_stat = stat($File::Find::name);
-				unless (defined($file_stat))
-				{
-					my $is_ENOENT = $!{ENOENT};
-					my $msg = "unable to stat $File::Find::name: $!";
-					if ($is_ENOENT)
-					{
-						warn $msg;
-						return;
-					}
-					else
-					{
-						die $msg;
-					}
-				}
-
-				my $file_mode = S_IMODE($file_stat->mode);
-
-				# Is this a file?
-				if (S_ISREG($file_stat->mode))
-				{
-					if ($file_mode != $expected_file_mode)
-					{
-						print(
-							*STDERR,
-							sprintf("$File::Find::name mode must be %04o\n",
-								$expected_file_mode));
-
-						$result = 0;
-						return;
-					}
-				}
-
-				# Else a directory?
-				elsif (S_ISDIR($file_stat->mode))
-				{
-					if ($file_mode != $expected_dir_mode)
-					{
-						print(
-							*STDERR,
-							sprintf("$File::Find::name mode must be %04o\n",
-								$expected_dir_mode));
-
-						$result = 0;
-						return;
-					}
-				}
-
-				# Else something we can't handle
-				else
-				{
-					die "unknown file type for $File::Find::name";
-				}
-			}
-		},
-		$dir);
-
-	return $result;
-}
-
-# Change mode recursively on a directory
-sub chmod_recursive
-{
-	my ($dir, $dir_mode, $file_mode) = @_;
-
-	find(
-		{
-			follow_fast => 1,
-			wanted      => sub {
-				my $file_stat = stat($File::Find::name);
-
-				if (defined($file_stat))
-				{
-					chmod(
-						S_ISDIR($file_stat->mode) ? $dir_mode : $file_mode,
-						$File::Find::name
-					) or die "unable to chmod $File::Find::name";
-				}
-			}
-		},
-		$dir);
-	return;
-}
-
-# Check presence of a given regexp within pg_config.h for the installation
-# where tests are running, returning a match status result depending on
-# that.
-sub check_pg_config
-{
-	my ($regexp) = @_;
-	my ($stdout, $stderr);
-	my $result = IPC::Run::run [ 'pg_config', '--includedir' ], '>',
-	  \$stdout, '2>', \$stderr
-	  or die "could not execute pg_config";
-	chomp($stdout);
-
-	open my $pg_config_h, '<', "$stdout/pg_config.h" or die "$!";
-	my $match = (grep { /^$regexp/ } <$pg_config_h>);
-	close $pg_config_h;
-	return $match;
-}
-
-#
-# Test functions
-#
-sub command_ok
-{
-	local $Test::Builder::Level = $Test::Builder::Level + 1;
-	my ($cmd, $test_name) = @_;
-	my $result = run_log($cmd);
-	ok($result, $test_name);
-	return;
-}
-
-sub command_fails
-{
-	local $Test::Builder::Level = $Test::Builder::Level + 1;
-	my ($cmd, $test_name) = @_;
-	my $result = run_log($cmd);
-	ok(!$result, $test_name);
-	return;
-}
-
-sub command_exit_is
-{
-	local $Test::Builder::Level = $Test::Builder::Level + 1;
-	my ($cmd, $expected, $test_name) = @_;
-	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
-	# process's exit code, while on Unix, it's returned in the high bits
-	# of the exit code (see WEXITSTATUS macro in the standard <sys/wait.h>
-	# header file). IPC::Run's result function always returns exit code >> 8,
-	# 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);
-	is($result, $expected, $test_name);
-	return;
-}
-
-sub program_help_ok
-{
-	local $Test::Builder::Level = $Test::Builder::Level + 1;
-	my ($cmd) = @_;
-	my ($stdout, $stderr);
-	print("# Running: $cmd --help\n");
-	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");
-	return;
-}
-
-sub program_version_ok
-{
-	local $Test::Builder::Level = $Test::Builder::Level + 1;
-	my ($cmd) = @_;
-	my ($stdout, $stderr);
-	print("# Running: $cmd --version\n");
-	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");
-	return;
-}
-
-sub program_options_handling_ok
-{
-	local $Test::Builder::Level = $Test::Builder::Level + 1;
-	my ($cmd) = @_;
-	my ($stdout, $stderr);
-	print("# Running: $cmd --not-a-valid-option\n");
-	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");
-	return;
-}
-
-sub command_like
-{
-	local $Test::Builder::Level = $Test::Builder::Level + 1;
-	my ($cmd, $expected_stdout, $test_name) = @_;
-	my ($stdout, $stderr);
-	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
-	ok($result, "$test_name: exit code 0");
-	is($stderr, '', "$test_name: no stderr");
-	like($stdout, $expected_stdout, "$test_name: matches");
-	return;
-}
-
-sub command_like_safe
-{
-	local $Test::Builder::Level = $Test::Builder::Level + 1;
-
-	# Doesn't rely on detecting end of file on the file descriptors,
-	# which can fail, causing the process to hang, notably on Msys
-	# when used with 'pg_ctl start'
-	my ($cmd, $expected_stdout, $test_name) = @_;
-	my ($stdout, $stderr);
-	my $stdoutfile = File::Temp->new();
-	my $stderrfile = File::Temp->new();
-	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	my $result = IPC::Run::run $cmd, '>', $stdoutfile, '2>', $stderrfile;
-	$stdout = slurp_file($stdoutfile);
-	$stderr = slurp_file($stderrfile);
-	ok($result, "$test_name: exit code 0");
-	is($stderr, '', "$test_name: no stderr");
-	like($stdout, $expected_stdout, "$test_name: matches");
-	return;
-}
-
-sub command_fails_like
-{
-	local $Test::Builder::Level = $Test::Builder::Level + 1;
-	my ($cmd, $expected_stderr, $test_name) = @_;
-	my ($stdout, $stderr);
-	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
-	ok(!$result, "$test_name: exit code not 0");
-	like($stderr, $expected_stderr, "$test_name: matches");
-	return;
-}
-
-# Run a command and check its status and outputs.
-# The 5 arguments are:
-# - cmd: ref to list for command, options and arguments to run
-# - ret: expected exit status
-# - out: ref to list of re to be checked against stdout (all must match)
-# - err: ref to list of re to be checked against stderr (all must match)
-# - test_name: name of test
-sub command_checks_all
-{
-	local $Test::Builder::Level = $Test::Builder::Level + 1;
-
-	my ($cmd, $expected_ret, $out, $err, $test_name) = @_;
-
-	# run command
-	my ($stdout, $stderr);
-	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	IPC::Run::run($cmd, '>', \$stdout, '2>', \$stderr);
-
-	# See http://perldoc.perl.org/perlvar.html#%24CHILD_ERROR
-	my $ret = $?;
-	die "command exited with signal " . ($ret & 127)
-	  if $ret & 127;
-	$ret = $ret >> 8;
-
-	# check status
-	ok($ret == $expected_ret,
-		"$test_name status (got $ret vs expected $expected_ret)");
-
-	# check stdout
-	for my $re (@$out)
-	{
-		like($stdout, $re, "$test_name stdout /$re/");
-	}
-
-	# check stderr
-	for my $re (@$err)
-	{
-		like($stderr, $re, "$test_name stderr /$re/");
-	}
-
-	return;
-}
-
-1;
+=pod
+
+=head1 NAME
+
+TestLib - class containing routines for environment setup, low-level routines for command execution, logging and test functions 
+
+=head1 SYNOPSIS
+
+  use TestLib;
+	
+  #Checks if all the tests passed or not
+  all_tests_passing()
+
+  #Creates a temporary directory
+  tempdir(prefix)
+	
+  #Creates a temporary directory outside the build tree for the Unix-domain socket
+  tempdir_short()
+
+  #Returns the real directory for a virtual path directory under msys
+  real_dir(dir)
+
+  #Runs the input command and returns its exit status
+  system_log()	
+
+  #Runs the input command. On failure terminates the execution of other tests
+  system_or_bail()	
+
+  #Runs the input command. Returns both the stdout and stderr
+  run_log()	
+
+  #Returns the output after running a command
+  run_command(dir)	
+
+  #Generate a string made of the given range of ASCII characters
+  generate_ascii_string(from_char, to_char)	
+
+  #Returns the contents of a directory
+  slurp_dir(dir)	
+
+  #Returns the contents of a file
+  slurp_file(filename)	
+
+  #Appends a string at the end of a given file
+  append_to_file(filename, str)	
+
+  #Check that all file/dir modes in a directory match the expected values
+  check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
+
+  #Change mode recursively on a directory
+  chmod_recursive(dir, dir_mode, file_mode)	
+
+  #Check presence of a given regexp within pg_config.h
+  check_pg_config(regexp)	
+
+  #Test function to check that the command runs successfully
+  command_ok(cmd, test_name)	
+
+  #Test function to check that the command fails
+  command_fails(cmd, test_name)	
+
+  #Test function to check that the command exit code matches with the expected exit code
+  command_exit_is(cmd, expected, test_name)	
+
+  #Test function to check that the command supports --help option 
+  program_help_ok(cmd)	
+
+  #Test function to check that the command supports --version option 
+  program_version_ok(cmd)	
+
+  #Test function to check that a command with invalid option returns non-zero exit code and error message
+  program_options_handling_ok(cmd)	
+
+  #Test function to check that the command runs successfully and the output 
+  matches with the given regular expression
+  command_like(cmd, expected_stdout, test_name)	
+
+  #Test function to check that the command runs successfully and the output 
+  matches with the given regular expression
+  command_like_safe(cmd, expected_stdout, test_name)	
+
+  #Test function to check that the command fails and the error message
+  matches with the given regular expression
+  command_fails_like(cmd, expected_stderr, test_name)	
+
+  #Test function to run a command and check its status and outputs
+  command_checks_all(cmd, expected_ret, out, err, test_name)	
+		
+
+=head1 DESCRIPTION
+
+Testlib module contains a set of routines dedicated to environment setup for
+a PostgreSQL regression test run 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.
+
+The IPC::Run module is required.
+
+=cut
+
+package TestLib;
+
+use strict;
+use warnings;
+
+use Config;
+use Cwd;
+use Exporter 'import';
+use Fcntl qw(:mode);
+use File::Basename;
+use File::Find;
+use File::Spec;
+use File::stat qw(stat);
+use File::Temp ();
+use IPC::Run;
+use SimpleTee;
+
+# specify a recent enough version of Test::More to support the done_testing() function
+use Test::More 0.87;
+
+our @EXPORT = qw(
+  generate_ascii_string
+  slurp_dir
+  slurp_file
+  append_to_file
+  check_mode_recursive
+  chmod_recursive
+  check_pg_config
+  system_or_bail
+  system_log
+  run_log
+  run_command
+
+  command_ok
+  command_fails
+  command_exit_is
+  program_help_ok
+  program_version_ok
+  program_options_handling_ok
+  command_like
+  command_like_safe
+  command_fails_like
+  command_checks_all
+
+  $windows_os
+);
+
+our ($windows_os, $tmp_check, $log_path, $test_logfile);
+
+BEGIN
+{
+
+	# 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};
+	delete $ENV{PG_COLOR};
+
+	$ENV{PGAPPNAME} = basename($0);
+
+	# Must be set early
+	$windows_os = $Config{osname} eq 'MSWin32' || $Config{osname} eq 'msys';
+}
+
+INIT
+{
+
+	# Return EPIPE instead of killing the process with SIGPIPE.  An affected
+	# test may still fail, but it's more likely to report useful facts.
+	$SIG{PIPE} = 'IGNORE';
+
+	# 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 my $testlog, '>', $test_logfile
+	  or die "could not open STDOUT to logfile \"$test_logfile\": $!";
+
+	# Hijack STDOUT and STDERR to the log file
+	open(my $orig_stdout, '>&', \*STDOUT);
+	open(my $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;
+}
+
+END
+{
+
+	# Preserve temporary directory for this test on failure
+	$File::Temp::KEEP_ALL = 1 unless all_tests_passing();
+}
+
+
+=pod
+
+=head1 METHODS
+
+=over
+
+=item all_tests_passing()
+
+Returns 1 if all the tests pass. Otherwise returns 0
+
+=cut
+
+sub all_tests_passing
+{
+	my $fail_count = 0;
+	foreach my $status (Test::More->builder->summary)
+	{
+		return 0 unless $status;
+	}
+	return 1;
+}
+
+=pod
+
+=item tempdir(prefix)
+
+Creates a temporary directory with name prefix_XXXX if prefix argument is passed. Otherwise a temporary directory with name tmp_test_XXXX is created. 
+XXXX represents four random characters.Temporary directory is created inside the folder represented by $tmp_check and it is deleted at the end of the tests.
+
+=cut
+
+sub tempdir
+{
+	my ($prefix) = @_;
+	$prefix = "tmp_test" unless defined $prefix;
+	return File::Temp::tempdir(
+		$prefix . '_XXXX',
+		DIR     => $tmp_check,
+		CLEANUP => 1);
+}
+
+
+=pod
+
+=item tempdir_short()
+
+Use a separate temp dir outside the build tree for the
+Unix-domain socket, to avoid file name length issues.
+
+=cut
+
+sub tempdir_short
+{
+
+	return File::Temp::tempdir(CLEANUP => 1);
+}
+
+=pod
+
+=item real_dir(dir)
+
+Return the real directory for a virtual path directory under msys.
+The directory  must exist. If it's not an existing directory or we're
+not under msys, return the input argument unchanged.
+
+=cut
+
+sub real_dir
+{
+	my $dir = "$_[0]";
+	return $dir unless -d $dir;
+	return $dir unless $Config{osname} eq 'msys';
+	my $here = cwd;
+	chdir $dir;
+
+	# this odd way of calling 'pwd -W' is the only way that seems to work.
+	$dir = qx{sh -c "pwd -W"};
+	chomp $dir;
+	chdir $here;
+	return $dir;
+}
+
+=pod
+
+=item system_log()
+
+Runs the input command and returns its exit status
+
+=cut
+
+sub system_log
+{
+	print("# Running: " . join(" ", @_) . "\n");
+	return system(@_);
+}
+
+=pod
+
+=item system_or_bail()
+
+Runs the input command. On failure terminates the execution of other tests
+
+=cut
+
+sub system_or_bail
+{
+	if (system_log(@_) != 0)
+	{
+		BAIL_OUT("system $_[0] failed");
+	}
+	return;
+}
+
+=pod
+
+=item run_log()
+
+Returns the output after running a command
+
+=cut
+
+sub run_log
+{
+	print("# Running: " . join(" ", @{ $_[0] }) . "\n");
+	return IPC::Run::run(@_);
+}
+
+=pod
+
+=item run_command(cmd)
+
+Returns the output after running the command
+
+=cut
+
+sub run_command
+{
+	my ($cmd) = @_;
+	my ($stdout, $stderr);
+	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
+	chomp($stdout);
+	chomp($stderr);
+	return ($stdout, $stderr);
+}
+
+=pod
+
+=item generate_ascii_string(from_char, to_char)
+
+Generate a string made of the given range of ASCII characters
+
+=cut
+
+sub generate_ascii_string
+{
+	my ($from_char, $to_char) = @_;
+	my $res;
+
+	for my $i ($from_char .. $to_char)
+	{
+		$res .= sprintf("%c", $i);
+	}
+	return $res;
+}
+
+=pod
+
+=item slurp_dir(dir)
+
+Returns the contents of a directory
+
+=cut
+
+sub slurp_dir
+{
+	my ($dir) = @_;
+	opendir(my $dh, $dir)
+	  or die "could not opendir \"$dir\": $!";
+	my @direntries = readdir $dh;
+	closedir $dh;
+	return @direntries;
+}
+
+=pod
+
+=item slurp_file(filename)
+
+Returns the contents of a file
+
+=cut
+
+sub slurp_file
+{
+	my ($filename) = @_;
+	local $/;
+	open(my $in, '<', $filename)
+	  or die "could not read \"$filename\": $!";
+	my $contents = <$in>;
+	close $in;
+	$contents =~ s/\r//g if $Config{osname} eq 'msys';
+	return $contents;
+}
+
+=pod
+
+=item append_to_file(filename, str)
+
+Append a string at the end of a given file
+
+=cut
+
+
+sub append_to_file
+{
+	my ($filename, $str) = @_;
+	open my $fh, ">>", $filename
+	  or die "could not write \"$filename\": $!";
+	print $fh $str;
+	close $fh;
+	return;
+}
+
+
+=pod
+
+=item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
+
+Check that all file/dir modes in a directory match the expected values,
+ignoring the mode of any specified files.
+
+=cut
+
+sub check_mode_recursive
+{
+	my ($dir, $expected_dir_mode, $expected_file_mode, $ignore_list) = @_;
+
+	# Result defaults to true
+	my $result = 1;
+
+	find(
+		{
+			follow_fast => 1,
+			wanted      => sub {
+				# Is file in the ignore list?
+				foreach my $ignore ($ignore_list ? @{$ignore_list} : [])
+				{
+					if ("$dir/$ignore" eq $File::Find::name)
+					{
+						return;
+					}
+				}
+
+				# Allow ENOENT.  A running server can delete files, such as
+				# those in pg_stat.  Other stat() failures are fatal.
+				my $file_stat = stat($File::Find::name);
+				unless (defined($file_stat))
+				{
+					my $is_ENOENT = $!{ENOENT};
+					my $msg = "unable to stat $File::Find::name: $!";
+					if ($is_ENOENT)
+					{
+						warn $msg;
+						return;
+					}
+					else
+					{
+						die $msg;
+					}
+				}
+
+				my $file_mode = S_IMODE($file_stat->mode);
+
+				# Is this a file?
+				if (S_ISREG($file_stat->mode))
+				{
+					if ($file_mode != $expected_file_mode)
+					{
+						print(
+							*STDERR,
+							sprintf("$File::Find::name mode must be %04o\n",
+								$expected_file_mode));
+
+						$result = 0;
+						return;
+					}
+				}
+
+				# Else a directory?
+				elsif (S_ISDIR($file_stat->mode))
+				{
+					if ($file_mode != $expected_dir_mode)
+					{
+						print(
+							*STDERR,
+							sprintf("$File::Find::name mode must be %04o\n",
+								$expected_dir_mode));
+
+						$result = 0;
+						return;
+					}
+				}
+
+				# Else something we can't handle
+				else
+				{
+					die "unknown file type for $File::Find::name";
+				}
+			}
+		},
+		$dir);
+
+	return $result;
+}
+
+=pod
+
+=item chmod_recursive(dir, dir_mode, file_mode)
+
+Change mode recursively on a directory
+
+=cut
+
+sub chmod_recursive
+{
+	my ($dir, $dir_mode, $file_mode) = @_;
+
+	find(
+		{
+			follow_fast => 1,
+			wanted      => sub {
+				my $file_stat = stat($File::Find::name);
+
+				if (defined($file_stat))
+				{
+					chmod(
+						S_ISDIR($file_stat->mode) ? $dir_mode : $file_mode,
+						$File::Find::name
+					) or die "unable to chmod $File::Find::name";
+				}
+			}
+		},
+		$dir);
+	return;
+}
+
+
+=pod
+
+=item check_pg_config(regexp)
+
+Check presence of a given regexp within pg_config.h for the installation
+where tests are running, returning a match status result depending on
+that.
+
+=cut
+
+sub check_pg_config
+{
+	my ($regexp) = @_;
+	my ($stdout, $stderr);
+	my $result = IPC::Run::run [ 'pg_config', '--includedir' ], '>',
+	  \$stdout, '2>', \$stderr
+	  or die "could not execute pg_config";
+	chomp($stdout);
+
+	open my $pg_config_h, '<', "$stdout/pg_config.h" or die "$!";
+	my $match = (grep { /^$regexp/ } <$pg_config_h>);
+	close $pg_config_h;
+	return $match;
+}
+
+
+=pod
+
+=item command_ok(cmd, test_name)
+
+Test function to check that the command runs successfully
+
+=cut
+
+sub command_ok
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd, $test_name) = @_;
+	my $result = run_log($cmd);
+	ok($result, $test_name);
+	return;
+}
+
+=pod
+
+=item command_fails(cmd, test_name)
+
+Test function to check that the command fails
+
+=cut
+
+sub command_fails
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd, $test_name) = @_;
+	my $result = run_log($cmd);
+	ok(!$result, $test_name);
+	return;
+}
+
+=pod
+
+=item command_exit_is(cmd, expected, test_name)
+
+Test function to check that the command exit code matches with the expected exit code.
+
+=cut
+
+sub command_exit_is
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd, $expected, $test_name) = @_;
+	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
+	# process's exit code, while on Unix, it's returned in the high bits
+	# of the exit code (see WEXITSTATUS macro in the standard <sys/wait.h>
+	# header file). IPC::Run's result function always returns exit code >> 8,
+	# 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);
+	is($result, $expected, $test_name);
+	return;
+}
+
+=pod
+
+=item program_help_ok(cmd)
+
+Test function to check that the command supports --help option.
+
+=cut
+
+sub program_help_ok
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd) = @_;
+	my ($stdout, $stderr);
+	print("# Running: $cmd --help\n");
+	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");
+	return;
+}
+
+=pod
+
+=item program_version_ok(cmd)
+
+Test function to check that the command supports --version option.
+
+=cut
+
+sub program_version_ok
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd) = @_;
+	my ($stdout, $stderr);
+	print("# Running: $cmd --version\n");
+	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");
+	return;
+}
+
+=pod
+
+=item program_options_handling_ok(cmd)
+
+Test function to check that a command with invalid option returns non-zero exit code and error message.
+
+=cut
+
+sub program_options_handling_ok
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd) = @_;
+	my ($stdout, $stderr);
+	print("# Running: $cmd --not-a-valid-option\n");
+	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");
+	return;
+}
+
+=pod
+
+=item command_like(cmd, expected_stdout, test_name)
+
+Test function to check that the command runs successfully and the output matches with the given regular expression.
+
+=cut
+
+sub command_like
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd, $expected_stdout, $test_name) = @_;
+	my ($stdout, $stderr);
+	print("# Running: " . join(" ", @{$cmd}) . "\n");
+	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
+	ok($result, "$test_name: exit code 0");
+	is($stderr, '', "$test_name: no stderr");
+	like($stdout, $expected_stdout, "$test_name: matches");
+	return;
+}
+
+=pod
+
+=item command_like_safe(cmd, expected_stdout, test_name)
+
+Test function to check that the command runs successfully and the output matches with the given regular expression.
+
+
+=cut
+
+sub command_like_safe
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+	# Doesn't rely on detecting end of file on the file descriptors,
+	# which can fail, causing the process to hang, notably on Msys
+	# when used with 'pg_ctl start'
+	my ($cmd, $expected_stdout, $test_name) = @_;
+	my ($stdout, $stderr);
+	my $stdoutfile = File::Temp->new();
+	my $stderrfile = File::Temp->new();
+	print("# Running: " . join(" ", @{$cmd}) . "\n");
+	my $result = IPC::Run::run $cmd, '>', $stdoutfile, '2>', $stderrfile;
+	$stdout = slurp_file($stdoutfile);
+	$stderr = slurp_file($stderrfile);
+	ok($result, "$test_name: exit code 0");
+	is($stderr, '', "$test_name: no stderr");
+	like($stdout, $expected_stdout, "$test_name: matches");
+	return;
+}
+
+=pod
+
+=item command_fails_like(cmd, expected_stderr, test_name)
+
+Test function to check that the command fails and the error message matches with the given regular expression.
+
+=cut
+
+sub command_fails_like
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd, $expected_stderr, $test_name) = @_;
+	my ($stdout, $stderr);
+	print("# Running: " . join(" ", @{$cmd}) . "\n");
+	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
+	ok(!$result, "$test_name: exit code not 0");
+	like($stderr, $expected_stderr, "$test_name: matches");
+	return;
+}
+
+
+=pod
+
+=item command_checks_all(cmd, expected_ret, out, err, test_name)
+
+Run a command and check its status and outputs.
+The 5 arguments are:
+
+- cmd: ref to list for command, options and arguments to run
+
+- ret: expected exit status
+
+- out: ref to list of re to be checked against stdout (all must match)
+
+- err: ref to list of re to be checked against stderr (all must match)
+
+- test_name: name of test
+
+=cut
+
+sub command_checks_all
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+	my ($cmd, $expected_ret, $out, $err, $test_name) = @_;
+
+	# run command
+	my ($stdout, $stderr);
+	print("# Running: " . join(" ", @{$cmd}) . "\n");
+	IPC::Run::run($cmd, '>', \$stdout, '2>', \$stderr);
+
+	# See http://perldoc.perl.org/perlvar.html#%24CHILD_ERROR
+	my $ret = $?;
+	die "command exited with signal " . ($ret & 127)
+	  if $ret & 127;
+	$ret = $ret >> 8;
+
+	# check status
+	ok($ret == $expected_ret,
+		"$test_name status (got $ret vs expected $expected_ret)");
+
+	# check stdout
+	for my $re (@$out)
+	{
+		like($stdout, $re, "$test_name stdout /$re/");
+	}
+
+	# check stderr
+	for my $re (@$err)
+	{
+		like($stderr, $re, "$test_name stderr /$re/");
+	}
+
+	return;
+}
+
+=pod
+
+=back
+
+=cut
+
+1;
diff --git a/src/test/perl/TestLib2.pm b/src/test/perl/TestLib2.pm
new file mode 100644
index 0000000..3bef7b8
--- /dev/null
+++ b/src/test/perl/TestLib2.pm
@@ -0,0 +1,874 @@
+=pod
+
+=head1 NAME
+
+TestLib - class containing routines for environment setup, low-level routines for command execution, logging and test functions 
+
+=head1 SYNOPSIS
+
+  use TestLib;
+	
+  #Checks if all the tests passed or not
+  all_tests_passing()
+
+  #Creates a temporary directory
+  tempdir(prefix)
+	
+  #Creates a temporary directory outside the build tree for the Unix-domain socket
+  tempdir_short()
+
+  #Returns the real directory for a virtual path directory under msys
+  real_dir(dir)
+
+  #Runs the command which is passed as an argument 
+  system_log()	
+
+  #Runs the command which is passed as an argument. On failure abandons all other tests
+  system_or_bail()	
+
+  #Runs the command which is passed as an argument.
+  run_log()	
+
+  #Returns the output after running a command
+  run_command(dir)	
+
+  #Generate a string made of the given range of ASCII characters
+  generate_ascii_string(from_char, to_char)	
+
+  #Read the contents of the directory
+  slurp_dir(dir)	
+
+  #Read the contents of the file 
+  slurp_file(filename)	
+
+  #Appends a string at the end of a given file
+  append_to_file(filename, str)	
+
+  #Check that all file/dir modes in a directory match the expected values
+  check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
+
+  #Change mode recursively on a directory
+  chmod_recursive(dir, dir_mode, file_mode)	
+
+  #Check presence of a given regexp within pg_config.h
+  check_pg_config(regexp)	
+
+  #Test function to check that the command runs successfully
+  command_ok(cmd, test_name)	
+
+  #Test function to check that the command fails
+  command_fails(cmd, test_name)	
+
+  #Test function to check that the command exit code matches with the expected exit code
+  command_exit_is(cmd, expected, test_name)	
+
+  #Test function to check that the command supports --help option 
+  program_help_ok(cmd)	
+
+  #Test function to check that the command supports --version option 
+  program_version_ok(cmd)	
+
+  #Test function to check that a command with invalid option returns non-zero exit code and error message
+  program_options_handling_ok(cmd)	
+
+  #Test function to check that the command runs successfully and the output 
+  matches with the given regular expression
+  command_like(cmd, expected_stdout, test_name)	
+
+  #TODO
+  command_like_safe(cmd, expected_stdout, test_name)	
+
+  #Test function to check that the command fails and the error message
+  matches with the given regular expression
+  command_fails_like(cmd, expected_stderr, test_name)	
+
+  #Test function to run a command and check its status and outputs
+  command_checks_all(cmd, expected_ret, out, err, test_name)	
+		
+
+=head1 DESCRIPTION
+
+Testlib module contains a set of routines dedicated to environment setup for
+a PostgreSQL regression test run 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.
+
+The IPC::Run module is required.
+
+=cut
+
+package TestLib;
+
+use strict;
+use warnings;
+
+use Config;
+use Cwd;
+use Exporter 'import';
+use Fcntl qw(:mode);
+use File::Basename;
+use File::Find;
+use File::Spec;
+use File::stat qw(stat);
+use File::Temp ();
+use IPC::Run;
+use SimpleTee;
+
+# specify a recent enough version of Test::More to support the done_testing() function
+use Test::More 0.87;
+
+our @EXPORT = qw(
+  generate_ascii_string
+  slurp_dir
+  slurp_file
+  append_to_file
+  check_mode_recursive
+  chmod_recursive
+  check_pg_config
+  system_or_bail
+  system_log
+  run_log
+  run_command
+
+  command_ok
+  command_fails
+  command_exit_is
+  program_help_ok
+  program_version_ok
+  program_options_handling_ok
+  command_like
+  command_like_safe
+  command_fails_like
+  command_checks_all
+
+  $windows_os
+);
+
+our ($windows_os, $tmp_check, $log_path, $test_logfile);
+
+BEGIN
+{
+
+	# 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};
+
+	$ENV{PGAPPNAME} = basename($0);
+
+	# Must be set early
+	$windows_os = $Config{osname} eq 'MSWin32' || $Config{osname} eq 'msys';
+}
+
+INIT
+{
+
+	# Return EPIPE instead of killing the process with SIGPIPE.  An affected
+	# test may still fail, but it's more likely to report useful facts.
+	$SIG{PIPE} = 'IGNORE';
+
+	# 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 my $testlog, '>', $test_logfile
+	  or die "could not open STDOUT to logfile \"$test_logfile\": $!";
+
+	# Hijack STDOUT and STDERR to the log file
+	open(my $orig_stdout, '>&', \*STDOUT);
+	open(my $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;
+}
+
+END
+{
+
+	# Preserve temporary directory for this test on failure
+	$File::Temp::KEEP_ALL = 1 unless all_tests_passing();
+}
+
+
+=pod
+
+=head1 METHODS
+
+=over
+
+=item all_tests_passing()
+
+Returns 1 if all the tests pass. Otherwise returns 0
+
+=cut
+
+sub all_tests_passing
+{
+	my $fail_count = 0;
+	foreach my $status (Test::More->builder->summary)
+	{
+		return 0 unless $status;
+	}
+	return 1;
+}
+
+=pod
+
+=item tempdir(prefix)
+
+Creates a temporary directory with name prefix_XXXX if prefix argument is passed. Otherwise a temporary directory with name tmp_test_XXXX is created. 
+XXXX represents four random characters.Temporary directory is created inside the folder represented by $tmp_check and it is deleted at the end of the tests.
+
+=cut
+
+sub tempdir
+{
+	my ($prefix) = @_;
+	$prefix = "tmp_test" unless defined $prefix;
+	return File::Temp::tempdir(
+		$prefix . '_XXXX',
+		DIR     => $tmp_check,
+		CLEANUP => 1);
+}
+
+
+=pod
+
+=item tempdir_short()
+
+Use a separate temp dir outside the build tree for the
+Unix-domain socket, to avoid file name length issues.
+
+=cut
+
+sub tempdir_short
+{
+
+	return File::Temp::tempdir(CLEANUP => 1);
+}
+
+=pod
+
+=item real_dir(dir)
+
+Return the real directory for a virtual path directory under msys.
+The directory  must exist. If it's not an existing directory or we're
+not under msys, return the input argument unchanged.
+
+=cut
+
+sub real_dir
+{
+	my $dir = "$_[0]";
+	return $dir unless -d $dir;
+	return $dir unless $Config{osname} eq 'msys';
+	my $here = cwd;
+	chdir $dir;
+
+	# this odd way of calling 'pwd -W' is the only way that seems to work.
+	$dir = qx{sh -c "pwd -W"};
+	chomp $dir;
+	chdir $here;
+	return $dir;
+}
+
+=pod
+
+=item system_log()
+
+Runs the command which is passed as argument to the function and returns 0 if successful. Otherwise returns non-zero value.
+
+=cut
+
+sub system_log
+{
+	print("# Running: " . join(" ", @_) . "\n");
+	return system(@_);
+}
+
+=pod
+
+=item system_or_bail()
+
+Runs the command which is passed as argument to the function. On failure it abandons further tests and exits the program.
+
+=cut
+
+sub system_or_bail
+{
+	if (system_log(@_) != 0)
+	{
+		BAIL_OUT("system $_[0] failed");
+	}
+	return;
+}
+
+=pod
+
+=item run_log()
+
+Runs the command which is passed as argument to the function
+
+=cut
+
+sub run_log
+{
+	print("# Running: " . join(" ", @{ $_[0] }) . "\n");
+	return IPC::Run::run(@_);
+}
+
+=pod
+
+=item run_command(cmd)
+
+Returns the output after running the command
+
+=cut
+
+sub run_command
+{
+	my ($cmd) = @_;
+	my ($stdout, $stderr);
+	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
+	chomp($stdout);
+	chomp($stderr);
+	return ($stdout, $stderr);
+}
+
+=pod
+
+=item generate_ascii_string(from_char, to_char)
+
+Generate a string made of the given range of ASCII characters
+
+=cut
+
+sub generate_ascii_string
+{
+	my ($from_char, $to_char) = @_;
+	my $res;
+
+	for my $i ($from_char .. $to_char)
+	{
+		$res .= sprintf("%c", $i);
+	}
+	return $res;
+}
+
+=pod
+
+=item slurp_dir(dir)
+
+Opens the directory provided as an argument to the function.
+
+If the opendir function returns false, the error message is printed to STDERR and comes out of the program.
+
+If the directory is opened successfully, the readdir returns the next directory entry for a directory
+opened by opendir.
+
+=cut
+
+sub slurp_dir
+{
+	my ($dir) = @_;
+	opendir(my $dh, $dir)
+	  or die "could not opendir \"$dir\": $!";
+	my @direntries = readdir $dh;
+	closedir $dh;
+	return @direntries;
+}
+
+=pod
+
+=item slurp_file(filename)
+
+Opens the file provided as an argument to the function in read mode(as indicated by <).
+
+If the open function returns 0, the error message is printed to STDERR and comes out of the program.
+
+If the file is opened successfully, the contents are read using the filehandle and the file is closed.
+
+=cut
+
+sub slurp_file
+{
+	my ($filename) = @_;
+	local $/;
+	open(my $in, '<', $filename)
+	  or die "could not read \"$filename\": $!";
+	my $contents = <$in>;
+	close $in;
+	$contents =~ s/\r//g if $Config{osname} eq 'msys';
+	return $contents;
+}
+
+=pod
+
+=item append_to_file(filename, str)
+
+Append a string at the end of a given file
+
+=cut
+
+
+sub append_to_file
+{
+	my ($filename, $str) = @_;
+	open my $fh, ">>", $filename
+	  or die "could not write \"$filename\": $!";
+	print $fh $str;
+	close $fh;
+	return;
+}
+
+
+=pod
+
+=item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
+
+Check that all file/dir modes in a directory match the expected values,
+ignoring the mode of any specified files.
+
+=cut
+
+sub check_mode_recursive
+{
+	my ($dir, $expected_dir_mode, $expected_file_mode, $ignore_list) = @_;
+
+	# Result defaults to true
+	my $result = 1;
+
+	find(
+		{
+			follow_fast => 1,
+			wanted      => sub {
+				# Is file in the ignore list?
+				foreach my $ignore ($ignore_list ? @{$ignore_list} : [])
+				{
+					if ("$dir/$ignore" eq $File::Find::name)
+					{
+						return;
+					}
+				}
+
+				# Allow ENOENT.  A running server can delete files, such as
+				# those in pg_stat.  Other stat() failures are fatal.
+				my $file_stat = stat($File::Find::name);
+				unless (defined($file_stat))
+				{
+					my $is_ENOENT = $!{ENOENT};
+					my $msg = "unable to stat $File::Find::name: $!";
+					if ($is_ENOENT)
+					{
+						warn $msg;
+						return;
+					}
+					else
+					{
+						die $msg;
+					}
+				}
+
+				my $file_mode = S_IMODE($file_stat->mode);
+
+				# Is this a file?
+				if (S_ISREG($file_stat->mode))
+				{
+					if ($file_mode != $expected_file_mode)
+					{
+						print(
+							*STDERR,
+							sprintf("$File::Find::name mode must be %04o\n",
+								$expected_file_mode));
+
+						$result = 0;
+						return;
+					}
+				}
+
+				# Else a directory?
+				elsif (S_ISDIR($file_stat->mode))
+				{
+					if ($file_mode != $expected_dir_mode)
+					{
+						print(
+							*STDERR,
+							sprintf("$File::Find::name mode must be %04o\n",
+								$expected_dir_mode));
+
+						$result = 0;
+						return;
+					}
+				}
+
+				# Else something we can't handle
+				else
+				{
+					die "unknown file type for $File::Find::name";
+				}
+			}
+		},
+		$dir);
+
+	return $result;
+}
+
+=pod
+
+=item chmod_recursive(dir, dir_mode, file_mode)
+
+Change mode recursively on a directory
+
+=cut
+
+sub chmod_recursive
+{
+	my ($dir, $dir_mode, $file_mode) = @_;
+
+	find(
+		{
+			follow_fast => 1,
+			wanted      => sub {
+				my $file_stat = stat($File::Find::name);
+
+				if (defined($file_stat))
+				{
+					chmod(
+						S_ISDIR($file_stat->mode) ? $dir_mode : $file_mode,
+						$File::Find::name
+					) or die "unable to chmod $File::Find::name";
+				}
+			}
+		},
+		$dir);
+	return;
+}
+
+
+=pod
+
+=item check_pg_config(regexp)
+
+Check presence of a given regexp within pg_config.h for the installation
+where tests are running, returning a match status result depending on
+that.
+
+=cut
+
+sub check_pg_config
+{
+	my ($regexp) = @_;
+	my ($stdout, $stderr);
+	my $result = IPC::Run::run [ 'pg_config', '--includedir' ], '>',
+	  \$stdout, '2>', \$stderr
+	  or die "could not execute pg_config";
+	chomp($stdout);
+
+	open my $pg_config_h, '<', "$stdout/pg_config.h" or die "$!";
+	my $match = (grep { /^$regexp/ } <$pg_config_h>);
+	close $pg_config_h;
+	return $match;
+}
+
+
+=pod
+
+=item command_ok(cmd, test_name)
+
+Test function to check that the command runs successfully
+
+=cut
+
+sub command_ok
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd, $test_name) = @_;
+	my $result = run_log($cmd);
+	ok($result, $test_name);
+	return;
+}
+
+=pod
+
+=item command_fails(cmd, test_name)
+
+Test function to check that the command fails
+
+=cut
+
+sub command_fails
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd, $test_name) = @_;
+	my $result = run_log($cmd);
+	ok(!$result, $test_name);
+	return;
+}
+
+=pod
+
+=item command_exit_is(cmd, expected, test_name)
+
+Test function to check that the command exit code matches with the expected exit code.
+
+=cut
+
+sub command_exit_is
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd, $expected, $test_name) = @_;
+	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
+	# process's exit code, while on Unix, it's returned in the high bits
+	# of the exit code (see WEXITSTATUS macro in the standard <sys/wait.h>
+	# header file). IPC::Run's result function always returns exit code >> 8,
+	# 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);
+	is($result, $expected, $test_name);
+	return;
+}
+
+=pod
+
+=item program_help_ok(cmd)
+
+Test function to check that the command supports --help option.
+
+=cut
+
+sub program_help_ok
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd) = @_;
+	my ($stdout, $stderr);
+	print("# Running: $cmd --help\n");
+	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");
+	return;
+}
+
+=pod
+
+=item program_version_ok(cmd)
+
+Test function to check that the command supports --version option.
+
+=cut
+
+sub program_version_ok
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd) = @_;
+	my ($stdout, $stderr);
+	print("# Running: $cmd --version\n");
+	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");
+	return;
+}
+
+=pod
+
+=item program_options_handling_ok(cmd)
+
+Test function to check that a command with invalid option returns non-zero exit code and error message.
+
+=cut
+
+sub program_options_handling_ok
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd) = @_;
+	my ($stdout, $stderr);
+	print("# Running: $cmd --not-a-valid-option\n");
+	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");
+	return;
+}
+
+=pod
+
+=item command_like(cmd, expected_stdout, test_name)
+
+Test function to check that the command runs successfully and the output matches with the given regular expression.
+
+=cut
+
+sub command_like
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd, $expected_stdout, $test_name) = @_;
+	my ($stdout, $stderr);
+	print("# Running: " . join(" ", @{$cmd}) . "\n");
+	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
+	ok($result, "$test_name: exit code 0");
+	is($stderr, '', "$test_name: no stderr");
+	like($stdout, $expected_stdout, "$test_name: matches");
+	return;
+}
+
+=pod
+
+=item command_like_safe(cmd, expected_stdout, test_name)
+
+TODO
+
+=cut
+
+sub command_like_safe
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+	# Doesn't rely on detecting end of file on the file descriptors,
+	# which can fail, causing the process to hang, notably on Msys
+	# when used with 'pg_ctl start'
+	my ($cmd, $expected_stdout, $test_name) = @_;
+	my ($stdout, $stderr);
+	my $stdoutfile = File::Temp->new();
+	my $stderrfile = File::Temp->new();
+	print("# Running: " . join(" ", @{$cmd}) . "\n");
+	my $result = IPC::Run::run $cmd, '>', $stdoutfile, '2>', $stderrfile;
+	$stdout = slurp_file($stdoutfile);
+	$stderr = slurp_file($stderrfile);
+	ok($result, "$test_name: exit code 0");
+	is($stderr, '', "$test_name: no stderr");
+	like($stdout, $expected_stdout, "$test_name: matches");
+	return;
+}
+
+=pod
+
+=item command_fails_like(cmd, expected_stderr, test_name)
+
+Test function to check that the command fails and the error message matches with the given regular expression.
+
+=cut
+
+sub command_fails_like
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+	my ($cmd, $expected_stderr, $test_name) = @_;
+	my ($stdout, $stderr);
+	print("# Running: " . join(" ", @{$cmd}) . "\n");
+	my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
+	ok(!$result, "$test_name: exit code not 0");
+	like($stderr, $expected_stderr, "$test_name: matches");
+	return;
+}
+
+
+=pod
+
+=item command_checks_all(cmd, expected_ret, out, err, test_name)
+
+Run a command and check its status and outputs.
+The 5 arguments are:
+
+- cmd: ref to list for command, options and arguments to run
+
+- ret: expected exit status
+
+- out: ref to list of re to be checked against stdout (all must match)
+
+- err: ref to list of re to be checked against stderr (all must match)
+
+- test_name: name of test
+
+=cut
+
+sub command_checks_all
+{
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+	my ($cmd, $expected_ret, $out, $err, $test_name) = @_;
+
+	# run command
+	my ($stdout, $stderr);
+	print("# Running: " . join(" ", @{$cmd}) . "\n");
+	IPC::Run::run($cmd, '>', \$stdout, '2>', \$stderr);
+
+	# See http://perldoc.perl.org/perlvar.html#%24CHILD_ERROR
+	my $ret = $?;
+	die "command exited with signal " . ($ret & 127)
+	  if $ret & 127;
+	$ret = $ret >> 8;
+
+	# check status
+	ok($ret == $expected_ret,
+		"$test_name status (got $ret vs expected $expected_ret)");
+
+	# check stdout
+	for my $re (@$out)
+	{
+		like($stdout, $re, "$test_name stdout /$re/");
+	}
+
+	# check stderr
+	for my $re (@$err)
+	{
+		like($stderr, $re, "$test_name stderr /$re/");
+	}
+
+	return;
+}
+
+=pod
+
+=back
+
+=cut
+
+1;
diff --git a/src/test/perl/v1_perldoc_testlib.patch b/src/test/perl/v1_perldoc_testlib.patch
new file mode 100644
index 0000000..a1ccf11
--- /dev/null
+++ b/src/test/perl/v1_perldoc_testlib.patch
@@ -0,0 +1,550 @@
+diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm
+index ce59401..61018f1 100644
+--- a/src/test/perl/TestLib.pm
++++ b/src/test/perl/TestLib.pm
+@@ -1,9 +1,101 @@
+-# TestLib, low-level routines and actions regression tests.
+-#
+-# This module contains a set of routines dedicated to environment setup for
+-# a PostgreSQL regression test run 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.
++=pod
++
++=head1 NAME
++
++TestLib - class containing routines for environment setup, low-level routines for command execution, logging and test functions 
++
++=head1 SYNOPSIS
++
++  use TestLib;
++	
++  #Checks if all the tests passed or not
++  all_tests_passing()
++
++  #Creates a temporary directory
++  tempdir(prefix)
++	
++  #Creates a temporary directory outside the build tree for the Unix-domain socket
++  tempdir_short()
++
++  #Returns the real directory for a virtual path directory under msys
++  real_dir(dir)
++
++  #TODO
++  system_log()	
++
++  #TODO
++  system_or_bail()	
++
++  #TODO
++  run_log()	
++
++  #Returns the output after running a command
++  run_command(dir)	
++
++  #Generate a string made of the given range of ASCII characters
++  generate_ascii_string(from_char, to_char)	
++
++  #TODO
++  slurp_dir(dir)	
++
++  #TODO
++  slurp_file(filename)	
++
++  #Appends a string at the end of a given file
++  append_to_file(filename, str)	
++
++  #Check that all file/dir modes in a directory match the expected values
++  check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
++
++  #Change mode recursively on a directory
++  chmod_recursive(dir, dir_mode, file_mode)	
++
++  #Check presence of a given regexp within pg_config.h
++  check_pg_config(regexp)	
++
++  #Test function to check that the command runs successfully
++  command_ok(cmd, test_name)	
++
++  #Test function to check that the command fails
++  command_fails(cmd, test_name)	
++
++  #Test function to check that the command exit code matches with the expected exit code
++  command_exit_is(cmd, expected, test_name)	
++
++  #Test function to check that the command supports --help option 
++  program_help_ok(cmd)	
++
++  #Test function to check that the command supports --version option 
++  program_version_ok(cmd)	
++
++  #Test function to check that a command with invalid option returns non-zero exit code and error message
++  program_options_handling_ok(cmd)	
++
++  #Test function to check that the command runs successfully and the output 
++  matches with the given regular expression
++  command_like(cmd, expected_stdout, test_name)	
++
++  #TODO
++  command_like_safe(cmd, expected_stdout, test_name)	
++
++  #Test function to check that the command fails and the error message
++  matches with the given regular expression
++  command_fails_like(cmd, expected_stderr, test_name)	
++
++  #Test function to run a command and check its status and outputs
++  command_checks_all(cmd, expected_ret, out, err, test_name)	
++		
++
++=head1 DESCRIPTION
++
++Testlib module contains a set of routines dedicated to environment setup for
++a PostgreSQL regression test run 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.
++
++The IPC::Run module is required.
++
++=cut
+ 
+ package TestLib;
+ 
+@@ -134,6 +226,19 @@ END
+ 	$File::Temp::KEEP_ALL = 1 unless all_tests_passing();
+ }
+ 
++
++=pod
++
++=head1 METHODS
++
++=over
++
++=item all_tests_passing()
++
++Returns 1 if all the tests pass. Otherwise returns 0
++
++=cut
++
+ sub all_tests_passing
+ {
+ 	my $fail_count = 0;
+@@ -144,9 +249,15 @@ sub all_tests_passing
+ 	return 1;
+ }
+ 
+-#
+-# Helper functions
+-#
++=pod
++
++=item tempdir(prefix)
++
++Creates a temporary directory with name prefix_XXXX if prefix argument is passed. Otherwise a temporary directory with name tmp_test_XXXX is created. 
++XXXX represents four random characters.Temporary directory is created inside the folder represented by $tmp_check and it is deleted at the end of the tests.
++
++=cut
++
+ sub tempdir
+ {
+ 	my ($prefix) = @_;
+@@ -157,17 +268,32 @@ sub tempdir
+ 		CLEANUP => 1);
+ }
+ 
++
++=pod
++
++=item tempdir_short()
++
++Use a separate temp dir outside the build tree for the
++Unix-domain socket, to avoid file name length issues.
++
++=cut
++
+ sub tempdir_short
+ {
+ 
+-	# Use a separate temp dir outside the build tree for the
+-	# Unix-domain socket, to avoid file name length issues.
+ 	return File::Temp::tempdir(CLEANUP => 1);
+ }
+ 
+-# Return the real directory for a virtual path directory under msys.
+-# The directory  must exist. If it's not an existing directory or we're
+-# not under msys, return the input argument unchanged.
++=pod
++
++=item real_dir(dir)
++
++Return the real directory for a virtual path directory under msys.
++The directory  must exist. If it's not an existing directory or we're
++not under msys, return the input argument unchanged.
++
++=cut
++
+ sub real_dir
+ {
+ 	my $dir = "$_[0]";
+@@ -183,12 +309,28 @@ sub real_dir
+ 	return $dir;
+ }
+ 
++=pod
++
++=item system_log()
++
++TODO
++
++=cut
++
+ sub system_log
+ {
+ 	print("# Running: " . join(" ", @_) . "\n");
+ 	return system(@_);
+ }
+ 
++=pod
++
++=item system_or_bail()
++
++TODO
++
++=cut
++
+ sub system_or_bail
+ {
+ 	if (system_log(@_) != 0)
+@@ -198,12 +340,28 @@ sub system_or_bail
+ 	return;
+ }
+ 
++=pod
++
++=item run_log()
++
++TODO
++
++=cut
++
+ sub run_log
+ {
+ 	print("# Running: " . join(" ", @{ $_[0] }) . "\n");
+ 	return IPC::Run::run(@_);
+ }
+ 
++=pod
++
++=item run_command(cmd)
++
++Returns the output after running the command
++
++=cut
++
+ sub run_command
+ {
+ 	my ($cmd) = @_;
+@@ -214,7 +372,14 @@ sub run_command
+ 	return ($stdout, $stderr);
+ }
+ 
+-# Generate a string made of the given range of ASCII characters
++=pod
++
++=item generate_ascii_string(from_char, to_char)
++
++Generate a string made of the given range of ASCII characters
++
++=cut
++
+ sub generate_ascii_string
+ {
+ 	my ($from_char, $to_char) = @_;
+@@ -227,6 +392,14 @@ sub generate_ascii_string
+ 	return $res;
+ }
+ 
++=pod
++
++=item slurp_dir(dir)
++
++TODO
++
++=cut
++
+ sub slurp_dir
+ {
+ 	my ($dir) = @_;
+@@ -237,6 +410,14 @@ sub slurp_dir
+ 	return @direntries;
+ }
+ 
++=pod
++
++=item slurp_file(filename)
++
++TODO
++
++=cut
++
+ sub slurp_file
+ {
+ 	my ($filename) = @_;
+@@ -249,6 +430,15 @@ sub slurp_file
+ 	return $contents;
+ }
+ 
++=pod
++
++=item append_to_file(filename, str)
++
++Append a string at the end of a given file
++
++=cut
++
++
+ sub append_to_file
+ {
+ 	my ($filename, $str) = @_;
+@@ -259,8 +449,16 @@ sub append_to_file
+ 	return;
+ }
+ 
+-# Check that all file/dir modes in a directory match the expected values,
+-# ignoring the mode of any specified files.
++
++=pod
++
++=item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
++
++Check that all file/dir modes in a directory match the expected values,
++ignoring the mode of any specified files.
++
++=cut
++
+ sub check_mode_recursive
+ {
+ 	my ($dir, $expected_dir_mode, $expected_file_mode, $ignore_list) = @_;
+@@ -343,7 +541,14 @@ sub check_mode_recursive
+ 	return $result;
+ }
+ 
+-# Change mode recursively on a directory
++=pod
++
++=item chmod_recursive(dir, dir_mode, file_mode)
++
++Change mode recursively on a directory
++
++=cut
++
+ sub chmod_recursive
+ {
+ 	my ($dir, $dir_mode, $file_mode) = @_;
+@@ -367,9 +572,17 @@ sub chmod_recursive
+ 	return;
+ }
+ 
+-# Check presence of a given regexp within pg_config.h for the installation
+-# where tests are running, returning a match status result depending on
+-# that.
++
++=pod
++
++=item check_pg_config(regexp)
++
++Check presence of a given regexp within pg_config.h for the installation
++where tests are running, returning a match status result depending on
++that.
++
++=cut
++
+ sub check_pg_config
+ {
+ 	my ($regexp) = @_;
+@@ -385,9 +598,15 @@ sub check_pg_config
+ 	return $match;
+ }
+ 
+-#
+-# Test functions
+-#
++
++=pod
++
++=item command_ok(cmd, test_name)
++
++Test function to check that the command runs successfully
++
++=cut
++
+ sub command_ok
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -397,6 +616,14 @@ sub command_ok
+ 	return;
+ }
+ 
++=pod
++
++=item command_fails(cmd, test_name)
++
++Test function to check that the command fails
++
++=cut
++
+ sub command_fails
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -406,6 +633,14 @@ sub command_fails
+ 	return;
+ }
+ 
++=pod
++
++=item command_exit_is(cmd, expected, test_name)
++
++Test function to check that the command exit code matches with the expected exit code.
++
++=cut
++
+ sub command_exit_is
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -429,6 +664,14 @@ sub command_exit_is
+ 	return;
+ }
+ 
++=pod
++
++=item program_help_ok(cmd)
++
++Test function to check that the command supports --help option.
++
++=cut
++
+ sub program_help_ok
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -443,6 +686,14 @@ sub program_help_ok
+ 	return;
+ }
+ 
++=pod
++
++=item program_version_ok(cmd)
++
++Test function to check that the command supports --version option.
++
++=cut
++
+ sub program_version_ok
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -457,6 +708,14 @@ sub program_version_ok
+ 	return;
+ }
+ 
++=pod
++
++=item program_options_handling_ok(cmd)
++
++Test function to check that a command with invalid option returns non-zero exit code and error message.
++
++=cut
++
+ sub program_options_handling_ok
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -471,6 +730,14 @@ sub program_options_handling_ok
+ 	return;
+ }
+ 
++=pod
++
++=item command_like(cmd, expected_stdout, test_name)
++
++Test function to check that the command runs successfully and the output matches with the given regular expression.
++
++=cut
++
+ sub command_like
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -484,6 +751,15 @@ sub command_like
+ 	return;
+ }
+ 
++=pod
++
++=item command_like_safe(cmd, expected_stdout, test_name)
++
++TODO
++
++
++=cut
++
+ sub command_like_safe
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -505,6 +781,14 @@ sub command_like_safe
+ 	return;
+ }
+ 
++=pod
++
++=item command_fails_like(cmd, expected_stderr, test_name)
++
++Test function to check that the command fails and the error message matches with the given regular expression.
++
++=cut
++
+ sub command_fails_like
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -517,13 +801,26 @@ sub command_fails_like
+ 	return;
+ }
+ 
+-# Run a command and check its status and outputs.
+-# The 5 arguments are:
+-# - cmd: ref to list for command, options and arguments to run
+-# - ret: expected exit status
+-# - out: ref to list of re to be checked against stdout (all must match)
+-# - err: ref to list of re to be checked against stderr (all must match)
+-# - test_name: name of test
++
++=pod
++
++=item command_checks_all(cmd, expected_ret, out, err, test_name)
++
++Run a command and check its status and outputs.
++The 5 arguments are:
++
++- cmd: ref to list for command, options and arguments to run
++
++- ret: expected exit status
++
++- out: ref to list of re to be checked against stdout (all must match)
++
++- err: ref to list of re to be checked against stderr (all must match)
++
++- test_name: name of test
++
++=cut
++
+ sub command_checks_all
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -560,4 +857,10 @@ sub command_checks_all
+ 	return;
+ }
+ 
++=pod
++
++=back
++
++=cut
++
+ 1;
diff --git a/src/test/perl/v2_perldoc_testlib.patch b/src/test/perl/v2_perldoc_testlib.patch
new file mode 100644
index 0000000..3a44de2
--- /dev/null
+++ b/src/test/perl/v2_perldoc_testlib.patch
@@ -0,0 +1,572 @@
+diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm
+index ce59401cef..a352e912ce 100644
+--- a/src/test/perl/TestLib.pm
++++ b/src/test/perl/TestLib.pm
+@@ -1,9 +1,102 @@
+-# TestLib, low-level routines and actions regression tests.
+-#
+-# This module contains a set of routines dedicated to environment setup for
+-# a PostgreSQL regression test run 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.
++=pod
++
++=head1 NAME
++
++TestLib - class containing routines for environment setup, low-level routines for command execution, logging and test functions 
++
++=head1 SYNOPSIS
++
++  use TestLib;
++	
++  #Checks if all the tests passed or not
++  all_tests_passing()
++
++  #Creates a temporary directory
++  tempdir(prefix)
++	
++  #Creates a temporary directory outside the build tree for the Unix-domain socket
++  tempdir_short()
++
++  #Returns the real directory for a virtual path directory under msys
++  real_dir(dir)
++
++  #Returns the exit status of the command run using the system function
++  system_log()	
++
++  #Abandons all the remaining tests when a particular system execution fails
++  system_or_bail()	
++
++  #Returns the exit status of the command passed as an argument to the run() function.
++  run_log()	
++
++  #Returns the output after running a command
++  run_command(dir)	
++
++  #Generate a string made of the given range of ASCII characters
++  generate_ascii_string(from_char, to_char)	
++
++  #Read the contents of the directory
++  slurp_dir(dir)	
++
++  #Read the contents of the file 
++  slurp_file(filename)	
++
++  #Appends a string at the end of a given file
++  append_to_file(filename, str)	
++
++  #Check that all file/dir modes in a directory match the expected values
++  check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
++
++  #Change mode recursively on a directory
++  chmod_recursive(dir, dir_mode, file_mode)	
++
++  #Check presence of a given regexp within pg_config.h
++  check_pg_config(regexp)	
++
++  #Test function to check that the command runs successfully
++  command_ok(cmd, test_name)	
++
++  #Test function to check that the command fails
++  command_fails(cmd, test_name)	
++
++  #Test function to check that the command exit code matches with the expected exit code
++  command_exit_is(cmd, expected, test_name)	
++
++  #Test function to check that the command supports --help option 
++  program_help_ok(cmd)	
++
++  #Test function to check that the command supports --version option 
++  program_version_ok(cmd)	
++
++  #Test function to check that a command with invalid option returns non-zero exit code and error message
++  program_options_handling_ok(cmd)	
++
++  #Test function to check that the command runs successfully and the output 
++  matches with the given regular expression
++  command_like(cmd, expected_stdout, test_name)	
++
++  #Test function to check the command's successful execution which is safer option as the output is compared
++  using a temporary file.
++  command_like_safe(cmd, expected_stdout, test_name)	
++
++  #Test function to check that the command fails and the error message
++  matches with the given regular expression
++  command_fails_like(cmd, expected_stderr, test_name)	
++
++  #Test function to run a command and check its status and outputs
++  command_checks_all(cmd, expected_ret, out, err, test_name)	
++		
++
++=head1 DESCRIPTION
++
++Testlib module contains a set of routines dedicated to environment setup for
++a PostgreSQL regression test run 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.
++
++The IPC::Run module is required.
++
++=cut
+ 
+ package TestLib;
+ 
+@@ -134,6 +227,19 @@ END
+ 	$File::Temp::KEEP_ALL = 1 unless all_tests_passing();
+ }
+ 
++
++=pod
++
++=head1 METHODS
++
++=over
++
++=item all_tests_passing()
++
++Returns 1 if all the tests pass. Otherwise returns 0
++
++=cut
++
+ sub all_tests_passing
+ {
+ 	my $fail_count = 0;
+@@ -144,9 +250,15 @@ sub all_tests_passing
+ 	return 1;
+ }
+ 
+-#
+-# Helper functions
+-#
++=pod
++
++=item tempdir(prefix)
++
++Creates a temporary directory with name prefix_XXXX if prefix argument is passed. Otherwise a temporary directory with name tmp_test_XXXX is created. 
++XXXX represents four random characters.Temporary directory is created inside the folder represented by $tmp_check and it is deleted at the end of the tests.
++
++=cut
++
+ sub tempdir
+ {
+ 	my ($prefix) = @_;
+@@ -157,17 +269,32 @@ sub tempdir
+ 		CLEANUP => 1);
+ }
+ 
++
++=pod
++
++=item tempdir_short()
++
++Use a separate temp dir outside the build tree for the
++Unix-domain socket, to avoid file name length issues.
++
++=cut
++
+ sub tempdir_short
+ {
+ 
+-	# Use a separate temp dir outside the build tree for the
+-	# Unix-domain socket, to avoid file name length issues.
+ 	return File::Temp::tempdir(CLEANUP => 1);
+ }
+ 
+-# Return the real directory for a virtual path directory under msys.
+-# The directory  must exist. If it's not an existing directory or we're
+-# not under msys, return the input argument unchanged.
++=pod
++
++=item real_dir(dir)
++
++Return the real directory for a virtual path directory under msys.
++The directory  must exist. If it's not an existing directory or we're
++not under msys, return the input argument unchanged.
++
++=cut
++
+ sub real_dir
+ {
+ 	my $dir = "$_[0]";
+@@ -183,12 +310,32 @@ sub real_dir
+ 	return $dir;
+ }
+ 
++=pod
++
++=item system_log()
++
++This function executes the command specified by the first argument,followed by passing 
++options/parameters as subsequent arguments to the command.
++
++The return value is the exit status of the program as returned by the wait function.
++
++=cut
++
+ sub system_log
+ {
+ 	print("# Running: " . join(" ", @_) . "\n");
+ 	return system(@_);
+ }
+ 
++=pod
++
++=item system_or_bail()
++
++If the command execution fails with system_log function returning a non zero exit status,
++the BAIL_OUT function is called which terminates the further tests and exits from the program. 
++
++=cut
++
+ sub system_or_bail
+ {
+ 	if (system_log(@_) != 0)
+@@ -198,12 +345,31 @@ sub system_or_bail
+ 	return;
+ }
+ 
++=pod
++
++=item run_log()
++
++Runs the particular command passed as an argument and returns 0 if the command is executed successfully.
++The return value is opposite to that returned by the system() function.
++
++Various redirection operators can be used in colloboration with the run function.
++
++=cut
++
+ sub run_log
+ {
+ 	print("# Running: " . join(" ", @{ $_[0] }) . "\n");
+ 	return IPC::Run::run(@_);
+ }
+ 
++=pod
++
++=item run_command(cmd)
++
++Returns the output after running the command
++
++=cut
++
+ sub run_command
+ {
+ 	my ($cmd) = @_;
+@@ -214,7 +380,14 @@ sub run_command
+ 	return ($stdout, $stderr);
+ }
+ 
+-# Generate a string made of the given range of ASCII characters
++=pod
++
++=item generate_ascii_string(from_char, to_char)
++
++Generate a string made of the given range of ASCII characters
++
++=cut
++
+ sub generate_ascii_string
+ {
+ 	my ($from_char, $to_char) = @_;
+@@ -227,6 +400,19 @@ sub generate_ascii_string
+ 	return $res;
+ }
+ 
++=pod
++
++=item slurp_dir(dir)
++
++Opens the directory provided as an argument to the function.
++
++If the opendir function returns false, the error message is printed to STDERR and comes out of the program.
++
++If the directory is opened successfully, the readdir returns the next directory entry for a directory
++opened by opendir.
++
++=cut
++
+ sub slurp_dir
+ {
+ 	my ($dir) = @_;
+@@ -237,6 +423,18 @@ sub slurp_dir
+ 	return @direntries;
+ }
+ 
++=pod
++
++=item slurp_file(filename)
++
++Opens the file provided as an argument to the function in read mode(as indicated by <).
++
++If the open function returns 0, the error message is printed to STDERR and comes out of the program.
++
++If the file is opened successfully, the contents are read using the filehandle and the file is closed.
++
++=cut
++
+ sub slurp_file
+ {
+ 	my ($filename) = @_;
+@@ -249,6 +447,15 @@ sub slurp_file
+ 	return $contents;
+ }
+ 
++=pod
++
++=item append_to_file(filename, str)
++
++Append a string at the end of a given file
++
++=cut
++
++
+ sub append_to_file
+ {
+ 	my ($filename, $str) = @_;
+@@ -259,8 +466,16 @@ sub append_to_file
+ 	return;
+ }
+ 
+-# Check that all file/dir modes in a directory match the expected values,
+-# ignoring the mode of any specified files.
++
++=pod
++
++=item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
++
++Check that all file/dir modes in a directory match the expected values,
++ignoring the mode of any specified files.
++
++=cut
++
+ sub check_mode_recursive
+ {
+ 	my ($dir, $expected_dir_mode, $expected_file_mode, $ignore_list) = @_;
+@@ -343,7 +558,14 @@ sub check_mode_recursive
+ 	return $result;
+ }
+ 
+-# Change mode recursively on a directory
++=pod
++
++=item chmod_recursive(dir, dir_mode, file_mode)
++
++Change mode recursively on a directory
++
++=cut
++
+ sub chmod_recursive
+ {
+ 	my ($dir, $dir_mode, $file_mode) = @_;
+@@ -367,9 +589,17 @@ sub chmod_recursive
+ 	return;
+ }
+ 
+-# Check presence of a given regexp within pg_config.h for the installation
+-# where tests are running, returning a match status result depending on
+-# that.
++
++=pod
++
++=item check_pg_config(regexp)
++
++Check presence of a given regexp within pg_config.h for the installation
++where tests are running, returning a match status result depending on
++that.
++
++=cut
++
+ sub check_pg_config
+ {
+ 	my ($regexp) = @_;
+@@ -385,9 +615,15 @@ sub check_pg_config
+ 	return $match;
+ }
+ 
+-#
+-# Test functions
+-#
++
++=pod
++
++=item command_ok(cmd, test_name)
++
++Test function to check that the command runs successfully
++
++=cut
++
+ sub command_ok
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -397,6 +633,14 @@ sub command_ok
+ 	return;
+ }
+ 
++=pod
++
++=item command_fails(cmd, test_name)
++
++Test function to check that the command fails
++
++=cut
++
+ sub command_fails
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -406,6 +650,14 @@ sub command_fails
+ 	return;
+ }
+ 
++=pod
++
++=item command_exit_is(cmd, expected, test_name)
++
++Test function to check that the command exit code matches with the expected exit code.
++
++=cut
++
+ sub command_exit_is
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -429,6 +681,14 @@ sub command_exit_is
+ 	return;
+ }
+ 
++=pod
++
++=item program_help_ok(cmd)
++
++Test function to check that the command supports --help option.
++
++=cut
++
+ sub program_help_ok
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -443,6 +703,14 @@ sub program_help_ok
+ 	return;
+ }
+ 
++=pod
++
++=item program_version_ok(cmd)
++
++Test function to check that the command supports --version option.
++
++=cut
++
+ sub program_version_ok
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -457,6 +725,14 @@ sub program_version_ok
+ 	return;
+ }
+ 
++=pod
++
++=item program_options_handling_ok(cmd)
++
++Test function to check that a command with invalid option returns non-zero exit code and error message.
++
++=cut
++
+ sub program_options_handling_ok
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -471,6 +747,14 @@ sub program_options_handling_ok
+ 	return;
+ }
+ 
++=pod
++
++=item command_like(cmd, expected_stdout, test_name)
++
++Test function to check that the command runs successfully and the output matches with the given regular expression.
++
++=cut
++
+ sub command_like
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -484,6 +768,20 @@ sub command_like
+ 	return;
+ }
+ 
++=pod
++
++=item command_like_safe(cmd, expected_stdout, test_name)
++
++Test function to check whether the command runs successfully wherein the output and error log is redirected 
++to temporary files instead of variables. The contents of the files are later copied into variables using the
++slurp_file() function. Also checks whether the output of the command matches with the expected output.
++
++This is a safer option for the command_like() as the output of the command is redirected to a temporary file 
++rather than variables wherein the newline characters can be clearly extracted.
++
++
++=cut
++
+ sub command_like_safe
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -505,6 +803,14 @@ sub command_like_safe
+ 	return;
+ }
+ 
++=pod
++
++=item command_fails_like(cmd, expected_stderr, test_name)
++
++Test function to check that the command fails and the error message matches with the given regular expression.
++
++=cut
++
+ sub command_fails_like
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -517,13 +823,26 @@ sub command_fails_like
+ 	return;
+ }
+ 
+-# Run a command and check its status and outputs.
+-# The 5 arguments are:
+-# - cmd: ref to list for command, options and arguments to run
+-# - ret: expected exit status
+-# - out: ref to list of re to be checked against stdout (all must match)
+-# - err: ref to list of re to be checked against stderr (all must match)
+-# - test_name: name of test
++
++=pod
++
++=item command_checks_all(cmd, expected_ret, out, err, test_name)
++
++Run a command and check its status and outputs.
++The 5 arguments are:
++
++- cmd: ref to list for command, options and arguments to run
++
++- ret: expected exit status
++
++- out: ref to list of re to be checked against stdout (all must match)
++
++- err: ref to list of re to be checked against stderr (all must match)
++
++- test_name: name of test
++
++=cut
++
+ sub command_checks_all
+ {
+ 	local $Test::Builder::Level = $Test::Builder::Level + 1;
+@@ -560,4 +879,10 @@ sub command_checks_all
+ 	return;
+ }
+ 
++=pod
++
++=back
++
++=cut
++
+ 1;
#9Iwata, Aya
iwata.aya@jp.fujitsu.com
In reply to: Ramanarayana (#8)
RE: Contribution to Perldoc for TestLib module in Postgres

Hi Ram,

I think this documentation helps people who want to understand functions.

Please find the updated patch. Added to the commitfest as well

I have some comments.

I think some users who would like to know custom function check src/test/perl/README at first.
How about add comments to the README file, such as "See Testlib.pm if you want to know more details"?

In the above document, why not write a description after the function name?
I think it is better to write the function name first and then the description.
In your code;
#Checks if all the tests passed or not
all_tests_passing()

In my suggestion;
all_tests_passing()
Checks if all the tests passed or not

And some functions return value. How about adding return information to the above doc?

I find ^M character in your new code. Please use LF line ending not CRLF or get rid of it in next patch.

Regards,
Aya Iwata

#10Daniel Gustafsson
daniel@yesql.se
In reply to: Ramanarayana (#8)
Re: Contribution to Perldoc for TestLib module in Postgres

On 7 Apr 2019, at 20:04, Ramanarayana <raam.soft@gmail.com> wrote:

Please find the updated patch. Added to the commitfest as well

The v2 patch is somewhat confused as it has Windows carriage returns rather
than newlines, so it replaces the entire file making the diff hard to read. It
also includes a copy of TestLib and the v1 patch and has a lot of whitespace
noise.

Please redo the patch on a clean tree to get a more easily digestable patch.

cheers ./daniel

#11Michael Paquier
michael@paquier.xyz
In reply to: Daniel Gustafsson (#10)
Re: Contribution to Perldoc for TestLib module in Postgres

On Tue, Jul 09, 2019 at 03:16:01PM +0200, Daniel Gustafsson wrote:

The v2 patch is somewhat confused as it has Windows carriage returns rather
than newlines, so it replaces the entire file making the diff hard to read. It
also includes a copy of TestLib and the v1 patch and has a lot of whitespace
noise.

Nobody can provide a clear review if the files are just fully
rewritten even based on a read of the patch. Perhaps you are working
on Windows and forgot to configure core.autocrlf with "git config".
That could make your life easier.

I have switched the patch as "waiting on author" for now.
--
Michael

#12Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Iwata, Aya (#9)
Re: Contribution to Perldoc for TestLib module in Postgres

On 2019-Apr-11, Iwata, Aya wrote:

In the above document, why not write a description after the function name?
I think it is better to write the function name first and then the description.
In your code;
#Checks if all the tests passed or not
all_tests_passing()

In my suggestion;
all_tests_passing()
Checks if all the tests passed or not

Yeah, so there are two parts in the submitted patch: first the synopsis
list the methods using this format you describe, and later the METHODS
section lists then again, using your suggested style. I think we should
do away with the long synopsis -- maybe keep it as just the "use
TestLib" line, and then let the METHODS section list and describe the
methods.

And some functions return value. How about adding return information
to the above doc?

That's already in the METHODS section for some of them. For example:

all_tests_passing()
Returns 1 if all the tests pass. Otherwise returns 0

It's missing for others, such as "tempdir".

In slurp_file you have this:
Opens the file provided as an argument to the function in read mode(as
indicated by <).
I think the parenthical comment is useless; remove that.

Please break long source lines (say to 78 chars -- make sure pgperltidy
agrees), and keep some spaces after sentence-ending periods and other
punctuation.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#13Andrew Dunstan
andrew.dunstan@2ndquadrant.com
In reply to: Alvaro Herrera (#12)
1 attachment(s)
Re: Contribution to Perldoc for TestLib module in Postgres

On 7/10/19 9:38 AM, Alvaro Herrera wrote:

On 2019-Apr-11, Iwata, Aya wrote:

In the above document, why not write a description after the function name?
I think it is better to write the function name first and then the description.
In your code;
#Checks if all the tests passed or not
all_tests_passing()

In my suggestion;
all_tests_passing()
Checks if all the tests passed or not

Yeah, so there are two parts in the submitted patch: first the synopsis
list the methods using this format you describe, and later the METHODS
section lists then again, using your suggested style. I think we should
do away with the long synopsis -- maybe keep it as just the "use
TestLib" line, and then let the METHODS section list and describe the
methods.

And some functions return value. How about adding return information
to the above doc?

That's already in the METHODS section for some of them. For example:

all_tests_passing()
Returns 1 if all the tests pass. Otherwise returns 0

It's missing for others, such as "tempdir".

In slurp_file you have this:
Opens the file provided as an argument to the function in read mode(as
indicated by <).
I think the parenthical comment is useless; remove that.

Please break long source lines (say to 78 chars -- make sure pgperltidy
agrees), and keep some spaces after sentence-ending periods and other
punctuation.

I've fixed the bitrot and some other infelicities on this patch. It's
not commitable yet but I think it's more reviewable.

cheers

andrew

--
Andrew Dunstan https://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

testlib-doc-3.patchtext/x-patch; name=testlib-doc-3.patchDownload
diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm
index 6195c21c59..ebb0eb82e7 100644
--- a/src/test/perl/TestLib.pm
+++ b/src/test/perl/TestLib.pm
@@ -1,9 +1,108 @@
-# TestLib, low-level routines and actions regression tests.
-#
-# This module contains a set of routines dedicated to environment setup for
-# a PostgreSQL regression test run 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.
+
+=pod
+
+=head1 NAME
+
+TestLib - module containing routines for environment setup, low-level routines
+for command execution, logging and test functions
+
+=head1 SYNOPSIS
+
+  use TestLib;
+
+  # Checks if all the tests passed or not
+  all_tests_passing()
+
+  # Creates a temporary directory
+  tempdir(prefix)
+
+  # Creates a temporary directory outside the build tree for the
+  # Unix-domain socket
+  tempdir_short()
+
+  # Returns the real directory for a virtual path directory under msys
+  real_dir(dir)
+
+  # Runs the command which is passed as an argument
+  system_log()
+
+  # Runs the command which is passed as an argument. On failure abandons
+  # all other tests
+  system_or_bail()
+
+  # Runs the command which is passed as an argument.
+  run_log()
+
+  # Returns the output after running a command
+  run_command(dir)
+
+  # Generate a string made of the given range of ASCII characters
+  generate_ascii_string(from_char, to_char)
+
+  # Read the contents of the directory
+  slurp_dir(dir)
+
+  # Read the contents of the file
+  slurp_file(filename)
+
+  # Appends a string at the end of a given file
+  append_to_file(filename, str)
+
+  # Check that all file/dir modes in a directory match the expected values
+  check_mode_recursive(dir, expected_dir_mode, expected_file_mode,
+                       ignore_list)
+
+  # Change mode recursively on a directory
+  chmod_recursive(dir, dir_mode, file_mode)
+
+  # Check presence of a given regexp within pg_config.h
+  check_pg_config(regexp)
+
+  # Test function to check that the command runs successfully
+  command_ok(cmd, test_name)
+
+  # Test function to check that the command fails
+  command_fails(cmd, test_name)
+
+  # Test function to check that the command exit code matches the
+  # expected exit code
+  command_exit_is(cmd, expected, test_name)
+
+  # Test function to check that the command supports --help option
+  program_help_ok(cmd)
+
+  # Test function to check that the command supports --version option
+  program_version_ok(cmd)
+
+  # Test function to check that a command with invalid option returns
+  # non-zero exit code and error message
+  program_options_handling_ok(cmd)
+
+  # Test function to check that the command runs successfully and the
+  # output matches the given regular expression
+  command_like(cmd, expected_stdout, test_name)
+
+  #TODO
+  command_like_safe(cmd, expected_stdout, test_name)
+
+  # Test function to check that the command fails and the error message
+  # matches the given regular expression
+  command_fails_like(cmd, expected_stderr, test_name)
+
+  # Test function to run a command and check its status and outputs
+  command_checks_all(cmd, expected_ret, out, err, test_name)
+
+
+=head1 DESCRIPTION
+
+Testlib module contains a set of routines dedicated to environment setup for
+a PostgreSQL regression test run 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.
+
+The IPC::Run module is required.
+
+=cut
 
 package TestLib;
 
@@ -22,7 +121,8 @@ use File::Temp ();
 use IPC::Run;
 use SimpleTee;
 
-# specify a recent enough version of Test::More to support the done_testing() function
+# specify a recent enough version of Test::More to support the
+# done_testing() function
 use Test::More 0.87;
 
 our @EXPORT = qw(
@@ -135,6 +235,19 @@ END
 	$File::Temp::KEEP_ALL = 1 unless all_tests_passing();
 }
 
+
+=pod
+
+=head1 METHODS
+
+=over
+
+=item all_tests_passing()
+
+Returns 1 if all the tests pass. Otherwise returns 0
+
+=cut
+
 sub all_tests_passing
 {
 	my $fail_count = 0;
@@ -145,9 +258,18 @@ sub all_tests_passing
 	return 1;
 }
 
-#
-# Helper functions
-#
+=pod
+
+=item tempdir(prefix)
+
+Creates a temporary directory with name prefix_XXXX if prefix argument is
+passed. Otherwise a temporary directory with name tmp_test_XXXX is created.
+XXXX represents four random characters. The emporary directory is created
+inside the folder represented by $tmp_check and it is deleted at the end of
+the tests.
+
+=cut
+
 sub tempdir
 {
 	my ($prefix) = @_;
@@ -158,17 +280,32 @@ sub tempdir
 		CLEANUP => 1);
 }
 
+
+=pod
+
+=item tempdir_short()
+
+Use a separate temp dir outside the build tree for the
+Unix-domain socket, to avoid file name length issues.
+
+=cut
+
 sub tempdir_short
 {
 
-	# Use a separate temp dir outside the build tree for the
-	# Unix-domain socket, to avoid file name length issues.
 	return File::Temp::tempdir(CLEANUP => 1);
 }
 
-# Translate a Perl file name to a host file name.  Currently, this is a no-op
-# except for the case of Perl=msys and host=mingw32.  The subject need not
-# exist, but its parent directory must exist.
+=pod
+
+=item perl2host()
+
+Translate a Perl file name to a host file name.  Currently, this is a no-op
+except for the case of Perl=msys and host=mingw32.  The subject need not
+exist, but its parent directory must exist.
+
+=cut
+
 sub perl2host
 {
 	my ($subject) = @_;
@@ -193,12 +330,30 @@ sub perl2host
 	return $dir . $leaf;
 }
 
+=pod
+
+=item system_log()
+
+Runs the command which is passed as argument to the function and returns 0 if
+successful. Otherwise returns non-zero value.
+
+=cut
+
 sub system_log
 {
 	print("# Running: " . join(" ", @_) . "\n");
 	return system(@_);
 }
 
+=pod
+
+=item system_or_bail()
+
+Runs the command which is passed as argument to the function. On failure it
+abandons further tests and exits the program.
+
+=cut
+
 sub system_or_bail
 {
 	if (system_log(@_) != 0)
@@ -208,12 +363,28 @@ sub system_or_bail
 	return;
 }
 
+=pod
+
+=item run_log()
+
+Runs the command which is passed as argument to the function
+
+=cut
+
 sub run_log
 {
 	print("# Running: " . join(" ", @{ $_[0] }) . "\n");
 	return IPC::Run::run(@_);
 }
 
+=pod
+
+=item run_command(cmd)
+
+Returns the output after running the command
+
+=cut
+
 sub run_command
 {
 	my ($cmd) = @_;
@@ -224,7 +395,14 @@ sub run_command
 	return ($stdout, $stderr);
 }
 
-# Generate a string made of the given range of ASCII characters
+=pod
+
+=item generate_ascii_string(from_char, to_char)
+
+Generate a string made of the given range of ASCII characters
+
+=cut
+
 sub generate_ascii_string
 {
 	my ($from_char, $to_char) = @_;
@@ -237,6 +415,20 @@ sub generate_ascii_string
 	return $res;
 }
 
+=pod
+
+=item slurp_dir(dir)
+
+Opens the directory provided as an argument to the function.
+
+If the opendir function returns false, the error message is printed to STDERR
+and comes out of the program.
+
+If the directory is opened successfully, the readdir returns the next
+directory entry for a directory opened by opendir.
+
+=cut
+
 sub slurp_dir
 {
 	my ($dir) = @_;
@@ -247,6 +439,21 @@ sub slurp_dir
 	return @direntries;
 }
 
+=pod
+
+=item slurp_file(filename)
+
+Opens the file provided as an argument to the function in read mode (as
+indicated by <).
+
+If the open function returns 0, the error message is printed to STDERR and
+comes out of the program.
+
+If the file is opened successfully, the contents are read using the filehandle
+and the file is closed.
+
+=cut
+
 sub slurp_file
 {
 	my ($filename) = @_;
@@ -259,6 +466,15 @@ sub slurp_file
 	return $contents;
 }
 
+=pod
+
+=item append_to_file(filename, str)
+
+Append a string at the end of a given file
+
+=cut
+
+
 sub append_to_file
 {
 	my ($filename, $str) = @_;
@@ -269,8 +485,17 @@ sub append_to_file
 	return;
 }
 
-# Check that all file/dir modes in a directory match the expected values,
-# ignoring the mode of any specified files.
+
+=pod
+
+=item check_mode_recursive(dir, expected_dir_mode, expected_file_mode,
+                           ignore_list)
+
+Check that all file/dir modes in a directory match the expected values,
+ignoring the mode of any specified files.
+
+=cut
+
 sub check_mode_recursive
 {
 	my ($dir, $expected_dir_mode, $expected_file_mode, $ignore_list) = @_;
@@ -353,7 +578,14 @@ sub check_mode_recursive
 	return $result;
 }
 
-# Change mode recursively on a directory
+=pod
+
+item chmod_recursive(dir, dir_mode, file_mode)
+
+Change mode recursively on a directory
+
+=cut
+
 sub chmod_recursive
 {
 	my ($dir, $dir_mode, $file_mode) = @_;
@@ -377,9 +609,17 @@ sub chmod_recursive
 	return;
 }
 
-# Check presence of a given regexp within pg_config.h for the installation
-# where tests are running, returning a match status result depending on
-# that.
+
+=pod
+
+=item check_pg_config(regexp)
+
+Check presence of a given regexp within pg_config.h for the installation
+where tests are running, returning a match status result depending on
+that.
+
+=cut
+
 sub check_pg_config
 {
 	my ($regexp) = @_;
@@ -395,9 +635,15 @@ sub check_pg_config
 	return $match;
 }
 
-#
-# Test functions
-#
+
+=pod
+
+=item command_ok(cmd, test_name)
+
+Test function to check that the command runs successfully
+
+=cut
+
 sub command_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -407,6 +653,14 @@ sub command_ok
 	return;
 }
 
+=pod
+
+=item command_fails(cmd, test_name)
+
+Test function to check that the command fails
+
+=cut
+
 sub command_fails
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -416,6 +670,14 @@ sub command_fails
 	return;
 }
 
+=pod
+
+=item command_exit_is(cmd, expected, test_name)
+
+Test function to check that the command exit code matches with the expected exit code.
+
+=cut
+
 sub command_exit_is
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -439,6 +701,14 @@ sub command_exit_is
 	return;
 }
 
+=pod
+
+=item program_help_ok(cmd)
+
+Test function to check that the command supports --help option.
+
+=cut
+
 sub program_help_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -453,6 +723,14 @@ sub program_help_ok
 	return;
 }
 
+=pod
+
+=item program_version_ok(cmd)
+
+Test function to check that the command supports --version option.
+
+=cut
+
 sub program_version_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -467,6 +745,15 @@ sub program_version_ok
 	return;
 }
 
+=pod
+
+=item program_options_handling_ok(cmd)
+
+Test function to check that a command with invalid option returns non-zero
+exit code and error message.
+
+=cut
+
 sub program_options_handling_ok
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -481,6 +768,15 @@ sub program_options_handling_ok
 	return;
 }
 
+=pod
+
+=item command_like(cmd, expected_stdout, test_name)
+
+Test function to check that the command runs successfully and the output
+matches the given regular expression.
+
+=cut
+
 sub command_like
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -494,6 +790,14 @@ sub command_like
 	return;
 }
 
+=pod
+
+=item command_like_safe(cmd, expected_stdout, test_name)
+
+TODO
+
+=cut
+
 sub command_like_safe
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -515,6 +819,15 @@ sub command_like_safe
 	return;
 }
 
+=pod
+
+=item command_fails_like(cmd, expected_stderr, test_name)
+
+Test function to check that the command fails and the error message matches
+the given regular expression.
+
+=cut
+
 sub command_fails_like
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -527,13 +840,26 @@ sub command_fails_like
 	return;
 }
 
-# Run a command and check its status and outputs.
-# The 5 arguments are:
-# - cmd: ref to list for command, options and arguments to run
-# - ret: expected exit status
-# - out: ref to list of re to be checked against stdout (all must match)
-# - err: ref to list of re to be checked against stderr (all must match)
-# - test_name: name of test
+
+=pod
+
+=item command_checks_all(cmd, expected_ret, out, err, test_name)
+
+Run a command and check its status and outputs.
+The 5 arguments are:
+
+- cmd: ref to list for command, options and arguments to run
+
+- ret: expected exit status
+
+- out: ref to list of re to be checked against stdout (all must match)
+
+- err: ref to list of re to be checked against stderr (all must match)
+
+- test_name: name of test
+
+=cut
+
 sub command_checks_all
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
@@ -570,4 +896,11 @@ sub command_checks_all
 	return;
 }
 
+=pod
+
+=back
+
+=cut
+
+
 1;
#14Michael Paquier
michael@paquier.xyz
In reply to: Andrew Dunstan (#13)
Re: Contribution to Perldoc for TestLib module in Postgres

On Fri, Jul 26, 2019 at 09:51:34AM -0400, Andrew Dunstan wrote:

I've fixed the bitrot and some other infelicities on this patch. It's
not commitable yet but I think it's more reviewable.

Thanks, I had a look at this version.

+  # Returns the real directory for a virtual path directory under msys
+  real_dir(dir)
real_dir() is no more.

perl2host() is missing.

+  #TODO
+  command_like_safe(cmd, expected_stdout, test_name)
[...]
+=pod
+
+=item command_like_safe(cmd, expected_stdout, test_name)
+
+TODO
+
+=cut
Update not to miss.
+Runs the command which is passed as argument to the function. On failure it
+abandons further tests and exits the program.
"On failure the test suite exits immediately."

I think that the SYNOPSIS could be shaped better. As of now it is a
simple succession of the same commands listed without any link to each
other, which is contrary for example to what we do in PostgresNode.pm,
where it shows up a set of small examples which are useful to
understand how to shape the tests and the possible interactions
between the routines of the module. My take would be to keep it
simple and minimal as TestLib.pm is the lowest level of our TAP test
infrastructure. So here are some simple suggestions, and we could go
with this set to begin with:
# Test basic output of a command.
program_help_ok('initdb');
program_version_ok('initdb');
program_options_handling_ok('initdb');

# Test option combinations
command_fails(['initdb', '--invalid-option'],
'command fails with invalid option');
my $tempdir = TestLib::tempdir;
command_ok('initdb', '-D', $tempdir);

Another thing is that the examples should not overlap with what
PostgresNode.pm presents, and that it is not necessary to show up all
the routines. It also makes little sense to describe in the synopsis
the routines in a way which duplicates with the descriptions on top of
each routine.
--
Michael

#15Thomas Munro
thomas.munro@gmail.com
In reply to: Michael Paquier (#14)
Re: Contribution to Perldoc for TestLib module in Postgres

On Tue, Jul 30, 2019 at 4:47 PM Michael Paquier <michael@paquier.xyz> wrote:

On Fri, Jul 26, 2019 at 09:51:34AM -0400, Andrew Dunstan wrote:

I've fixed the bitrot and some other infelicities on this patch. It's
not commitable yet but I think it's more reviewable.

Thanks, I had a look at this version.

[review listing things to fix]

Hi Ram,

Based on the above, it sounds like we want this patch but it needs a
bit more work. It's now the end of CF1. I'm moving this one to CF2
(September). Please post a new patch when ready.

Thanks!

--
Thomas Munro
https://enterprisedb.com

#16Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#14)
Re: Contribution to Perldoc for TestLib module in Postgres

On 2019-Jul-30, Michael Paquier wrote:

I think that the SYNOPSIS could be shaped better. As of now it is a
simple succession of the same commands listed without any link to each
other, which is contrary for example to what we do in PostgresNode.pm,
where it shows up a set of small examples which are useful to
understand how to shape the tests and the possible interactions
between the routines of the module. My take would be to keep it
simple and minimal as TestLib.pm is the lowest level of our TAP test
infrastructure.

Agreed ... that's pretty much the same thing I tried to say upthread. I
wrote my own synopsis, partly using your suggestions. I reworded the
description for the routines (I don't think there's any I didn't touch),
added a mention of $windows_os, added a =head1 to split out the ad-hoc
routines from the Test::More wrappers.

And pushed.

Please give it another look. It might need more polish.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#17Michael Paquier
michael@paquier.xyz
In reply to: Alvaro Herrera (#16)
Re: Contribution to Perldoc for TestLib module in Postgres

On Mon, Sep 02, 2019 at 01:48:14PM -0400, Alvaro Herrera wrote:

Agreed ... that's pretty much the same thing I tried to say upthread. I
wrote my own synopsis, partly using your suggestions. I reworded the
description for the routines (I don't think there's any I didn't touch),
added a mention of $windows_os, added a =head1 to split out the ad-hoc
routines from the Test::More wrappers.

And pushed.

Please give it another look. It might need more polish.

Thanks for committing. I have read through the commit and I am not
noticing any issue sorting out. One thing may be to give a short
description for some of the routine's arguments like
check_mode_recursive, but I think that we could live without that
either.
--
Michael