#!/bin/bash

### Usage info
function show_help {
cat << EOF
Usage: ${0##*/} [-hvl] [-e/d TEST] [TEST]...
Without arguments the script runs all enabled tests.
When a test name is given then run this test.

-h         Display this help and exit
-e/d TEST  Enable/Diasble the specified test.
-l         List all tests and their status.
-r         Run all not enabled tests.
-v         Verbose mode. Can be used multiple times for increased
           verbotisty.
EOF
}

### Function for reading in-/output files
function readtest {
  unset STDIN
  unset STDOUT
  unset STDERR

  FILE=$1
  i=0
  # 0=STDIN, 1=STDOUT, 2=STDERR
  mode=0

  while read -r line; do
    if [ "$line" = "#" ]; then
      # Next test case OR the stdout/stderr section of a test case.
      if [ $mode -gt 0 ]; then
        # Next test case.
        i=$(($i + 1))
        mode=0
        continue
      fi

      # Else $mode is 0. This means we are now reading the
      # stdout/stderr section of a test case. It consists
      # of two sections (for stdout and stderr), delimited
      # by a line containg a single percent symbol (%). The second
      # section (for stderr) and its leading "percent symbol line"
      # are optional for backward compatibility.
      mode=1
      continue
    elif [ "$line" = "%" ]; then
      # Now comes the stderr section.
      mode=2
      continue
    fi

    # Else this is a normal input/output line.
    case "$mode" in
      0)
        STDIN[$i]="${STDIN[$i]}${line}"
        ;;
      1)
        STDOUT[$i]="${STDOUT[$i]}${line}"
        ;;
      2)
        STDERR[$i]="${STDERR[$i]}${line}"
        ;;
    esac
   done < "$FILE"

   UNIT_TESTCASES=$(($i + 1))
}

### Function to get the correct test name for a file.
function get_name {
  filename="${1##*/}"
  filename="${filename%%.*}"
  echo "$filename"
}

### Get the filename to a given test name
function get_filename {
  name="$1"
  echo "$TESTDIR/$name.rail"
}

### Function to run a single test
function run_one {
  dontrun=false
  filename=$(get_name "$1")

  if [ -f "$TESTDIR/$filename$EXT" ]
    then
      readtest "$TESTDIR/$filename$EXT"
    else
      fail=$(($fail + 1))
      echo -e "`$red`ERROR`$NC` testing: \"$filename.rail\". $EXT-file is missing."
      return
  fi

  errormsg=$(dist/build/RailCompiler/RailCompiler -c -i "$1" -o "$TMPDIR/$filename.ll" 2>&1) \
    && llvm-link "$TMPDIR/$filename.ll" src/RailCompiler/stack.ll > "$TMPDIR/$filename" \
    && chmod +x "$TMPDIR/$filename" || {
      TOTAL_TESTCASES=$(($TOTAL_TESTCASES + 1))

      # Check STDOUT first for backward compatibility.
      if [[ "$errormsg" == "${STDOUT[0]}" || "$errormsg" == "${STDERR[0]}" ]]; then
        [ $verbose -gt 0 ] && echo -en "`$green`Passed`$NC` expected fail \"$filename.rail\"."
	if [ $verbose -gt 1 ]; then
          echo "  The error message was: \"$errormsg\""
        else
          [ $verbose -gt 0 ] && echo -ne "\n"
        fi
      else
        fail=$(($fail + 1))
        echo -e "`$red`ERROR`$NC` compiling/linking \"$filename.rail\" with error: \"$errormsg\""
      fi

      return
  }

  # Create temporary files for stdout and stderr.
  stdoutfile=$(mktemp --tmpdir="$TMPDIR" swp14_ci_stdout.XXXXX)
  if [ $? -gt 0 ]; then
    echo -e "`$red`ERROR`$NC` testing: \"$filename.rail\". Could not create temporary file for stdout."
    fail=$(($fail + 1))
    return
  fi

  stderrfile=$(mktemp --tmpdir="$TMPDIR" swp14_ci_stderr.XXXXX)
  if [ $? -gt 0 ]; then
    echo -e "`$red`ERROR`$NC` testing: \"$filename.rail\". Could not create temporary file for stderr."
    fail=$(($fail + 1))
    return
  fi

  for i in $(seq 0 $(($UNIT_TESTCASES - 1))); do
    TOTAL_TESTCASES=$(($TOTAL_TESTCASES + 1))

    # Execute the test!
    echo -ne "${STDIN[$i]}" | do_lli "$TMPDIR/$filename" 1>"$stdoutfile" 2>"$stderrfile"

    # Read stdout and stderr, while converting all actual newlines to \n.
    # Really ugly: bash command substitution eats trailing newlines so we
    # need to add a terminating character and then remove it again.
    stdout=$(cat "$stdoutfile"; echo x)
    stdout=${stdout%x}
    stdout=${stdout//$'\n'/\\n}

    stderr=$(cat "$stderrfile"; echo x)
    stderr=${stderr%x}
    stderr=${stderr//$'\n'/\\n}

    if [[ "$stdout" == "${STDOUT[$i]}" && "$stderr" == "${STDERR[$i]}" ]]; then
      [ $verbose -gt 0 ] && echo -n "`$green`Passed`$NC` \"$filename.rail\" with input \"${STDIN[$i]}\""
	if [ $verbose -gt 1 ]; then
          echo "  Got output: \"$stdout\". Stderr: \"$stderr\"."
        else
          [ $verbose -gt 0 ] && echo -ne "\n"
        fi
    else
      fail=$(($fail + 1))
      echo "`$red`ERROR`$NC` testing \"$filename.rail\" with input \"${STDIN[$i]}\"!" \
        "Expected \"${STDOUT[$i]}\" on stdin, got \"$stdout\";" \
        "expected \"${STDERR[$i]}\" on stderr, got \"$stderr\"."
    fi
  done
}

### Function to compile and run all .rail files
function run_all {
  for f in "$TESTDIR"/*.rail; do
    if [ "$reverse" = true ]; then
      if [ ! -f "$TESTDIR/run/$(get_name "$f").rail" ]; then 
        run_one "$f"
      fi
    else
      run_one "$f"
    fi
  done
}

### Function to correctly call the LLVM interpreter
function do_lli {
  # On some platforms, the LLVM IR interpreter is not called "lli", but
  # something like "lli-x.y", where x.y is the LLVM version -- there may be
  # multiple such binaries for different LLVM versions.
  # Instead of trying to find the right version, we currently assume that
  # such platforms use binfmt_misc to execute LLVM IR files directly (e. g. Ubuntu).
  if command -v lli >/dev/null; then
      lli "$@"
  else
      "$@"
  fi
}


### Directory magic, so our cwd is the project home directory.
OLDDIR=$(pwd)
unset CDPATH
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
cd "$DIR/.."

### Define Terminal Colours
red="eval tput setaf 1; tput bold"
green="eval tput setaf 2; tput bold"
NC="tput sgr 0" # No Color

### Parse commandline options.
verbose=0
test=""
enable=""
disable=""

OPTIND=1
while getopts "hvlre:d:" opt; do
  case "$opt" in
    h)
      show_help
      exit 0
      ;;
    v)
      verbose=$(($verbose + 1))
      ;;
    l)
      list=true
      ;;
    r)
      reverse=true
      ;;
    e)
      enable=$OPTARG
      ;;
    d)
      disable=$OPTARG
      ;;
    '?')
      show_help >&2
      exit 1
      ;;
  esac
done
shift "$((OPTIND-1))" # Shift off the options and optional --.
test="$1"

### Checking for incompatible options.
count=0
[[ -n $list ]] && count=$(($count + 1))
[[ -n "$disable" ]] && count=$(($count + 1))
[[ -n "$enable" ]] && count=$(($count + 1))
if (( $count > 1 )); then
  echo "Only specify one of -l, -e, -d."
  exit 1
fi

### Main function.
TOTAL_TESTCASES=0

if [ "$reverse" = true ]; then
  TESTDIR="integration-tests"
else
  TESTDIR="integration-tests/run"
fi
EXT=".io"
if [ -n "$disable" ];then
  rm "$TESTDIR"/"$disable".{rail,io}
  exit 0
fi
if [ -n "$enable" ];then
  ln -s -t "$TESTDIR" ../$enable.{rail,io}
  exit 0
fi
if [ -n "$list" ]; then
  echo -ne "`$green`Tests to run:`$NC`\n\n"
  for file in "$TESTDIR"/*.rail;do
    echo $(get_name $file)
  done
  echo -ne "\n\n`$red`Disabled tests:`$NC`\n\n"
  for file in "$TESTDIR"/../*.rail;do
    if [ ! -f "$TESTDIR"/`basename "$file"` ];then
      echo $(get_name $file)
    fi
  done
  exit 0
fi

TMPDIR=tests/tmp
mkdir -p $TMPDIR
fail=0
if [ -n "$test" ];then
  if [ "${test##*.}" == "rail" ]; then
    # Set the TESTDIR to the directory the .rail file is in.
    test="$OLDDIR"/"$test"
    TESTDIR=${test%/*}
  else
    TESTDIR="integration-tests"
    test=$(get_filename "$test") # Find the path to the specified test
  fi
  if [ -f "$test" ]; then
    run_one "$test"
  else
    echo "`$red`ERROR:`$NC` Test $test not found."
  fi
else
  run_all
fi
rm -r tests/tmp

echo
echo "RAN $TOTAL_TESTCASES TESTCASES IN TOTAL."


if [ ! $fail -eq 0 ];then
  echo "`$red`FAILED`$NC` $fail test cases."
  exit 1
fi
echo "All testcases `$green`PASSED`$NC`."

### DEBUGGING:
function debugprint {
echo "STDIN"
for e in "${STDIN[@]}";do
  echo "$e"
done

echo "STDOUT"
for e in "${STDOUT[@]}";do
  echo "$e"
done

echo "STDERR"
for e in "${STDERR[@]}";do
  echo "$e"
done
}

#debugprint


# vim:ts=2 sw=2 et