#!/usr/bin/bash
# MKit - simple install helper
# See LICENSE file for copyright and license details.

mkit_import build
mkit_import ini
mkit_import target
mkit_import util

VDK_TEST__UTILDIR=${VDK_TEST__UTILDIR:-/usr/share/vdk/utils}

VDK_TEST__FAILFAST=${VDK_TEST__FAILFAST:-}

VDK_TEST__KIT=${VDK_TEST__KIT:-}

vdk_test__main() {
    #
    # Run tests
    #
    local BinPath="$VDK_TEST__UTILDIR/vdk_test"
    local ProjVersion
    local ProjLastGitSummary
    local es=0
    local ses
    local suite
    local suites_togo
    local failfast
    local Shell
    local Clean
    local SelectedSuites
    local DeniedSuites
    local ExtraArgs
    local Kit=${VDK_TEST__KIT:-"$VDK_KIT"}
    test -n "$Kit" || die "could not detect kit! use 'make vdk_test' to invoke or set VDK_TEST__KIT to one of: $(__vdk_test__ls_supported_kits | paste -sd,)"
    __vdk_test__ls_supported_kits \
      | grep -qxF "$Kit"
    test -x "$BinPath" || die "no such executable: $BinPath"
    failfast=$(__vdk_test__load_failfast) || return 3
    Shell=$(__vdk_test__load_shell) || return 3
    Clean=$(__vdk_test__load_clean) || return 3
    util__loadarr SelectedSuites    __vdk_test__load_suitenames "select"    || return 3
    util__loadarr DeniedSuites      __vdk_test__load_suitenames "deny"      || return 3
    debug_var failfast Shell SelectedSuites DeniedSuites
    util__loadarr suites_togo __vdk_test__suites_select
    test "${#suites_togo[*]}" -eq 0 \
     && util__warnbox "←" "no suites selected: DeniedSuites=(${DeniedSuites[*]}) SelectedSuites=(${SelectedSuites[*]})" \
     && return 0
    debug_var suites_togo
    target__run build
    target__run _mkit_metadata
    ProjVersion=$(build__cached semver)
    ProjLastGitSummary=$(build__cached git_lastsummary)
    warn " .. __MKIT_PROJ_VERSION__ = $ProjVersion" \
         " .. __MKIT_PROJ_GIT_LASTSUMMARY__ = $ProjLastGitSummary"
    build__recordr vdk_test
    warn " .. Kit = $Kit"
    for suite in "${suites_togo[@]}"; do
        ExtraArgs=$(__vdk_test__load_extra_args "$suite" | paste -sd' ')
        debug_var ExtraArgs
        __vdk_test__runsuite "$suite"; ses=$?
        $failfast \
         && test "$ses" -gt 0 \
         && __vdk_test__clean "$ses" \
         && warn "stopping on first failure (failfast=$failfast)" \
         && return "$ses"
        test "$ses" -gt "$es" && es=$ses
    done
    __vdk_test__clean "$es"
    return $es
}

__vdk_test__clean() {
    #
    # Clean up after test if exit status $1 is OK
    #
    # We've registered our sub-dir to MKit so its
    # native cleanup will suffice.
    #
    local es=$1
    case $es:$Clean in
        0:onpass)   target__run clean ;;
        *:onpass)   warn "skipping clean; vdk_test failed with: $es (clean=$Clean)" ;;
        *:always)   target__run clean ;;
        *:never)    true ;;
        *)          warn "bug: invalid option value: clean=$Clean"
                    return 3 ;;
    esac
}

__vdk_test__load_failfast() {
    #
    # Safely load 'failfast' option
    #
    util__loadenv_bool VDK_TEST__FAILFAST && return 0
    plugin__option_bool failfast && return 0
    warn "falling back to: failfast = true" \
         " .. set VDK_TEST__FAILFAST or [options:vdk_test:failfast]" \
         " .. to true|false to change it"
    echo true
}

__vdk_test__load_shell() {
    #
    # Safely load 'shell' option
    #
    local allowed=(onfail always never)
    util__loadenv_enum VDK_TEST__SHELL "${allowed[@]}" && return 0
    plugin__option_enum shell "${allowed[@]}" && return 0
    warn "falling back to: shell = never" \
         " .. set VDK_TEST__SHELL or [options:vdk_test:shell]" \
         " .. to onfail|always|never to change it"
    echo never
}

__vdk_test__load_clean() {
    #
    # Safely load 'clean' option
    #
    local allowed=(onpass always never)
    util__loadenv_enum VDK_TEST__CLEAN "${allowed[@]}" && return 0
    plugin__option_enum clean "${allowed[@]}" && return 0
    warn "falling back to: clean = onpass" \
         " .. set VDK_TEST__CLEAN or [options:vdk_test:clean]" \
         " .. to onpass|always|never to change it"
    echo onpass
}

__vdk_test__load_suitenames() {
    #
    # Safely load suite names from option $1
    #
    local optname=$1
    local values
    local es=0
    util__loadarr values plugin__option_multi "$optname" || return 0
    debug_var values
    for value in "${values[@]}"; do
        test -n "$value" || continue
        __vdk_test__ls_supported | grep -xF -e "$value" && continue
        util__warnbox "!" "unsupported suite name: $optname = '$value' (Kit=$Kit)"; es=3
    done
    return "$es"
}

__vdk_test__load_extra_args() {
    #
    # Safely load extra args for suite $1
    #
    local suite=$1
    util__loadenv VDK_TEST__EXTRA_ARGS && return 0
    plugin__option_multi "$suite.extra_args" && return 0
}

__vdk_test__runsuite() {
    #
    # Run suite $1
    #
    local suite=$1
    local es
    util__warnbox "→" "running suite: $suite"
    (
        local args=()
        eval "args=($ExtraArgs)"
        if test "$Kit" == pylib; then
            PYLIB_MODNAME=$(ini 1value macros:__VDK_PYLIB_MODNAME__)
            test -n "$PYLIB_MODNAME" \
             || die "could not determine Python module name: make sure to set __VDK_PYLIB_MODNAME__ macro in mkit.ini"
            export VDK_TEST__PYLIB_MODNAME="$PYLIB_MODNAME"
            export VDK_TEST__BUILDDIR="src/$PYLIB_MODNAME"
        fi
        export VDK_TEST__OPEN_SHELL="$Shell"
        "$BinPath" "$suite" "${args[@]}"
    ); es=$?
    test "$es" -eq 0 && return 0
    util__warnbox "↑" \
        "suite failed: $suite" \
        "at $ProjVersion" \
        ".. ($ProjLastGitSummary)"
    return "$es"
}

__vdk_test__suites_filter() {
    #
    # Filter suites based on 'except' option
    #
    local deny_re
    test "${#DeniedSuites[*]}" -eq 0 \
     && debug "no exceptions" \
     && cat \
     && return 0
    deny_re=$(printf '%s\n' "${DeniedSuites[@]}" | paste -sd'|')
    debug_var deny_re
    grep -vE -e "$deny_re"
}

__vdk_test__suites_ls() {
    #
    # List suites to potentially run
    #
    test "${#SelectedSuites[*]}" -gt 0 \
     && printf '%s\n' "${SelectedSuites[@]}" \
     && return 0
    __vdk_test__ls_supported
}

__vdk_test__ls_supported() {
    #
    # List all supported suites
    #
    echo bin_t
    if test "$Kit" == pylib; then
        echo doctest
        echo mypy
        echo unittest
        echo pytest
    fi
    echo tfkit
}

__vdk_test__ls_supported_kits() {
    #
    # List all supported kits
    #
    echo pylib
}

__vdk_test__suites_select() {
    #
    # Print suites we want to run
    #
    __vdk_test__suites_ls \
      | __vdk_test__suites_filter
}
