#!/bin/sh # # SPDX-License-Identifier: AGPL-3.0-or-later # # PASST - Plug A Simple Socket Transport # for qemu/UNIX domain socket mode # # PASTA - Pack A Subtle Tap Abstraction # for network namespace/tap device mode # # test/lib/test - List tests and run them, evaluating directives from files # # Copyright (c) 2021 Red Hat GmbH # Author: Stefano Brivio # test_iperf3() - Ugly helper for iperf3c/iperf3s directives # $1: Role: client or server # $2: Pane name, can be lowercase # $3: Destination name or address for client # $4: Port number, ${i} is translated to process index # $5: Number of processes to run in parallel # $@: Options test_iperf3() { __role="${1}"; shift __pane="$(echo "${1}" | tr [a-z] [A-Z])"; shift [ "${__role}" = "client" ] && __dest="${1}" && shift || __dest="" __port="${1}"; shift __procs="$((${1} - 1))"; shift [ "${__role}" = "server" ] && __role_opt="-c" || __role_opt="-s1J" if [ ${__role} = "client" ]; then UDP_CLIENT=0 for __opt in ${@}; do [ "${__opt}" = "-u" ] && UDP_CLIENT=1 done ( sleep 2 pane_run "${__pane}" 'for i in $(seq 0 '${__procs}');' \ 'do ( iperf3 -c '"${__dest}"' -p '"${__port}" \ "${@}" ' -T s${i} & echo $! > c${i}.pid & ); done' sleep 40 pane_run "${__pane}" 'for i in $(seq 0 '${__procs}'); do'\ 'kill -INT $(cat c${i}.pid) 2>/dev/null; done' ) & return fi pane_run "${__pane}" 'for i in $(seq 0 '${__procs}'); do' \ ':> s${i}.bw; done' pane_status "${__pane}" if [ ${UDP_CLIENT} -eq 0 ]; then pane_run "${__pane}" 'for i in $(seq 0 '${__procs}');' \ 'do ( ( iperf3 -s1J -p '"${__port} ${@}" \ '& echo $! > s${i}.pid ) 2>/dev/null' \ '| jq -rM ".end.sum_received.bits_per_second"' \ '> s${i}.bw & );' \ 'done' else pane_run "${__pane}" 'for i in $(seq 0 '${__procs}');' \ 'do ( ( iperf3 -s1J -i 30 -p '"${__port} ${@}" \ '& echo $! > s${i}.pid ) 2>/dev/null' \ '| jq -rM ".intervals[0].sum.bits_per_second"' \ '> s${i}.bw & );' \ 'done' fi pane_status "${__pane}" sleep 45 pane_run "${__pane}" 'for i in $(seq 0 '${__procs}'); do' \ 'kill -INT $(cat s${i}.pid) 2>/dev/null; done' sleep 4 pane_wait "${__pane}" pane_run "${__pane}" '(cat s*.bw |' \ 'sed '"'"'s/\(.*\)/\1\+/g'"'"' |' \ 'tr -d "\n"; echo 0) | bc -l' pane_wait "${__pane}" pane_parse "${__pane}" pane_run "${__pane}" 'for i in $(seq 0 '${__procs}'); do' \ 'rm -f s${i}.bw; done' pane_status "${__pane}" } test_one_line() { __line="${1}" [ ${DEBUG} -eq 1 ] && info DEBUG: "${__line}" # Strip comments __line="${__line%%#*}" if [ -n "${TEST_ONE_in_def}" ]; then [ "${__line}" = "endef" ] && TEST_ONE_in_def= && return # Append $__line to variable TEST_ONE_DEF_ __ifs="${IFS}" IFS= eval TEST_ONE_DEF_$TEST_ONE_in_def=\"\$\(printf \"%s\\n%s\" \"\$TEST_ONE_DEF_$TEST_ONE_in_def\" \"$__line\"\)\" IFS="${__ifs}" return fi # tab-split command and arguments, apply variable substitutions __cmd="${__line%%$(printf '\t')*}" __arg="${__line#*$(printf '\t')*}" __arg="$(subs_apply "${TEST_ONE_subs}" "${__arg}")" [ ${TEST_ONE_nok} -eq 1 ] && [ "${__cmd}" != "test" ] && continue case ${__cmd} in "def") TEST_ONE_in_def="${__arg}" # Clear variable TEST_ONE_DEF_ __ifs="${IFS}" IFS= eval TEST_ONE_DEF_$TEST_ONE_in_def= IFS="${__ifs}" ;; "tempdir") __tmpdir="$(mktemp -d)" TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__arg}__" "${__tmpdir}")" TEST_ONE_dirclean="$(list_add "${TEST_ONE_dirclean}" "${__tmpdir}")" ;; "temp") __tmpfile="$(mktemp)" TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__arg}__" "${__tmpfile}")" TEST_ONE_dirclean="$(list_add "${TEST_ONE_dirclean}" "${__tmpfile}")" ;; "test") [ ${TEST_ONE_perf_nok} -eq 0 ] || TEST_ONE_nok=1 [ ${TEST_ONE_nok} -eq 1 ] && status_test_fail [ ${TEST_ONE_nok} -eq 0 ] && status_test_ok status_test_start "${__arg}" TEST_ONE_nok=0 TEST_ONE_perf_nok=0 ;; "host") pane_run HOST "${__arg}" pane_status HOST || TEST_ONE_nok=1 ;; "hostb") pane_run HOST "${__arg}" ;; "hostw") pane_status HOST || TEST_ONE_nok=1 ;; "hint") tmux send-keys -t ${PANE_HOST} "C-c" ;; "htools") pane_run HOST 'which '"${__arg}"' >/dev/null' pane_status HOST || TEST_ONE_skip=1 ;; "passt") pane_run PASST "${__arg}" pane_status PASST || TEST_ONE_nok=1 ;; "passtb") pane_run PASST "${__arg}" ;; "passtw") pane_status PASST || TEST_ONE_nok=1 ;; "pout") __varname="${__arg%% *}" pane_run PASST "${__arg#* }" pane_wait PASST TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__varname}__" "$(pane_parse PASST)")" ;; "guest") pane_run GUEST "${__arg}" pane_status GUEST || TEST_ONE_nok=1 ;; "guestb") pane_run GUEST "${__arg}" ;; "guestw") pane_status GUEST || TEST_ONE_nok=1 ;; "guest1") pane_run GUEST_1 "${__arg}" pane_status GUEST_1 || TEST_ONE_nok=1 ;; "guest1b") pane_run GUEST_1 "${__arg}" ;; "guest1w") pane_status GUEST_1 || TEST_ONE_nok=1 ;; "gtools") pane_run GUEST 'which '"${__arg}"' >/dev/null' pane_status GUEST || TEST_ONE_skip=1 ;; "g1tools") pane_run GUEST_1 'which '"${__arg}"' >/dev/null' pane_status GUEST_1 || TEST_ONE_skip=1 ;; "g2tools") pane_run GUEST_2 'which '"${__arg}"' >/dev/null' pane_status GUEST_2 || TEST_ONE_skip=1 ;; "guest2") pane_run GUEST_2 "${__arg}" pane_status GUEST_2 || TEST_ONE_nok=1 ;; "guest2b") pane_run GUEST_2 "${__arg}" ;; "guest2w") pane_status GUEST_2 || TEST_ONE_nok=1 ;; "ns") pane_run NS "${__arg}" pane_status NS || TEST_ONE_nok=1 ;; "ns1") pane_run NS1 "${__arg}" pane_status NS1 || TEST_ONE_nok=1 ;; "ns2") pane_run NS2 "${__arg}" pane_status NS2 || TEST_ONE_nok=1 ;; "nsb") pane_run NS "${__arg}" ;; "ns1b") pane_run NS1 "${__arg}" ;; "ns2b") pane_run NS2 "${__arg}" ;; "nsw") pane_status NS || TEST_ONE_nok=1 ;; "ns1w") pane_status NS1 || TEST_ONE_nok=1 ;; "ns2w") pane_status NS2 || TEST_ONE_nok=1 ;; "nstools") pane_run NS 'which '"${__arg}"' >/dev/null' pane_status NS || TEST_ONE_skip=1 ;; "gout") __varname="${__arg%% *}" pane_run GUEST "${__arg#* }" pane_wait GUEST TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__varname}__" "$(pane_parse GUEST)")" ;; "g1out") __varname="${__arg%% *}" pane_run GUEST_1 "${__arg#* }" pane_wait GUEST_1 TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__varname}__" "$(pane_parse GUEST_1)")" ;; "g2out") __varname="${__arg%% *}" pane_run GUEST_2 "${__arg#* }" pane_wait GUEST_2 TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__varname}__" "$(pane_parse GUEST_2)")" ;; "hout") __varname="${__arg%% *}" pane_run HOST "${__arg#* }" pane_wait HOST TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__varname}__" "$(pane_parse HOST)")" ;; "nsout") __varname="${__arg%% *}" pane_run NS "${__arg#* }" pane_wait NS TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__varname}__" "$(pane_parse NS)")" ;; "ns1out") __varname="${__arg%% *}" pane_run NS1 "${__arg#* }" pane_wait NS1 TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__varname}__" "$(pane_parse NS1)")" ;; "ns2out") __varname="${__arg%% *}" pane_run NS2 "${__arg#* }" pane_wait NS2 TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__varname}__" "$(pane_parse NS2)")" ;; "check") info_check "${__arg}" __nok=0 eval "${__arg} || __nok=1" if [ ${__nok} -eq 1 ]; then TEST_ONE_nok=1 info_check_failed else info_check_passed fi ;; "sleep") sleep "${__arg}" ;; "info") info "${__arg}" ;; "report") perf_report ${__arg} ;; "th") table_header ${__arg} ;; "tr") table_row "${__arg}" ;; "tl") table_line "${__arg}" ;; "te") table_end ;; "bw") table_value_throughput ${__arg} || TEST_ONE_perf_nok=1 ;; "lat") table_value_latency ${__arg} || TEST_ONE_perf_nok=1 ;; "iperf3c") test_iperf3 client ${__arg} ;; "iperf3s") TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__arg%% *}__" "$(test_iperf3 server ${__arg#* })" )" ;; "set") TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__arg%% *}__" "${__arg#* }")" ;; # Demo commands "say") text_write "${__arg}" ;; "em") em_write "${__arg}" ;; "nl") info_nolog "" ;; "hl") pane_highlight "${__arg}" ;; "bsp") text_backspace "${__arg}" ;; "killp") pane_kill "${__arg}" ;; "resize") pane_resize ${__arg} ;; *) __def_body="$(eval printf \"\$TEST_ONE_DEF_$__cmd\")" if [ -n "${__def_body}" ]; then __ifs="${IFS}" IFS=' ' for __def_line in ${__def_body}; do IFS= test_one_line "${__def_line}" done IFS="${__ifs}" fi ;; esac } # test_one() - Run a single test file evaluating directives # $1: Name of test file, relative to test/ directory test_one() { TEST_ONE_dirclean= __test_file="test/${1}" __type="$(file -b --mime-type ${__test_file})" if [ "${__type}" = "text/x-shellscript" ]; then status_file_start "${1}" 1 "${__test_file}" && status_test_ok || status_test_fail return fi if [ ${DEMO} -eq 0 ]; then __ntests="$(grep -c "^test$(printf '\t')" "${__test_file}")" status_file_start "${1}" "${__ntests}" fi [ ${CI} -eq 1 ] && video_link "${1}" TEST_ONE_subs="$(list_add_pair "" "__BASEPATH__" "${BASEPATH}")" TEST_ONE_nok=-1 TEST_ONE_perf_nok=0 TEST_ONE_skip=0 TEST_ONE_in_def= while IFS= read -r __line; do test_one_line "${__line}" [ ${TEST_ONE_skip} -eq 1 ] && break done < "${__test_file}" for __d in ${TEST_ONE_dirclean}; do rm -rf ${__d} done [ ${DEMO} -eq 1 ] && return [ ${TEST_ONE_skip} -eq 1 ] && status_test_skip && return [ ${TEST_ONE_perf_nok} -eq 0 ] || TEST_ONE_nok=1 [ ${TEST_ONE_nok} -eq 0 ] && status_test_ok || status_test_fail } # test() - Build list of tests to run, in order, then issue test_one() # $@: Test files to run, relative to test/ test() { __list= cd test for __f; do __type="$(file -b --mime-type ${__f})" if [ "${__type}" = "text/x-shellscript" ]; then __list="$(list_add "${__list}" "${__f}")" continue fi __list="$(list_add "${__list}" "${__f}")" done cd .. for __f in ${__list}; do test_one "${__f}" done }