From 98753c29da00612ccbefd6d47c23adde33cc91fd Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Mon, 23 Aug 2021 13:04:32 +0200
Subject: [PATCH] Refactor psql query cancellation test to use interactive_psql

Utilize the interactive_psql client to inject query cancellation.

Discussion: https://www.postgresql.org/message-id/flat/E1mH14Q-0002gh-HS%40gemulon.postgresql.org
---
 src/bin/psql/t/020_cancel.pl | 66 ++++++++++++++++++++----------------
 1 file changed, 36 insertions(+), 30 deletions(-)

diff --git a/src/bin/psql/t/020_cancel.pl b/src/bin/psql/t/020_cancel.pl
index 7713fff8e4..ed654869d4 100644
--- a/src/bin/psql/t/020_cancel.pl
+++ b/src/bin/psql/t/020_cancel.pl
@@ -6,42 +6,48 @@ use warnings;
 
 use PostgresNode;
 use TestLib;
-use Test::More tests => 2;
+use IPC::Run qw(pump finish timer);
+use Test::More tests => 4;
 
-my $tempdir = TestLib::tempdir;
+plan skip_all => "Testsuite requires a Unix shell" if ($windows_os);
 
 my $node = PostgresNode->new('main');
 $node->init;
 $node->start;
 
 # Test query canceling by sending SIGINT to a running psql
-#
+my $in;
+my $out;
+my $timer = timer(5);
+my $h = $node->interactive_psql('postgres', \$in, \$out, $timer);
+
 # There is, as of this writing, no documented way to get the PID of
 # the process from IPC::Run.  As a workaround, we have psql print its
-# own PID (which is the parent of the shell launched by psql) to a
-# file.
-SKIP: {
-	skip "cancel test requires a Unix shell", 2 if $windows_os;
-
-	local %ENV = $node->_get_env();
-
-	my ($stdin, $stdout, $stderr);
-
-	# Test whether shell supports $PPID.  It's part of POSIX, but some
-	# pre-/non-POSIX shells don't support it (e.g., NetBSD, Solaris).
-	$stdin = "\\! echo \$PPID";
-	IPC::Run::run(['psql', '-X', '-v', 'ON_ERROR_STOP=1'], '<', \$stdin, '>', \$stdout, '2>', \$stderr);
-	$stdout =~ /^\d+$/ or skip "shell apparently does not support \$PPID", 2;
-
-	local $SIG{ALRM} = sub {
-		my $psql_pid = TestLib::slurp_file("$tempdir/psql.pid");
-		kill 'INT', $psql_pid;
-	};
-	alarm 1;
-
-	$stdin = "\\! echo \$PPID >$tempdir/psql.pid\nselect pg_sleep(3);";
-	my $result = IPC::Run::run(['psql', '-X', '-v', 'ON_ERROR_STOP=1'], '<', \$stdin, '>', \$stdout, '2>', \$stderr);
-
-	ok(!$result, 'query failed as expected');
-	like($stderr, qr/canceling statement due to user request/, 'query was canceled');
-}
+# own PID (which is the parent of the shell launched by psql)
+$out = '';
+$timer->start(5);
+$in = "\\! echo \"\$PPID\"\"x\"\n";
+pump $h until ($out =~ /[^\D]+x/ || $timer->is_expired);
+ok(!$timer->is_expired, "PPID timer expired");
+
+# Grab PID from the output and clear the output buffer. If no PID was found
+# then skip the rest of the tests as the platform doesn't support PPID.
+(my $psql_pid = $out) =~ s/[^\d]//g;
+$out = '';
+scalar($psql_pid > 0) or skip "shell apparently does not support \$PPID", 3;
+
+# Put the interactive client to sleep. Pumping on the output is tricky so for
+# now allow the timer to expire and assume we are at sleep by then.
+$timer->start(2);
+$in .= "SELECT pg_sleep(15);\n";
+pump $h until $timer->is_expired;
+
+# Signal the client and await the expected output
+$timer->start(10);
+my $killed = kill 'INT', $psql_pid;
+ok($killed eq '1', "psql was signaled");
+pump $h until ($out =~ /request/ || $timer->is_expired);
+like($out, qr/canceling statement due to user request/, 'query was canceled');
+ok(!$timer->is_expired, "Sleep query timer expired");
+
+$node->stop;
-- 
2.24.3 (Apple Git-128)

