#!/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 [-cross target] [-resume test] [-run-only target] [-short] [-test-reg reg] [mlton flags ...]"
        exit 1
}

cross='false'
fail='false'
resume='true'
resumeTest='nil'
runOnly='false'
short='false'
testReg='false'
declare -a testRegs
declare -a flags
declare -a extraFlags
flags[${#flags[@]}]="-type-check"
flags[${#flags[@]}]="true"
while [ "$#" -gt 0 ]; do
        case "$1" in
        -cross)
                cross='true'
                shift
                if [ "$#" = 0 ]; then
                        usage
                fi
                crossTarget="$1"
                shift
                ;;
        -fail)
                fail='true'
                shift
                ;;
        -resume)
                resume='false'
                shift
                if [ "$#" = 0 ]; then
                        usage
                fi
                resumeTest="$1"
                shift
                ;;
        -run-only)
                runOnly='true'
                shift
                if [ "$#" = 0 ]; then
                        usage
                fi
                crossTarget="$1"
                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="$bin/mlton"
if $cross; then
	flags[${#flags[@]}]="-target"
	flags[${#flags[@]}]="$crossTarget"
	flags[${#flags[@]}]="-stop"
	flags[${#flags[@]}]="g"
fi
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:$src/bin/.:$PATH"

eval `"$lib/platform"`

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

"$mlton" -verbose 1 || echo 'no mlton present'
echo "flags = ${flags[*]}"

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" ) ||
                        true
        done
        exit 0
fi

forMinGW='false'
if [ `host-os` = mingw ]; then
	forMinGW='true'
fi
case $crossTarget in
*mingw)
	forMinGW='true'
;;
esac

for f in *.sml; do
        f=`basename "$f" .sml`
 	if ! ($resume); then
                if [ "$f" == "$resumeTest" ]; then
                        resume='true'
                else
                        continue
                fi
        fi
        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 `host-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"
        ;;
        esac
	if (! $runOnly); then
                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\"
                                \"nonexhaustiveMatch ignore\"
                                \"redundantMatch ignore\"
                        in $f.sml 
                        end" >"$mlb"
                "$mlton" "${flags[@]}" "${extraFlags[@]}" -output "$f" "$mlb"
                if [ "$?" -ne '0' ] || ((! $cross) && [ ! -x "$f" ]); then
                        compFail "$f"
                fi
                rm "$mlb"
	else
                case "$crossTarget" in
                *mingw)
                        libs='-lws2_32 -lkernel32 -lpsapi -lnetapi32'
                ;;
                *solaris)
                        libs='-lnsl -lsocket -lrt'
                ;;
                *)
                        libs=''
                ;;
                esac
                libs="-lmlton -lgmp $libs -lgdtoa -lm"
                # Must use $f.[0-9].[cS], not $f.*.[cS], because the
                # latter will include other files, e.g. for finalize,
                # it will also include finalize.2. This happens only 
		# when running cross regressions, which builds all the
		# C files and saves them in this directory.
		files="$f.[0-9].[cS]"
		if [ 0 -ne `ls $f.[0-9][0-9].[cS] 2>/dev/null | wc -l` ]; then 
			files="$files $f.[0-9][0-9].[cS]"
		fi
                gcc -o "$f" -w -O1                              \
                        -I "../build/lib/include"               \
			-I "../build/lib/$crossTarget/include"  \
                        -L"../build/lib/$crossTarget"           \
                        -L/usr/pkg/lib                          \
                        -L/usr/local/lib                        \
                        $files $libs
	fi
        if [ ! -r "$f".nonterm -a $cross = 'false' -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.$HOST_ARCH-$HOST_OS.ok"
                        if [ ! -r $compare ]; then
                                compare="$f.ok"
                        fi
			if $forMinGW; then
                                newcompare="$f.sed.ok"
                                sed $'s/$/\r/' <"$compare" > "$newcompare"
                                compare="$newcompare"
			fi
                        if ! diff "$compare" "$tmp"; then
                                echo "difference with ${flags[*]} ${extraFlags[*]}"
                        fi
                fi
        fi
done
if $cross || $runOnly || $short || $testReg ; then
        exit 0
fi
mmake clean >/dev/null
cd "$src/benchmark/tests"
for f in *.sml; do
        f=`basename "$f" .sml`
        tmpf="/tmp/$f.$$"
        case "$f" in
        fxp|hamlet)
                echo "skipping $f"
        ;;
        *)
                echo "testing $f"
                echo "val _ = Main.doit 0" | cat "$f.sml" - > "$tmpf.sml"
                $mlton -output "$tmpf" "${flags[@]}"            \
                        -default-ann 'nonexhaustiveMatch ignore'\
                        -default-ann 'redundantMatch ignore'    \
                        "$tmpf.sml"
                if [ $? -ne 0 ]; then
                        compFail "$f"
                fi
                rm -f "$tmpf" "$tmpf.sml"
        ;;
        esac
done 
mmake clean >/dev/null
cd "$src"
for f in mllex mlyacc mlprof; do
    tmpf="/tmp/$f.$$"
    cd "$src/$f"
    echo "testing $f"
    mmake -W "$f" >/dev/null
    "$mlton" "${flags[@]}" -output "$tmpf" "$f.mlb"
    if [ $? -ne 0 ]; then
        compFail "$f"
    fi
    rm -f "$tmpf"
done

rm -f "$tmp"
