#!/bin/bash
#
# $Id: gmtest.in 12688 2013-12-29 18:55:29Z pwessel $
#
# Bash script inclusion to test all GMT examples.
# It sets the environment such that the examples are created as in the manual.

test -z "$1" && exit 1

# Where the current script resides (need absolute path)
script_name="$1"
script_dir=$(dirname "${script_name}")
script="@GMT_SOURCE_DIR@/doc/examples/${script_name}"
if ! [ -x "${script}" ]; then
  echo "error: cannot execute script ${script}." >&2
  exit 1
fi

shift

# 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

# Temporary change LANG to C
LANG=C

# Define variables that are needed *within* test scripts
GMT_SOURCE_DIR="@GMT_SOURCE_DIR@"
GRAPHICSMAGICK="@GRAPHICSMAGICK@"
src="@GMT_SOURCE_DIR@/doc/examples/${script_dir}"

# Font lookup path for Ghostscript (invoked from gm compare and ps2raster)
export GS_FONTPATH="@CMAKE_CURRENT_SOURCE_DIR@/ex31/fonts"

# 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 PATH="@GMT_SOURCE_DIR@/src:$PATH" # for gmt_shell_functions.sh

# Reset error count
ERROR=0

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

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

# export function definitions to subshells
export -f gmt gmtlogo

# 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.
pscmp () {
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}/${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 "${ps}: RMS Error = $rms [FAIL]"
    echo "$now ${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 "${ps}: RMS Error = $rms [PASS]"
  fi
done
}

# 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 before exiting
function on_exit()
{
  trap - EXIT # Restore EXIT trap
  pscmp
  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
# Then copy all of its contents (except files excluded by GLOBIGNORE)
# Run remainder of this GMT script there
exec_dir="@CMAKE_CURRENT_BINARY_DIR@/${script_dir}"
rm -rf "$exec_dir"
mkdir -p "$exec_dir"
cd "$exec_dir"
GLOBIGNORE="*.bat:*.ps:*.sh"
for file in "$src"/* ; do
  test -f "$file" && ln -s "$file" .
done
unset GLOBIGNORE

# Start with proper GMT defaults
gmt set -Du FORMAT_TIME_STAMP "Version 5"

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

# vim: ft=sh
