xref: /kvm-unit-tests/scripts/runtime.bash (revision be704aff683c54fc108deaafacc7cb89ad0648d9)
1: "${RUNTIME_arch_run?}"
2: ${MAX_SMP:=$(getconf _NPROCESSORS_ONLN)}
3: ${TIMEOUT:=90s}
4
5PASS() { echo -ne "\e[32mPASS\e[0m"; }
6SKIP() { echo -ne "\e[33mSKIP\e[0m"; }
7FAIL() { echo -ne "\e[31mFAIL\e[0m"; }
8
9extract_summary()
10{
11    local cr=$'\r'
12    tail -3 | grep '^SUMMARY: ' | sed 's/^SUMMARY: /(/;s/'"$cr"'\{0,1\}$/)/'
13}
14
15# We assume that QEMU is going to work if it tried to load the kernel
16premature_failure()
17{
18    local log="$(eval $(get_cmdline _NO_FILE_4Uhere_) 2>&1)"
19
20    echo "$log" | grep "_NO_FILE_4Uhere_" |
21        grep -q -e "could not \(load\|open\) kernel" -e "error loading" &&
22        return 1
23
24    RUNTIME_log_stderr <<< "$log"
25
26    echo "$log"
27    return 0
28}
29
30get_cmdline()
31{
32    local kernel=$1
33    echo "TESTNAME=$testname TIMEOUT=$timeout ACCEL=$accel $RUNTIME_arch_run $kernel -smp $smp $opts"
34}
35
36skip_nodefault()
37{
38    [ "$run_all_tests" = "yes" ] && return 1
39    [ "$STANDALONE" != "yes" ] && return 0
40
41    while true; do
42        read -r -p "Test marked not to be run by default, are you sure (y/N)? " yn
43        case $yn in
44            "Y" | "y" | "Yes" | "yes")
45                return 1
46                ;;
47            "" | "N" | "n" | "No" | "no" | "q" | "quit" | "exit")
48                return 0
49                ;;
50        esac
51    done
52}
53
54function print_result()
55{
56    # output test results in a TAP format
57    # https://testanything.org/tap-version-13-specification.html
58
59    local status="$1"
60    local testname="$2"
61    local summary="$3"
62    local reason="$4"
63
64    if [ -z "$reason" ]; then
65        echo "`$status` $testname $summary"
66    else
67        echo "`$status` $testname ($reason)"
68    fi
69}
70
71function find_word()
72{
73    grep -Fq " $1 " <<< " $2 "
74}
75
76function run()
77{
78    local testname="$1"
79    local groups="$2"
80    local smp="$3"
81    local kernel="$4"
82    local opts="$5"
83    local arch="$6"
84    local check="${CHECK:-$7}"
85    local accel="$8"
86    local timeout="${9:-$TIMEOUT}" # unittests.cfg overrides the default
87
88    if [ -z "$testname" ]; then
89        return
90    fi
91
92    if [ -n "$only_tests" ] && ! find_word "$testname" "$only_tests"; then
93        return
94    fi
95
96    if [ -n "$only_group" ] && ! find_word "$only_group" "$groups"; then
97        return
98    fi
99
100    if [ -z "$only_group" ] && find_word nodefault "$groups" &&
101            skip_nodefault; then
102        print_result "SKIP" $testname "" "test marked as manual run only"
103        return;
104    fi
105
106    if [ -n "$arch" ] && [ "$arch" != "$ARCH" ]; then
107        print_result "SKIP" $testname "" "$arch only"
108        return 2
109    fi
110
111    if [ -n "$accel" ] && [ -n "$ACCEL" ] && [ "$accel" != "$ACCEL" ]; then
112        print_result "SKIP" $testname "" "$accel only, but ACCEL=$ACCEL"
113        return 2
114    elif [ -n "$ACCEL" ]; then
115        accel="$ACCEL"
116    fi
117
118    # check a file for a particular value before running a test
119    # the check line can contain multiple files to check separated by a space
120    # but each check parameter needs to be of the form <path>=<value>
121    for check_param in "${check[@]}"; do
122        path=${check_param%%=*}
123        value=${check_param#*=}
124        if [ "$path" ] && [ "$(cat $path)" != "$value" ]; then
125            print_result "SKIP" $testname "" "$path not equal to $value"
126            return 2
127        fi
128    done
129
130    last_line=$(premature_failure > >(tail -1)) && {
131        print_result "SKIP" $testname "" "$last_line"
132        return 77
133    }
134
135    cmdline=$(get_cmdline $kernel)
136    if grep -qw "migration" <<<$groups ; then
137        cmdline="MIGRATION=yes $cmdline"
138    fi
139    if [ "$verbose" = "yes" ]; then
140        echo $cmdline
141    fi
142
143    # extra_params in the config file may contain backticks that need to be
144    # expanded, so use eval to start qemu.  Use "> >(foo)" instead of a pipe to
145    # preserve the exit status.
146    summary=$(eval $cmdline 2> >(RUNTIME_log_stderr) \
147                             > >(tee >(RUNTIME_log_stdout $kernel) | extract_summary))
148    ret=$?
149    [ "$STANDALONE" != "yes" ] && echo > >(RUNTIME_log_stdout $kernel)
150
151    if [ $ret -eq 0 ]; then
152        print_result "PASS" $testname "$summary"
153    elif [ $ret -eq 77 ]; then
154        print_result "SKIP" $testname "$summary"
155    elif [ $ret -eq 124 ]; then
156        print_result "FAIL" $testname "" "timeout; duration=$timeout"
157    elif [ $ret -gt 127 ]; then
158        print_result "FAIL" $testname "" "terminated on SIG$(kill -l $(($ret - 128)))"
159    else
160        print_result "FAIL" $testname "$summary"
161    fi
162
163    return $ret
164}
165
166#
167# Probe for MAX_SMP, in case it's less than the number of host cpus.
168#
169# This probing currently only works for ARM, as x86 bails on another
170# error first. Also, this probing isn't necessary for any ARM hosts
171# running kernels later than v4.3, i.e. those including ef748917b52
172# "arm/arm64: KVM: Remove 'config KVM_ARM_MAX_VCPUS'". So, at some
173# point when maintaining the while loop gets too tiresome, we can
174# just remove it...
175while $RUNTIME_arch_run _NO_FILE_4Uhere_ -smp $MAX_SMP \
176		|& grep -qi 'exceeds max CPUs'; do
177	MAX_SMP=$((MAX_SMP >> 1))
178done
179