diff --git a/doc/src/sgml/install-windows.sgml b/doc/src/sgml/install-windows.sgml
index 9b77648..d3d8f5f 100644
--- a/doc/src/sgml/install-windows.sgml
+++ b/doc/src/sgml/install-windows.sgml
@@ -438,6 +438,7 @@ $ENV{CONFIG}="Debug";
 <userinput>vcregress contribcheck</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 e92d3c6..333cb17 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -334,14 +334,14 @@ ifeq ($(enable_tap_tests),yes)
 
 define prove_installcheck
 rm -rf $(CURDIR)/tmp_check/log
-cd $(srcdir) && TESTDIR='$(CURDIR)' PATH="$(bindir):$$PATH" PGPORT='6$(DEF_PGPORT)' top_builddir='$(CURDIR)/$(top_builddir)' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) t/*.pl
+cd $(srcdir) && TESTDIR='$(CURDIR)' PATH="$(bindir):$$PATH" PGPORT='6$(DEF_PGPORT)' top_builddir='$(CURDIR)/$(top_builddir)' PG_REGRESS='$(CURDIR)/$(top_builddir)/src/test/regress/pg_regress' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) t/*.pl
 endef
 
 define prove_check
 rm -rf $(CURDIR)/tmp_check/log
 $(MKDIR_P) tmp_check/log
 $(MAKE) -C $(top_builddir) DESTDIR='$(CURDIR)'/tmp_check/install install >'$(CURDIR)'/tmp_check/log/install.log 2>&1
-cd $(srcdir) && TESTDIR='$(CURDIR)' PATH="$(CURDIR)/tmp_check/install$(bindir):$$PATH" $(call add_to_path,$(ld_library_path_var),$(CURDIR)/tmp_check/install$(libdir)) top_builddir='$(CURDIR)/$(top_builddir)' PGPORT='6$(DEF_PGPORT)' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) t/*.pl
+cd $(srcdir) && TESTDIR='$(CURDIR)' PATH="$(CURDIR)/tmp_check/install$(bindir):$$PATH" $(call add_to_path,$(ld_library_path_var),$(CURDIR)/tmp_check/install$(libdir)) top_builddir='$(CURDIR)/$(top_builddir)' PGPORT='6$(DEF_PGPORT)' PG_REGRESS='$(CURDIR)/$(top_builddir)/src/test/regress/pg_regress' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) t/*.pl
 endef
 
 else
diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
index 538ca0a..b40f89a 100644
--- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl
+++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
@@ -25,11 +25,7 @@ if (open BADCHARS, ">>$tempdir/pgdata/FOO\xe0\xe0\xe0BAR")
 	close BADCHARS;
 }
 
-open HBA, ">>$tempdir/pgdata/pg_hba.conf";
-print HBA "local replication all trust\n";
-print HBA "host replication all 127.0.0.1/32 trust\n";
-print HBA "host replication all ::1/128 trust\n";
-close HBA;
+configure_hba_for_replication "$tempdir/pgdata";
 system_or_bail 'pg_ctl', '-D', "$tempdir/pgdata", 'reload';
 
 command_fails(
@@ -57,54 +53,60 @@ command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup", '-Ft' ],
 	'tar format');
 ok(-f "$tempdir/tarbackup/base.tar", 'backup tar was created');
 
-# Create a temporary directory in the system location and symlink it
-# to our physical temp location.  That way we can use shorter names
-# for the tablespace directories, which hopefully won't run afoul of
-# the 99 character length limit.
-my $shorter_tempdir = tempdir_short . "/tempdir";
-symlink "$tempdir", $shorter_tempdir;
-
-mkdir "$tempdir/tblspc1";
-psql 'postgres', "CREATE TABLESPACE tblspc1 LOCATION '$shorter_tempdir/tblspc1';";
-psql 'postgres', "CREATE TABLE test1 (a int) TABLESPACE tblspc1;";
-command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup2", '-Ft' ],
-	'tar format with tablespaces');
-ok(-f "$tempdir/tarbackup2/base.tar", 'backup tar was created');
-my @tblspc_tars = glob "$tempdir/tarbackup2/[0-9]*.tar";
-is(scalar(@tblspc_tars), 1, 'one tablespace tar was created');
-
-command_fails(
-	[ 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp' ],
-	'plain format with tablespaces fails without tablespace mapping');
-
-command_ok(
-	[   'pg_basebackup',    '-D',
-		"$tempdir/backup1", '-Fp',
-		"-T$shorter_tempdir/tblspc1=$tempdir/tbackup/tblspc1" ],
-	'plain format with tablespaces succeeds with tablespace mapping');
-ok(-d "$tempdir/tbackup/tblspc1", 'tablespace was relocated');
-opendir(my $dh, "$tempdir/pgdata/pg_tblspc") or die;
-ok( (   grep
-		{
-			-l "$tempdir/backup1/pg_tblspc/$_"
-			  and readlink "$tempdir/backup1/pg_tblspc/$_" eq
-			  "$tempdir/tbackup/tblspc1"
-		  } readdir($dh)),
-	"tablespace symlink was updated");
-closedir $dh;
-
-mkdir "$tempdir/tbl=spc2";
-psql 'postgres', "DROP TABLE test1;";
-psql 'postgres', "DROP TABLESPACE tblspc1;";
-psql 'postgres', "CREATE TABLESPACE tblspc2 LOCATION '$shorter_tempdir/tbl=spc2';";
-command_ok(
-	[   'pg_basebackup',    '-D',
-		"$tempdir/backup3", '-Fp',
-		"-T$shorter_tempdir/tbl\\=spc2=$tempdir/tbackup/tbl\\=spc2" ],
-	'mapping tablespace with = sign in path');
-ok(-d "$tempdir/tbackup/tbl=spc2", 'tablespace with = sign was relocated');
-
-psql 'postgres', "DROP TABLESPACE tblspc2;";
+# The following tests test symlinks. Windows doesn't have symlinks, so
+# skip on Windows.
+SKIP: {
+	# Create a temporary directory in the system location and symlink it
+	# to our physical temp location.  That way we can use shorter names
+	# for the tablespace directories, which hopefully won't run afoul of
+	# the 99 character length limit.
+	skip "symlinks not supported on Windows", 9 if ($windows_os);
+
+	my $shorter_tempdir = tempdir_short . "/tempdir";
+	symlink "$tempdir", $shorter_tempdir;
+
+	mkdir "$tempdir/tblspc1";
+	psql 'postgres', "CREATE TABLESPACE tblspc1 LOCATION '$shorter_tempdir/tblspc1';";
+	psql 'postgres', "CREATE TABLE test1 (a int) TABLESPACE tblspc1;";
+	command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup2", '-Ft' ],
+		'tar format with tablespaces');
+	ok(-f "$tempdir/tarbackup2/base.tar", 'backup tar was created');
+	my @tblspc_tars = glob "$tempdir/tarbackup2/[0-9]*.tar";
+	is(scalar(@tblspc_tars), 1, 'one tablespace tar was created');
+
+	command_fails(
+		[ 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp' ],
+		'plain format with tablespaces fails without tablespace mapping');
+
+	command_ok(
+		[   'pg_basebackup',    '-D',
+			"$tempdir/backup1", '-Fp',
+			"-T$shorter_tempdir/tblspc1=$tempdir/tbackup/tblspc1" ],
+		'plain format with tablespaces succeeds with tablespace mapping');
+		ok(-d "$tempdir/tbackup/tblspc1", 'tablespace was relocated');
+	opendir(my $dh, "$tempdir/pgdata/pg_tblspc") or die;
+	ok( (   grep
+			{
+				-l "$tempdir/backup1/pg_tblspc/$_"
+				  and readlink "$tempdir/backup1/pg_tblspc/$_" eq
+				  "$tempdir/tbackup/tblspc1"
+			  } readdir($dh)),
+		"tablespace symlink was updated");
+	closedir $dh;
+
+	mkdir "$tempdir/tbl=spc2";
+	psql 'postgres', "DROP TABLE test1;";
+	psql 'postgres', "DROP TABLESPACE tblspc1;";
+	psql 'postgres', "CREATE TABLESPACE tblspc2 LOCATION '$shorter_tempdir/tbl=spc2';";
+	command_ok(
+		[   'pg_basebackup',    '-D',
+			"$tempdir/backup3", '-Fp',
+			"-T$shorter_tempdir/tbl\\=spc2=$tempdir/tbackup/tbl\\=spc2" ],
+		'mapping tablespace with = sign in path');
+	ok(-d "$tempdir/tbackup/tbl=spc2", 'tablespace with = sign was relocated');
+
+	psql 'postgres', "DROP TABLESPACE tblspc2;";
+}
 
 command_fails(
 	[ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T=/foo" ],
diff --git a/src/bin/pg_ctl/t/001_start_stop.pl b/src/bin/pg_ctl/t/001_start_stop.pl
index 1572083..a85a539 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 => 16;
 
@@ -12,12 +13,20 @@ program_options_handling_ok('pg_ctl');
 
 command_ok([ 'pg_ctl', 'initdb', '-D', "$tempdir/data" ], 'pg_ctl initdb');
 command_ok(
-	[   "$ENV{top_builddir}/src/test/regress/pg_regress", '--config-auth',
+	[ $ENV{PG_REGRESS}, '--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";
+print CONF "fsync = off\n";
+if (! $windows_os)
+{
+	print CONF "listen_addresses = ''\n";
+	print CONF "unix_socket_directories = '$tempdir_short'\n";
+}
+else
+{
+	print CONF "listen_addresses = '127.0.0.1'\n";
+}
 close CONF;
 command_ok([ 'pg_ctl', 'start', '-D', "$tempdir/data", '-w' ],
 	'pg_ctl start -w');
diff --git a/src/bin/pg_ctl/t/002_status.pl b/src/bin/pg_ctl/t/002_status.pl
index 86e8101..46b432e 100644
--- a/src/bin/pg_ctl/t/002_status.pl
+++ b/src/bin/pg_ctl/t/002_status.pl
@@ -7,10 +7,6 @@ my $tempdir = TestLib::tempdir;
 my $tempdir_short = TestLib::tempdir_short;
 
 standard_initdb "$tempdir/data";
-open CONF, ">>$tempdir/data/postgresql.conf";
-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');
diff --git a/src/test/perl/SimpleTee.pm b/src/test/perl/SimpleTee.pm
index 8d31a40..5da82d0 100644
--- a/src/test/perl/SimpleTee.pm
+++ b/src/test/perl/SimpleTee.pm
@@ -20,6 +20,7 @@ sub PRINT {
 	my $ok = 1;
 	for my $fh (@$self) {
 		print $fh @_ or $ok = 0;
+		$fh->flush or $ok = 0;
 	}
 	return $ok;
 }
diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm
index fdb7b53..2c86696 100644
--- a/src/test/perl/TestLib.pm
+++ b/src/test/perl/TestLib.pm
@@ -9,9 +9,12 @@ our @EXPORT = qw(
   tempdir
   tempdir_short
   standard_initdb
+  configure_hba_for_replication
   start_test_server
   restart_test_server
   psql
+  slurp_dir
+  slurp_file
   system_or_bail
   system_log
   run_log
@@ -117,29 +120,75 @@ sub tempdir_short
 	return File::Temp::tempdir(CLEANUP => 1);
 }
 
+# Initialize a new cluster for testing.
+#
+# The PGHOST environment variable is set to connect to the new cluster.
+#
+# Authentication is set up so that only the current OS user can access the
+# cluster. On Unix, we use Unix domain socket connections, with the socket in
+# a directory that's only accessible to the current user to ensure that.
+# On Windows, we use SSPI authentication to ensure the same (by pg_regress
+# --config-auth).
 sub standard_initdb
 {
 	my $pgdata = shift;
 	system_or_bail('initdb', '-D', "$pgdata", '-A' , 'trust', '-N');
-	system_or_bail("$ENV{top_builddir}/src/test/regress/pg_regress",
-				   '--config-auth', $pgdata);
+	system_or_bail($ENV{PG_REGRESS}, '--config-auth', $pgdata);
+
+	my $tempdir_short = tempdir_short;
+
+	open CONF, ">>$pgdata/postgresql.conf";
+	print CONF "\n# Added by TestLib.pm)\n";
+	print CONF "fsync = off\n";
+	if ($windows_os)
+	{
+		print CONF "listen_addresses = '127.0.0.1'\n";
+	}
+	else
+	{
+		print CONF "unix_socket_directories = '$tempdir_short'\n";
+		print CONF "listen_addresses = ''\n";
+	}
+	close CONF;
+
+	$ENV{PGHOST}         = $windows_os ? "127.0.0.1" : $tempdir_short;
+}
+
+# Set up the cluster to allow replication connections, in the same way that
+# standard_initdb does for normal connections.
+sub configure_hba_for_replication
+{
+	my $pgdata = shift;
+
+	open HBA, ">>$pgdata/pg_hba.conf";
+	print HBA "\n# Allow replication (set up by TestLib.pm)\n";
+	if (! $windows_os)
+	{
+		print HBA "local replication all trust\n";
+	}
+	else
+	{
+		print HBA "host replication all 127.0.0.1/32 sspi include_realm=1 map=regress\n";
+	}
+	close HBA;
 }
 
 my ($test_server_datadir, $test_server_logfile);
 
+
+# Initialize a new cluster for testing in given directory, and start it.
 sub start_test_server
 {
 	my ($tempdir) = @_;
 	my $ret;
 
-	my $tempdir_short = tempdir_short;
-
 	print("### Starting test server in $tempdir\n");
 	standard_initdb "$tempdir/pgdata";
+
 	$ret = system_log('pg_ctl', '-D', "$tempdir/pgdata", '-w', '-l',
-	  "$log_path/postmaster.log", '-o',
-"--fsync=off -k \"$tempdir_short\" --listen-addresses='' --log-statement=all",
-					'start');
+	  "$log_path/postmaster.log", '-o', "--log-statement=all",
+	  'start');
+
 	if ($ret != 0)
 	{
 		print "# pg_ctl failed; logfile:\n";
@@ -147,7 +196,6 @@ sub start_test_server
 		BAIL_OUT("pg_ctl failed");
 	}
 
-	$ENV{PGHOST}         = $tempdir_short;
 	$test_server_datadir = "$tempdir/pgdata";
 	$test_server_logfile = "$log_path/postmaster.log";
 }
@@ -171,8 +219,30 @@ END
 sub psql
 {
 	my ($dbname, $sql) = @_;
+	my ($stdout, $stderr);
 	print("# Running SQL command: $sql\n");
-	run [ 'psql', '-X', '-q', '-d', $dbname, '-f', '-' ], '<', \$sql or die;
+	run [ 'psql', '-X', '-A', '-t', '-q', '-d', $dbname, '-f', '-' ], '<', \$sql, '>', \$stdout, '2>', \$stderr or die;
+	chomp $stdout;
+	$stdout =~ s/\r//g if $Config{osname} eq 'msys';
+	return $stdout;
+}
+
+sub slurp_dir
+{
+	my ($dir) = @_;
+	opendir(my $dh, $dir) or die;
+	my @direntries = readdir $dh;
+	closedir $dh;
+	return @direntries;
+}
+
+sub slurp_file
+{
+	local $/;
+	local @ARGV = @_;
+	my $contents = <>;
+	$contents =~ s/\r//g if $Config{osname} eq 'msys';
+	return $contents;
 }
 
 sub system_or_bail
@@ -221,7 +291,17 @@ sub command_exit_is
 	print("# Running: " . join(" ", @{$cmd}) ."\n");
 	my $h = start $cmd;
 	$h->finish();
-	is($h->result(0), $expected, $test_name);
+
+	# 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);
 }
 
 sub program_help_ok
@@ -274,7 +354,7 @@ sub issues_sql_like
 	truncate $test_server_logfile, 0;
 	my $result = run_log($cmd);
 	ok($result, "@$cmd exit code 0");
-	my $log = `cat '$test_server_logfile'`;
+	my $log = slurp_file($test_server_logfile);
 	like($log, $expected_sql, "$test_name: SQL found in server log");
 }
 
diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat
index e06838c..b071cb7 100755
--- a/src/tools/msvc/clean.bat
+++ b/src/tools/msvc/clean.bat
@@ -74,6 +74,12 @@ if exist src\test\regress\regress.dll del /q src\test\regress\regress.dll
 if exist src\test\regress\refint.dll del /q src\test\regress\refint.dll
 if exist src\test\regress\autoinc.dll del /q src\test\regress\autoinc.dll
 
+if exist src\bin\initdb\tmp_check rd /s /q src\bin\initdb\tmp_check
+if exist src\bin\pg_basebackup\tmp_check rd /s /q src\bin\pg_basebackup\tmp_check
+if exist src\bin\pg_config\tmp_check rd /s /q src\bin\pg_config\tmp_check
+if exist src\bin\pg_ctl\tmp_check rd /s /q src\bin\pg_ctl\tmp_check
+if exist src\bin\scripts\tmp_check rd /s /q src\bin\scripts\tmp_check
+
 REM Clean up datafiles built with contrib
 REM cd contrib
 REM for /r %%f in (*.sql) do if exist %%f.in del %%f
diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl
index 8f9fc79..8411420 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);
 
@@ -31,7 +33,7 @@ if (-e "src/tools/msvc/buildenv.pl")
 
 my $what = shift || "";
 if ($what =~
-/^(check|installcheck|plcheck|contribcheck|ecpgcheck|isolationcheck|upgradecheck)$/i
+/^(check|installcheck|plcheck|contribcheck|ecpgcheck|isolationcheck|upgradecheck|tapcheck)$/i
   )
 {
 	$what = uc $what;
@@ -58,7 +60,7 @@ unless ($schedule)
 	$schedule = "parallel" if ($what eq 'CHECK' || $what =~ /PARALLEL/);
 }
 
-$ENV{PERL5LIB} = "$topdir/src/tools/msvc";
+$ENV{PERL5LIB} = "$topdir/src/tools/msvc;$ENV{PERL5LIB}";
 
 my $maxconn = "";
 $maxconn = "--max_connections=$ENV{MAX_CONNECTIONS}"
@@ -77,6 +79,7 @@ my %command = (
 	ECPGCHECK      => \&ecpgcheck,
 	CONTRIBCHECK   => \&contribcheck,
 	ISOLATIONCHECK => \&isolationcheck,
+	TAPCHECK       => \&tapcheck,
 	UPGRADECHECK   => \&upgradecheck,);
 
 my $proc = $command{$what};
@@ -162,6 +165,44 @@ sub isolationcheck
 	exit $status if $status;
 }
 
+sub tapcheck
+{
+	my @args = ( "prove", "--verbose", "t/*.pl");
+	my $mstat = 0;
+
+	$ENV{PERL5LIB} = "$topdir/src/test/perl;$ENV{PERL5LIB}";
+	$ENV{PG_REGRESS} = "$topdir/$Config/pg_regress/pg_regress";
+
+	# Find out all the existing TAP tests by looking for t/ directories
+	# 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)
+	{
+		my $dir = dirname($test_path);
+		my $tmp_root = "$dir/tmp_check";
+		(mkdir $tmp_root || die $!) unless -d $tmp_root;
+		my $tmp_install = "$tmp_root/install";
+		Install($tmp_install, "all", $config);
+		chdir $dir;
+		# Reset those values, they may have been changed by another test.
+		$ENV{TESTDIR} = "$dir";
+		system(@args);
+		my $status = $? >> 8;
+		$mstat ||= $status;
+	}
+	exit $mstat if $mstat;
+}
+
 sub plcheck
 {
 	chdir "../../pl";
