#!/usr/bin/bash
# Copyright (c) [2021] Huawei Technologies Co.,Ltd.ALL rights reserved.
# This program is licensed under Mulan PSL v2.
# You can use it according to the terms and conditions of the Mulan PSL v2.
#          http://license.coscl.org.cn/MulanPSL2
# THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
####################################
# @Author  : lemon-higgins
# @email   : lemon.higgins@aliyun.com
# @Date    : 2021-04-20 15:12:00
# @License : Mulan PSL v2
# @Version : 1.0
# @Desc    :
#####################################
# shellcheck disable=SC2181,SC1090,SC2086

OET_PATH=$(
    cd "$(dirname "$0")" || exit 1
    pwd
)
export OET_PATH

source "${OET_PATH}"/libs/locallibs/common_lib.sh

if [[ -d "/etc/mugen" ]]; then
    export conf_file="/etc/mugen/mugen.json"
else
    export conf_file="${OET_PATH}/conf/mugen.json"
fi

TIMEOUT="30m"
COMMAND_X="no"
COMMAND_S="no"
COPY_DONE="no"
CASE_NUM=0
SUCCESS_NUM=0
FAIL_NUM=0
SKIP_NUM=0

function usage() {
    printf "Usage:  \n
    -c: configuration environment of test framework\n
    -a: execute all use cases\n
    -f: designated test suite\n
    -r: designated test case\n
    -x: the shell script is executed in debug mode\n
    -b: do make for test suite if test suite path have makefile or Makefile file\n
    -s: runing test case at remote NODE1
    -m: Modify the configuration file that comes with the test case
    \n
    Example:
        run all cases:
          normal mode:
            bash mugen.sh -a
          debug mode:
            bash mugen.sh -a -x

        run test suite:
          normal mode:
            bash mugen.sh -f test_suite
          debug mode:
            bash mugen.sh -f test_suite -x

        run test case:
          normal mode:
            bash mugen.sh -f test_suite -r test_case
          debug mode:
            bash mugen.sh -f test_suite -r test_case -x

        run at remote:
          normal mode:
            bash mugen.sh -a -s
            bash mugen.sh -f test_suite -s
            bash mugen.sh -f test_suite -r test_case -s
          debug mode:
            bash mugen.sh -a -s -x
            bash mugen.sh -f test_suite -s -x
            bash mugen.sh -f test_suite -r test_case -s -x
        \n
	configure env of test framework:
	    bash mugen.sh -c --ip \$ip --password \$passwd --user \$user --port \$port\n
          if want run at remote should add --run_remote
          if want run at remote copy all testcase once add --put_all
    \n
    configure env of test case:
            bash mugen.sh -m --suite \$suite --conf \$conf --key \$key --value \$value\n
    \n
    do make for test suite:
        for all test suite:
            bash mugen.sh -b -a
        for one test suite:
            bash mugen.sh -b test_suite\n"
}

function deploy_conf() {
    python3 "${OET_PATH}"/libs/locallibs/write_conf.py "$@"
    test $? -ne 0 && exit 1 || exit 0
}

function modify_conf() {
    python3 "${OET_PATH}"/libs/locallibs/modify_conf.py "$@"
    test $? -ne 0 && exit 1 || exit 0
}

function load_conf() {
    rm -rf "${OET_PATH}"/results

    export_var=$(python3 "${OET_PATH}"/libs/locallibs/read_conf.py env-var)
    test $? -ne 0 && exit 1

    $export_var

    env_conf="$(echo -e "$conf_file" | sed -e 's/json/env/')"
    printf "%s\n" "$export_var" >"$env_conf"

    if [[ $COMMAND_S == "yes" ]]; then
        env_path="$(echo -e "$conf_file" | sed -e 's/mugen.json//')"
        SFTP put --node 1 --remotedir /mugen_re/conf/ --localdir "$env_path" --localfile "mugen.env"
    fi
}

function generate_result_file() {
    local suite=$1
    local case=$2
    local exitcode=$3

    if [ "$exitcode" -eq 0 ]; then
        LOG_INFO "The case exit by code $ret_code."
        ((SUCCESS_NUM++))
        result="succeed"
    elif [ "$exitcode" -eq 255 ]; then
        LOG_ERROR "The case exit by code $ret_code."
        ((SKIP_NUM++))
        result="skipped"
    else
        LOG_ERROR "The case exit by code $ret_code."
        ((FAIL_NUM++))
        result="failed"
    fi

    local result_path="$OET_PATH/results/$suite/$result"
    mkdir -p "$result_path"
    touch "$result_path"/"$case"
}

function exec_case() {
    local cmd=$1
    local log_path=$2
    local case_name=$3
    local test_suite=$4

    exec 6>&1
    exec 7>&2
    exec >>"$log_path"/"$(date +%Y-%m-%d-%T)".log 2>&1

    SLEEP_WAIT $TIMEOUT "$cmd"
    ret_code=$?

    exec 1>&6 6>&-
    exec 2>&7 7>&-

    test "$ret_code"x == "143"x && {
        cmd_pid=$(pgrep -f "$cmd")
        if [ -n "$cmd_pid" ]; then
            for pid in ${cmd_pid}; do
                pstree -p "$pid" | grep -o '([0-9]*)' | tr -d '()' | xargs kill -9
            done
        fi
        LOG_WARN "The case execution timeout."
    }

    generate_result_file "$test_suite" "$case_name" "$ret_code"
}

function run_test_case() {

    local test_suite=$1
    local test_case=$2
    export exec_result

    if [[ -z "$test_suite" || -z "$test_case" ]]; then
        LOG_ERROR "Parameter(test suite or test case) loss."
        exit 1
    fi

    result_files=$(find "${OET_PATH}"/results/"${test_suite}" -name "$test_case" >/dev/null 2>&1)
    for result_file in $result_files; do
        test -f "$result_file" && rm -rf "$result_file"
    done

    suite_path=$(python3 "${OET_PATH}"/libs/locallibs/suite_case.py --suite "$test_suite" --key path)
    test $? -ne 0 && return 1
    test -d "$suite_path" || {
        LOG_ERROR "Path value:${suite_path} in a JSON file that does not exist in the environment."
        return 1
    }

    if ! find suite2cases/ -name "${test_suite}.json" | grep -q .; then
        LOG_ERROR "In the suite2cases directory, can't find the file for testsuite: ${test_suite}."
        return 1
    fi

    mapfile -t case_path < <(find "${suite_path}" -name "${test_case}.*" | sed -e "s#/${test_case}.\(sh\|py\)##")
    test ${#case_path[@]} -gt 1 && {
        LOG_ERROR "Multiple identical test case scripts have been found under the test suite. Please check your use case scripts. Test suite path:${suite_path}"
        return 1
    }

    ((CASE_NUM++))

    log_path="${OET_PATH}/logs/${test_suite}/${test_case}"
    mkdir -p "${log_path}"

    LOG_INFO "start to run testcase:$test_case."

    if [ -z "${case_path[*]}" ]; then
        echo -e "Can't find the test script:$test_case, Please confirm whether the code is submitted." >>"$log_path"/"$(date +%Y-%m-%d-%T)".log 2>&1
        exec_case "exit 255" "$log_path" "$test_case" "$test_suite"
        return 1
    else
        suite_json=$(find suite2cases/ -name "${test_suite}.json")
        CHECK_ENV "${suite_json}" "${test_case}"
        if [ $? -ne 0 ]; then
            echo -e "The test environment don't meet the requirements, please check your environment." >>"$log_path"/"$(date +%Y-%m-%d-%T)".log 2>&1
            exec_case "exit 255" "$log_path" "$test_case" "$test_suite"
            return 1
        fi
    fi

    if [[ $COMMAND_S == "yes" && $COPY_DONE == "no" ]]; then
        common_files=$(python3 "${OET_PATH}"/libs/locallibs/suite_case.py --suite "$test_suite" --key common-files)
        test $? -ne 0 && return 1

        for one_file in $common_files; do
            local_dir=${one_file%/*}
            local_file=${one_file##*/}
            remote_dir=$(echo -e "${local_dir}" | sed -e "s#${OET_PATH}#/mugen_re#")
            SFTP put --node 1 --remotedir "$remote_dir" --remotefile "$local_file" --localdir "$local_dir" --localfile "$local_file"
            if [ $? -ne 0 ]; then
                LOG_ERROR "Copy $test_suite suite2cases to remote fail."
                return 1
            fi
        done

        remote_dir=$(echo -e "${case_path%/*}" | sed -e "s#${OET_PATH}#/mugen_re#")
        SFTP put --node 1 --remotedir "$remote_dir" --localdir "${case_path[*]}"
        test $? -ne 0 && return 1
    fi

    pushd "${case_path[*]}" >/dev/null || return 1

    local time_out
    time_out=$(grep -w --fixed-strings EXECUTE_T "${test_case}".* 2>/dev/null | awk -F '=' '{print $NF}' | tr -d '"')
    test -n "$time_out" && local TIMEOUT=$time_out

    local script_type
    script_type=$(find . -name "${test_case}.*" | awk -F '.' '{print $NF}')

    if [[ "$script_type"x == "sh"x ]] || [[ "$script_type"x == "bash"x ]]; then
        if [ "$COMMAND_X"x == "yes"x ]; then
            run_cmd="bash -x ${test_case}.sh"
        else
            run_cmd="bash ${test_case}.sh"
        fi
    elif [ "$script_type"x == "py"x ]; then
        run_cmd="python3 ${test_case}.py"
    fi

    if [[ $COMMAND_S == "yes" ]]; then
        remote_case_path=$(echo -e "${case_path[*]}" | sed -e "s#${OET_PATH}#/mugen_re#")
        remote_cmd="bash -c '. /mugen_re/conf/mugen.env &&
                 export OET_PATH=/mugen_re &&
                 pushd \"$remote_case_path\" >/dev/null || exit 1 &&
                 $run_cmd > /tmp/mugen_re.log 2>&1 &&
                 (cat /tmp/mugen_re.log && rm -rf /tmp/mugen_re.log && popd >/dev/null || exit 1) ||
                 (cat /tmp/mugen_re.log 1>&2 && rm -rf /tmp/mugen_re.log && popd >/dev/null && exit 1)
                '"
        run_cmd="python3 \"${OET_PATH}\"/libs/locallibs/ssh_cmd.py --node 1 --cmd \"${remote_cmd}\""
    fi

    exec_case "$run_cmd" "$log_path" "$test_case" "$test_suite"

    popd >/dev/null || return 1

    if [[ $COMMAND_S == "yes" && $COPY_DONE == "no" ]]; then
        remote_dir=$(echo -e "${suite_path}" | sed -e "s#${OET_PATH}#/mugen_re#")
        P_SSH_CMD --node 1 --cmd "rm -rf $remote_dir"
    fi

    LOG_INFO "End to run testcase:$test_case."
}

function run_test_suite() {
    local test_suite=$1

    [ -z "$(find "${OET_PATH}"/suite2cases -name "${test_suite}".json)" ] && {
        LOG_ERROR "In the suite2cases directory, Can't find the file of testsuite:${test_suite}."
        return 1
    }

    local this_copy=false
    local suite_path=""
    if [[ $COMMAND_S == "yes" && $COPY_DONE == "no" && ${NODE1_COPY_ALL}x == "true"x ]]; then
        suite_path=$(python3 "${OET_PATH}"/libs/locallibs/suite_case.py --suite "$test_suite" --key path)
        test $? -ne 0 && return 1

        remote_dir=$(echo -e "${suite_path%/*}" | sed -e "s#${OET_PATH}#/mugen_re#")
        SFTP put --node 1 --remotedir "$remote_dir" --localdir "$suite_path"
        if [ $? -ne 0 ]; then
            LOG_ERROR "Copy $test_suite testcases to remote fail."
            return 1
        fi
        COPY_DONE="yes"
        this_copy=true
    fi

    for test_case in $(python3 "${OET_PATH}"/libs/locallibs/suite_case.py --suite "$test_suite" --key cases-name | shuf); do
        run_test_case "$test_suite" "$test_case"
    done

    if $this_copy; then
        remote_dir=$(echo -e "${suite_path}" | sed -e "s#${OET_PATH}#/mugen_re#")
        P_SSH_CMD --node 1 --cmd "rm -rf $remote_dir"
    fi
}

function run_all_cases() {
    mapfile -t test_suites < <(find "${OET_PATH}"/suite2cases/ -type f -name "*.json" | awk -F '/' '{print $NF}' | sed -e 's/.json$//g' | sort -u)
    test ${#test_suites[@]} -eq 0 && {
        LOG_ERROR "Can't find recording about test_suites."
        return 1
    }

    local this_copy=false
    if [[ $COMMAND_S == "yes" && $COPY_DONE == "no" && ${NODE1_COPY_ALL}x == "true"x ]]; then
        local this_copy=true
    fi

    for test_suite in "${test_suites[@]}"; do
        run_test_suite "$test_suite"
        COPY_DONE="no"
        local this_copy=true
    done

    if $this_copy; then
        P_SSH_CMD --node 1 --cmd "rm -rf /mugen_re/testcases"
    fi
}

function statistic_result() {
    LOG_INFO "A total of ${CASE_NUM} use cases were executed, with ${SUCCESS_NUM} successes ${FAIL_NUM} failures and ${SKIP_NUM} skips."
    exit "${FAIL_NUM}"
}

DO_MAKE_COUNT=0
NOT_DO_MAKE_COUNT=0
TOTAL_MAKE_COUNT=0

function build_one_suite() {
    ((TOTAL_MAKE_COUNT++))
    local test_suite=$1

    [ -z "$(find "${OET_PATH}"/suite2cases -name "${test_suite}".json)" ] && {
        LOG_ERROR "In the suite2cases directory, Can't find the file of testsuite:${test_suite}."
        return 1
    }

    suite_path=$(python3 "${OET_PATH}"/libs/locallibs/suite_case.py --suite "$test_suite" --key path)
    if [ $? -ne 0 ]; then
        LOG_ERROR "find ${test_suite} test suite path fail"
        return 1
    fi
    test -d "$suite_path" || {
        LOG_ERROR "Path value:${suite_path} in a JSON file that does not exist in the environment."
        return 1
    }

    pushd "$suite_path" >/dev/null || return 1

    if [[ -f "./makefile" || -f "./Makefile" ]]; then
        LOG_INFO "do make for testsuite: ${test_suite}"
        make
        if [ $? -ne 0 ]; then
            LOG_ERROR "do make for testsuite: ${test_suite} fail"
            popd >/dev/null || exit
            return 1
        fi
        ((DO_MAKE_COUNT++))
    else
        if [[ $2 != "no_print" ]]; then
            LOG_INFO "This test suite $suite_path have no makefile or Makefile"
        fi
        ((NOT_DO_MAKE_COUNT++))
    fi

    popd >/dev/null || return 1

    return 0
}

function build_all() {
    mapfile -t test_suites < <(find "${OET_PATH}"/suite2cases/ -type f -name "*.json" | awk -F '/' '{print $NF}' | sed -e 's/.json$//g')
    test ${#test_suites[@]} -eq 0 && {
        LOG_ERROR "Can't find recording about test_suites."
        return 1
    }

    do_error_count=0

    for test_suite in ${test_suites[*]}; do
        build_one_suite "$test_suite" "no_print"
        if [ $? -ne 0 ]; then
            LOG_ERROR "doing test suite $test_suite make fail"
            ((do_error_count++))
        fi
    done

    LOG_INFO "A total ${TOTAL_MAKE_COUNT} test suite run make, ${DO_MAKE_COUNT} had make, ${NOT_DO_MAKE_COUNT} no need do make, ${do_error_count} do make fail"
    if [ "$do_error_count" -ne 0 ]; then
        exit 1
    fi

    exit 0
}

function copy_libs_to_node1() {
    if [[ $COMMAND_S == "no" ]]; then
        return
    fi
    P_SSH_CMD --node 1 --cmd "ls -l /mugen_re/" | grep -q "libs"
    if [ $? -ne 0 ]; then
        SFTP put --node 1 --remotedir /mugen_re/ --localdir "${OET_PATH}"/libs/
    fi
}

while getopts "c:af:r:dxb:s:m" option; do
    case $option in
    c)
        deploy_conf ${*//-c/}
        ;;
    m)
        modify_conf ${*//-m/}
        ;;
    d)
        echo -e "The test script download function has been discarded."
        ;;
    a)
        if echo "$@" | grep -q -e '-a * -x *$\| -x * -a *$\| -ax *$\| -xa *$'; then
            COMMAND_X="yes"
        elif echo "$@" | grep -q -e '-a * -s *$\| -s * -a *$\| -as *$\| -sa *$'; then
            COMMAND_S="yes"
        elif echo "$@" | grep -q -e '-a * -b *$\| -b * -a *$\| -ab *$\| -ba *$'; then
            ret=$(build_all)
            exit "$ret"
        elif ! echo "$@" | grep -q -e '-a *$'; then
            usage
            exit 1
        fi

        load_conf
        copy_libs_to_node1
        run_all_cases
        statistic_result
        ;;
    b)
        test_suite=$OPTARG
        op_list=('-f' '-r' '-x' '-d' '-c' '-s')

        for op in ${op_list[*]}; do
            if [[ $test_suite == "$op" ]]; then
                usage
                exit 1
            fi
        done

        if echo "$@" | grep -q -e ' * -a *$\| -ba *$'; then
            build_all
        else
            build_one_suite "$test_suite"
        fi

        ;;
    f)
        test_suite=$OPTARG

        echo "$@" | grep -q -e " -b *$"
        if [ $? -eq 0 ]; then
            usage
            exit 1
        fi

        echo "$test_suite" | grep -q -e ' -a \| -r \| -x \| -d ' && {
            usage
            exit 1
        }

        echo "$@" | grep -q -e ' * -x *\| * -xf *' && {
            COMMAND_X="yes"
        }

        echo "$@" | grep -q -e ' * -s *\| * -sf *' && {
            COMMAND_S="yes"
        }

        echo "$@" | grep -q -e ' -r ' || {
            load_conf
            copy_libs_to_node1
            run_test_suite "$test_suite"
            statistic_result
        }
        ;;
    r)
        test_case=$OPTARG

        echo "$@" | grep -q -e " -b *$"
        if [ $? -eq 0 ]; then
            usage
            exit 1
        fi

        echo "$test_case" | grep -q -e ' -a \| -f \| -x \| -d ' && {
            usage
            exit 1
        }

        echo "$@" | grep -q -e ' * -x *\| * -xr *\| * -xf *' && {
            COMMAND_X="yes"
        }

        echo "$@" | grep -q -e ' * -s *\| * -sr *\| * -sf *' && {
            COMMAND_S="yes"
        }

        load_conf
        copy_libs_to_node1
        run_test_case "$test_suite" "$test_case"
        statistic_result
        ;;
    x)
        echo "$@" | grep -q -e '^ [a-zA-Z0-9_-]-x *$' && {
            LOG_ERROR "The -x parameter must be used in combination with -a, -f, and -r."
            exit 1
        }
        ;;
    s)
        echo "$@" | grep -q -e '^ [a-Z0-9_-]-x *$' && {
            LOG_ERROR "The -s parameter must be used in combination with -a, -f, and -r."
            exit 1
        }
        ;;
    *)
        usage
        exit 1
        ;;

    esac
done
