#! /bin/sh
#
# The regression-test driver script.  This used to be explicit in the
# makefile before we regrouped the regression tests by stable and unstable
# drivers.
#
# This file is Copyright by the GPSD project
# SPDX-License-Identifier: BSD-2-clause

regressor () {
    # common clause to build regressions to a pipe
    case $testing in
        daemon) ${PYTHON} ${PYTHON_COVERAGE} "${GPSD_HOME}/gpsfake" -s 38400 -1 -p $opts ${f} | ${GPSFILTER} || return 1 ;;
        clientlib) "${GPSD_HOME}/tests/test_libgps" -b <${f} || return 1 ;;
    esac
    return 0
}

# Without GNU date extensions, %N won't expand and we only get 1sec precision
starttime=`date +"%s  * 1000000000 + %N" | sed '/+ N/s///' 2>/dev/null`

# We need to have the build directory in $GPSD_HOME to find the new gpsd
if [ "`dirname $0`" = "." ]; then
    GPSD_HOME=`pwd`
else
    GPSD_HOME=`dirname "$0"`
fi

# sed may choke on binary data with some LANG settings
unset LANG

# Arrange to call a gpsfake in the source directory without fuss.
if [ -z ${PYTHON} ]; then
    PYTHON="python"
fi
# Define the directories we ask setup.py to install the
# modules/extensions and scripts to. The way chosen here reproduces
# the internal behaviour of distutils. Unfortunately distutils does
# not export the pre-defined/configured directories, so we have to
# define them on our own. For default installations of distutils the
# chosen values here will match what distutils uses.
#
# distutils loudly deprecated in 3.10, use sysconfig from 3.2
export PROG='
import sys
try:
 import sysconfig
 n = sysconfig.get_platform()
except:
 import distutils.util
 n = distutils.util.get_platform()
print("lib.%s-%s" %(n, sys.version[0:3]))
'
py_libdir=${GPSD_HOME}/build/`${PYTHON} -c "${PROG}"`

py_scriptdir=${GPSD_HOME}/build/`${PYTHON} -c 'import sys; print("scripts-%s" %(sys.version[0:3], ))'`
if [ -d "${py_libdir}" ] && [ -d "${py_scriptdir}" ]; then
    PYTHONPATH=${py_libdir}
    export PYTHONPATH

    PATH=${py_scriptdir}:${PATH}
fi
export GPSD_HOME PATH

# Use a non-default value of the SHM key to avoid colliding with
# production instances. Value must be legal for strtol(3);
# this is the default key plus one.
export GPSD_SHM_KEY=0x47505345

baton=false
color=""
help="0"
logfile=""
mode=regress
opts=""
quiet=false
quieter=false
testing=daemon
while getopts bcChl:o:qQsStuv opt
do
    case $opt in
        b) mode=build ;;                # Rebuild regression check files
        c) testing=clientlib ;;         # 'client' rather than the daemon
        C) color="--color=always" ;;    # colorize diff
        h) help="1" ;;
        l) logfile=$OPTARG ;;           # Logfile to save diffs to
        o) opts="$opts $OPTARG" ;;      # Pass options to gpsfake
        q) quiet=true ;;                # Suppress header/trailer messages
        Q) quiet=true; quieter=true ;;  # Suppress all success chatter
        s) mode=regress ;;              # Run regression tests
        S) mode=slowregress ;;          # Run regression tests with big delays
        t) baton=true mode=regress ;;   # Run regression tests w/baton
        u) opts="$opts -u" ;;           # Force UDP
        v) mode=view ;;                 # View result of generating check file
    esac
done
shift $(($OPTIND - 1))

if [ $help -eq "1" ]
then
        echo
        echo
        echo "Regression test driver script"
        echo "-b             - Rebuild regression check files"
        echo "-c             - test with 'client' rather than the daemon"
        echo "-C             - colorize diffs"
        echo "-h             - this help"
        echo "-l <filename>  - where to log diffs to"
        echo "-o <opt>       - Pass options to gpsfake"
        echo "-q             - Suppress header/trailer messages"
        echo "-Q             - Suppress all success chatter"
        echo "-s             - run regression tests"
        echo "-S             - run tests with realistic timing delays"
        echo "-t <ots>       - run regression tests w/baton"
        echo "-u             - Force UDP"
        echo "-v             - view result of generating a check file"
        echo

        exit 0
fi

# Enables us to turn debugging up high without screwing up the diff checks
# First and Second filter out gpsd log messages.
# Third filters out gps.py verbose logging
# Fourth filters out WATCH responses
# Fifth filters out DEVICE responses
# Sixth filters out VERSION responses
# Seventh filters out device fields
GPSFILTER="sed -e /^gpsd:/d -e /^gpsfake/d -e /GPS-DATA/d -e /WATCH/d -e /DEVICE/d -e /VERSION/d -e s/,\"device\":[^,}]*//"

# Use ALTFILTER to set up custom filtering when a regression test fails
# This example filters out altitude and some fields computed by gpsd's error
# modeling - these are fragile in the presence of changes to the fix-buffering
# logic. Note that as the last attribute mode needs to be handled differently.
#ALTFILTER="-e s/\"alt\":[^,]*,// -e s/\"ep[vhs]\":[-+0-9.]*// -e s/,\"mode\":[^}]*//"

TMP=`mktemp -d -t gpsd-test-XXXXXXXXXXXXXX`

# Only twirl the baton on a tty, avoids junk in transcripts.
if [ -t 0 ]
then
    baton=false
fi
if [ $baton = true ]
then
    opts="$opts -b"
fi

if [ $mode = slowregress ]
then
    opts="$opts -S"
fi

if [ $quieter = true ]
then
    opts="$opts -q"
fi

case $mode in
    regress|slowregress)
        [ $quiet = true ] || echo "Testing the $testing..." >&2
        errors=0; total=0; notfound=0;error_list="";
        for f in $*; do
            if [ -r "$f.chk" ]
            then
                trap 'rm -f "${TMP}/test-$$.chk"; exit $errors' EXIT HUP INT TERM
                regressor ${f} > "${TMP}/test-$$.chk"
                if [ "${ALTFILTER}" ]
                then
                    trap 'rm -f "${TMP}/test-$$.chk" "${TMP}/testout-$$.chk" "${TMP}/testin-$$.chk" "${TMP}/diff-$$"; exit $errors' EXIT HUP INT TERM
                    sed -n < "${f}.chk" > "${TMP}/testin-$$.chk" ${ALTFILTER} -e 'p';
                    sed -n < "${TMP}/test-$$.chk " > "${TMP}/testout-$$.chk" ${ALTFILTER} -e 'p';
                    diff -ub ${color} "${TMP}/testin-$$.chk" "${TMP}/testout-$$.chk" > "${TMP}/diff-$$";
                else
                    diff -ub ${color} "${f}.chk" "${TMP}/test-$$.chk" > "${TMP}/diff-$$";
                fi
                if test -s "${TMP}/diff-$$" ; then
                    errors=`expr $errors + 1`;
                    error_list="$error_list \"${f}\""
                    if ! test -s "${TMP}/test-$$.chk" ; then
                        echo " Output missing for ${f}" > "${TMP}/diff-$$"
                    fi
                    if [ -z "$logfile" ]
                    then
                        cat "${TMP}/diff-$$"
                    else
                        cat "${TMP}/diff-$$" >> "$logfile"
                    fi
                fi;
                rm -f "${TMP}/test-$$.chk" "${TMP}/testout-$$.chk" "${TMP}/testin-$$.chk" "${TMP}/diff-$$"
            else
                echo "*** No check log $f.chk exists"
                notfound=`expr $notfound + 1`;
                # Not Found is an Error.
                errors=`expr $errors + 1`;
            fi
            total=`expr $total + 1`;
        done;
        if test $errors -gt 0; then
            echo "Regression test FAILED: $errors errors in $total tests total ($notfound not found).";
            echo "The following test Failed:"
            echo "================================================================"
            for err in $error_list
            do
                echo $err
            done
            echo "================================================================"
            status=1;
        else
            if [ $quiet = true ] && [ $total -eq 1 ] && [ $notfound -eq 0 ]
            then
                [ $quieter = true ] || echo "Regression test $1 successful"
            else
                echo "Regression test successful: no errors in $total tests ($notfound not found).";
            fi
            status=0;
        fi
        ;;
    build)
        [ $quiet = true ] || echo "Rebuilding $testing regressions..." >&2
        for f in $*; do
            regressor ${f} >${f}.chk
        done
        status=0
        ;;
    view)
        [ $quiet = true ] || echo "Viewing..." >&2
        for f in $*; do
            regressor ${f}
        done
        status=0
        ;;
esac

# See starttime above
endtime=`date +"%s * 1000000000 + %N" | sed '/+ N/s///' 2>/dev/null`

if [ $quiet = false ] && [ "$starttime" -a "$endtime" ]
then
    # Avoid using -n here since some shells don't support it.
    echo "Elapsed time:" \
    $(echo "scale=2; (${endtime} - ${starttime}) / 1000000000" | bc) >&2
fi

rm -fr ${TMP}
exit $status
# vim: set expandtab shiftwidth=4
