#!/bin/sh

should_run_test() {
    test_name="$1"

    if echo "$test_name"|grep -q _lwip; then
        if [ "$has_lwipovpn" = "no" ]; then
            return 1
        fi
    fi

    return 0
}

launch_client() {
    test_name=$1
    log="${test_name}.log"
    pid="${test_name}.pid"
    client_exec=$2
    client_conf=$3

    # Ensure that old log and pid files are gone
    rm -f "${log}" "${pid}"

    "${client_exec}" \
        $client_conf \
        --writepid "${pid}" \
        --setenv pid "$pid" \
        --setenv test_name "$test_name" \
        --log "${t_server_null_logdir}/${log}" &
}

ping_and_kill() {
    if fping -q -c 5 $1; then
        echo "PASS: fping lwipovpn client $target"
    else
        echo "FAIL: fping lwipovpn client $target"

        # This function runs multiple times in parallel in subshells. That
        # makes it hard to implement "fail the test suite if any single fping
        # test fails" using exit codes or variables given the limitations of
        # "wait".  Therefore we use a marker file here, which solves the
        # problem trivially.
        touch ./lwip_failed
    fi
    kill -15 $2
}

ping_lwip_clients() {
    if [ "$has_lwipovpn" = "yes" ]; then
        lwip_client_count=$(echo "$lwip_test_names"|wc -w|tr -d " ")
    else
        lwip_client_count=0
    fi

    if [ $lwip_client_count -eq 0 ]; then
        return 0
    fi

    count=0
    maxcount=10
    while [ $count -le $maxcount ]; do
        lwip_client_ips=$(cat ./*.lwip 2>/dev/null|wc -l)
        if [ $lwip_client_ips -lt $lwip_client_count ]; then
            echo "Waiting for LWIP clients to start up ($count/$maxcount)"
            count=$(( count + 1))
            sleep 1
        else
            echo "$lwip_client_ips/$lwip_client_count LWIP clients up"
            break
        fi
    done

    wait_pids=""
    for line in $(cat ./*.lwip 2>/dev/null); do
        target_ip=$(echo $line|cut -d "," -f 1)
        client_pid=$(echo $line|cut -d "," -f 2)
        ping_and_kill $target_ip $client_pid &
        wait_pids="$wait_pids $!"
    done

    wait $wait_pids

    test -e ./lwip_failed && return 1 || return 0
}

wait_for_results() {
    tests_running="yes"

    # Wait a bit to allow an OpenVPN client process to create a pidfile to
    # prevent exiting too early
    sleep 1

    while [ "${tests_running}" = "yes" ]; do
        tests_running="no"
        for t in $test_names; do
            if [ -f "${t}.pid" ]; then
                tests_running="yes"
            fi
        done

        if [ "${tests_running}" = "yes" ]; then
            echo "Clients still running"
            sleep 1
        fi
    done
}

get_client_test_result() {
    test_name=$1
    should_pass=$2
    log="${test_name}.log"

    grep "Initialization Sequence Completed" "${t_server_null_logdir}/${log}" > /dev/null
    exit_code=$?

    if [ $exit_code -eq 0 ] && [ "${should_pass}" = "yes" ]; then
        echo "PASS ${test_name}"
    elif [ $exit_code -eq 1 ] && [ "${should_pass}" = "no" ]; then
        echo "PASS ${test_name} (test failure)"
    elif [ $exit_code -eq 0 ] && [ "${should_pass}" = "no" ]; then
        echo "FAIL ${test_name} (test failure)"
        cat "${t_server_null_logdir}/${log}"
        retval=1
    elif [ $exit_code -eq 1 ] && [ "${should_pass}" = "yes" ]; then
        echo "FAIL ${test_name}"
        cat "${t_server_null_logdir}/${log}"
        retval=1
    fi
}

# Load basic/default tests
. ${srcdir}/t_server_null_default.rc || exit 1

# Load additional local tests, if any
test -r ./t_server_null.rc && . ./t_server_null.rc

# Return value for the entire test suite. Gets set to 1 if any test fails.
export retval=0

# Wait until servers are up. This check is based on the presence of processes
# matching the PIDs in each servers PID files
count=0
server_max_wait=15
while [ $count -lt $server_max_wait ]; do
    servers_up=0
    server_count=$(echo "$TEST_SERVER_LIST"|wc -w|tr -d " ")

    # We need to trim single-quotes because some shells return quoted values
    # and some don't. Using "set -o posix" which would resolve this problem is
    # not supported in all shells.
    #
    # While inactive server configurations may get checked they won't increase
    # the active server count as the processes won't be running.
    for i in $(set|grep 'SERVER_NAME_'|cut -d "=" -f 2|tr -d "[\']"); do
        server_pid=$(cat "$i.pid" 2> /dev/null)
        if [ -z "$server_pid" ] ; then
            continue
        fi
        if $RUN_SUDO kill -0 $server_pid > /dev/null 2>&1; then
            servers_up=$(( $servers_up + 1 ))
        fi
    done

    echo "OpenVPN test servers up: ${servers_up}/${server_count}"

    if [ $servers_up -ge $server_count ]; then
        retval=0
        break
    else
        count=$(( count + 1))
        sleep 1
    fi

    if [ $count -eq $server_max_wait ]; then
        retval=1
        exit $retval
    fi
done

# Check for presence of the lwipovpn executable
if test -r "$LWIPOVPN_PATH"; then
    has_lwipovpn="yes"
else
    has_lwipovpn="no"
    echo "WARNING: lwipovpn executable is missing: lwip tests will be skipped"
fi

# Remove existing LWIP client IP files. This is to avoid pinging non-existent
# IP addresses when tests are disabled.
rm -f ./*.lwip
rm -f ./lwip_failed

# Wait a while to let server processes to settle down
sleep 1

# Launch OpenVPN clients. While at it, construct a list of test names. The list
# is used later to determine when all OpenVPN clients have exited and it is
# safe to check the test results.
test_names=""
lwip_test_names=""
for SUF in $TEST_RUN_LIST
do
    eval test_name=\"\$TEST_NAME_$SUF\"
    eval client_exec=\"\$CLIENT_EXEC_$SUF\"
    eval client_conf=\"\$CLIENT_CONF_$SUF\"

    test_names="${test_names} ${test_name}"

    if echo "$test_name"|grep -q _lwip; then
        lwip_test_names="${lwip_test_names} ${test_name}"
    fi

    if should_run_test "$test_name"; then
        (launch_client "${test_name}" "${client_exec}" "${client_conf}")
    fi
done

ping_lwip_clients
retval=$?


# Wait until all OpenVPN clients have exited
(wait_for_results)

# Check test results
for SUF in $TEST_RUN_LIST
do
    eval test_name=\"\$TEST_NAME_$SUF\"
    eval should_pass=\"\$SHOULD_PASS_$SUF\"

    if should_run_test "$test_name"; then
        get_client_test_result "${test_name}" "${should_pass}"
    fi
done

exit $retval