#!/bin/bash

### Put e.g. in
#
# #!/bin/sh
# NONPAIRS=/path/to/apertium-nno t/run-tests
#
### in your .git/hooks/pre-commit and chmod +x .git/hooks/pre-commit
###
### Optional first argument is a free port number to use.

### Tests (TODO: get these from a file instead):
declare -ar INPUTS=(   "government" "ja"        "ikkje"            "ja<ij>"   "^ja<ij>$" "ignored"    "ignored")
declare -ar OUTPUTS=(  "Gobierno"   "og"        "ikkje/ikkje<adv>" "ja"       "ja"       "400"        "400")
declare -ar MODES=(    "eng|spa"    "sme|nob"   "nno"              "nno"      "nno"      "typomode"   "non|mod")
declare -ar TYPES=(    "translate"  "translate" "analyse"          "generate" "generate" "translate?" "translate?")
declare -ar EXTRACTS=( ""           ""          ""                 ""         ""         ".code"      ".code")

### Paths to apertium test data:
### The tests assume you have apertium-sme-nob and apertium-en-es
### installed, and apertium-nno checked out from SVN and compiled and
### available from INSTALLEDPAIRS and NONPAIRS directories
### respectively:
declare -r INSTALLEDPAIRS=${INSTALLEDPAIRS:-/usr/share/apertium}
declare -r NONPAIRS=${NONPAIRS:-/l/a/languages}
### You don't have to change these variables here, instead run the
### script like this:
# $ NONPAIRS=/path/to/apertium-nno t/run-tests
### to set the path to apertium-nno before running, or
# $ NONPAIRS=/path/to/apertium-nno INSTALLEDPAIRS=~/local t/run-tests
### to set both paths before running.


### Actual script follows:
set -e -u -o pipefail

PORT="${1:-2737}"
APYPID=
cleanup () {
    [[ -n ${APYPID} ]] && kill "${APYPID}"
}
trap cleanup EXIT

declare -r CHECK="[2K[999D[1;32m✓[00m"
declare -r CROSS="[2K[999D[1;31m❌[00m"

wait_for_startup () {
    local -i max_secs=10
    local -i i=0
    while [[ $i -lt ${max_secs} ]]; do
        echo -n "."
        sleep 1
        if curl -s "http://localhost:${PORT}" >/dev/null; then
            echo "${CHECK} APY seems to have started up"
            return 0
        fi
        (( i++ )) || true
    done
    echo "${CROSS} Waited ${max_secs} secs without any response from APY"
    return 1
}

extract_response () {
    local type=${TYPES[$1]}
    local extract=${EXTRACTS[$1]}
    if [[ ${extract} = "" ]]; then
        case ${type} in
            translate)
                jq -r .responseData.translatedText
                ;;
            generate|analyse)
                jq -r .[][] | awk 'NR%2==1'
                ;;
            *)
                echo "Unknown test type ${type} and no method given in EXTRACTS" >&2
                exit 1
                ;;
        esac
    else
        jq -r "${extract}"
    fi
}

ensure_installed () {
    local type=${TYPES[$1]}
    local mode=${MODES[$1]}
    case ${type} in
        translate)
            curl -s "http://localhost:${PORT}/list?q=pairs" \
                | jq -e ".responseData|map(.sourceLanguage+\"|\"+.targetLanguage)|index(\"$mode\")" &>/dev/null
            ;;
        generate)
            curl -s "http://localhost:${PORT}/list?q=generators" \
                | jq -e  "has(\"${mode}\")" &>/dev/null
            ;;
        analyse)
            curl -s "http://localhost:${PORT}/list?q=analysers" \
                | jq -e  "has(\"${mode}\")" &>/dev/null
            ;;
        # Anything else we let slide
    esac
}

run_test () {
    local -ri i=$1
    local -r in=${INPUTS[$i]}
    local -r mode=${MODES[$i]}
    local -r type=${TYPES[$i]}
    local url="http://localhost:${PORT}/${type}?lang=${mode}&q=${in}"
    if [[ ${type} = translate ]]; then
        url="http://localhost:${PORT}/translate?langpair=${mode}&q=${in}"
    fi
    if ! ensure_installed "$i"; then
        cat <<EOF
[1;31m❌[00m TEST FAILED FOR ${mode} ${type}
It seems like ${mode} (${type}) is not installed; install ${mode} and
set NONPAIRS/INSTALLEDPAIRS variables before running this script.

EOF
        return 1
    fi
    local -r got=$(curl -s "${url}" | extract_response "$i")
    local -r want=${OUTPUTS[$i]}
    if [[ ${got} != ${want} ]]; then
        cat <<EOF

[1;31m❌[00m TEST FAILED FOR ${mode} ${type}
WANTED: "${want}"
GOT: "${got}"

EOF
        return 1
    fi
    return 0
}

run_tests () {
    local -i failures=0

    for (( i=0; i<${#INPUTS[@]}; i++ )); do
        if ! run_test "$i"; then
            (( failures++ )) || true
        fi
    done

    local got=$(curl -s "http://localhost:${PORT}/translate?langpair=typo&q=whatever" | jq -r .code)
    if [[ "${got}" -ne 400 ]]; then
        (( failures++ )) || true
    fi

    if [[ ${failures} -eq 0 ]]; then
        cat <<EOF
${CHECK} All $i tests passed
EOF
    else
        cat <<EOF
${CROSS} Ran $i tests, ${failures} failures
EOF
        return "${failures}"
    fi
    return 0
}


if ! command -V jq &>/dev/null; then
    echo "Please install jq, e.g. 'sudo apt-get install jq'" 2>/dev/null
fi

if netstat -lnt|awk "\$4~/:${PORT}\$/"|grep -q .; then
    lsof -i :"${PORT}"
    echo >&2
    echo "Port ${PORT} seems taken, can't run tests" >&2
    echo "(you can pass an alternative port as first argument to this script)" >&2
    exit 10
fi

cd "$(dirname "$0")"
rm -f apertium-apy.log apertium-apy.err
../servlet.py -p "${PORT}" -d -j1 -i3 -u1 -n1 -m3 -s "${NONPAIRS}"  -- "${INSTALLEDPAIRS}" & APYPID=$!
wait_for_startup
if run_tests; then
    exit $?
else
    result=$?
    tail -n 999 apertium-apy.log apertium-apy.err
    exit "${result}"
fi

# TODO: concurrency tests
