#!/bin/sh

DB=postgis_reg
SHP2PGSQL=../loader/shp2pgsql
PGSQL2SHP=../loader/pgsql2shp

###################################################
#
# Usage ./run_test <testname> [<testname>]
#
# Create the spatial database 'postgis_reg'
# (or whatever $DB is set to) if it doesn't
# already exist.
#
# Run the <testname>.sql script
# Diff output against <testname>_expected
#
#
###################################################

if [ -n "$USE_VERSION" ]; then
	if [ "$USE_VERSION" -gt 74 ]; then
		PGOPTIONS="${PGOPTIONS} -c lc_messages=C"
		export PGOPTIONS
	fi
fi

PSQL="psql"

if [ -z "$TMPDIR" ]; then
	TMPDIR=/tmp/pgis_reg_$$
fi

mkdir -p ${TMPDIR}

VERBOSE=0
OPT_DROP=yes
OPT_CREATE=yes

if echo '\c' | grep c >/dev/null 2>&1; then
	ECHO_N='echo -n'
	ECHO_C=''
else
	ECHO_N='echo'
	ECHO_C='\c'
fi

###################################################
# 
# Helper functions
#
###################################################

# Print a single dot
show_progress()
{
	${ECHO_N} ".${ECHO_C}"
}

#
# fail <msg> <log>
#
fail ()
{
	_msg="$1"
	_log="$2"

	if [ -z "$_log" ]; then
		echo " failed ($_msg)"
	elif test "$VERBOSE" -eq "1"; then
		echo " failed ($_msg: $_log)"
		echo "-----------------------------------------------------------------------------"
		cat $_log
		echo "-----------------------------------------------------------------------------"
	else
		echo " failed ($_msg: $_log)"
	fi

	FAIL=`expr $FAIL + 1`
}

#
#  run_simple_test 
#
#  Run an sql script and compare results with the given expected output
#
#  SQL input is ${TEST}.sql, expected output is {$TEST}_expected
#
run_simple_test ()
{
	_sql="$1"
	_expected="$2"
	if [ -n "$3" ]; then
		_msg="$3: "
	else
		_msg=
	fi

	if [ ! -r "$_sql" ]; then
		fail "can't read $_sql"
		return 1
	fi

	if [ ! -r "$_expected" ]; then
		fail "can't read $_expected"
		return 1
	fi

	show_progress

	OUTFILE="${TMPDIR}/test_${RUN}_out"
	TMPFILE="${TMPDIR}/test_${RUN}_tmp"
	DIFFILE="${TMPDIR}/test_${RUN}_diff"

	# Use intermediate file to prevent MingW buffering problems
	${PSQL} -tA < "${_sql}" ${DB} > ${TMPFILE} 2>&1
	cat ${TMPFILE} \
		| grep -v "^$" \
		| grep -v "^INSERT" \
		| grep -v "^DELETE" \
		| grep -v "^CONTEXT" \
		| grep -v "^UPDATE" \
		| grep -v "^DROP" \
		| grep -v "^CREATE" \
		| grep -v "^SET" \
		| sed 's/Infinity/inf/g;s/Inf/inf/g;s/1\.#INF/inf/g' \
		| sed 's/[eE]\([+-]\)0\{1,\}\([0-9]\{1,\}\)/e\1\2/g' \
		| sed 's/Self-intersection .*/Self-intersection/' \
		| sed 's/^ROLLBACK/COMMIT/' \
		> "${OUTFILE}"
	rm ${TMPFILE}

	if diff "${_expected}" "${OUTFILE}" > ${DIFFILE}; then
		#SUCCESS=`expr $SUCCESS + 1`
		rm "${OUTFILE}" "${DIFFILE}" # we don't need these anymore
		return 0
	else
		fail "${_msg}diff expected obtained" "${DIFFILE}"
		rm "${OUTFILE}" # diff is enough
		return 1
	fi
}

#
#  run_loader_test 
#
#  Load a shapefile with different methods, create a 'select *' SQL
#  test and run simple test with provided expected output. 
#
#  SHP input is ${TEST}.shp, expected output is {$TEST}_expected
#
run_loader_test ()
{
	_tblname=loadedshp

	# ON_ERROR_STOP is used by psql to return non-0 on an error
	_psql_opts="--variable ON_ERROR_STOP=true"

	#echo "SELECT * from ${_tblname}" > ${TEST}.sql


	#
	# Run in HEXWKB insert mode
	#

	show_progress

	${SHP2PGSQL} ${TEST}.shp $_tblname \
		> ${TMPDIR}/loader \
		2> ${TMPDIR}/loader.err

	if [ $? -gt 0 ]; then
		fail "running shp2pgsql" "${TMPDIR}/loader.err"
		return 1

	fi

	show_progress

	${PSQL} -c "DROP table ${_tblname}" "${DB}" >> ${TMPDIR}/regress_log 2>&1
	${PSQL} ${_psql_opts} -f ${TMPDIR}/loader "${DB}" > ${TMPDIR}/loader.err 2>&1
	if [ $? -gt 0 ]; then
		fail "sourcing shp2pgsql output" "${TMPDIR}/loader.err"
		return 1
	fi

	if [ -f "${TEST}-wkb.sql" ]; then
		if run_simple_test ${TEST}-wkb.sql ${TEST}-wkb.expected "wkb insert"; then
			:
		else
			return 1
		fi
	fi


	#
	# Run in HEXWKB dump mode
	#

	show_progress

	${SHP2PGSQL} -D ${TEST}.shp $_tblname \
		> ${TMPDIR}/loader \
		2> ${TMPDIR}/loader.err

	if [ $? -gt 0 ]; then
		fail "running shp2pgsql -D" "${TMPDIR}/loader.err"
		return 1

	fi

	show_progress

	${PSQL} -c "DROP table ${_tblname}" "${DB}" >> ${TMPDIR}/regress_log 2>&1
	${PSQL} ${_psql_opts} -f ${TMPDIR}/loader "${DB}" > ${TMPDIR}/loader.err 2>&1
	if [ $? -gt 0 ]; then
		fail "sourcing shp2pgsql -D output" "${TMPDIR}/loader.err"
		return 1
	fi

	if [ -f "${TEST}-wkb.sql" ]; then
		if run_simple_test ${TEST}-wkb.sql ${TEST}-wkb.expected "wkb dump"; then
			:
		else
			return 1
		fi
	fi

	###########################################################
	#
	# Dump and compare.
	# Do this using WKB mode, as WKT is unable to reproduce
	# M values
	#

	show_progress

	${PGSQL2SHP} -f ${TMPDIR}/dumper ${DB} "${_tblname}" > "${TMPDIR}/dumper.err" 2>&1
	if [ $? -gt 0 ]; then
		fail "dumping loaded table" "${TMPDIR}/dumper.err"
		return 1
	fi

	show_progress

	if diff "${TMPDIR}"/dumper.shp "${TEST}".shp > /dev/null; then
		:
	else
		ls -lL "${TMPDIR}"/dumper.shp "${TEST}".shp > "${TMPDIR}"/dumper.diff
		fail "dumping loaded table" "${TMPDIR}/dumper.diff"
		return 1
	fi

# I'm not sure it's safe to compare indexes
#	if diff "${TMPDIR}"/dumper.shx "${TEST}".shx; then
#		:
#	else
#		ls -lL "${TMPDIR}"/dumper.shx "${TEST}".shx > "${TMPDIR}"/dumper.diff
#		fail "dumping loaded table" "${TMPDIR}/dumper.diff"
#		return 1
#	fi

# Change in attribute sizes would make this fail
#	if diff "${TMPDIR}"/dumper.dbf "${TEST}".dbf; then
#		:
#	else
#		ls -lL "${TMPDIR}"/dumper.dbf "${TEST}".dbf > "${TMPDIR}"/dumper.diff
#		fail "dumping loaded table" "${TMPDIR}/dumper.diff"
#		return 1
#	fi


	#
	# End of dump and compare.
	#
	################################################

	show_progress

	${SHP2PGSQL} -w ${TEST}.shp $_tblname \
		> ${TMPDIR}/loader \
		2> ${TMPDIR}/loader.err

	if [ $? -gt 0 ]; then
		fail "running shp2pgsql -w" "${TMPDIR}/loader.err"
		return 1
	fi

	show_progress

	${PSQL} -c "DROP table ${_tblname}" "${DB}" >> ${TMPDIR}/regress_log 2>&1
	${PSQL} ${_psql_opts} -f ${TMPDIR}/loader "${DB}" > ${TMPDIR}/loader.err 2>&1
	if [ $? -gt 0 ]; then
		fail "sourcing shp2pgsql -w output" "${TMPDIR}/loader.err"
		return 1
	fi

	if [ -f "${TEST}-wkt.sql" ]; then
		if run_simple_test ${TEST}-wkt.sql ${TEST}-wkt.expected "wkt insert"; then
			:
		else
			return 1
		fi
	fi

	#
	# Run in WKT dump mode
	#

	show_progress

	${SHP2PGSQL} -D -w ${TEST}.shp $_tblname \
		> ${TMPDIR}/loader \
		2> ${TMPDIR}/loader.err

	if [ $? -gt 0 ]; then
		fail "running shp2pgsql -D -w" "${TMPDIR}/loader.err"
		return 1

	fi

	show_progress

	${PSQL} -c "DROP table ${_tblname}" "${DB}" >> ${TMPDIR}/regress_log 2>&1
	${PSQL} ${_psql_opts} -f ${TMPDIR}/loader "${DB}" > ${TMPDIR}/loader.err 2>&1
	if [ $? -gt 0 ]; then
		fail "sourcing shp2pgsql -D -w output" "${TMPDIR}/loader.err"
		return 1
	fi

	if [ -f "${TEST}-wkt.sql" ]; then
		if run_simple_test ${TEST}-wkt.sql ${TEST}-wkt.expected "wkt dump"; then
			:
		else
			return 1
		fi
	fi

	#rm ${TEST}.sql

	return 0;
}

###################################################
# 
# Parse command line opts
#
###################################################

while [ -n "$1" ]; do

	if test "$1" = "-v"; then
		VERBOSE=1
		shift
		continue
	elif test "$1" = "--nodrop"; then
		OPT_DROP=no
		shift
		continue
	elif test "$1" = "--nocreate"; then
		OPT_CREATE=no
		shift
		continue
	else
		break
	fi
done

if [ -z "$1" ]; then
	echo "Usage: $0 [-v] [--nocreate] [--nodrop] <test> [<test>]" >&2
	exit 1
fi

###################################################
# 
# Prepare the database
#
###################################################

db_exists=`${PSQL} -l | grep -w ${DB}`

if test -z "$db_exists"; then

	if test x"$OPT_CREATE" = "xyes"; then
		echo "Creating spatial db ${DB} " 

		createdb "${DB}" > ${TMPDIR}/regress_log
		createlang plpgsql "${DB}" >> ${TMPDIR}/regress_log
		${PSQL} -f lwpostgis.sql "${DB}" >> ${TMPDIR}/regress_log 2>&1
	else

		echo "Database ${DB} does not exist" >&2
		echo "Run w/out the --nocreate flag to create it" >&2
		exit 1
	fi
else
	if test x"$OPT_CREATE" = "xyes"; then
		echo "Database ${DB} already exist." >&2
		echo "Run with the --nocreate flag to use it " \
			"or drop it and try again." >&2
		exit 1
	else
		echo "Using existing database ${DB}"
	fi
fi

libver=`${PSQL} -tAc "select postgis_lib_version()" "${DB}"`

if [ -z "$libver" ]; then
	echo
	echo " Something went wrong (no postgis installed in ${DB})." 
	if [ -z "$db_exists" ]; then
		echo " For details, check ${TMPDIR}/regress_log"
	else
		echo " Try dropping the database, it will be recreated" \
			" on next run."
	fi
	echo
	exit 1
fi

###################################################
# 
# Report runtime environment 
#
###################################################

geosver=`${PSQL} -tAc "select postgis_geos_version()" "${DB}"`
jtsver=`${PSQL} -tAc "select postgis_jts_version()" "${DB}"`
projver=`${PSQL} -tAc "select postgis_proj_version()" "${DB}"`
libbuilddate=`${PSQL} -tAc "select postgis_lib_build_date()" "${DB}"`
pgsqlver=`${PSQL} -tAc "select version()" "${DB}"`

echo "TMPDIR is ${TMPDIR}"

echo
echo " $pgsqlver"
echo " Postgis $libver - $libbuilddate"
if [ -n "$geosver" ]; then
	echo "   GEOS: $geosver"
fi
if [ -n "$jtsver" ]; then
	echo "   JTS: $jtsver"
fi
if [ -n "$projver" ]; then
	echo "   PROJ: $projver"
fi

###################################################
# 
# Run the tests
#
###################################################

echo 
echo "Running tests"
echo

RUN=0
SKIP=0
FAIL=0
#SUCCESS=0
while [ -n "$1" ]; do
	TEST="$1"; shift;

	# catch a common mistake (strip trailing .sql)
	TEST=`echo "$TEST" | sed 's/\.sql$//'`

	#printf %20s " ${TEST}"
	#echo -ne " ${TEST}"
	${ECHO_N} " ${TEST}${ECHO_C}"

	RUN=`expr $RUN + 1`

	# Check .shp *before* .sql as loader test would
	# create the .sql
	if [ -r "${TEST}.shp" ]; then
		if run_loader_test; then
			echo " ok"
		fi
	elif [ -r "${TEST}.sql" ]; then
		if run_simple_test ${TEST}.sql ${TEST}_expected; then
			echo " ok"
		fi
	else
		echo "Skipped (can't read ${TEST}.sql)"
		SKIP=`expr $SKIP + 1`
		continue
	fi


done

echo
echo "Run tests: $RUN"
#echo "Skipped: $SKIP"
#echo "Successful: $SUCCESS"
echo "Failed: $FAIL"

if test x"$OPT_DROP" = "xyes" -a x"$OPT_CREATE" = "xyes"; then
	sleep 1
	dropdb "${DB}" > /dev/null
else
	: echo "Drop database ${DB} manually"
fi

