#!/bin/bash
#
# $Id: gmtest.in 12688 2013-12-29 18:55:29Z pwessel $
#
# Functions to be used with test scripts

# Print the shell script name and purpose and fill out to 72 characters
# and make sure to use US system defaults

test -z "$1" && exit 1

# Name of the script and the directory portion of it
script_name="$1"
script_dir=$(dirname "${script_name}")
script="@GMT_SOURCE_DIR@/test/${script_name}"
if ! [ -x "${script}" ]; then
  echo "error: cannot execute script ${script}." >&2
  exit 1
fi

shift

# Temporary change LANG to C
LANG=C

# Define variables that are needed *within* test scripts
GMT_BINARY_DIR="@GMT_BINARY_DIR@"
GMT_SOURCE_DIR="@GMT_SOURCE_DIR@"
GMT_VERSION="@GMT_PACKAGE_VERSION_WITH_SVN_REVISION@"
GSHHG_DIR="@GSHHG_PATH@"
HAVE_GMT_DEBUG_SYMBOLS="@HAVE_GMT_DEBUG_SYMBOLS@"
HAVE_OPENMP="@HAVE_OPENMP@"
GRAPHICSMAGICK="@GRAPHICSMAGICK@"
src="@GMT_SOURCE_DIR@/test/${script_dir}"

# choose awk
if type gawk 2>&1 >/dev/null ; then
  export AWK=gawk
elif type nawk 2>&1 >/dev/null ; then
  export AWK=nawk
else
  export AWK=awk
fi

# Use executables from GMT_BINARY_DIR, fallback to CMAKE_INSTALL_PREFIX/GMT_BINDIR
unset GMT5_SHAREDIR
export GMT_SHAREDIR="@GMT_SOURCE_DIR@/share"
export GMT_USERDIR="@GMT_BINARY_DIR@/share"
export GMT_DATADIR="$src"

# Reset error count
ERROR=0

# valgrind wrapper
function valgrind_wrapper()
{
  if [ -n "${VALGRIND_ARGS}" ]; then
    valgrind ${VALGRIND_ARGS} --log-file=valgrind_%p.log --dsymutil=yes "$@"
  else
    "$@"
  fi
}

# gmt wrapper
function gmt()
{
  valgrind_wrapper @GMT_BINARY_DIR@/src/gmt "$@"
}

# gmtlogo wrapper
function gmtlogo()
{
  @GMT_SOURCE_DIR@/src/gmtlogo "$@"
}

# psldemo wrapper
function psldemo()
{
  valgrind_wrapper @GMT_BINARY_DIR@/src/psldemo "$@"
}

# testapi wrapper
function testapi()
{
  valgrind_wrapper @GMT_BINARY_DIR@/src/testapi "$@"
}

# testgrdio wrapper
function testgrdio()
{
  valgrind_wrapper @GMT_BINARY_DIR@/src/testgrdio "$@"
}

# export function definitions to subshells
export -f gmt gmtlogo psldemo valgrind_wrapper

# invalidate module calls without "gmt" prefix, which would bypass gmt from build dir
. @GMT_SOURCE_DIR@/test/invalidate_modules.sh

# Convert PS to PDF
function make_pdf()
{
  pdf="${ps%.ps}.pdf"
  test -f "$ps" || return 1
  gmt ps2raster -Tf -A -P "$ps" || ((++ERROR))
  test -f "$pdf" || ((++ERROR))
}

# Compare the ps file with its original. Check $ps against original $ps or against $1.ps (if $1 given)
pscmp () {
test ${#ps} -gt 0 || return 0
test -f "$ps" || return 1
if ! [ -x "$GRAPHICSMAGICK" ]; then
  echo "[PASS] (without comparison)"
  return
fi
for ps in *.ps ; do
  # syntax: gm compare [ options ... ] reference-image [ options ... ] compare-image [ options ... ]
  rms=$("${GRAPHICSMAGICK}" compare -density 200 -maximum-error 0.001 -highlight-color magenta -highlight-style assign -metric rmse -file "${ps%.ps}.png" "$ps" "$src/${psref:-$ps}") || pscmpfailed="yes"
  rms=$(perl -ne 'print $1 if /Total: ([0-9.]+)/' <<< "$rms")
  if [ -z "$rms" ]; then
    rms="NA"
  else
    rms=$(printf "%.3f\n" $rms)
  fi
  if [ "$pscmpfailed" ]; then
    now=$(date "+%F %T")
    echo "${script_dir}/${ps}: RMS Error = $rms [FAIL]"
    echo "$now ${script_dir}/${ps}: RMS Error = $rms" >> "@CMAKE_CURRENT_BINARY_DIR@/fail_count.d"
    make_pdf "$ps" # try to make pdf file
    ((++ERROR))
  else
    test -z "$rms" && rms=NA
    echo "${script_dir}/${ps}: RMS Error = $rms [PASS]"
  fi
done
}

passfail () {
  test -f fail || return 0
  if [ -s fail ]; then
    now=$(date "+%F %T")
    echo "[FAIL]"
    echo "$now ${script_name}: $(wc -l fail) failed lines" >> "@CMAKE_CURRENT_BINARY_DIR@/fail_count.d"
    mv -f fail $1.log
    ((++ERROR))
  else
    echo "[PASS]"
  fi
}

# Make sure to cleanup at end
function cleanup()
{
  memtrack_err=0
  for log_file in gmt_memtrack_*.log; do
    test -f ${log_file} || continue
    n_err=$(perl -lne '$a++ if /(Memory not freed|^!)/; END {print $a+0}' ${log_file})
    (( memtrack_err += n_err )) || : # second assignment in case return code != 0
    test ${n_err} -eq 0 && rm -f ${log_file} # remove logs w/o errors
  done
  echo "memtrack errors: $memtrack_err" >&2

  valgrind_err=0
  if [ -n "${VALGRIND_ARGS}" ]; then
    for log_file in valgrind_*.log; do
      test -f ${log_file} || continue
      n_err=$(perl -ne 'print $1 if /ERROR SUMMARY: ([0-9]+)/' ${log_file})
      n_err=${n_err:-1} # if valgrind crashes itself, there is no ERROR SUMMARY
      (( valgrind_err += n_err )) || : # second assignment in case return code != 0
      test ${n_err} -eq 0 && rm -f ${log_file} # remove logs w/o errors
    done
    echo "valgrind errors: $valgrind_err" >&2
  fi

  cd "@CMAKE_CURRENT_BINARY_DIR@" # get out of exec_dir before removing it
  test "$ERROR" -eq 0 -a "$memtrack_err" -eq 0 -a "$valgrind_err" -eq 0 && rm -rf "$exec_dir"
  echo "exit status: $ERROR" >&2
  exit $ERROR
}

# Test the output image(s) and/or fail file before exiting
function on_exit()
{
  trap - EXIT # Restore EXIT trap
  pscmp
  passfail
  cleanup
}
trap on_exit EXIT

set -E # Shell functions and subshells need to inherit ERR trap

function on_err()
{
  trap - EXIT ERR SIGSEGV SIGTRAP SIGBUS # Restore trap
  ((++ERROR))
  echo "ERROR: ${1}:${2}" >&2 # Report error line
  cleanup
}
trap 'on_err "${BASH_SOURCE}" "${LINENO}"' ERR SIGSEGV SIGTRAP SIGBUS

# Create a temporary directory exec_dir in the build dir
# and run remainder of this GMT script there
exec_dir="@CMAKE_CURRENT_BINARY_DIR@/${script_name%.sh}"
rm -rf "$exec_dir"
mkdir -p "$exec_dir"
cd "$exec_dir"
ln -sf "$script" .

# Make a script to capture everything that can be run again
cat > gmtest.sh << EOF
export PATH="@GMT_BINARY_DIR@/src:\$PATH"
unset GMT5_SHAREDIR
export GMT_SHAREDIR="$GMT_SHAREDIR"
export GMT_USERDIR="$GMT_USERDIR"
export GMT_DATADIR="$src"
gmt set -Du
. "${script}"
EOF
chmod 755 gmtest.sh

# Start with proper GMT defaults
gmt set -Du

# Now run the original script
. "${script}"

# vim: ft=sh
