#! /bin/bash
#
# @file         peter-js
#               A simple test framework in the spirit of Peter (npmjs.com/packages/peter) for testing
#               basic PythonMonkey functionality.
#
#               Exit Codes:
#               0 - all tests passed
#               1 - one or more tests failed
#               2 - no tests ran
#               3 - internal error
#
# @author       Wes Garland, wes@distributive.network
# @date         Jun 2023
#
runDir=`pwd`
cd `dirname "$0"`
topDir=`pwd`
cd "$runDir"

set -u
set -o pipefail
peter_exit_code=2
defaultTimeout="${defaultTimeout:-15}"

if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
cat <<EOF
Peter-jr - A simple test framework in the spirit of Peter for testing basic
           PythonMonkey functionality. (Peter: npmjs.com/packages/peter)
Copyright (c) 2023 Distributive. Released under the terms of the MIT License.

Usage: $0 [-h] [-v] [test [test...]]
Where: -h or --help shows this help
       -v enables verbose mode (shows tests' stdout)
       test is the name of a test or a directory containing tests. If not
       specified, uses ${topDir}/tests/js.
EOF
  exit 0
fi
if [ "${1:-}" = "-v" ]; then
  VERBOSE=1
  shift
else
  VERBOSE="${VERBOSE:-}"
fi

[ "${1:-}" ] || set "${topDir}/tests/js"
testLocations=("$@")

export PMJS="pmjs"

function panic()
{
  echo "PANIC: $*" >&2
  exit 3
}

TMP=`mktemp -d`
([ "${TMP}" ] && [ -d "${TMP}" ]) || exit 3

trap "rm -r \"${TMP}\"" EXIT

if [ "$VERBOSE" ]; then
  stdout="/dev/stdout"
  stderr="/dev/stderr"
else
  stdout="$TMP/stdout"
  stderr="$TMP/stderr"
fi

red()
{
  printf "\e[0;31m%s\e[0m" "$*"
}

green()
{
  printf "\e[0;32m%s\e[0m" "$*"
}

dred()
{
  printf "\e[22m\e[0;31m%s\e[0m" "$*"
}

bggreen()
{
  printf "\e[42m%s\e[0m" "$*"
}

yellow()
{
  printf "\e[0;33m%s\e[0m" "$*"
}

grey()
{
  printf "\e[0;90m%s\e[0m" "$*"
}

if ! realpath --relative-to=. . >/dev/null 2>&1; then 
  realpath()
  {
    local remove
    if [[ "$1" == --relative-to=* ]]; then
      remove="${1:14}/"
      shift 1
    else
      remove="$"
    fi
    local filename="$1"
    local dirname=`dirname "$1"`
    local basename=`basename "$1"`
    (
      [ "$dirname" ] || dirname="."
      cd "$dirname" && echo "`pwd -P`/$basename"
    ) | sed -e "s;${remove};;g"
  }
fi

findTests()
{
  for loc in "${testLocations[@]}"
  do
    find $(realpath "$loc") -type f -name \*.simple
    find $(realpath "$loc") -type f -name \*.bash
    find $(realpath "$loc") -type f -name \*.failing
  done\
  | while read file
    do
      realpath --relative-to="${runDir}" "${file}"
    done
}

if wc -L </dev/null >/dev/null 2>&1; then
  longestFilenameLen=`findTests | wc -L`
else
  longestFilenameLen=`findTests | awk '{print length}' | sort -rn | head -1`
fi

findTests \
| (shuf 2>/dev/null || cat) \
| while read file
  do
    trap 'echo -e "\nAborted." >&2; exit 130' 2
    printf "Testing %-${longestFilenameLen}s ... " "${file}"
    ext="${file##*.}"
    testName="${file%.failing}"
    testType="${testName##*.}"

    thisStdout="${stdout}" 
    thisStderr="${stderr}"

    if [ "${ext}" = "failing" ]; then
      knownFail="yes"
      PASS="$(bggreen PASS) $(grey \(unexpected\))"
      FAIL="$(dred F̶A̶I̶L̶) $(grey \(expected\))"
      [ ! "${VERBOSE}" ] && thisStderr="/dev/null"
    else
      knownFail=""
      PASS="$(green PASS)"
      FAIL="$(red FAIL)"
    fi
    (
      timeout=$(egrep '^( *[*#] *timeout: )' "$file" | head -1 | sed -e 's/^\( *[*#] *timeout: \)//')
      case "$testType" in
        "simple")
          eval timeout -s9 "${timeout:-${defaultTimeout}}" "${PMJS}" \"$file\"
          exitCode="$?"
          ;;
        "bash")
          eval timeout -s9 --foreground "${timeout:-${defaultTimeout}}" bash \"$file\"
          exitCode="$?"
          ;;
        *)
          echo
          panic "${file}: invalid test type '$testType'"
          ;;
      esac
      exit "$exitCode"
    )> ${thisStdout} 2>${thisStderr}
    exitCode="$?"

    if [ "$exitCode" = "0" ]; then
      echo "${PASS}"
      [ "${peter_exit_code}" = "2" ] && peter_exit_code=0
      printf "\e[0;31m"
      [ "$VERBOSE" ] || cat "$stderr"
      printf "\e[0m"
    else
      echo "${FAIL}"

      if [ ! "$knownFail" ]; then
        peter_exit_code=1
      fi

      if [ ! "$VERBOSE" ] && [ ! "$knownFail" ]; then
        echo
        echo "$(grey --) $(yellow ${file}) $(grey vvvvvvvvvvvvvv)"
        cat "$stdout" | sed 's/^/   /'
        printf "\e[0;31m"
        cat "$stderr" | sed -e 's/^/   /'
        printf "\e[0m"
        echo "$(grey --) $(yellow ${file}) $(grey ^^^^^^^^^^^^^^)"
        echo
      fi
    fi
    (exit ${peter_exit_code})
  done
peter_exit_code="$?"

exit ${peter_exit_code}
