#!/bin/bash -ex

EXEC_PATH=$(dirname "$(realpath "$0")")
PROJECT_PATH="$(dirname $EXEC_PATH)"
EXPORT_DIR="$PWD/exported-artifacts"
CONT_EXPORT_DIR="/exported-artifacts"

CONTAINER_WORKSPACE="/workspace/nmstate"

TEST_TYPE_ALL="all"
TEST_TYPE_FORMAT="format"
TEST_TYPE_LINT="lint"
TEST_TYPE_UNIT_PY36="unit_py36"
TEST_TYPE_UNIT_PY38="unit_py38"
TEST_TYPE_INTEG="integ"
TEST_TYPE_INTEG_TIER1="integ_tier1"
TEST_TYPE_INTEG_TIER2="integ_tier2"
TEST_TYPE_INTEG_SLOW="integ_slow"

FEDORA_IMAGE_DEV="docker.io/nmstate/fedora-nmstate-dev"
CENTOS_IMAGE_DEV="docker.io/nmstate/centos8-nmstate-dev"
CENTOS_STREAM_IMAGE_DEV="docker.io/nmstate/centos-stream-nmstate-dev"

CREATED_INTERFACES=""
INTERFACES="eth1 eth2"

PYTEST_OPTIONS="--verbose --verbose \
        --log-level=DEBUG \
        --log-date-format='%Y-%m-%d %H:%M:%S' \
        --log-format='%(asctime)s %(filename)s:%(lineno)d %(levelname)s %(message)s' \
        --durations=5 \
        --cov /usr/lib/python*/site-packages/libnmstate \
        --cov /usr/lib/python*/site-packages/nmstatectl \
        --cov-report=term \
        --cov-report=xml \
        --log-file=pytest-run.log"

NMSTATE_TEMPDIR=$(mktemp -d /tmp/nmstate-test-XXXX)

VETH_PEER_NS="nmstate_test"

: ${CONTAINER_CMD:=podman}

test -t 1 && USE_TTY="-t"
source automation/tests-container-utils.sh
source automation/tests-machine-utils.sh

function pyclean {
    exec_cmd '
        find . -type f -name "*.py[co]" -delete
        find . -type d -name "__pycache__" -delete
    '
}

function exec_cmd {
    if [ -z ${RUN_BAREMETAL} ];then
        container_exec "$1"
    else
        bash -c "$1"
    fi
}

function dump_network_info {
    exec_cmd '
      nmcli dev; \
      # Use empty PAGER variable to stop nmcli send output to less which hang \
      # the CI. \
      PAGER= nmcli con; \
      ip addr; \
      ip route; \
      cat /etc/resolv.conf; \
      head /proc/sys/net/ipv6/conf/*/disable_ipv6; \
    '
}

function install_nmstate {
    if [ $INSTALL_NMSTATE == "true" ];then
        exec_cmd '
          rpm -ivh `./packaging/make_rpm.sh|tail -1 || exit 1`
        '
    fi
}

function run_tests {
    if [ $TEST_TYPE == $TEST_TYPE_ALL ] || \
       [ $TEST_TYPE == $TEST_TYPE_FORMAT ];then
        exec_cmd "tox -e black"
    fi

    if [ $TEST_TYPE == $TEST_TYPE_ALL ] || \
       [ $TEST_TYPE == $TEST_TYPE_LINT ];then
        exec_cmd "tox -e flake8,pylint,yamllint"
    fi

    if [ $TEST_TYPE == $TEST_TYPE_ALL ] || \
       [ $TEST_TYPE == $TEST_TYPE_UNIT_PY36 ];then
        if [[ $CONTAINER_IMAGE == *"centos"* ]]; then
            # Due to https://github.com/pypa/virtualenv/issues/1009
            # Instruct virtualenv not to upgrade to the latest versions of pip,
            # setuptools, wheel and etc
            exec_cmd 'env VIRTUALENV_NO_DOWNLOAD=1 \
                            tox --sitepackages -e py36'
        else
            exec_cmd "tox -e py36"
        fi
    fi

    if [ $TEST_TYPE == $TEST_TYPE_ALL ] || \
       [ $TEST_TYPE == $TEST_TYPE_UNIT_PY38 ];then
        if [[ $CONTAINER_IMAGE == *"centos"* ]]; then
            echo "Running unit test in $CONTAINER_IMAGE container is not " \
                 "support yet"
        else
            exec_cmd "tox -e py38"
        fi
    fi

    if [ $TEST_TYPE == $TEST_TYPE_ALL ] || \
       [ $TEST_TYPE == $TEST_TYPE_INTEG ];then
        exec_cmd "cd $CONTAINER_WORKSPACE"
        exec_cmd "
            pytest \
            $PYTEST_OPTIONS \
            --cov-report=html:htmlcov-integ \
            tests/integration \
            ${nmstate_pytest_extra_args}"
    fi

    if  [ $TEST_TYPE == $TEST_TYPE_INTEG_TIER1 ];then
        exec_cmd "cd $CONTAINER_WORKSPACE"
        exec_cmd "
          pytest \
            $PYTEST_OPTIONS \
            --cov-report=html:htmlcov-integ_tier1 \
            -m tier1 \
            tests/integration \
            ${nmstate_pytest_extra_args}"
    fi

    if  [ $TEST_TYPE == $TEST_TYPE_INTEG_TIER2 ];then
        exec_cmd "cd $CONTAINER_WORKSPACE"
        exec_cmd "
          pytest \
            $PYTEST_OPTIONS \
            --cov-report=html:htmlcov-integ_tier2 \
            -m tier2 \
            tests/integration \
            ${nmstate_pytest_extra_args}"
    fi

    if [ $TEST_TYPE == $TEST_TYPE_ALL ] || \
       [ $TEST_TYPE == $TEST_TYPE_INTEG_SLOW ];then
        exec_cmd "cd $CONTAINER_WORKSPACE"
        exec_cmd "
          pytest \
            $PYTEST_OPTIONS \
            --cov-report=html:htmlcov-integ_slow \
            -m slow --runslow \
            tests/integration \
            ${nmstate_pytest_extra_args}"
    fi
}

function write_separator {
    set +x
    local text="$(echo "${1}" | sed 's,., \0,g') "
    local char="="

    local textlength=$(echo -n "${text}" | wc --chars)
    local cols="$(tput cols)"
    local wraplength=$(((cols - textlength) / 2))

    eval printf %.1s "${char}"'{1..'"${wraplength}"\}
    echo -n "${text}"
    wraplength=$((wraplength + ((cols - textlength) % 2)))
    eval printf %.1s "${char}"'{1..'"${wraplength}"\}
    echo
    set -x
}

function run_exit {
    write_separator "TEARDOWN"
    dump_network_info
    if [ -z ${RUN_BAREMETAL} ];then
        collect_artifacts
        remove_container
        remove_tempdir
    else
        teardown_network_environment
    fi
}

function modprobe_ovs {
    lsmod | grep -q ^openvswitch || modprobe openvswitch || { echo 1>&2 "Please run 'modprobe openvswitch' as root"; exit 1; }
}

function check_services {
    exec_cmd 'while ! systemctl is-active dbus; do sleep 1; done'
    exec_cmd 'systemctl start systemd-udevd
                 while ! systemctl is-active systemd-udevd; do sleep 1; done
    '
    exec_cmd '
        systemctl restart NetworkManager
        while ! systemctl is-active NetworkManager; do sleep 1; done
    '
}

function upload_coverage {
    if [[ "$CI" == "true" ]] ;then
        container_exec "
            cd $CONTAINER_WORKSPACE &&
            COVERALLS_PARALLEL=true COVERALLS_SERVICE_NAME=travis-ci coveralls
        " || true
        container_exec "
            cd $CONTAINER_WORKSPACE &&
            bash <(curl -s https://codecov.io/bash)
        " || true
    fi
}

function check_iface_exist {
    exec_cmd "ip link | grep -q $1"
}

function prepare_network_environment {
    set +e
    exec_cmd "ip netns add ${VETH_PEER_NS}"
    for device in $INTERFACES;
    do
        peer="${device}peer"
        check_iface_exist $device
        if [ $? -eq 1 ]; then
            CREATED_INTERFACES="${CREATED_INTERFACES} ${device}"
            exec_cmd "ip link add ${device} type veth peer name ${peer}"
            exec_cmd "ip link set ${peer} netns ${VETH_PEER_NS}"
            exec_cmd "ip netns exec ${VETH_PEER_NS} ip link set ${peer} up"
            exec_cmd "ip link set ${device} up"
            exec_cmd "nmcli device set ${device} managed yes"
        fi
    done
    set -e
}

function teardown_network_environment {
    for device in $CREATED_INTERFACES;
    do
        exec_cmd "ip link del ${device}"
    done
    exec_cmd "ip netns del ${VETH_PEER_NS}"
}

function clean_dnf_cache {
    # Workaround for dnf failure:
    # [Errno 2] No such file or directory: '/var/cache/dnf/metadata_lock.pid'
    if [[ "$CI" == "true" ]];then
        exec_cmd "rm -fv /var/cache/dnf/metadata_lock.pid"
        exec_cmd "dnf clean all"
        exec_cmd "dnf makecache || :"
    fi
}

function upgrade_nm_from_copr {
    local copr_repo=$1
    # The repoid for a Copr repo is the name with the slash replaces by a colon
    local copr_repo_id="copr:copr.fedorainfracloud.org:${copr_repo/\//:}"
    clean_dnf_cache
    exec_cmd "command -v dnf && plugin='dnf-command(copr)' || plugin='yum-plugin-copr'; yum install --assumeyes \$plugin;"
    exec_cmd "yum copr enable --assumeyes ${copr_repo}"
    # Update only from Copr to limit the changes in the environment
    exec_cmd "yum update --assumeyes --disablerepo '*' --enablerepo '${copr_repo_id}'"
    exec_cmd "systemctl restart NetworkManager"
}

function run_customize_command {
    if [[ -n "$customize_cmd" ]];then
        clean_dnf_cache
        exec_cmd "${customize_cmd}"
    fi
}

options=$(getopt --options "" \
    --long customize:,pytest-args:,help,debug-shell,test-type:,el8,copr:,artifacts-dir:,test-vdsm,machine,use-installed-nmstate\
    -- "${@}")
eval set -- "$options"
while true; do
    case "$1" in
    --pytest-args)
        shift
        nmstate_pytest_extra_args="$1"
        ;;
    --copr)
        shift
        copr_repo="$1"
        ;;
    --customize)
        shift
        customize_cmd="$1"
        ;;
    --debug-shell)
        debug_exit_shell="1"
        ;;
    --test-type)
        shift
        TEST_TYPE="$1"
        ;;
    --el8)
        CONTAINER_IMAGE=$CENTOS_IMAGE_DEV
        ;;
    --centos-stream)
        CONTAINER_IMAGE=$CENTOS_STREAM_IMAGE_DEV
        ;;
    --artifacts-dir)
        shift
        EXPORT_DIR="$1"
        ;;
    --test-vdsm)
        vdsm_tests
        exit
        ;;
    --machine)
        RUN_BAREMETAL="true"
        ;;
    --use-installed-nmstate)
        INSTALL_NMSTATE="false"
        ;;
    --help)
        set +x
        echo -n "$0 [--copr=...] [--customize=...] [--debug-shell] [--el8] "
        echo -n "[--help] [--pytest-args=...] [--machine] "
        echo "[--use-installed-nmstate] [--test-type=<TEST_TYPE>] [--test-vdsm]"
        echo "    Valid TEST_TYPE are:"
        echo "     * $TEST_TYPE_ALL (default)"
        echo "     * $TEST_TYPE_FORMAT"
        echo "     * $TEST_TYPE_LINT"
        echo "     * $TEST_TYPE_INTEG"
        echo "     * $TEST_TYPE_INTEG_TIER1"
        echo "     * $TEST_TYPE_INTEG_TIER2"
        echo "     * $TEST_TYPE_INTEG_SLOW"
        echo "     * $TEST_TYPE_UNIT_PY36"
        echo "     * $TEST_TYPE_UNIT_PY38"
        echo -n "--customize allows to specify a command to customize the "
        echo "container before running the tests"
        exit
        ;;
    --)
        shift
        break
        ;;
    esac
    shift
done

: ${TEST_TYPE:=$TEST_TYPE_ALL}
: ${CONTAINER_IMAGE:=$FEDORA_IMAGE_DEV}
: ${INSTALL_NMSTATE:="true"}
: ${INSTALL_DEPS:="false"}

modprobe_ovs

if [ -n "${RUN_BAREMETAL}" ];then
    CONTAINER_WORKSPACE="."
    run_customize_command
    start_machine_services
else
    container_pre_test_setup
    run_customize_command
fi

if [[ -v copr_repo ]];then
    upgrade_nm_from_copr "${copr_repo}"
fi

check_services
prepare_network_environment

if [ -n "$RUN_BAREMETAL" ];then
    trap run_exit ERR EXIT
fi

exec_cmd '(source /etc/os-release; echo $PRETTY_NAME); rpm -q NetworkManager'

dump_network_info

pyclean
if [ -z ${RUN_BAREMETAL} ];then
    copy_workspace_container
fi

install_nmstate
run_tests
upload_coverage
