diff --git a/doc/src/sgml/install-windows.sgml b/doc/src/sgml/install-windows.sgml
index d154b44..2047790 100644
--- a/doc/src/sgml/install-windows.sgml
+++ b/doc/src/sgml/install-windows.sgml
@@ -439,6 +439,7 @@ $ENV{CONFIG}="Debug";
 <userinput>vcregress modulescheck</userinput>
 <userinput>vcregress ecpgcheck</userinput>
 <userinput>vcregress isolationcheck</userinput>
+<userinput>vcregress tapcheck</userinput>
 <userinput>vcregress upgradecheck</userinput>
 </screen>
 
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 8eab178..29bc874 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -337,7 +337,7 @@ cd $(srcdir) && TESTDIR='$(CURDIR)' PATH="$(bindir):$$PATH" PGPORT='6$(DEF_PGPOR
 endef
 
 define prove_check
-cd $(srcdir) && TESTDIR='$(CURDIR)' $(with_temp_install) PGPORT='6$(DEF_PGPORT)' top_builddir='$(CURDIR)/$(top_builddir)' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) t/*.pl
+cd $(srcdir) && TESTDIR='$(CURDIR)' TESTREGRESS='$(top_builddir)/src/test/regress/pg_regress' $(with_temp_install) PGPORT='6$(DEF_PGPORT)' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) t/*.pl
 endef
 
 else
diff --git a/src/bin/initdb/t/001_initdb.pl b/src/bin/initdb/t/001_initdb.pl
index 299dcf5..095cbf3 100644
--- a/src/bin/initdb/t/001_initdb.pl
+++ b/src/bin/initdb/t/001_initdb.pl
@@ -4,6 +4,7 @@
 
 use strict;
 use warnings;
+use Config;
 use TestLib;
 use Test::More tests => 14;
 
diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
index c8c9250..59e8cb4 100644
--- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl
+++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
@@ -1,8 +1,9 @@
 use strict;
 use warnings;
 use Cwd;
+use Config;
 use TestLib;
-use Test::More tests => 35;
+use Test::More tests => ($Config{osname} eq "MSWin32") ? 25 : 35;
 
 program_help_ok('pg_basebackup');
 program_version_ok('pg_basebackup');
@@ -25,12 +26,22 @@ if (open BADCHARS, ">>$tempdir/pgdata/FOO\xe0\xe0\xe0BAR")
 	close BADCHARS;
 }
 
+# Use SSPI on Windows, node has been initialized already accordingly
+# by pg_regress --config-auth.
 open HBA, ">>$tempdir/pgdata/pg_hba.conf";
-print HBA "local replication all trust\n";
-print HBA "host replication all 127.0.0.1/32 trust\n";
-print HBA "host replication all ::1/128 trust\n";
+if ($Config{osname} ne "MSWin32")
+{
+	print HBA "local replication all trust\n";
+	print HBA "host replication all 127.0.0.1/32 trust\n";
+	print HBA "host replication all ::1/128 trust\n";
+}
+else
+{
+	print HBA "host replication all 127.0.0.1/32 sspi include_realm=1 map=regress\n";
+	print HBA "host replication all ::1/128 sspi include_realm=1 map=regress\n";
+}
 close HBA;
-system_or_bail 'pg_ctl', '-s', '-D', "$tempdir/pgdata", 'reload';
+system_or_bail 'pg_ctl', '-D', "$tempdir/pgdata", 'reload';
 
 command_fails(
 	[ 'pg_basebackup', '-D', "$tempdir/backup" ],
@@ -64,6 +75,33 @@ command_fails([ 'pg_basebackup', '-D', "$tempdir/tarbackup_l1", '-Ft' ],
 	'pg_basebackup tar with long name fails');
 unlink "$tempdir/pgdata/$superlongname";
 
+command_fails(
+	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T=/foo" ],
+	'-T with empty old directory fails');
+command_fails(
+	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=" ],
+	'-T with empty new directory fails');
+command_fails(
+	[   'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp',
+		"-T/foo=/bar=/baz" ],
+	'-T with multiple = fails');
+command_fails(
+	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo=/bar" ],
+	'-T with old directory not absolute fails');
+command_fails(
+	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=bar" ],
+	'-T with new directory not absolute fails');
+command_fails(
+	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo" ],
+	'-T with invalid format fails');
+
+# Windows does not support symlink, which is what the rest of the tests
+# need, so simply leave.
+if ($Config{osname} eq "MSWin32")
+{
+	exit 0;
+}
+
 # Create a temporary directory in the system location and symlink it
 # to our physical temp location.  That way we can use shorter names
 # for the tablespace directories, which hopefully won't run afoul of
@@ -109,29 +147,8 @@ command_ok(
 		"-T$shorter_tempdir/tbl\\=spc2=$tempdir/tbackup/tbl\\=spc2" ],
 	'mapping tablespace with = sign in path');
 ok(-d "$tempdir/tbackup/tbl=spc2", 'tablespace with = sign was relocated');
-
 psql 'postgres', "DROP TABLESPACE tblspc2;";
 
-command_fails(
-	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T=/foo" ],
-	'-T with empty old directory fails');
-command_fails(
-	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=" ],
-	'-T with empty new directory fails');
-command_fails(
-	[   'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp',
-		"-T/foo=/bar=/baz" ],
-	'-T with multiple = fails');
-command_fails(
-	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo=/bar" ],
-	'-T with old directory not absolute fails');
-command_fails(
-	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=bar" ],
-	'-T with new directory not absolute fails');
-command_fails(
-	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo" ],
-	'-T with invalid format fails');
-
 mkdir "$tempdir/$superlongname";
 psql 'postgres',
   "CREATE TABLESPACE tblspc3 LOCATION '$tempdir/$superlongname';";
diff --git a/src/bin/pg_controldata/t/001_pg_controldata.pl b/src/bin/pg_controldata/t/001_pg_controldata.pl
index a4180e7..0ef1de7 100644
--- a/src/bin/pg_controldata/t/001_pg_controldata.pl
+++ b/src/bin/pg_controldata/t/001_pg_controldata.pl
@@ -1,5 +1,6 @@
 use strict;
 use warnings;
+use Config;
 use TestLib;
 use Test::More tests => 13;
 
@@ -11,6 +12,7 @@ program_options_handling_ok('pg_controldata');
 command_fails(['pg_controldata'], 'pg_controldata without arguments fails');
 command_fails([ 'pg_controldata', 'nonexistent' ],
 	'pg_controldata with nonexistent directory fails');
-system_or_bail "initdb -D '$tempdir'/data -A trust >/dev/null";
+
+system_or_bail "initdb -D $tempdir/data -A trust";
 command_like([ 'pg_controldata', "$tempdir/data" ],
 	qr/checkpoint/, 'pg_controldata produces output');
diff --git a/src/bin/pg_ctl/t/001_start_stop.pl b/src/bin/pg_ctl/t/001_start_stop.pl
index 6c9ec5c..bebcd6e 100644
--- a/src/bin/pg_ctl/t/001_start_stop.pl
+++ b/src/bin/pg_ctl/t/001_start_stop.pl
@@ -1,5 +1,6 @@
 use strict;
 use warnings;
+use Config;
 use TestLib;
 use Test::More tests => 17;
 
@@ -15,12 +16,19 @@ command_exit_is([ 'pg_ctl', 'start', '-D', "$tempdir/nonexistent" ],
 
 command_ok([ 'pg_ctl', 'initdb', '-D', "$tempdir/data" ], 'pg_ctl initdb');
 command_ok(
-	[   "$ENV{top_builddir}/src/test/regress/pg_regress", '--config-auth',
+	[ $ENV{TESTREGRESS}, '--config-auth',
 		"$tempdir/data" ],
 	'configure authentication');
 open CONF, ">>$tempdir/data/postgresql.conf";
-print CONF "listen_addresses = ''\n";
-print CONF "unix_socket_directories = '$tempdir_short'\n";
+if ($Config{osname} eq "MSWin32")
+{
+	print CONF "listen_addresses = '127.0.0.1'\n";
+}
+else
+{
+	print CONF "listen_addresses = ''\n";
+	print CONF "unix_socket_directories = '$tempdir_short'\n";
+}
 close CONF;
 command_ok([ 'pg_ctl', 'start', '-D', "$tempdir/data", '-w' ],
 	'pg_ctl start -w');
@@ -36,4 +44,4 @@ command_ok([ 'pg_ctl', 'restart', '-D', "$tempdir/data", '-w', '-m', 'fast' ],
 command_ok([ 'pg_ctl', 'restart', '-D', "$tempdir/data", '-w', '-m', 'fast' ],
 	'pg_ctl restart with server running');
 
-system_or_bail 'pg_ctl', '-s', 'stop', '-D', "$tempdir/data", '-m', 'fast';
+system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/data", '-m', 'fast';
diff --git a/src/bin/pg_ctl/t/002_status.pl b/src/bin/pg_ctl/t/002_status.pl
index 0558854..f68cd62 100644
--- a/src/bin/pg_ctl/t/002_status.pl
+++ b/src/bin/pg_ctl/t/002_status.pl
@@ -1,5 +1,6 @@
 use strict;
 use warnings;
+use Config;
 use TestLib;
 use Test::More tests => 3;
 
@@ -11,16 +12,23 @@ command_exit_is([ 'pg_ctl', 'status', '-D', "$tempdir/nonexistent" ],
 
 standard_initdb "$tempdir/data";
 open CONF, ">>$tempdir/data/postgresql.conf";
-print CONF "listen_addresses = ''\n";
-print CONF "unix_socket_directories = '$tempdir_short'\n";
+if ($Config{osname} eq "MSWin32")
+{
+	print CONF "listen_addresses = '127.0.0.1'\n";
+}
+else
+{
+	print CONF "listen_addresses = ''\n";
+	print CONF "unix_socket_directories = '$tempdir_short'\n";
+}
 close CONF;
 
 command_exit_is([ 'pg_ctl', 'status', '-D', "$tempdir/data" ],
 	3, 'pg_ctl status with server not running');
 
-system_or_bail 'pg_ctl', '-s', '-l', "$tempdir/logfile", '-D',
+system_or_bail 'pg_ctl', '-l', "$tempdir/logfile", '-D',
   "$tempdir/data", '-w', 'start';
 command_exit_is([ 'pg_ctl', 'status', '-D', "$tempdir/data" ],
 	0, 'pg_ctl status with server running');
 
-system_or_bail 'pg_ctl', '-s', 'stop', '-D', "$tempdir/data", '-m', 'fast';
+system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/data", '-m', 'fast';
diff --git a/src/bin/pg_rewind/.gitignore b/src/bin/pg_rewind/.gitignore
index 9ade7ef..79ddca3 100644
--- a/src/bin/pg_rewind/.gitignore
+++ b/src/bin/pg_rewind/.gitignore
@@ -4,4 +4,3 @@
 
 # Generated by test suite
 /tmp_check/
-/regress_log/
diff --git a/src/bin/pg_rewind/Makefile b/src/bin/pg_rewind/Makefile
index 7d60715..92b5d20 100644
--- a/src/bin/pg_rewind/Makefile
+++ b/src/bin/pg_rewind/Makefile
@@ -45,7 +45,7 @@ uninstall:
 
 clean distclean maintainer-clean:
 	rm -f pg_rewind$(X) $(OBJS) xlogreader.c
-	rm -rf tmp_check regress_log
+	rm -rf tmp_check
 
 check:
 	$(prove_check)
diff --git a/src/bin/pg_rewind/RewindTest.pm b/src/bin/pg_rewind/RewindTest.pm
index 5219ec9..3c9e34c 100644
--- a/src/bin/pg_rewind/RewindTest.pm
+++ b/src/bin/pg_rewind/RewindTest.pm
@@ -38,6 +38,7 @@ use warnings;
 use TestLib;
 use Test::More;
 
+use Config;
 use File::Copy;
 use File::Path qw(remove_tree);
 use IPC::Run qw(run start);
@@ -91,14 +92,16 @@ sub master_psql
 {
 	my $cmd = shift;
 
-	system_or_bail("psql -q --no-psqlrc -d $connstr_master -c \"$cmd\"");
+	system_or_bail 'psql', '-q', '--no-psqlrc', '-d', $connstr_master,
+	  '-c', "$cmd";
 }
 
 sub standby_psql
 {
 	my $cmd = shift;
 
-	system_or_bail("psql -q --no-psqlrc -d $connstr_standby -c \"$cmd\"");
+	system_or_bail 'psql', '-q', '--no-psqlrc', '-d', $connstr_standby,
+	  '-c', "$cmd";
 }
 
 # Run a query against the master, and check that the output matches what's
@@ -177,7 +180,6 @@ sub init_rewind_test
 	my $test_mode = shift;
 
 	$log_path = "regress_log/pg_rewind_log_${testname}_${test_mode}";
-
 	remove_tree $log_path;
 }
 
@@ -204,18 +206,30 @@ max_connections = 10
 ));
 
 	# Accept replication connections on master
-	append_to_file(
-		"$test_master_datadir/pg_hba.conf", qq(
+	if ($Config{osname} eq "MSWin32")
+	{
+		append_to_file("$test_master_datadir/pg_hba.conf", qq(
+host replication all 127.0.0.1/32 sspi include_realm=1 map=regress
+host replication all ::1/128 sspi include_realm=1 map=regress
+));
+	}
+	else
+	{
+		append_to_file("$test_master_datadir/pg_hba.conf", qq(
 local replication all trust
 ));
+	}
 
-	system_or_bail(
-"pg_ctl -w -D $test_master_datadir -o \"-k $tempdir_short --listen-addresses='' -p $port_master\" start >>$log_path 2>&1"
-	);
+	my $ctl_options = ($Config{osname} eq "MSWin32") ?
+		"--listen-addresses=127.0.0.1 -p $port_master" :
+		"-k $tempdir_short --listen-addresses='' -p $port_master";
+
+	system_or_bail("pg_ctl -w -D $test_master_datadir -o \"$ctl_options\" start");
 
 	#### Now run the test-specific parts to initialize the master before setting
 	# up standby
-	$ENV{PGHOST} = $tempdir_short;
+	$ENV{PGHOST} = ($Config{osname} eq "MSWin32") ?
+		"127.0.0.1" : $tempdir_short;
 }
 
 sub create_standby
@@ -225,19 +239,19 @@ sub create_standby
 	remove_tree $test_standby_datadir;
 
 	# Base backup is taken with xlog files included
-	system_or_bail(
-"pg_basebackup -D $test_standby_datadir -p $port_master -x >>$log_path 2>&1");
-	append_to_file(
-		"$test_standby_datadir/recovery.conf", qq(
+	system_or_bail("pg_basebackup -D $test_standby_datadir -p $port_master -x");
+	append_to_file("$test_standby_datadir/recovery.conf", qq(
 primary_conninfo='$connstr_master application_name=rewind_standby'
 standby_mode=on
 recovery_target_timeline='latest'
 ));
 
+	my $ctl_options = ($Config{osname} eq "MSWin32") ?
+		"--listen-addresses=127.0.0.1 -p $port_standby" :
+		"-k $tempdir_short --listen-addresses='' -p $port_standby";
+
 	# Start standby
-	system_or_bail(
-"pg_ctl -w -D $test_standby_datadir -o \"-k $tempdir_short --listen-addresses='' -p $port_standby\" start >>$log_path 2>&1"
-	);
+	system_or_bail("pg_ctl -w -D $test_standby_datadir -o \"$ctl_options\" start");
 
 	# Wait until the standby has caught up with the primary, by polling
 	# pg_stat_replication.
@@ -255,8 +269,7 @@ sub promote_standby
 	# Now promote slave and insert some new data on master, this will put
 	# the master out-of-sync with the standby. Wait until the standby is
 	# out of recovery mode, and is ready to accept read-write connections.
-	system_or_bail(
-		"pg_ctl -w -D $test_standby_datadir promote >>$log_path 2>&1");
+	system_or_bail('pg_ctl', '-w', '-D', $test_standby_datadir, 'promote');
 	poll_query_until("SELECT NOT pg_is_in_recovery()", $connstr_standby)
 	  or die "Timed out while waiting for promotion of standby";
 
@@ -274,8 +287,7 @@ sub run_pg_rewind
 	my $test_mode = shift;
 
 	# Stop the master and be ready to perform the rewind
-	system_or_bail(
-		"pg_ctl -w -D $test_master_datadir stop -m fast >>$log_path 2>&1");
+	system_or_bail("pg_ctl -w -D $test_master_datadir -m fast stop");
 
 	# At this point, the rewind processing is ready to run.
 	# We now have a very simple scenario with a few diverged WAL record.
@@ -291,39 +303,31 @@ sub run_pg_rewind
 	# Now run pg_rewind
 	if ($test_mode eq "local")
 	{
-
 		# Do rewind using a local pgdata as source
 		# Stop the master and be ready to perform the rewind
 		system_or_bail(
-			"pg_ctl -w -D $test_standby_datadir stop -m fast >>$log_path 2>&1"
+			"pg_ctl -w -D $test_standby_datadir -m fast stop"
 		);
-		my $result = run(
+		command_ok(
 			[   'pg_rewind',
 				"--debug",
 				"--source-pgdata=$test_standby_datadir",
 				"--target-pgdata=$test_master_datadir" ],
-			'>>',
-			$log_path,
-			'2>&1');
-		ok($result, 'pg_rewind local');
+			'pg_rewind local');
 	}
 	elsif ($test_mode eq "remote")
 	{
-
 		# Do rewind using a remote connection as source
-		my $result = run(
+		command_ok(
 			[   'pg_rewind',
+				"--debug",
 				"--source-server",
 				"port=$port_standby dbname=postgres",
 				"--target-pgdata=$test_master_datadir" ],
-			'>>',
-			$log_path,
-			'2>&1');
-		ok($result, 'pg_rewind remote');
+			'pg_rewind remote');
 	}
 	else
 	{
-
 		# Cannot come here normally
 		die("Incorrect test mode specified");
 	}
@@ -341,10 +345,12 @@ standby_mode=on
 recovery_target_timeline='latest'
 ));
 
+	my $ctl_options = ($Config{osname} eq "MSWin32") ?
+		"--listen-addresses=127.0.0.1 -p $port_master" :
+		"-k $tempdir_short --listen-addresses='' -p $port_master";
+
 	# Restart the master to check that rewind went correctly
-	system_or_bail(
-"pg_ctl -w -D $test_master_datadir -o \"-k $tempdir_short --listen-addresses='' -p $port_master\" start >>$log_path 2>&1"
-	);
+	system_or_bail("pg_ctl -w -D $test_master_datadir -o \"$ctl_options\" start");
 
 	#### Now run the test-specific parts to check the result
 }
@@ -354,13 +360,13 @@ sub clean_rewind_test
 {
 	if ($test_master_datadir)
 	{
-		system
-		  "pg_ctl -D $test_master_datadir -s -m immediate stop 2> /dev/null";
+		system_log('pg_ctl', '-D', $test_master_datadir, '-w', '-m',
+				   'immediate', 'stop');
 	}
 	if ($test_standby_datadir)
 	{
-		system
-		  "pg_ctl -D $test_standby_datadir -s -m immediate stop 2> /dev/null";
+		system_log('pg_ctl', '-D', $test_standby_datadir, '-w', '-m',
+				   'immediate', 'stop');
 	}
 }
 
diff --git a/src/bin/pg_rewind/t/001_basic.pl b/src/bin/pg_rewind/t/001_basic.pl
index f60368b..be7d887 100644
--- a/src/bin/pg_rewind/t/001_basic.pl
+++ b/src/bin/pg_rewind/t/001_basic.pl
@@ -9,7 +9,6 @@ sub run_test
 {
 	my $test_mode = shift;
 
-	RewindTest::init_rewind_test('basic', $test_mode);
 	RewindTest::setup_cluster();
 
 	# Create a test table and insert a row in master.
diff --git a/src/bin/pg_rewind/t/002_databases.pl b/src/bin/pg_rewind/t/002_databases.pl
index 7564fa9..b0b007a 100644
--- a/src/bin/pg_rewind/t/002_databases.pl
+++ b/src/bin/pg_rewind/t/002_databases.pl
@@ -9,7 +9,6 @@ sub run_test
 {
 	my $test_mode = shift;
 
-	RewindTest::init_rewind_test('databases', $test_mode);
 	RewindTest::setup_cluster();
 
 	# Create a database in master.
diff --git a/src/bin/pg_rewind/t/003_extrafiles.pl b/src/bin/pg_rewind/t/003_extrafiles.pl
index 9a95268..0cd0ac4 100644
--- a/src/bin/pg_rewind/t/003_extrafiles.pl
+++ b/src/bin/pg_rewind/t/003_extrafiles.pl
@@ -14,7 +14,6 @@ sub run_test
 {
 	my $test_mode = shift;
 
-	RewindTest::init_rewind_test('extrafiles', $test_mode);
 	RewindTest::setup_cluster();
 
 	my $test_master_datadir = $RewindTest::test_master_datadir;
diff --git a/src/bin/scripts/t/020_createdb.pl b/src/bin/scripts/t/020_createdb.pl
index a44283c..7e769e4 100644
--- a/src/bin/scripts/t/020_createdb.pl
+++ b/src/bin/scripts/t/020_createdb.pl
@@ -1,5 +1,6 @@
 use strict;
 use warnings;
+use Config;
 use TestLib;
 use Test::More tests => 13;
 
@@ -14,6 +15,8 @@ issues_sql_like(
 	[ 'createdb', 'foobar1' ],
 	qr/statement: CREATE DATABASE foobar1/,
 	'SQL CREATE DATABASE run');
+
+my $encoding = ($Config{osname} eq "MSWin32") ? "win1252" : "LATIN1" ;
 issues_sql_like(
 	[ 'createdb', '-l', 'C', '-E', 'LATIN1', '-T', 'template0', 'foobar2' ],
 	qr/statement: CREATE DATABASE foobar2 ENCODING 'LATIN1'/,
diff --git a/src/test/perl/SimpleTee.pm b/src/test/perl/SimpleTee.pm
new file mode 100644
index 0000000..8d31a40
--- /dev/null
+++ b/src/test/perl/SimpleTee.pm
@@ -0,0 +1,27 @@
+# A simple 'tee' implementation, using perl tie.
+#
+# Whenever you print to the handle, it gets forwarded to a list of
+# handles. The list of output filehandles is passed to the constructor.
+#
+# This is similar to IO::Tee, but only used for output. Only the PRINT
+# method is currently implemented; that's all we need. We don't want to
+# depend on IO::Tee just for this.
+
+package SimpleTee;
+use strict;
+
+sub TIEHANDLE {
+	my $self = shift;
+	bless \@_, $self;
+}
+
+sub PRINT {
+	my $self = shift;
+	my $ok = 1;
+	for my $fh (@$self) {
+		print $fh @_ or $ok = 0;
+	}
+	return $ok;
+}
+
+1;
diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm
index ef42366..35ea94f 100644
--- a/src/test/perl/TestLib.pm
+++ b/src/test/perl/TestLib.pm
@@ -3,6 +3,7 @@ package TestLib;
 use strict;
 use warnings;
 
+use Config;
 use Exporter 'import';
 our @EXPORT = qw(
   tempdir
@@ -12,6 +13,8 @@ our @EXPORT = qw(
   restart_test_server
   psql
   system_or_bail
+  system_log
+  run_log
 
   command_ok
   command_fails
@@ -24,11 +27,47 @@ our @EXPORT = qw(
 );
 
 use Cwd;
+use File::Basename;
 use File::Spec;
 use File::Temp ();
 use IPC::Run qw(run start);
+
+use SimpleTee;
+
 use Test::More;
 
+# Open log file. For each test, the log file name uses the name of the
+# file launching this module, without its file prefix for readability.
+my $log_path = 'tmp_check/log';
+mkdir 'tmp_check';
+mkdir $log_path;
+my $test_logfile = basename($0);
+$test_logfile =~ s/\.[^.]+$//;
+$test_logfile = "$log_path/regress_log_$test_logfile";
+open TESTLOG, '>', $test_logfile or die "Cannot open STDOUT to logfile: $!";
+
+# Hijack STDOUT and STDERR to the log file
+open(ORIG_STDOUT, ">&STDOUT");
+open(ORIG_STDERR, ">&STDERR");
+open(STDOUT, ">&TESTLOG");
+open(STDERR, ">&TESTLOG");
+
+# The test output (ok ...) needs to be printed to the original STDOUT
+# so that the 'prove' program can parse it, and also display it to the
+# user in real time. But also copy it to the log file, to provide more
+# context in the log file.
+my $builder = Test::More->builder;
+my $fh = $builder->output;
+tie *$fh, "SimpleTee", *ORIG_STDOUT, *TESTLOG;
+$fh = $builder->failure_output;
+tie *$fh, "SimpleTee", *ORIG_STDERR, *TESTLOG;
+
+# Enable auto-flushing for all the file handles. Stderr and stdout are
+# redirected to the same file, and buffering causes the lines to appear
+# in the log in confusing order.
+autoflush STDOUT 1;
+autoflush STDERR 1;
+autoflush TESTLOG 1;
 
 # Set to untranslated messages, to be able to compare program output
 # with expected strings.
@@ -52,7 +91,6 @@ if (!$ENV{PGPORT})
 
 $ENV{PGPORT} = int($ENV{PGPORT}) % 65536;
 
-
 #
 # Helper functions
 #
@@ -77,9 +115,9 @@ sub tempdir_short
 sub standard_initdb
 {
 	my $pgdata = shift;
-	system_or_bail("initdb -D '$pgdata' -A trust -N >/dev/null");
-	system_or_bail("$ENV{top_builddir}/src/test/regress/pg_regress",
-		'--config-auth', $pgdata);
+	system_or_bail('initdb', '-D', "$pgdata", '-A' , 'trust', '-N');
+	system_or_bail($ENV{TESTREGRESS},
+				   '--config-auth', $pgdata);
 }
 
 my ($test_server_datadir, $test_server_logfile);
@@ -91,47 +129,68 @@ sub start_test_server
 
 	my $tempdir_short = tempdir_short;
 
+	my $listen_addresses = ($Config{osname} eq "MSWin32") ? "127.0.0.1" : "''";
+
+	print("### Starting test server in $tempdir\n");
 	standard_initdb "$tempdir/pgdata";
-	$ret = system 'pg_ctl', '-D', "$tempdir/pgdata", '-s', '-w', '-l',
+	$ret = system_log('pg_ctl', '-D', "$tempdir/pgdata", '-w', '-l',
 	  "$tempdir/logfile", '-o',
-"--fsync=off -k $tempdir_short --listen-addresses='' --log-statement=all",
-	  'start';
+	  "--fsync=off --listen-addresses=$listen_addresses --log-statement=all",
+	  'start');
 
 	if ($ret != 0)
 	{
+		print "# pg_ctl failed; logfile:\n";
 		system('cat', "$tempdir/logfile");
 		BAIL_OUT("pg_ctl failed");
 	}
 
-	$ENV{PGHOST}         = $tempdir_short;
+	$ENV{PGHOST}         = ($Config{osname} eq "MSWin32") ? "127.0.0.1" : $tempdir_short;
 	$test_server_datadir = "$tempdir/pgdata";
 	$test_server_logfile = "$tempdir/logfile";
 }
 
 sub restart_test_server
 {
-	system 'pg_ctl', '-s', '-D', $test_server_datadir, '-w', '-l',
-	  $test_server_logfile, 'restart';
+	print("### Restarting test server\n");
+	system_log('pg_ctl', '-D', $test_server_datadir, '-w', '-l',
+	  $test_server_logfile, 'restart');
 }
 
 END
 {
 	if ($test_server_datadir)
 	{
-		system 'pg_ctl', '-D', $test_server_datadir, '-s', '-w', '-m',
-		  'immediate', 'stop';
+		system_log('pg_ctl', '-D', $test_server_datadir, '-m',
+		  'immediate', 'stop');
 	}
 }
 
 sub psql
 {
 	my ($dbname, $sql) = @_;
+	print("# Running SQL command: $sql\n");
 	run [ 'psql', '-X', '-q', '-d', $dbname, '-f', '-' ], '<', \$sql or die;
 }
 
 sub system_or_bail
 {
-	system(@_) == 0 or BAIL_OUT("system @_ failed: $?");
+	if (system_log(@_) != 0)
+	{
+		BAIL_OUT("system $_[0] failed: $?");
+	}
+}
+
+sub system_log
+{
+	print("# Running: " . join(" ", @_) ."\n");
+	return system(@_);
+}
+
+sub run_log
+{
+	print("# Running: " . join(" ", @{$_[0]}) ."\n");
+	return run (@_);
 }
 
 
@@ -143,32 +202,33 @@ sub system_or_bail
 sub command_ok
 {
 	my ($cmd, $test_name) = @_;
-	my $result = run $cmd, '>', File::Spec->devnull(), '2>',
-	  File::Spec->devnull();
+	my $result = run_log($cmd);
 	ok($result, $test_name);
 }
 
 sub command_fails
 {
 	my ($cmd, $test_name) = @_;
-	my $result = run $cmd, '>', File::Spec->devnull(), '2>',
-	  File::Spec->devnull();
+	my $result = run_log($cmd);
 	ok(!$result, $test_name);
 }
 
 sub command_exit_is
 {
 	my ($cmd, $expected, $test_name) = @_;
-	my $h = start $cmd, '>', File::Spec->devnull(), '2>',
-	  File::Spec->devnull();
+	print("# Running: " . join(" ", @{$cmd}) ."\n");
+	my $h = start $cmd;
 	$h->finish();
-	is($h->result(0), $expected, $test_name);
+	my $result = ($Config{osname} eq "MSWin32") ?
+		($h->full_results)[0] : $h->result(0);
+	is($result, $expected, $test_name);
 }
 
 sub program_help_ok
 {
 	my ($cmd) = @_;
 	my ($stdout, $stderr);
+	print("# Running: $cmd --help\n");
 	my $result = run [ $cmd, '--help' ], '>', \$stdout, '2>', \$stderr;
 	ok($result, "$cmd --help exit code 0");
 	isnt($stdout, '', "$cmd --help goes to stdout");
@@ -179,6 +239,7 @@ sub program_version_ok
 {
 	my ($cmd) = @_;
 	my ($stdout, $stderr);
+	print("# Running: $cmd --version\n");
 	my $result = run [ $cmd, '--version' ], '>', \$stdout, '2>', \$stderr;
 	ok($result, "$cmd --version exit code 0");
 	isnt($stdout, '', "$cmd --version goes to stdout");
@@ -189,6 +250,7 @@ sub program_options_handling_ok
 {
 	my ($cmd) = @_;
 	my ($stdout, $stderr);
+	print("# Running: $cmd --not-a-valid-option\n");
 	my $result = run [ $cmd, '--not-a-valid-option' ], '>', \$stdout, '2>',
 	  \$stderr;
 	ok(!$result, "$cmd with invalid option nonzero exit code");
@@ -199,6 +261,7 @@ sub command_like
 {
 	my ($cmd, $expected_stdout, $test_name) = @_;
 	my ($stdout, $stderr);
+	print("# Running: " . join(" ", @{$cmd}) . "\n");
 	my $result = run $cmd, '>', \$stdout, '2>', \$stderr;
 	ok($result, "@$cmd exit code 0");
 	is($stderr, '', "@$cmd no stderr");
@@ -208,9 +271,8 @@ sub command_like
 sub issues_sql_like
 {
 	my ($cmd, $expected_sql, $test_name) = @_;
-	my ($stdout, $stderr);
 	truncate $test_server_logfile, 0;
-	my $result = run $cmd, '>', \$stdout, '2>', \$stderr;
+	my $result = run_log($cmd);
 	ok($result, "@$cmd exit code 0");
 	my $log = `cat '$test_server_logfile'`;
 	like($log, $expected_sql, "$test_name: SQL found in server log");
diff --git a/src/test/ssl/ServerSetup.pm b/src/test/ssl/ServerSetup.pm
index bbff99a..8c1b517 100644
--- a/src/test/ssl/ServerSetup.pm
+++ b/src/test/ssl/ServerSetup.pm
@@ -125,8 +125,6 @@ sub switch_server_cert
    # restart_test_server() because that overrides listen_addresses to only all
    # Unix domain socket connections.
 
-	system_or_bail 'pg_ctl', 'stop',  '-s', '-D', "$tempdir/pgdata", '-w';
-	system_or_bail 'pg_ctl', 'start', '-s', '-D', "$tempdir/pgdata", '-w',
-	  '-l',
-	  "$tempdir/logfile";
+	system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/pgdata";
+	system_or_bail 'pg_ctl', 'start', '-D', "$tempdir/pgdata", '-w';
 }
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 6b16e69..8abc81d 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -619,6 +619,7 @@ sub GetFakeConfigure
 	$cfg .= ' --enable-integer-datetimes'
 	  if ($self->{options}->{integer_datetimes});
 	$cfg .= ' --enable-nls' if ($self->{options}->{nls});
+	$cfg .= ' --enable-tap-tests' if ($self->{options}->{taptest});
 	$cfg .= ' --with-ldap'  if ($self->{options}->{ldap});
 	$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
 	$cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index b9f2ff4..5b8c8cd 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -14,6 +14,7 @@ our $config = {
 	# wal_blocksize => 8,     # --with-wal-blocksize, 8kB by default
 	# wal_segsize => 16,      # --with-wal-segsize, 16MB by default
 	ldap     => 1,        # --with-ldap
+	taptest  => undef,    # --enable-tap-tests
 	extraver => undef,    # --with-extra-version=<string>
 	nls      => undef,    # --enable-nls=<path>
 	tcl      => undef,    # --with-tls=<path>
diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl
index 6196383..4dcc155 100644
--- a/src/tools/msvc/vcregress.pl
+++ b/src/tools/msvc/vcregress.pl
@@ -7,7 +7,9 @@ use strict;
 our $config;
 
 use Cwd;
+use File::Basename;
 use File::Copy;
+use File::Find ();
 
 use Install qw(Install);
 
@@ -32,7 +34,7 @@ if (-e "src/tools/msvc/buildenv.pl")
 
 my $what = shift || "";
 if ($what =~
-/^(check|installcheck|plcheck|contribcheck|modulescheck|ecpgcheck|isolationcheck|upgradecheck)$/i
+/^(check|installcheck|plcheck|contribcheck|modulescheck|ecpgcheck|isolationcheck|upgradecheck|tapcheck)$/i
   )
 {
 	$what = uc $what;
@@ -79,6 +81,7 @@ my %command = (
 	CONTRIBCHECK   => \&contribcheck,
 	MODULESCHECK   => \&modulescheck,
 	ISOLATIONCHECK => \&isolationcheck,
+	TAPCHECK       => \&tapcheck,
 	UPGRADECHECK   => \&upgradecheck,);
 
 my $proc = $command{$what};
@@ -172,6 +175,49 @@ sub isolationcheck
 	exit $status if $status;
 }
 
+sub tapcheck
+{
+	if (!defined($config->{taptest}))
+	{
+		print "TAP tests not enabled\n";
+		exit 0;
+	}
+
+	InstallTemp();
+
+	my @args = ( "prove", "--verbose", "t/*.pl");
+
+	$ENV{PATH} = "$tmp_installdir/bin;$ENV{PATH}";
+	$ENV{PERL5LIB} = "$topdir/src/test/perl;$ENV{PERL5LIB}";
+	$ENV{TESTREGRESS} = "$topdir/$Config/pg_regress/pg_regress";
+
+	# Find out all the existing TAP tests by simply looking for t/
+	# in the tree.
+	my $tap_dirs = [];
+	my @top_dir = ($topdir);
+	File::Find::find(
+		{   wanted => sub {
+				/^t\z/s
+				  && push(@$tap_dirs, $File::Find::name);
+			  }
+		},
+		@top_dir);
+
+	# Process each test
+	foreach my $test_path (@$tap_dirs)
+	{
+		next if ($test_path =~ /\/ssl\// && !defined($config->{openssl}));
+
+		my $dir = dirname($test_path);
+		chdir $dir;
+		# Reset those values, they may have been changed by another test.
+		$ENV{TESTDIR} = "$dir";
+		system(@args);
+		my $status = $? >> 8;
+		exit $status if $status;
+	}
+}
+
 sub plcheck
 {
 	chdir $startdir;
