#!/usr/bin/env bash

# This script runs the regression tests in src/regression.
# It also compiles the tests in benchmark/tests

# set -e

name=`basename "$0"`

usage () {
  echo >&2 "usage: $name [-fail] [-short] [-test-reg reg] [mlton flags ...]"
  exit 1
}

fail='false'
short='false'
testReg='false'
exitFail=false
declare -a testRegs
declare -a flags
declare -a extraFlags
flags[${#flags[@]}]="-type-check"
flags[${#flags[@]}]="true"
while [ "$#" -gt 0 ]; do
  case "$1" in
  -fail)
          fail='true'
          shift
          ;;
  -short)
          short='true'
          shift
          ;;
  -test-reg)
          testReg='true'
          shift
          if [ "$#" = 0 ]; then
                  usage
          fi
          testRegs[${#testRegs[@]}]="$1"
          shift
          ;;
  *)
          flags[${#flags[@]}]="$1"
          shift
          ;;
  esac
done


dir=`dirname "$0"`
src=`cd "$dir/.." && pwd`
bin="$src/build/bin"
lib="$src/build/lib/mlton"
mlton="$bin/mlton"
cont='callcc.sml callcc2.sml callcc3.sml once.sml'
flatArray='finalize.sml flat-array.sml flat-array.2.sml'
intInf='conv.sml conv2.sml fixed-integer.sml harmonic.sml int-inf.*.sml slow.sml slower.sml smith-normal-form.sml'
signal='finalize.sml signals.sml signals2.sml signals3.sml signals4.sml suspend.sml weak.sml'
thread='thread0.sml thread1.sml thread2.sml mutex.sml prodcons.sml same-fringe.sml timeout.sml'
world='world1.sml world2.sml world3.sml world4.sml world5.sml world6.sml'
tmp=/tmp/z.regression.$$
PATH="$bin:$PATH"

# whitelist tests that are known to fail (will still run but exit cleanly)
declare -A whitelisted
if [ -a  $src/regression/whitelist ] ; then
  while read f ; do
    echo "whitelisting $f..."
    whitelisted["$f"]=1
  done <$src/regression/whitelist
fi

isWhitelisted () {
  local f=$1
  if [[ ${whitelisted["$f"]} ]] ; then
    echo 1
  else
    echo 0
  fi
}

compFail () {
        echo "compilation of $f failed with ${flags[*]}"
}

"$mlton" -verbose 1 || (echo 'no mlton present' && exitFail=true)
echo "flags = ${flags[*]}"

TARGET_ARCH=`"$mlton" -show path-map | sed -n 's/TARGET_ARCH \(.*\)/\1/p'`
TARGET_OS=`"$mlton" -show path-map | sed -n 's/TARGET_OS \(.*\)/\1/p'`
OBJPTR_REP=`"$mlton" -show path-map | sed -n 's/OBJPTR_REP \(.*\)/\1/p'`
ALIGN=`echo "${flags[@]}" | sed -n 's/.*-align \(.\).*/\1/p'`
if [ -z "$ALIGN" ]; then
    ALIGN=`"$mlton" -z 2>&1 | sed -n 's/.*-align {\(.\).*/\1/p'`
fi

cd "$src/regression"

if $fail; then
        for f in fail/*.sml; do
                echo "testing $f"
                ( "$mlton" "${flags[@]}" -stop tc "$f" >/dev/null 2>&1 &&
                        echo "compilation of $f should have failed but did not" && ignore=$(isWhitelisted $f) && if [ "$ignore" -eq 0 ] ; then exitFail=true ; fi ) ||
                        true
        done

        if [ "$exitFail" = true ] ; then
          exit 1
        else
          exit 0
        fi
fi

forMinGW='false'
if [ $TARGET_OS = mingw ]; then
	forMinGW='true'
fi

for f in *.sml; do
        f=`basename "$f" .sml`
        if ($testReg); then
                skip='true'
                for (( i = 0 ; $i < ${#testRegs[@]} ; i++ )); do
                        if [ "$f" = "${testRegs[$i]}" ]; then
                                skip='false'
                        fi
                done
                if ($skip); then
                        continue
                fi
        fi
        case $TARGET_OS in
        cygwin)
                case "$f" in
                textio.2)
                        continue
                ;;
                esac
        ;;
        hurd)
                # Work-around hurd bug (http://bugs.debian.org/551470)
                case "$f" in
                mutex|prodcons|signals|signals2|signals3|signals4|suspend|thread2|timeout|world5)
                        continue
                ;;
                esac
        ;;
        mingw)
                case "$f" in
                cmdline|command-line|echo|filesys|posix-exit|signals|signals2|signals3|signals4|socket|suspend|textio.2|unixpath|world*)
                        continue
                ;;
                esac
        ;;
        esac
        case "$f" in
        serialize)
                continue
        ;;
	      esac
        echo "testing $f"
        unset extraFlags
        case "$f" in
        exn-history*)
                extraFlags[${#extraFlags[@]}]="-const"
                extraFlags[${#extraFlags[@]}]="Exn.keepHistory true"
        ;;
        gc-collect2)
                extraFlags[${#extraFlags[@]}]="-runtime"
                extraFlags[${#extraFlags[@]}]="mark-compact-ratio 1.001 copy-ratio 1.001 live-ratio 1.001"
        ;;
        world*)
                extraFlags[${#extraFlags[@]}]="-link-opt"
                extraFlags[${#extraFlags[@]}]="-no-pie"
        ;;
        esac

        mlb="$f.mlb"
        echo "\$(SML_LIB)/basis/basis.mlb
                \$(SML_LIB)/basis/mlton.mlb
                \$(SML_LIB)/basis/sml-nj.mlb
                ann 
                        \"allowFFI true\"
                        \"allowOverload true\"
                        \"allowExtendedTextConsts true\"
                        \"nonexhaustiveBind ignore\"
                        \"nonexhaustiveMatch ignore\"
                        \"redundantBind ignore\"
                        \"redundantMatch ignore\"
                in $f.sml 
                end" >"$mlb"
        "$mlton" "${flags[@]}" "${extraFlags[@]}" -output "$f" "$mlb"
        if [ "$?" -ne '0' ] || [ ! -x "$f" ]; then
                compFail "$f"
                exitFail=true
        fi
        rm "$mlb"

        if [ ! -r "$f".nonterm -a -x "$f" ]; then
                nonZeroMsg='Nonzero exit status.'
                if $forMinGW; then
                        nonZeroMsg="$nonZeroMsg"'\r'
                fi
                ( "./$f" || echo -e "$nonZeroMsg" ) >$tmp 2>&1 
                if [ -r "$f.ok" ]; then
                        compare="$f.ok"
                        for x in "$OBJPTR_REP" "${OBJPTR_REP}a$ALIGN" "$TARGET_OS" "$TARGET_ARCH" "$TARGET_ARCH-$TARGET_OS"; do
                                if [ -r "$f.$x.ok" ]; then
                                        compare="$f.$x.ok"
                                fi
                        done
                        if $forMinGW; then
                                newcompare="$f.sed.ok"
                                sed $'s/$/\r/' <"$compare" > "$newcompare"
                                compare="$newcompare"
                        fi
                        if ! diff "$compare" "$tmp"; then
                                echo "$f: difference with ${flags[*]} ${extraFlags[*]}"
                                ignore=$(isWhitelisted $f)
                                if [ "$ignore" -eq 0 ] ; then
                                  exitFail=true
                                fi
                        fi
                fi
        fi
done

if $short || $testReg ; then
  if [ "$exitFail" = true ] ; then
    exit 1
  else
    exit 0
  fi
fi

"$src/bin/mmake" clean >/dev/null
cd "$src/benchmark/tests"
for f in *.sml; do
        f=`basename "$f" .sml`
        tmpf="/tmp/$f.$$"
        case "$f" in
        fxp)
                echo "skipping $f"
        ;;
        *)
                echo "testing $f"
                echo "val _ = Main.doit 0" | cat "$f.sml" - > "$tmpf.sml"
                $mlton -output "$tmpf" "${flags[@]}"            \
                        -default-ann 'nonexhaustiveBind ignore'\
                        -default-ann 'nonexhaustiveMatch ignore'\
                        -default-ann 'redundantBind ignore'    \
                        -default-ann 'redundantMatch ignore'    \
                        "$tmpf.sml"
                if [ $? -ne 0 ]; then
                        compFail "$f"
                        exitFail=true
                fi
                rm -f "$tmpf" "$tmpf.sml"
        ;;
        esac
done
"$src/bin/mmake" clean >/dev/null
cd "$src"
for f in mllex mlyacc mlprof; do
    tmpf="/tmp/$f.$$"
    cd "$src/$f"
    echo "testing $f"
    "$src/bin/mmake" -W "$f" >/dev/null
    "$mlton" "${flags[@]}" -output "$tmpf" "$f.mlb"
    if [ $? -ne 0 ]; then
        compFail "$f"
        exitFail=true
    fi
    rm -f "$tmpf"
done

rm -f "$tmp"

if [ "$exitFail" = true ] ; then
  exit 1
else
  exit 0
fi
