From 39461822fc1361cc3a7f6e9d5c4492f4ce62e9ae Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Tue, 6 Apr 2021 21:46:25 -0700
Subject: [PATCH v2 2/2] Adding modules/test_cross_version

This creates a framework for checking interactions between various
installed versions of PostgreSQL.  This is still very much a work in
progress.
---
 src/test/modules/Makefile                     |   1 +
 .../test_cross_version/CrossVersion.pm        | 113 ++++++++++++++++++
 src/test/modules/test_cross_version/Makefile  |  20 ++++
 src/test/modules/test_cross_version/README    |  15 +++
 .../test_cross_version/t/001_cross_connect.pl |  54 +++++++++
 .../test_cross_version.control                |   5 +
 .../modules/test_cross_version/versions.dat   |  17 +++
 7 files changed, 225 insertions(+)
 create mode 100644 src/test/modules/test_cross_version/CrossVersion.pm
 create mode 100644 src/test/modules/test_cross_version/Makefile
 create mode 100644 src/test/modules/test_cross_version/README
 create mode 100644 src/test/modules/test_cross_version/t/001_cross_connect.pl
 create mode 100644 src/test/modules/test_cross_version/test_cross_version.control
 create mode 100644 src/test/modules/test_cross_version/versions.dat

diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index dffc79b2d9..f1a27bc9dd 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -15,6 +15,7 @@ SUBDIRS = \
 		  snapshot_too_old \
 		  spgist_name_ops \
 		  test_bloomfilter \
+		  test_cross_version \
 		  test_ddl_deparse \
 		  test_extensions \
 		  test_ginpostinglist \
diff --git a/src/test/modules/test_cross_version/CrossVersion.pm b/src/test/modules/test_cross_version/CrossVersion.pm
new file mode 100644
index 0000000000..8c1c6bcab1
--- /dev/null
+++ b/src/test/modules/test_cross_version/CrossVersion.pm
@@ -0,0 +1,113 @@
+
+=pod
+
+=head1 NAME
+
+CrossVersion - class representing multiple PostgreSQL server version information
+
+=head1 SYNOPSIS
+
+  use CrossVersion;
+
+=head1 DESCRIPTION
+
+CrossVersion
+
+=cut
+
+package CrossVersion;
+
+use strict;
+use warnings;
+use Test::More;
+
+sub config_info
+{
+	my ($node) = @_;
+
+	my %config;
+	my $node_name = $node->name();
+
+	local %ENV = $node->_get_env();
+	my $fh = IO::File->new("pg_config |")
+		or BAIL_OUT("Cannot run pg_config for node $node_name: $!");
+	while (my $line = <$fh>)
+	{
+		if ($line =~ m/^(.+?) = (.*)$/)
+		{
+			$config{$1} = $2;
+		}
+		else
+		{
+			die "Cannot parse config output for node $node_name: $line";
+		}
+	}
+
+	%config;
+}
+
+sub nodes
+{
+	my ($versions, %params) = @_;
+	my @parsed;
+	my %seen;
+
+	$versions = "versions.dat" unless (defined $versions);
+
+	# Parse all name/path pairs
+	my $fh = IO::File->new($versions)
+		or BAIL_OUT("Cannot open \"$versions\" for reading: $!");
+	while (my $line = <$fh>)
+	{
+		# Strip comments
+		$line =~ s/\s*#.*$//;
+
+		# Skip blank lines
+		next unless $line =~ m/\S/;
+
+		# Node names cannot contain whitespace characters
+		my $namere = qr/\S+/;
+
+		# Paths must begin and end with non-whitespace characters
+		my $pathre = qr/\S+(?:.*\S)?/;
+
+		# Data lines should be a name and path, whitespace separated
+		if ($line =~ m/^\s*($namere)\s+($pathre)\s*$/)
+		{
+			my ($node_name, $path) = ($1, $2);
+
+			# PostgresNode will fail with a filesystem error if given the
+			# same name twice.  Complaining here makes it easier for the
+			# user to debug and fix the problem
+			BAIL_OUT(sprintf("%s line %d: duplicate node name: %s: previously appeared on line %d",
+							 $versions, $fh->input_line_number, $node_name, $seen{$node_name}))
+				if ($seen{$node_name});
+
+			# Sanity-check this next directory parsed from the versions file
+			BAIL_OUT(sprintf("%s line %d: directory not found: %s",
+							 $versions, $fh->input_line_number, $path))
+				unless (-d $path);
+			BAIL_OUT(sprintf("%s line %d: directory does not appear to be a postgresql installation: %s",
+							 $versions, $fh->input_line_number, $path))
+				unless (-d "$path/bin" and -x "$path/bin/pg_config");
+
+			push (@parsed, { node_name => $node_name, path => $path });
+			$seen{$node_name} = $fh->input_line_number;
+		}
+		else
+		{
+			BAIL_OUT(sprintf("syntax error: %s line %d: $line",
+							 $versions, $fh->input_line_number));
+		}
+	}
+	$fh->close;
+
+	map { PostgresNode->get_new_node($_->{node_name},
+									 %params,
+									 install_path => $_->{path}) } @parsed;
+}
+
+use strict;
+use warnings;
+
+1;
diff --git a/src/test/modules/test_cross_version/Makefile b/src/test/modules/test_cross_version/Makefile
new file mode 100644
index 0000000000..1a8113daa0
--- /dev/null
+++ b/src/test/modules/test_cross_version/Makefile
@@ -0,0 +1,20 @@
+# src/test/modules/test_cross_version/Makefile
+
+MODULE = test_cross_version
+PGFILEDESC = "test_cross_version - Test PostgreSQL across multiple server versions"
+
+EXTENSION = test_cross_version
+
+# REGRESS = test_cross_version
+TAP_TESTS = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_cross_version
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_cross_version/README b/src/test/modules/test_cross_version/README
new file mode 100644
index 0000000000..5cfaf115da
--- /dev/null
+++ b/src/test/modules/test_cross_version/README
@@ -0,0 +1,15 @@
+The test_cross_version module exists to perform any tests requiring a database
+cluster that has been upgraded from one or more prior versions, or multiple
+database clusters running differing versions of PostgreSQL.  It is not intended
+to be used in production.
+
+Rationale
+=========
+
+Usage
+=====
+
+Author
+======
+
+Mark Dilger <mark.dilger@enterprisedb.com>
diff --git a/src/test/modules/test_cross_version/t/001_cross_connect.pl b/src/test/modules/test_cross_version/t/001_cross_connect.pl
new file mode 100644
index 0000000000..f4bcc6a0e1
--- /dev/null
+++ b/src/test/modules/test_cross_version/t/001_cross_connect.pl
@@ -0,0 +1,54 @@
+use strict;
+use warnings;
+
+use Config;
+use PostgresNode;
+use TestLib;
+use Test::More;
+use CrossVersion;
+
+my @nodes = CrossVersion::nodes();
+
+# By default, versions.dat is empty and no testing is done
+plan skip_all => "No older PostgreSQL installation directories supplied"
+	unless @nodes;
+
+# Ok, versions.dat must have contained entries.  Calculate how many tests we
+# will perform.
+plan tests => 2 + 4 * scalar(@nodes);
+
+my $new = get_new_node('new');
+spin_up($new);
+my $newconnstr = $new->connstr('postgres');
+
+my %initial_env = $new->_get_env;
+my $initial_path = $initial_env{PATH};
+
+for my $old (@nodes)
+{
+	my $oldname = $old->name();
+
+	spin_up($old);
+	my $oldconnstr = $old->connstr('postgres');
+
+	my $path = $ENV{PATH};
+
+	$old->connect_ok('postgres', "$oldname psql connects to new server",
+					 host => $new->host, port => $new->port);
+
+	$new->connect_ok('postgres', "new psql connects to $oldname server",
+					 host => $old->host, port => $old->port);
+}
+
+sub spin_up
+{
+	my ($node) = @_;
+	my $name = $node->name;
+
+	$node->init();
+	ok(1, "$name initializes without error");
+	$node->start();
+	my $host = $node->host;
+	my $port = $node->port;
+	ok(1, "$name starts on host $host, port $port without error");
+}
diff --git a/src/test/modules/test_cross_version/test_cross_version.control b/src/test/modules/test_cross_version/test_cross_version.control
new file mode 100644
index 0000000000..0084eec37a
--- /dev/null
+++ b/src/test/modules/test_cross_version/test_cross_version.control
@@ -0,0 +1,5 @@
+comment = 'test PostgreSQL cross version compatibility'
+default_version = '1.0'
+module_pathname = '$libdir/test_cross_version'
+relocatable = false
+trusted = false
diff --git a/src/test/modules/test_cross_version/versions.dat b/src/test/modules/test_cross_version/versions.dat
new file mode 100644
index 0000000000..5f51e74d7a
--- /dev/null
+++ b/src/test/modules/test_cross_version/versions.dat
@@ -0,0 +1,17 @@
+# When testing one or more older versions of PostgreSQL against the version
+# being developed, append this file with the paths to all installed older
+# PostgreSQL versions you wish to test, one name and path per line.  Lines
+# starting with a '#' are ignored.  Blank lines are ignored.  Leading and
+# trailing whitespace are ignored.  The first non-whitespace token is treated
+# as the node name, and everything else (including embedded whitespace) will be
+# treated as part of the installation path.  Names should be unique.
+#
+# For each PATH, $PATH/bin and $PATH/lib should exist and contain the postgres
+# binaries and library files.  No support exists for installations configured
+# with alternate --bindir or --libdir options.
+#
+# Examples:
+#
+#   8.1    /usr/local/postgres/8.1
+#   9.1    /usr/local/postgres/9.1.24
+#
-- 
2.21.1 (Apple Git-122.3)

