#!/bin/bash
#
# Usage:  ./test-cg.sh [-v] dir
#
#    where dir is the directory that contains the files to use.
#          [-v] is optional flag to turn on verbose output
#
# Each test file must have a .expected file with the compiler
# output and the output when run.  Example:
#
#   Success.
#   3
#
# for a program that compiles and prints 3 when run.
#
# A "bad" program (expected first line "Failed.") passes when the
# compiler rejects it; no code is assembled or run for it.
#
# This script expects the compiler jar built by `sbt assembly` to be
# at target/icc.jar, and rebuilds it if it is missing.
#
# Exits with status 0 if all tests pass, nonzero otherwise.

VERBOSE="off"
if [ "$1" == "-v" ]; then
    VERBOSE="on"
    shift
fi

DIR=$1

# Build the compiler jar once up front; starting sbt for every test
# file would be far too slow.
JAR=target/icc.jar
if [ ! -f "$JAR" ]; then
    echo "Building $JAR with sbt assembly..."
    sbt -batch assembly || exit 1
fi

PASSED=0
FAILED=0

# Iterate over all .ic files in DIR or its subdirectories
while IFS= read -r f; do

    echo "$f"

    # Step 1: Run the Compiler, catching stdout and stderr

    if [ "$VERBOSE" = "on" ]; then
	echo "   Compiling $f ..."
    fi

    java -jar "$JAR" "$f" > "$f.output" 2>&1

    # Compare the last line of the output to the first line of expected.
    # The lines should be either "Success." or "Failed."
    EXPECT=`head -1 "$f.expected"`
    OUTPUT=`tail -1 "$f.output"`

    if [ "$OUTPUT" != "$EXPECT" ]; then
	echo "  Compilation Failed."

	let FAILED=FAILED+1

	echo "    Expected: $EXPECT"
	echo "    Got:      $OUTPUT"

	if [ "$VERBOSE" = "on" ]; then
   	    # Dump $f too, indenting each line with some spaces.
	    echo "    Code:"
	    sed -e "s/^/        /g" "$f"
	    echo ""

	    # Dump full output
	    echo "    Full Output:"
	    sed -e "s/^/        /g" "$f.output"

	    echo ""
	fi

    elif [ "$OUTPUT" != "Success." ]; then

	# Correctly-rejected bad program: counts as a pass.
	if [ "$VERBOSE" = "on" ]; then
	    echo "   Bad program correctly rejected --- skipping rest"
	fi
	let PASSED=PASSED+1

    else

	# Step 2: Compile the .s file

	BASE_NAME="${f%.*}"

	if [ "$VERBOSE" = "on" ]; then
	    echo "   gcc ${BASE_NAME}.s ..."
	fi

	if ! gcc -m64 -g -o "${BASE_NAME}.exe" "${BASE_NAME}.s" libic64.a 2> "$f.gcc-errors"; then
	    echo "  Assembly Failed (gcc)."
	    let FAILED=FAILED+1
	    sed -e "s/^/        /g" "$f.gcc-errors" | head -8
	    continue
	fi

	# Step 3: Run the binary

	if [ "$VERBOSE" = "on" ]; then
	    echo "   running ${BASE_NAME}.exe ..."
	fi

	case "$BASE_NAME" in
	    /*) EXE="${BASE_NAME}.exe" ;;
	    *)  EXE="./${BASE_NAME}.exe" ;;
	esac
	"$EXE" > "$f.result" 2>&1

	# Compare output to expected output
	EXPECT=`tail -n +2 "$f.expected"`
	RESULT=`cat "$f.result"`

	# Report failure if we see the wrong result
	if [ "$EXPECT" != "$RESULT" ]; then
	    echo "  Test Run Failed."

	    let FAILED=FAILED+1

	    echo "    Expected: $EXPECT"
	    echo "    Got:      $RESULT"

	    if [ "$VERBOSE" = "on" ]; then
   		# Dump $f too, indenting each line with some spaces.
		echo "    Code:"
		sed -e "s/^/        /g" "$f"

		# Dump full output
		echo "    Full Result:"
		sed -e "s/^/        /g" "$f.result"

		echo ""
	    fi
	else
	    let PASSED=PASSED+1
	fi
    fi
done < <(find "$DIR" -name "*.ic")

echo "# PASSED: $PASSED"
echo "# FAILED: $FAILED"

if [ "$FAILED" -ne 0 ]; then
    exit 1
fi
exit 0
