From 0daf2083786d5ff8ac8bbebd590a89cf57c5575d Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Mon, 26 Jan 2015 09:01:29 +0200
Subject: [PATCH 1/2] Refactor regression tests to use a custom program instead
 of pg_regress.

Using pg_regress was awkward, because it used a dummy .sql file for each
test that just launched the test program. Also, it added a dependency;
the driver doesn't require PostgreSQL sources to build, just libpq
headers, but the pgxs program is not usually shipped with libpq headers.

The new "runsuite" program runs all the test programs given on the command
line, compares the results with the expected output, and produces
TAP-compatible output. It can be run stand-alone, as the TAP output is
human-readable, or with the perl 'prove' program which gives more pretty
output.
---
 configure.ac                           |   2 +
 test/Makefile.in                       |  54 +++---
 test/expected/alter.out                |   1 -
 test/expected/arraybinding.out         |   1 -
 test/expected/bindcol.out              |   1 -
 test/expected/bookmark.out             |   1 -
 test/expected/boolsaschar.out          |   1 -
 test/expected/bulkoperations.out       |   1 -
 test/expected/catalogfunctions.out     |   1 -
 test/expected/colattribute.out         |   1 -
 test/expected/colattribute_1.out       |   1 -
 test/expected/commands.out             |   1 -
 test/expected/connect.out              |   1 -
 test/expected/cte.out                  |   1 -
 test/expected/cursor-commit.out        |   1 -
 test/expected/cursor-movement.out      |   1 -
 test/expected/cursor-movement_1.out    |   1 -
 test/expected/cursor-name.out          |   1 -
 test/expected/cursors.out              |   1 -
 test/expected/cursors_1.out            |   1 -
 test/expected/cvtnulldate.out          |   1 -
 test/expected/dataatexecution.out      |   1 -
 test/expected/declare-fetch-commit.out |   1 -
 test/expected/deprecated.out           |   1 -
 test/expected/diagnostic.out           |   1 -
 test/expected/error-rollback.out       |   1 -
 test/expected/errors.out               |   1 -
 test/expected/errors_1.out             |   1 -
 test/expected/errors_2.out             |   1 -
 test/expected/getresult.out            |   1 -
 test/expected/insertreturning.out      |   1 -
 test/expected/large-object.out         |   1 -
 test/expected/lfconversion.out         |   1 -
 test/expected/multistmt.out            |   1 -
 test/expected/notice.out               |   1 -
 test/expected/numeric.out              |   1 -
 test/expected/odbc-escapes.out         |   1 -
 test/expected/param-conversions.out    |   1 -
 test/expected/param-conversions_1.out  |   1 -
 test/expected/params.out               |   1 -
 test/expected/params_1.out             |   1 -
 test/expected/positioned-update.out    |   1 -
 test/expected/prepare.out              |   1 -
 test/expected/quotes.out               |   1 -
 test/expected/result-conversions.out   |   1 -
 test/expected/sampletables.out         |  33 ----
 test/expected/select.out               |   1 -
 test/expected/stmthandles.out          |   1 -
 test/expected/update.out               |   1 -
 test/launcher                          |   5 -
 test/launcher_i                        |   6 -
 test/reset-db.c                        | 103 ++++++++++++
 test/runsuite.c                        | 293 +++++++++++++++++++++++++++++++++
 test/sampletables.sql                  |  38 +++++
 test/sql/.gitignore                    |   2 -
 test/sql/sampletables.sql              |  40 -----
 test/win.mak                           |  35 ++--
 57 files changed, 470 insertions(+), 187 deletions(-)
 delete mode 100644 test/expected/sampletables.out
 delete mode 100755 test/launcher
 delete mode 100755 test/launcher_i
 create mode 100644 test/reset-db.c
 create mode 100644 test/runsuite.c
 create mode 100644 test/sampletables.sql
 delete mode 100644 test/sql/.gitignore
 delete mode 100644 test/sql/sampletables.sql

diff --git a/configure.ac b/configure.ac
index b318ba3..c0587de 100644
--- a/configure.ac
+++ b/configure.ac
@@ -178,6 +178,8 @@ fi
 
 # 1. Programs
 
+# 'prove' is used to pretty-print regression test results, if available.
+AC_CHECK_PROGS(PROVE, prove)
 
 # 2. Libraries
 
diff --git a/test/Makefile.in b/test/Makefile.in
index b558ff7..580f127 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -11,57 +11,45 @@ include $(origdir)/tests
 # The included file defined variable TESTBINS, which is a list of program
 # names in format src/<testname>-test. Extract the base test names from it.
 TESTNAMES = $(patsubst src/%-test,%, $(TESTBINS))
-TESTSQLS = $(patsubst %,sql/%.sql, $(TESTNAMES))
 
 # Set by autoconf
 LDFLAGS = @LDFLAGS@
 CFLAGS = @CFLAGS@
-PG_CONFIG = @PG_CONFIG@
 ODBC_CONFIG = @ODBC_CONFIG@
-
-ifneq (,$(findstring iodbc,$(ODBC_CONFIG)))
-	launcher=launcher_i
-else
-	launcher=launcher
-endif
-
-REGRESS = sampletables $(TESTNAMES)
+PROVE = @PROVE@
 
 LIBODBC := $(shell $(ODBC_CONFIG) --libs)
 
-all: $(TESTBINS) $(TESTSQLS)
+all: $(TESTBINS) runsuite reset-db
 
 odbc.ini:
 	./odbcini-gen.sh $(odbc_ini_extras)
 
-installcheck: all odbc.ini
+installcheck: all odbc.ini reset-db runsuite
+	rm -f regression.diffs
+	ODBCSYSINI=. ODBCINSTINI=./odbcinst.ini ODBCINI=./odbc.ini ./reset-db < sampletables.sql
+# If the perl 'prove' test running utility is available, use it. It produces
+# nice output. But otherwise just run the plain suite, it's usable as is too.
+ifdef PROVE
+	prove -e ./runsuite $(TESTNAMES)
+else
+	./runsuite $(TESTNAMES)
+endif
 
 override CFLAGS += -Wno-pointer-sign $(CFLAGS_ADD)
 
+runsuit: runsuite.c
+
+reset-db: reset-db.c
+	$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ $(LIBODBC)
+
 src/common.o: src/common.c
 	@if test ! -d src; then mkdir -p src; fi
 	$(COMPILE.c) -c $< -o $@
 
-# For each test file, compile the .c file, and create a .sql file that
-# when executed from psql, just runs the binary.
-src/%-test sql/%.sql: src/%-test.c src/common.o
+# For each test, compile the .c file.
+src/%-test: src/%-test.c src/common.o
 	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o src/$*-test $(LIBODBC)
-	@if test ! -d sql; then mkdir -p sql; fi
-	echo "\! \"./src/$*-test\"" > sql/$*.sql
-
-# For each shell file, create a .sql file that when executed from psql, 
-# just runs the shell.
-sql/%.sql: src/%.sh
-	@if test ! -d sql; then mkdir -p sql; fi
-	echo "\! \"$<\"" > $@
-
-EXTRA_CLEAN = $(TESTBINS) $(TESTSQLS) src/common.o
-
-REGRESS_OPTS = --launcher=$(origdir)/$(launcher) --inputdir=$(origdir)
-ifndef PGXS
-PGXS := $(shell $(PG_CONFIG) --pgxs)
-endif
-include $(PGXS)
 
 # This target runs the regression tests with all combinations of
 # UseDeclareFetch, UseServerSidePrepare and Protocol options.
@@ -93,3 +81,7 @@ installcheck-all:
 	$(MAKE) installcheck odbc_ini_extras="UseDeclareFetch=1 UseServerSidePrepare=0 Protocol=7.4-1"
 	rm -f odbc.ini odbcinst.ini
 	$(MAKE) installcheck odbc_ini_extras="UseDeclareFetch=1 UseServerSidePrepare=0 Protocol=7.4-0"
+
+clean:
+	rm -f $(TESTBINS) src/*.o runsuite reset-db
+	rm -f results/*
diff --git a/test/expected/alter.out b/test/expected/alter.out
index f08b546..f81dc35 100644
--- a/test/expected/alter.out
+++ b/test/expected/alter.out
@@ -1,4 +1,3 @@
-\! "./src/alter-test"
 connected
 Result set metadata:
 t: VARCHAR(40) digits: 0, nullable
diff --git a/test/expected/arraybinding.out b/test/expected/arraybinding.out
index ec1f9f2..5eebcd2 100644
--- a/test/expected/arraybinding.out
+++ b/test/expected/arraybinding.out
@@ -1,4 +1,3 @@
-\! "./src/arraybinding-test"
 connected
 Parameter	Status
 Result set:
diff --git a/test/expected/bindcol.out b/test/expected/bindcol.out
index 9c15254..0c4cb21 100644
--- a/test/expected/bindcol.out
+++ b/test/expected/bindcol.out
@@ -1,4 +1,3 @@
-\! "./src/bindcol-test"
 connected
 Result set:
 1 foo1
diff --git a/test/expected/bookmark.out b/test/expected/bookmark.out
index 7cd2a91..0c217cf 100644
--- a/test/expected/bookmark.out
+++ b/test/expected/bookmark.out
@@ -1,4 +1,3 @@
-\! "./src/bookmark-test"
 connected
 Getting bookmark to beginning of result set...
 fetched: foo1
diff --git a/test/expected/boolsaschar.out b/test/expected/boolsaschar.out
index 947dcbc..d73d325 100644
--- a/test/expected/boolsaschar.out
+++ b/test/expected/boolsaschar.out
@@ -1,4 +1,3 @@
-\! "./src/boolsaschar-test"
 connected
 Result set metadata:
 b: VARCHAR(5) digits: 0, nullable
diff --git a/test/expected/bulkoperations.out b/test/expected/bulkoperations.out
index eea9bb8..b832319 100644
--- a/test/expected/bulkoperations.out
+++ b/test/expected/bulkoperations.out
@@ -1,4 +1,3 @@
-\! "./src/bulkoperations-test"
 connected
 Creating test table bulkoperations_test
 Opening a cursor for update, and fetching 10 rows
diff --git a/test/expected/catalogfunctions.out b/test/expected/catalogfunctions.out
index 6f0e8c9..b45ea5d 100644
--- a/test/expected/catalogfunctions.out
+++ b/test/expected/catalogfunctions.out
@@ -1,4 +1,3 @@
-\! "./src/catalogfunctions-test"
 connected
 Check for SQLTypeInfo
 Result set metadata:
diff --git a/test/expected/colattribute.out b/test/expected/colattribute.out
index 17cf5be..734a304 100644
--- a/test/expected/colattribute.out
+++ b/test/expected/colattribute.out
@@ -1,4 +1,3 @@
-\! "./src/colattribute-test"
 Running tests with UnknownSizes=-1;MaxVarcharSize=100...
 connected
 Testing SQLColAttribute...
diff --git a/test/expected/colattribute_1.out b/test/expected/colattribute_1.out
index b1fb6c5..5d533b6 100644
--- a/test/expected/colattribute_1.out
+++ b/test/expected/colattribute_1.out
@@ -1,4 +1,3 @@
-\! "./src/colattribute-test"
 Running tests with UnknownSizes=-1;MaxVarcharSize=100...
 connected
 Testing SQLColAttribute...
diff --git a/test/expected/commands.out b/test/expected/commands.out
index 0d07575..3105fc2 100644
--- a/test/expected/commands.out
+++ b/test/expected/commands.out
@@ -1,4 +1,3 @@
-\! "./src/commands-test"
 connected
 Testing VACUUM with SQLExecDirect...
 Testing VACUUM with SQLPrepare/SQLExecute...
diff --git a/test/expected/connect.out b/test/expected/connect.out
index 354d9d2..32bb718 100644
--- a/test/expected/connect.out
+++ b/test/expected/connect.out
@@ -1,4 +1,3 @@
-\! "./src/connect-test"
 connected
 disconnecting
 Connecting with SQLConnect...connected
diff --git a/test/expected/cte.out b/test/expected/cte.out
index 1b80aff..a66e2ac 100644
--- a/test/expected/cte.out
+++ b/test/expected/cte.out
@@ -1,4 +1,3 @@
-\! "./src/cte-test"
 connected
 Result set:
 1	foo1
diff --git a/test/expected/cursor-commit.out b/test/expected/cursor-commit.out
index 5c2a124..341cb42 100644
--- a/test/expected/cursor-commit.out
+++ b/test/expected/cursor-commit.out
@@ -1,4 +1,3 @@
-\! "./src/cursor-commit-test"
 connected
 first row: 1
 row 2: 2
diff --git a/test/expected/cursor-movement.out b/test/expected/cursor-movement.out
index 13b0f20..c9bf2c6 100644
--- a/test/expected/cursor-movement.out
+++ b/test/expected/cursor-movement.out
@@ -1,4 +1,3 @@
-\! "./src/cursor-movement-test"
 connected
 SQL_CURSOR_COMMIT_BEHAVIOR: SQL_CB_PRESERVE
 SQL_CURSOR_ROLLBACK_BEHAVIOR: SQL_CB_PRESERVE
diff --git a/test/expected/cursor-movement_1.out b/test/expected/cursor-movement_1.out
index f24c809..a173e30 100644
--- a/test/expected/cursor-movement_1.out
+++ b/test/expected/cursor-movement_1.out
@@ -1,4 +1,3 @@
-\! "./src/cursor-movement-test"
 connected
 SQL_CURSOR_COMMIT_BEHAVIOR: SQL_CB_PRESERVE
 SQL_CURSOR_ROLLBACK_BEHAVIOR: SQL_CB_CLOSE
diff --git a/test/expected/cursor-name.out b/test/expected/cursor-name.out
index e2195fc..7bc49e5 100644
--- a/test/expected/cursor-name.out
+++ b/test/expected/cursor-name.out
@@ -1,4 +1,3 @@
-\! "./src/cursor-name-test"
 connected
 cursor name prefix: SQL_CUR
 disconnecting
diff --git a/test/expected/cursors.out b/test/expected/cursors.out
index 9018291..d167475 100644
--- a/test/expected/cursors.out
+++ b/test/expected/cursors.out
@@ -1,4 +1,3 @@
-\! "./src/cursors-test"
 connected
 SQL_CURSOR_COMMIT_BEHAVIOR: SQL_CB_PRESERVE
 SQL_CURSOR_ROLLBACK_BEHAVIOR: SQL_CB_PRESERVE
diff --git a/test/expected/cursors_1.out b/test/expected/cursors_1.out
index 88a3e4e..a65c698 100644
--- a/test/expected/cursors_1.out
+++ b/test/expected/cursors_1.out
@@ -1,4 +1,3 @@
-\! "./src/cursors-test"
 connected
 SQL_CURSOR_COMMIT_BEHAVIOR: SQL_CB_PRESERVE
 SQL_CURSOR_ROLLBACK_BEHAVIOR: SQL_CB_CLOSE
diff --git a/test/expected/cvtnulldate.out b/test/expected/cvtnulldate.out
index daafba7..b678aa7 100644
--- a/test/expected/cvtnulldate.out
+++ b/test/expected/cvtnulldate.out
@@ -1,4 +1,3 @@
-\! "./src/cvtnulldate-test"
 connected
 Result set:
 1
diff --git a/test/expected/dataatexecution.out b/test/expected/dataatexecution.out
index fbe8b39..51b8e32 100644
--- a/test/expected/dataatexecution.out
+++ b/test/expected/dataatexecution.out
@@ -1,4 +1,3 @@
-\! "./src/dataatexecution-test"
 connected
 Result set:
 2
diff --git a/test/expected/declare-fetch-commit.out b/test/expected/declare-fetch-commit.out
index ea80bf1..23b429f 100644
--- a/test/expected/declare-fetch-commit.out
+++ b/test/expected/declare-fetch-commit.out
@@ -1,4 +1,3 @@
-\! "./src/declare-fetch-commit-test"
 connected
 Result set:
 1
diff --git a/test/expected/deprecated.out b/test/expected/deprecated.out
index 4bfb342..17973fb 100644
--- a/test/expected/deprecated.out
+++ b/test/expected/deprecated.out
@@ -1,4 +1,3 @@
-\! "./src/deprecated-test"
 Check for SQLAllocEnv
 Check for SQLAllocConnect
 Check for SQLAllocStmt
diff --git a/test/expected/diagnostic.out b/test/expected/diagnostic.out
index 05f31cb..143bcb0 100644
--- a/test/expected/diagnostic.out
+++ b/test/expected/diagnostic.out
@@ -1,4 +1,3 @@
-\! "./src/diagnostic-test"
 connected
 SQLExecDirect
 42601=ERROR: syntax error at or near "broken";
diff --git a/test/expected/error-rollback.out b/test/expected/error-rollback.out
index 5581514..99eb359 100644
--- a/test/expected/error-rollback.out
+++ b/test/expected/error-rollback.out
@@ -1,4 +1,3 @@
-\! "./src/error-rollback-test"
 Test for rollback protocol 0
 connected
 Executing query that will succeed
diff --git a/test/expected/errors.out b/test/expected/errors.out
index 965f4fc..6ab9ac5 100644
--- a/test/expected/errors.out
+++ b/test/expected/errors.out
@@ -1,4 +1,3 @@
-\! "./src/errors-test"
 connected
 
 42703=ERROR: column "doesnotexist" does not exist;
diff --git a/test/expected/errors_1.out b/test/expected/errors_1.out
index 49d169d..348f6d2 100644
--- a/test/expected/errors_1.out
+++ b/test/expected/errors_1.out
@@ -1,4 +1,3 @@
-\! "./src/errors-test"
 connected
 
 42703=ERROR: column "doesnotexist" does not exist;
diff --git a/test/expected/errors_2.out b/test/expected/errors_2.out
index 3a37f89..65c8470 100644
--- a/test/expected/errors_2.out
+++ b/test/expected/errors_2.out
@@ -1,4 +1,3 @@
-\! "./src/errors-test"
 connected
 
 42703=ERROR: column "doesnotexist" does not exist;
diff --git a/test/expected/getresult.out b/test/expected/getresult.out
index 3bca7ac..1eb8de8 100644
--- a/test/expected/getresult.out
+++ b/test/expected/getresult.out
@@ -1,4 +1,3 @@
-\! "./src/getresult-test"
 connected
 Result set:
 varcharcol: foo
diff --git a/test/expected/insertreturning.out b/test/expected/insertreturning.out
index fb88477..f5e02e4 100644
--- a/test/expected/insertreturning.out
+++ b/test/expected/insertreturning.out
@@ -1,4 +1,3 @@
-\! "./src/insertreturning-test"
 Testing with UseServerSidePrepare=1
 connected
 # of result cols before SQLExecute: 1, after: 1
diff --git a/test/expected/large-object.out b/test/expected/large-object.out
index 78dd408..b60b322 100644
--- a/test/expected/large-object.out
+++ b/test/expected/large-object.out
@@ -1,4 +1,3 @@
-\! "./src/large-object-test"
 connected
 inserting large object...
 reading it back...
diff --git a/test/expected/lfconversion.out b/test/expected/lfconversion.out
index bb92181..6d733c2 100644
--- a/test/expected/lfconversion.out
+++ b/test/expected/lfconversion.out
@@ -1,4 +1,3 @@
-\! "./src/lfconversion-test"
 connected
 reading to char buffer...
 strlen 22, SQLGetData claims 22
diff --git a/test/expected/multistmt.out b/test/expected/multistmt.out
index 9e5ce9b..68a9e92 100644
--- a/test/expected/multistmt.out
+++ b/test/expected/multistmt.out
@@ -1,4 +1,3 @@
-\! "./src/multistmt-test"
 connected
 --1 Result set:
 1
diff --git a/test/expected/notice.out b/test/expected/notice.out
index 7c65314..aebf2a0 100644
--- a/test/expected/notice.out
+++ b/test/expected/notice.out
@@ -1,4 +1,3 @@
-\! "./src/notice-test"
 connected
 got SUCCESS_WITH_INFO
 00000=NOTICE: test notice: foo
diff --git a/test/expected/numeric.out b/test/expected/numeric.out
index 17f8ae1..4db4b19 100644
--- a/test/expected/numeric.out
+++ b/test/expected/numeric.out
@@ -1,4 +1,3 @@
-\! "./src/numeric-test"
 connected
 Testing SQL_NUMERIC_STRUCT params...
 
diff --git a/test/expected/odbc-escapes.out b/test/expected/odbc-escapes.out
index c32a024..a7a6cfd 100644
--- a/test/expected/odbc-escapes.out
+++ b/test/expected/odbc-escapes.out
@@ -1,4 +1,3 @@
-\! "./src/odbc-escapes-test"
 connected
 
 Query: SELECT {fn CONCAT(?, ?) }
diff --git a/test/expected/param-conversions.out b/test/expected/param-conversions.out
index da9a8a8..449a398 100644
--- a/test/expected/param-conversions.out
+++ b/test/expected/param-conversions.out
@@ -1,4 +1,3 @@
-\! "./src/param-conversions-test"
 connected
 
 Testing conversions...
diff --git a/test/expected/param-conversions_1.out b/test/expected/param-conversions_1.out
index a6e2a0c..8c8e003 100644
--- a/test/expected/param-conversions_1.out
+++ b/test/expected/param-conversions_1.out
@@ -1,4 +1,3 @@
-\! "./src/param-conversions-test"
 connected
 
 Testing conversions...
diff --git a/test/expected/params.out b/test/expected/params.out
index 9dda4b4..0205610 100644
--- a/test/expected/params.out
+++ b/test/expected/params.out
@@ -1,4 +1,3 @@
-\! "./src/params-test"
 connected
 # of result cols: 2
 Result set:
diff --git a/test/expected/params_1.out b/test/expected/params_1.out
index 00d6ad0..242d576 100644
--- a/test/expected/params_1.out
+++ b/test/expected/params_1.out
@@ -1,4 +1,3 @@
-\! "./src/params-test"
 connected
 # of result cols: 2
 Result set:
diff --git a/test/expected/positioned-update.out b/test/expected/positioned-update.out
index 0770206..8f4784b 100644
--- a/test/expected/positioned-update.out
+++ b/test/expected/positioned-update.out
@@ -1,4 +1,3 @@
-\! "./src/positioned-update-test"
 connected
 Creating test table pos_update_test
 Opening a cursor for update, and fetching 10 rows
diff --git a/test/expected/prepare.out b/test/expected/prepare.out
index b656f90..0afb1bf 100644
--- a/test/expected/prepare.out
+++ b/test/expected/prepare.out
@@ -1,4 +1,3 @@
-\! "./src/prepare-test"
 connected
 # of result cols: 2
 Result set:
diff --git a/test/expected/quotes.out b/test/expected/quotes.out
index 358d60c..6dc437b 100644
--- a/test/expected/quotes.out
+++ b/test/expected/quotes.out
@@ -1,4 +1,3 @@
-\! "./src/quotes-test"
 connected
 
 SET standard_conforming_strings=on
diff --git a/test/expected/result-conversions.out b/test/expected/result-conversions.out
index 91b0ee3..fe1ada3 100644
--- a/test/expected/result-conversions.out
+++ b/test/expected/result-conversions.out
@@ -1,4 +1,3 @@
-\! "./src/result-conversions-test"
 connected
 Executed: SET intervalstyle=postgres
 Executed: SET bytea_output=escape
diff --git a/test/expected/sampletables.out b/test/expected/sampletables.out
deleted file mode 100644
index c54b325..0000000
--- a/test/expected/sampletables.out
+++ /dev/null
@@ -1,33 +0,0 @@
--- This file creates some tables to be used in the tests
-SET client_min_messages TO WARNING;
-CREATE TABLE testtab1 (id integer PRIMARY KEY, t varchar(20));
-INSERT INTO testtab1 VALUES (1, 'foo');
-INSERT INTO testtab1 VALUES (2, 'bar');
-INSERT INTO testtab1 VALUES (3, 'foobar');
-CREATE TABLE testtab_fk (id integer REFERENCES testtab1, t varchar(20));
-INSERT INTO testtab_fk VALUES (1, 'hoge');
-INSERT INTO testtab_fk VALUES (2, 'pogno');
-INSERT INTO testtab_fk VALUES (3, 'poco');
-CREATE TABLE byteatab (id integer, t bytea);
-INSERT INTO byteatab VALUES (1, E'\\001\\002\\003\\004\\005\\006\\007\\010'::bytea);
-INSERT INTO byteatab VALUES (2, 'bar');
-INSERT INTO byteatab VALUES (3, 'foobar');
-INSERT INTO byteatab VALUES (4, 'foo');
-INSERT INTO byteatab VALUES (5, 'barf');
-CREATE TABLE intervaltable(id integer, iv interval, d varchar(100));
-INSERT INTO intervaltable VALUES (1, '1 day', 'one day');
-INSERT INTO intervaltable VALUES (2, '10 seconds', 'ten secs');
-INSERT INTO intervaltable VALUES (3, '100 years', 'hundred years');
-CREATE TABLE booltab (id integer, t varchar(5), b boolean);
-INSERT INTO booltab VALUES (1, 'yeah', true);
-INSERT INTO booltab VALUES (2, 'yes', true);
-INSERT INTO booltab VALUES (3, 'true', true);
-INSERT INTO booltab VALUES (4, 'false', false);
-INSERT INTO booltab VALUES (5, 'not', false);
--- Procedure for catalog function checks
-CREATE FUNCTION simple_add(in int, in int, out int)
-AS $$ SELECT $1 + $2;
-$$ LANGUAGE SQL;
--- Large object support
-CREATE DOMAIN lo AS oid;
-CREATE TABLE lo_test_tab (id int4, large_data lo);
diff --git a/test/expected/select.out b/test/expected/select.out
index 82dfd4a..c841073 100644
--- a/test/expected/select.out
+++ b/test/expected/select.out
@@ -1,4 +1,3 @@
-\! "./src/select-test"
 connected
 Result set:
 1
diff --git a/test/expected/stmthandles.out b/test/expected/stmthandles.out
index 864a54c..f9e2a94 100644
--- a/test/expected/stmthandles.out
+++ b/test/expected/stmthandles.out
@@ -1,4 +1,3 @@
-\! "./src/stmthandles-test"
 connected
 10 statements allocated...
 20 statements allocated...
diff --git a/test/expected/update.out b/test/expected/update.out
index 8ff0808..bf60f4d 100644
--- a/test/expected/update.out
+++ b/test/expected/update.out
@@ -1,4 +1,3 @@
-\! "./src/update-test"
 connected
 # of rows inserted: 1
 # of rows inserted: 1
diff --git a/test/launcher b/test/launcher
deleted file mode 100755
index cdafdc8..0000000
--- a/test/launcher
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-# Pass ODBCSYSINI env variable to psql, so that it finds the odbc.ini and
-# odbcinst.ini files from the current dir.
-ODBCSYSINI=. $*
diff --git a/test/launcher_i b/test/launcher_i
deleted file mode 100755
index 6537830..0000000
--- a/test/launcher_i
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-# Lancher script for iodbc
-# Pass ODBCINSTINI and ODBCINI env variables to psql, so that it finds 
-# the odbc.ini and odbcinst.ini files from the current dir.
-ODBCINSTINI=./odbcinst.ini ODBCINI=./odbc.ini $*
diff --git a/test/reset-db.c b/test/reset-db.c
new file mode 100644
index 0000000..2b869af
--- /dev/null
+++ b/test/reset-db.c
@@ -0,0 +1,103 @@
+/*
+ * Initializes contrib_regression database.
+ *
+ * DROPs and CREATEs the database, then executes all statements passed in
+ * stdin. Use "reset-db < sampletables.sql" to run.
+ *
+ * This uses the same psqlodbc_test_dsn datasource to connect that the
+ * actual regression tests use.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include <sql.h>
+#include <sqlext.h>
+
+static SQLHENV env;
+static SQLHDBC conn;
+static HSTMT hstmt = SQL_NULL_HSTMT;
+
+static void
+connect_to_db(char *dsn)
+{
+	SQLRETURN	ret;
+	char		errmsg[500];
+	SQLSMALLINT textlen;
+	char		sqlstate[20];
+
+	SQLAllocHandle(SQL_HANDLE_DBC, env, &conn);
+
+	ret = SQLDriverConnect(conn, NULL, (SQLCHAR *) dsn, SQL_NTS, NULL, 0, NULL,
+						   SQL_DRIVER_NOPROMPT);
+	if (!SQL_SUCCEEDED(ret))
+	{
+		printf("connection to %s failed\n", dsn);
+
+		ret = SQLGetDiagRec(SQL_HANDLE_DBC, conn, 1, sqlstate, NULL,
+							errmsg, sizeof(errmsg), &textlen);
+		if (ret == SQL_INVALID_HANDLE)
+			printf("Invalid handle\n");
+		else if (SQL_SUCCEEDED(ret))
+			printf("%s=%s\n", sqlstate, errmsg);
+		exit(1);
+	}
+
+	printf("connected to %s\n", dsn);
+
+	ret = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt);
+	if (!SQL_SUCCEEDED(ret))
+	{
+		printf("SQLAllocHandle failed\n");
+		exit(1);
+	}
+}
+
+static void
+run_statement(char *statement)
+{
+	SQLRETURN	ret;
+	char		errmsg[500];
+	SQLSMALLINT textlen;
+	char		sqlstate[20];
+
+	ret = SQLExecDirect(hstmt, (SQLCHAR *) statement, SQL_NTS);
+	if (!SQL_SUCCEEDED(ret))
+	{
+		printf("Statement failed: %s\n", statement);
+		ret = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, sqlstate, NULL,
+							errmsg, sizeof(errmsg), &textlen);
+		if (ret == SQL_INVALID_HANDLE)
+			printf("Invalid handle\n");
+		else if (SQL_SUCCEEDED(ret))
+			printf("%s=%s\n", sqlstate, errmsg);
+		exit(1);
+	}
+	(void) SQLFreeStmt(hstmt, SQL_CLOSE);
+}
+
+int main(int argc, char **argv)
+{
+	char		line[500];
+
+	SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
+	SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
+
+	connect_to_db("DSN=psqlodbc_test_dsn;Database=postgres");
+	printf("Dropping and creating database contrib_regression...\n");
+	run_statement("DROP DATABASE IF EXISTS contrib_regression");
+	run_statement("CREATE DATABASE contrib_regression");
+
+	connect_to_db("DSN=psqlodbc_test_dsn;Database=contrib_regression");
+
+	printf("Running initialization script...\n");
+	while (fgets(line, sizeof(line), stdin) != NULL)
+		run_statement(line);
+
+	printf("Done!\n");
+
+	return 0;
+}
diff --git a/test/runsuite.c b/test/runsuite.c
new file mode 100644
index 0000000..e4fea86
--- /dev/null
+++ b/test/runsuite.c
@@ -0,0 +1,293 @@
+/*
+ * A test driver for the psqlodbc regression tests.
+ *
+ * This program runs one regression tests from the src/ directory,
+ * and compares the output with the expected output in the expected/ directory.
+ * Reports success or failure in TAP compatible fashion.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef WIN32
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define strdup _strdup
+#endif
+
+static int rundiff(const char *testname);
+static int runtest(const char *binname, const char *testname, int testno);
+
+static char *slurpfile(const char *filename, size_t *len);
+
+static void
+bailout(const char *fmt, ...)
+{
+	va_list argp;
+
+	va_start(argp, fmt);
+
+	printf("Bail out! ");
+	vprintf(fmt, argp);
+
+	va_end(argp);
+
+	exit(1);
+}
+
+#ifdef WIN32
+#define DIR_SEP '\\'
+#else
+#define DIR_SEP '/'
+#endif
+
+/* Given a test program's name, get the test name */
+void
+parse_argument(const char *in, char *testname, char *binname)
+{
+	const char *basename;
+#ifdef WIN32
+	const char *suffix = "-test.exe";
+#else
+	const char *suffix = "-test";
+#endif
+	const char *s;
+	size_t		len;
+
+	/* if the input is a plain test name, construct the binary name from it */
+	if (strchr(in, DIR_SEP) == NULL)
+	{
+		strcpy(testname, in);
+		sprintf(binname, "src%c%s-test", DIR_SEP, in);
+		return;
+	}
+
+	/*
+	 * Otherwise the input is a binary name, and we'll construct the test name
+	 * from it.
+	 */
+	strcpy(binname, in);
+
+	/* Find the last / or \ character */
+	basename = in;
+	s = in;
+	while (*s != '\0' && (s = strchr(s + 1, DIR_SEP)) != NULL)
+		basename = s + 1;
+
+	/* Strip -test or -test.exe suffix */
+	if (strlen(basename) <= strlen(suffix))
+	{
+		strcpy(testname, basename);
+		return;
+	}
+
+	len = strlen(basename) - strlen(suffix);
+	if (strcmp(&basename[len], suffix) != 0)
+	{
+		strcpy(testname, basename);
+		return;
+	}
+
+	memcpy(testname, basename, len);
+	testname[len] = '\0';
+}
+
+int main(int argc, char **argv)
+{
+	char		binname[1000];
+	char		testname[100];
+	int			numtests;
+	int			i;
+	int			failures;
+
+	if (argc < 2)
+	{
+		printf("Usage: runsuite <test binary> ...\n");
+		exit(1);
+	}
+	numtests = argc - 1;
+
+	printf("TAP version 13\n");
+	printf("1..%d\n", numtests);
+
+	/*
+	 * We accept either test binary name or plain test name.
+	 */
+	failures = 0;
+	for (i = 1; i <= numtests; i++)
+	{
+		parse_argument(argv[i], testname, binname);
+		if (runtest(binname, testname, i) != 0)
+			failures++;
+	}
+
+	exit(failures > 254 ? 254 : failures);
+}
+
+/* Return 0 on success, 1 on failure */
+static int
+runtest(const char *binname, const char *testname, int testno)
+{
+	char		cmdline[1024];
+	int			rc;
+	int			ret;
+	int			diff;
+
+	/*
+	 * ODBCSYSINI=. tells unixodbc where to find the driver config file,
+	 * odbcinst.ini
+	 *
+	 * ODBCINSTINI=./odbcinst.ini tells the same for iodbc. iodbc also requires
+	 * ODBCINI=./odbc.ini to tell it where to find the datasource config.
+	 *
+	 * We wouldn't need to iodbc stuff when building with unixodbc and vice
+	 * versa, but it doesn't hurt either.
+	 */
+#ifndef WIN32
+	snprintf(cmdline, sizeof(cmdline),
+			 "ODBCSYSINI=. ODBCINSTINI=./odbcinst.ini ODBCINI=./odbc.ini "
+			 "%s > results/%s.out",
+			 binname, testname);
+#else
+	snprintf(cmdline, sizeof(cmdline),
+			 "%s > results\\%s.out",
+			 binname, testname);
+#endif
+	rc = system(cmdline);
+
+	diff = rundiff(testname);
+	if (rc != 0)
+	{
+		printf("not ok %d - %s test returned %d\n", testno, testname, rc);
+		ret = 1;
+	}
+	else if (diff != 0)
+	{
+		printf("not ok %d - %s test output differs\n", testno, testname);
+		ret = 1;
+	}
+	else
+	{
+		printf("ok %d - %s\n", testno, testname);
+		ret = 0;
+	}
+	fflush(stdout);
+
+	return ret;
+}
+
+static int
+rundiff(const char *testname)
+{
+	char		filename[1024];
+	char		cmdline[1024];
+	int			outputno;
+	char	   *result;
+	size_t		result_len;
+
+	snprintf(filename, sizeof(filename), "results/%s.out", testname);
+	result = slurpfile(filename, &result_len);
+
+	outputno = 0;
+	for (;;)
+	{
+		char	   *expected;
+		size_t		expected_len;
+
+		if (outputno == 0)
+			snprintf(filename, sizeof(filename), "expected/%s.out", testname);
+		else
+			snprintf(filename, sizeof(filename), "expected/%s_%d.out", testname, outputno);
+		expected = slurpfile(filename, &expected_len);
+		if (expected == NULL)
+		{
+			if (outputno == 0)
+				bailout("could not open file %s: %s\n", filename, strerror(ENOENT));
+			free(result);
+			break;
+		}
+
+		if (expected_len == result_len &&
+			memcmp(expected, result, expected_len) == 0)
+		{
+			/* The files are equal. */
+			free(result);
+			free(expected);
+			return 0;
+		}
+
+		free(expected);
+
+		outputno++;
+	}
+	/* no matching output found */
+
+	/*
+	 * Try to run diff. If this fails, e.g. because the 'diff' program is
+	 * not installed, which is typical on Windows system, that's OK. You'll
+	 * miss the regression.diff output, but we'll still report "not ok"
+	 * correctly. You can always compare the files manually...
+	 *
+	 * XXX: Somewhat arbitrarily, always run the diff against the primary
+	 * expected output file. Perhaps we should run it against all output
+	 * files and print the smallest diff?
+	 */
+	snprintf(cmdline, sizeof(cmdline),
+			 "diff -c expected/%s.out results/%s.out >> regression.diffs",
+			 testname, testname);
+	if (system(cmdline) == -1)
+		printf("# diff failed\n");
+
+	return 1;
+}
+
+
+/*
+ * Reads file to memory. The file is returned, or NULL if it doesn't exist.
+ * Length is returned in *len.
+ */
+static char *
+slurpfile(const char *filename, size_t *len)
+{
+	int			fd;
+	struct stat stbuf;
+	int			readlen;
+	off_t		filelen;
+	char	   *p;
+
+#ifdef WIN32
+	fd = open(filename, O_RDONLY | O_BINARY, 0);
+#else
+	fd = open(filename, O_RDONLY, 0);
+#endif
+	if (fd == -1)
+	{
+		if (errno == ENOENT)
+			return NULL;
+
+		bailout("could not open file %s: %s\n", filename, strerror(errno));
+	}
+	if (fstat(fd, &stbuf) < 0)
+		bailout("fstat failed on file %s: %s\n", filename, strerror(errno));
+
+	filelen = stbuf.st_size;
+	p = malloc(filelen + 1);
+	if (!p)
+		bailout("out of memory reading file %s\n", filename);
+	readlen = read(fd, p, filelen);
+	if (readlen != filelen)
+		bailout("read only %d bytes out of %d from %s\n", (int) readlen, (int) filelen, filename);
+	p[readlen] = '\0';
+	close(fd);
+
+	*len = readlen;
+	return p;
+}
diff --git a/test/sampletables.sql b/test/sampletables.sql
new file mode 100644
index 0000000..99715d9
--- /dev/null
+++ b/test/sampletables.sql
@@ -0,0 +1,38 @@
+-- This file creates some tables to be used in the tests
+SET client_min_messages TO WARNING;
+
+CREATE TABLE testtab1 (id integer PRIMARY KEY, t varchar(20));
+INSERT INTO testtab1 VALUES (1, 'foo');
+INSERT INTO testtab1 VALUES (2, 'bar');
+INSERT INTO testtab1 VALUES (3, 'foobar');
+
+CREATE TABLE testtab_fk (id integer REFERENCES testtab1, t varchar(20));
+INSERT INTO testtab_fk VALUES (1, 'hoge');
+INSERT INTO testtab_fk VALUES (2, 'pogno');
+INSERT INTO testtab_fk VALUES (3, 'poco');
+
+CREATE TABLE byteatab (id integer, t bytea);
+INSERT INTO byteatab VALUES (1, E'\\001\\002\\003\\004\\005\\006\\007\\010'::bytea);
+INSERT INTO byteatab VALUES (2, 'bar');
+INSERT INTO byteatab VALUES (3, 'foobar');
+INSERT INTO byteatab VALUES (4, 'foo');
+INSERT INTO byteatab VALUES (5, 'barf');
+
+CREATE TABLE intervaltable(id integer, iv interval, d varchar(100));
+INSERT INTO intervaltable VALUES (1, '1 day', 'one day');
+INSERT INTO intervaltable VALUES (2, '10 seconds', 'ten secs');
+INSERT INTO intervaltable VALUES (3, '100 years', 'hundred years');
+
+CREATE TABLE booltab (id integer, t varchar(5), b boolean);
+INSERT INTO booltab VALUES (1, 'yeah', true);
+INSERT INTO booltab VALUES (2, 'yes', true);
+INSERT INTO booltab VALUES (3, 'true', true);
+INSERT INTO booltab VALUES (4, 'false', false);
+INSERT INTO booltab VALUES (5, 'not', false);
+
+-- Procedure for catalog function checks
+CREATE FUNCTION simple_add(in int, in int, out int) AS $$ SELECT $1 + $2; $$ LANGUAGE SQL;
+
+-- Large object support
+CREATE DOMAIN lo AS oid;
+CREATE TABLE lo_test_tab (id int4, large_data lo);
diff --git a/test/sql/.gitignore b/test/sql/.gitignore
deleted file mode 100644
index 7739451..0000000
--- a/test/sql/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.sql
-!sampletables.sql
diff --git a/test/sql/sampletables.sql b/test/sql/sampletables.sql
deleted file mode 100644
index df774e1..0000000
--- a/test/sql/sampletables.sql
+++ /dev/null
@@ -1,40 +0,0 @@
--- This file creates some tables to be used in the tests
-SET client_min_messages TO WARNING;
-
-CREATE TABLE testtab1 (id integer PRIMARY KEY, t varchar(20));
-INSERT INTO testtab1 VALUES (1, 'foo');
-INSERT INTO testtab1 VALUES (2, 'bar');
-INSERT INTO testtab1 VALUES (3, 'foobar');
-
-CREATE TABLE testtab_fk (id integer REFERENCES testtab1, t varchar(20));
-INSERT INTO testtab_fk VALUES (1, 'hoge');
-INSERT INTO testtab_fk VALUES (2, 'pogno');
-INSERT INTO testtab_fk VALUES (3, 'poco');
-
-CREATE TABLE byteatab (id integer, t bytea);
-INSERT INTO byteatab VALUES (1, E'\\001\\002\\003\\004\\005\\006\\007\\010'::bytea);
-INSERT INTO byteatab VALUES (2, 'bar');
-INSERT INTO byteatab VALUES (3, 'foobar');
-INSERT INTO byteatab VALUES (4, 'foo');
-INSERT INTO byteatab VALUES (5, 'barf');
-
-CREATE TABLE intervaltable(id integer, iv interval, d varchar(100));
-INSERT INTO intervaltable VALUES (1, '1 day', 'one day');
-INSERT INTO intervaltable VALUES (2, '10 seconds', 'ten secs');
-INSERT INTO intervaltable VALUES (3, '100 years', 'hundred years');
-
-CREATE TABLE booltab (id integer, t varchar(5), b boolean);
-INSERT INTO booltab VALUES (1, 'yeah', true);
-INSERT INTO booltab VALUES (2, 'yes', true);
-INSERT INTO booltab VALUES (3, 'true', true);
-INSERT INTO booltab VALUES (4, 'false', false);
-INSERT INTO booltab VALUES (5, 'not', false);
-
--- Procedure for catalog function checks
-CREATE FUNCTION simple_add(in int, in int, out int)
-AS $$ SELECT $1 + $2;
-$$ LANGUAGE SQL;
-
--- Large object support
-CREATE DOMAIN lo AS oid;
-CREATE TABLE lo_test_tab (id int4, large_data lo);
diff --git a/test/win.mak b/test/win.mak
index 74a2eb6..0445301 100644
--- a/test/win.mak
+++ b/test/win.mak
@@ -10,14 +10,8 @@
 #
 
 # Environment checks
-!IF "$(CPU)" == ""
-!MESSAGE Making 64bit DLL...
-!MESSAGE You should set the CPU environment variable
-!MESSAGE to distinguish your OS
-!ENDIF
 
 !IFNDEF PG_BIN
-PG_LIB=C:\develop\bin\$(CPU)
 !MESSAGE Using default PostgreSQL Binary directory: $(PG_BIN)
 !ENDIF
 
@@ -32,18 +26,13 @@ PG_LIB=C:\develop\bin\$(CPU)
 # prefix/suffix to a list in nmake. Removing them is much easier.)
 TESTS = $(TESTBINS:src/=)
 TESTS = $(TESTS:-test=)
-TESTS = sampletables $(TESTS)
 
-# Now create names of the test .exe and .sql files from the base names
+# Now create names of the test .exe from the base names
 
 # src\<testname>.exe
 TESTEXES = $(TESTBINS:-test=-test.exe)
 TESTEXES = $(TESTEXES:src/=src\)
 
-# sql\<testname>.sql
-TESTSQLS = $(TESTBINS:-test=.sql)
-TESTSQLS = $(TESTSQLS:src/=sql\)
-
 
 # Flags
 CLFLAGS=/D WIN32
@@ -59,25 +48,23 @@ LINKFLAGS=/link odbc32.lib odbccp32.lib
 .c.exe:
 	cl /Fe.\src\ /Fo.\src\ $*.c src/common.c $(CLFLAGS) $(LINKFLAGS)
 
-all: $(TESTEXES) $(TESTSQLS)
+all: $(TESTEXES) runsuite.exe
 
-# A rule to generate sql/<testname>.sql files. The expected output files
-# are used as the dependent files, to give nmake a hint on how to build
-# them, even though the .sql files don't really depend on the .out files.
-{expected\}.out{sql\}.sql:
-	echo \! "./src/$(*:sql\=)-test" > $*.sql
+runsuite.exe: runsuite.c
+	cl runsuite.c $(CLFLAGS) $(LINKFLAGS)
+
+reset-db.exe: reset-db.c
+	cl reset-db.c $(CLFLAGS) $(LINKFLAGS)
 
 # activate the above inference rule
 .SUFFIXES: .out
 
-
 # Run regression tests
-installcheck:
-	cmd /c $(PG_BIN)\pg_regress --inputdir=. --psqldir="$(PG_BIN)" \
-	    --dbname="contrib_regression" $(REGRESSOPTS) \
-	    $(TESTS)
+installcheck: runsuite.exe $(TESTEXES) reset-db.exe
+	del regression.diffs
+	.\reset-db < sampletables.sql
+	.\runsuite $(TESTS)
 
 clean:
 	-del src\*.exe
 	-del src\*.obj
-	-del $(TESTSQLS)
-- 
2.1.4

