From 8b7a5e79d98ebe7d7e31e200d66fb249816fc17e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 2 Oct 2025 16:54:27 -0400
Subject: [PATCH v4 3/3] Add a TAP test to exercise psql's use of PAGER.

Up to now, we have had zero test coverage of these code paths,
because they aren't reached unless isatty(stdout).  We do have
the test infrastructure to improve that situation, though.
Following the lead of 010_tab_completion.pl, set up an
interactive psql and feed it some cases that should make it
decide it needs to use the pager.

It's not at all clear how portable this is, though, so I
await the results of CI with interest.

Another issue is that as written, the test cases should cause
use of the pager, but the test will not reveal whether they
did or not.  (Code coverage testing shows that the right
lines of code are reached now, but that's not really a good
answer.)  Doing better seems to require use of something
besides "cat" as $PAGER, which adds more portability questions.
---
 src/bin/psql/meson.build    |  1 +
 src/bin/psql/t/030_pager.pl | 84 +++++++++++++++++++++++++++++++++++++
 2 files changed, 85 insertions(+)
 create mode 100644 src/bin/psql/t/030_pager.pl

diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index f795ff28271..d344053c23b 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -77,6 +77,7 @@ tests += {
       't/001_basic.pl',
       't/010_tab_completion.pl',
       't/020_cancel.pl',
+      't/030_pager.pl',
     ],
   },
 }
diff --git a/src/bin/psql/t/030_pager.pl b/src/bin/psql/t/030_pager.pl
new file mode 100644
index 00000000000..4bd6759a32a
--- /dev/null
+++ b/src/bin/psql/t/030_pager.pl
@@ -0,0 +1,84 @@
+
+# Copyright (c) 2021-2025, PostgreSQL Global Development Group
+
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+use Data::Dumper;
+
+# If we don't have IO::Pty, forget it, because IPC::Run depends on that
+# to support pty connections
+eval { require IO::Pty; };
+if ($@)
+{
+	plan skip_all => 'IO::Pty is needed to run this test';
+}
+
+# start a new server
+my $node = PostgreSQL::Test::Cluster->new('main');
+$node->init;
+$node->start;
+
+# we don't really care about the pager's behavior, so just use cat
+$ENV{PAGER} = "cat";
+
+# fire up an interactive psql session
+my $h = $node->interactive_psql('postgres');
+
+# set the pty's window size to known values
+# (requires undesirable chumminess with the innards of IPC::Run)
+for my $pty (values %{ $h->{run}->{PTYS} })
+{
+	$pty->set_winsize(24, 80);
+}
+
+# Simple test case: type something and see if psql responds as expected
+sub do_command
+{
+	my ($send, $pattern, $annotation) = @_;
+
+	# report test failures from caller location
+	local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+	# restart per-command timer
+	$h->{timeout}->start($PostgreSQL::Test::Utils::timeout_default);
+
+	# send the data to be sent and wait for its result
+	my $out = $h->query_until($pattern, $send);
+	my $okay = ($out =~ $pattern && !$h->{timeout}->is_expired);
+	ok($okay, $annotation);
+	# for debugging, log actual output if it didn't match
+	local $Data::Dumper::Terse = 1;
+	local $Data::Dumper::Useqq = 1;
+	diag 'Actual output was ' . Dumper($out) . "Did not match \"$pattern\"\n"
+	  if !$okay;
+	return;
+}
+
+# Test invocation of the pager
+# (Note that interactive_psql starts psql with --no-align --tuples-only)
+
+do_command(
+	"SELECT generate_series(1,10*10) as g;\n",
+	qr/100/,
+	"execute SELECT query that needs pagination");
+
+do_command(
+	"\\pset expanded\nSELECT generate_series(1,20) as g;\n",
+	qr/g\|20/,
+	"execute SELECT query that needs pagination in expanded mode");
+
+do_command(
+	"\\pset tuples_only off\n\\d+ information_schema.referential_constraints\n",
+	qr/has_any_column_privilege/,
+	"execute command with footer that needs pagination");
+
+# send psql an explicit \q to shut it down, else pty won't close properly
+$h->quit or die "psql returned $?";
+
+# done
+$node->stop;
+done_testing();
-- 
2.43.7

