#!/bin/bash -efu

# Called from standard-test-beakerlib to run beakerlib tests
# This script is copied to test-environment.

debug() {
    if [ -n "$DEBUG" ]; then
        echo "$*" >&2
    fi
}

msg_usage() {
    cat << EOF

Run beakerlib-libraries test.

Usage:
$PROG <options>

Options:
-h, --help              display this help and exit
-v, --verbose           turn on debug
-w, --workdir           test environment work dir
-a, --artifactsdir      test environment dir to store artifacts
-t, --test              test to run, can be a path to directory or a runtest.sh file
    --timeout           test timeout
EOF
}

# Entry

PROG="${PROG:-${0##*/}}"
DEBUG="${DEBUG:-}"
STR_DEBUG="${STR_DEBUG:-}"
STR_VERBOSE="${STR_VERBOSE:-}"
STR_BKR_TEST="${STR_BKR_TEST:-}"
STR_WORKDIR="${STR_WORKDIR:-}"
STR_ARTIFACTS_DIR="${STR_ARTIFACTS_DIR:-}"
STR_TIMEOUT="${STR_TIMEOUT:-0}"

# http://wiki.bash-hackers.org/howto/getopts_tutorial
opt=$(getopt -n "$0" --options "hvt:w:a:" --longoptions "help,verbose,test:,workdir:,artifactsdir:,timeout:" -- "$@")
eval set -- "$opt"
while [[ $# -gt 0 ]]; do
    case "$1" in
        -t|--test)
            export STR_BKR_TEST="$2"
            shift 2
            ;;
        -w|--workdir)
            STR_WORKDIR="$2"
            shift 2
            ;;
        -a|--artifactsdir)
            export STR_ARTIFACTS_DIR="$2"
            shift 2
            ;;
        --timeout)
            STR_TIMEOUT="$2"
            shift 2
            ;;
        -v|--verbose)
            DEBUG="-v"
            shift
            ;;
        -h|--help)
            msg_usage
            exit 0
            ;;
        --)
            shift
            ;;
        *)
            msg_usage
            exit 1
    esac
done

# Entry

if [ -z "$STR_BKR_TEST" ] || [ -z "$STR_WORKDIR" ] || [ -z "$STR_ARTIFACTS_DIR" ]; then
    echo "Use: $PROG -h for help."
    exit 0
fi

debug "Test: $STR_BKR_TEST"
debug "Work dir: $STR_WORKDIR"
debug "Artifacts dir: $STR_ARTIFACTS_DIR"
debug "Timeout: $STR_TIMEOUT"
STR_BKR_TEST_DASHED=$(echo "$STR_BKR_TEST" | sed -e 's/\//-/g')

# Up to this point any fail is considered as ci-sytem fail. Exit code != 0.
# Starting from this point and bellow any fail is considered as a test fail. Exit code == 0.

clean_exit() {
    rc=$?
    # WARNING! At this place ansible closes all FD for STDIN STDERR.
    # echo "something" > ANY will not work, and will fail
    # With the above, relax:
    set +efu
    # Also any output to old tee-STDERR/STDOUT will terminate clean_exit()
    trap - SIGINT SIGTERM SIGABRT EXIT # clear the trap
    echo "Run test '$STR_BKR_NAME': done. Test's exit code: $rc" >&4
    if [[ $terminated_outside -eq 1 ]]; then
        echo "The test was terminated outside." >&4
        echo "Mark current test as ERROR." >&4
        rc=77
    fi
    # Close tee pipes
    for pid in $(ps -o pid --no-headers --ppid $$ 2>/dev/null); do
        if [ -n "$(ps -p $pid -o pid= 2>/dev/null)" ]; then
            kill -s HUP $pid > /dev/null 2>&1
        fi
    done
    # At this place STDIN/STDOUT(tee) are closed.
    # Can work original STDOUT/STDERR &3 and &4.
    # Depends how this command was invoked.
    # Check test result status
    local log_file_name="$STR_BKR_TEST_DASHED.log"
    local log_file_path="$STR_ARTIFACTS_DIR/$log_file_name"
    local status
    if [[ $rc -eq 127 ]]; then
        status="ERROR"
    elif [[ $rc -eq 124 ]]; then
        # test case timed out
        echo "$STR_BKR_TEST (test aborted due to timeout)" >&4
        status="ERROR"
    elif [[ $rc -eq 77 ]]; then
        # Test was terminated outside.
        # For example: ansible-playbook was terminated
        echo "$STR_TEST_NAME (test was terminated outside)" >&4
        status="ERROR"
    elif grep -q "RESULT: WARN" "$log_file_path"; then
        status="ERROR"
    elif grep -q "RESULT: FAIL" "$log_file_path"; then
        status="FAIL"
    elif grep -q "RESULT: PASS" "$log_file_path"; then
        status="PASS"
    elif grep -q "FAIL" "$log_file_path"; then
        status="FAIL"
    elif grep -q "PASS" "$log_file_path"; then
        status="PASS"
    else
        status="ERROR"
    fi
    echo "$status $STR_BKR_TEST" >> "$STR_ARTIFACTS_DIR/test.log"
    # Handle results.yml file, rename logs
    local results="$STR_ARTIFACTS_DIR/results.yml"
    local result=$(echo $status | tr '[:upper:]' '[:lower:]')
    test -f "$results" || echo 'results:' > "$results"
    printf '%s\n' '' \
        "- test: $STR_BKR_TEST" \
        "  result: $result"      \
        "  runtime: $runtime_seconds" \
        "  logs:"  \
        >> "$results"
    for log in "$log_file_path" "$logfile_stdout" "$logfile_stderr"; do
        if [ -f "$log" ]; then
            local prefixed_log="$STR_ARTIFACTS_DIR/${status}-$(basename $log)"
            mv -f "$log" "$prefixed_log"
            echo "  - $(basename $prefixed_log)" >> "$results"
        fi
    done
    # Exit code == 0, no matter of the test result.
    # If killed outside, return code will not be 0, no matter what.
    exit 0
}

# next vars are used in clean_exit()
runtime_start="$(date '+%s')"
# runtime == 0 if test is not started, or terminated outside
runtime_finish="$runtime_start"
runtime_seconds=0

trap clean_exit SIGINT SIGTERM SIGABRT EXIT
terminated_outside=1
rc=0

# For beakerlib-libraries
export PATH="$PATH:$STR_WORKDIR"
mkdir -p "$STR_ARTIFACTS_DIR"
logfile_stdout="$STR_ARTIFACTS_DIR/$STR_BKR_TEST_DASHED.log"
logfile_stderr="$STR_ARTIFACTS_DIR/$STR_BKR_TEST_DASHED-err.log"
# OUTPUTFILE has influence on beakerlib-libraries output
export OUTPUTFILE="$(realpath "$logfile_stdout")"
# Save real STDIN/STDERR to 3 and 4
exec 3>&1 4>&2 1> >(tee -a "$logfile_stdout" >&3) 2> >(tee -a "$logfile_stderr" >&4)
mkdir -p "$STR_WORKDIR"
cd "$STR_WORKDIR"

if ! [ -d "$STR_BKR_TEST" ] && ! [ -f "$STR_BKR_TEST" ]; then
    # Next string goes to .log file
    echo "FAIL test $STR_BKR_TEST does not appear to be a file or directory"
    terminated_outside=0
    exit 127
fi

if [ -f "$STR_BKR_TEST" ]; then
    debug "Running test from file: $STR_BKR_TEST"
    cd $(dirname "$STR_BKR_TEST")
    timeout --foreground "$STR_TIMEOUT" /bin/sh -e ./$(basename "$STR_BKR_TEST") || rc=$?
    terminated_outside=0
    runtime_finish="$(date '+%s')"
    runtime_seconds=$((runtime_finish - runtime_start))
    exit $rc
fi

if [ -d "$STR_BKR_TEST" ]; then
    debug "Running test from directory: $STR_BKR_TEST"
    cd "$STR_BKR_TEST"
    # get-test-deps - is part of beakerlib-libraries
    get-test-deps -i .
    if [ -f "Makefile" ] && command -p -v "make" >"/dev/null" 2>&1; then
        debug "Running test from Makefile"
        timeout --foreground "$STR_TIMEOUT" make run || rc=$?
    elif [ -f "runtest.sh" ]; then
        debug "Running test from runtest.sh"
        timeout --foreground "$STR_TIMEOUT" /bin/sh -e ./runtest.sh || rc=$?
    else
        echo "FAIL test $STR_BKR_TEST do not know how to run test"
    fi
    terminated_outside=0
    runtime_finish="$(date '+%s')"
    runtime_seconds=$((runtime_finish - runtime_start))
    exit $rc
fi
