Test in VM: support arm32/integratorcp and ppc32
[ci.git] / test-in-vm.sh
blob2dfeb4d245d7d8f117bf35c2b58b160342af9486
1 #!/bin/bash
4 # Copyright (c) 2016 Vojtech Horky
5 # All rights reserved.
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
11 # - Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # - Redistributions in binary form must reproduce the above copyright
14 # notice, this list of conditions and the following disclaimer in the
15 # documentation and/or other materials provided with the distribution.
16 # - The name of the author may not be used to endorse or promote products
17 # derived from this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 # This code was originally inspired by "virtual testing lab" scripts created
33 # by Jan Buchar for testing his firewall implementation in HelenOS:
34 # https://code.launchpad.net/~teyras/hpf-virtlab/trunk
37 xx_echo() {
38 echo ">>" "$@"
41 xx_echo2() {
42 echo " -" "$@"
45 xx_debug() {
46 $XX_DEBUG_ECHO " :" "$@"
49 xx_debug_filter() {
50 sed 's#.*# : &#'
54 xx_run_debug() {
55 xx_debug "$@"
56 "$@"
59 xx_activity() {
60 $XX_ACTIVITY_ECHO " +" "$@"
63 xx_fatal() {
64 echo "!!" "$@"
65 xx_shutdown
68 ## Prepares pipe to QEMU monitor.
69 # Feed QEMU commands to stdin of this function.
71 # Do not forget to check for return code as checking it inside pipe
72 # is not very useful (we cannot terminate the script from that).
74 # @param 1 Machine id.
75 xx_do_qemu_command_pipe() {
76 socat STDIN "UNIX-CONNECT:$XX_TEMP/$1.monitor"
79 ## Sends command to QEMU monitor.
80 # This function terminates the scenario on failure of the monitoring pipe.
82 # @param 1 Machine id.
83 # @param 2 Command to send.
84 xx_do_qemu_command() {
85 xx_debug "echo '$2' | socat STDIN 'UNIX-CONNECT:$XX_TEMP/$1.monitor'"
86 echo "$2" | socat STDIN "UNIX-CONNECT:$XX_TEMP/$1.monitor"
87 res=$?
88 if [ $res -ne 0 ]; then
89 xx_shutdown
93 ## Types the text to a specific machine.
95 # @param 1 Machine id.
96 # @param 2 Text to type.
97 xx_do_type() {
98 ( echo "$2" | fold -w 1 | sed \
99 -e 's/ /spc/' \
100 -e 's/\./dot/' \
101 -e 's/-/minus/' \
102 -e 's/+/shift-equal/' \
103 -e 's/_/shift-minus/' \
104 -e 's/@/shift-2/' \
105 -e 's/:/shift-semicolon/' \
106 -e 's/\//slash/' \
107 -e 's/=/equal/' \
108 -e 's/|/shift-backslash/' \
109 -e 's/\([[:upper:]]\)/shift-\L\1/' \
110 -e 's/\\/backslash/' | \
111 while read code; do
112 xx_do_qemu_command "$1" "sendkey $code"
113 done
115 res=$?
116 if [ $res -ne 0 ]; then
117 xx_shutdown
121 xx_get_var() {
122 local varname="$1"
123 local default="$2"
124 shift 2
125 local i
126 for i in "$@"; do
127 case $i in
128 $varname=*)
129 echo "$i" | cut '-d=' -f 2-
130 return
134 esac
135 done
136 echo "$default"
139 xx_get_def_var() {
140 local i
141 for i in "$@"; do
142 echo "$i" | grep -v '^[a-zA-Z][a-zA-Z0-9_]*='
143 done
144 echo
147 xx_get_boolean_var() {
148 value=`xx_get_var "$@" | tr 'A-Z' 'a-z'`
149 if [ "$value" = "yes" -o "$value" = "true" ]; then
150 echo "true"
151 elif [ "$value" = "no" -o "$value" = "false" ]; then
152 echo "false"
153 else
154 echo
159 xx_shutdown() {
160 local i
161 for i in $XX_KNOWN_MACHINES; do
162 xx_stop_machine name="$i"
163 done
164 exit 1
167 xx_assert_var() {
168 if [ -z "$2" ]; then
169 xx_echo "Option $1 not specified or invalid, terminating."
170 xx_shutdown
174 xx_do_check_var() {
175 if [ -z "$2" ]; then
176 xx_fatal "Option $1 not specified with no suitable default."
178 if [ -n "$3" ]; then
179 if ! echo "$2" | grep -q "$3"; then
180 xx_fatal "Option $1 does not match '$3' (got '$2')."
185 xx_do_compute_timeout() {
186 echo $(( `date +%s` + 10 * $1 ))
190 xx_start_machine() {
191 local cdrom=`xx_get_var cdrom "$XX_CDROM_FILE" "$@"`
192 local name=`xx_get_var name default "$@"`
193 local wait_for_vterm=`xx_get_boolean_var vterm true "$@"`
195 xx_do_check_var "xx_start_machine/name" "$name" '^[a-zA-Z][0-9a-zA-Z]*$'
196 xx_do_check_var "xx_start_machine/cdrom" "$cdrom"
198 local extra_opts=""
199 if $XX_HEADLESS; then
200 extra_opts="-display none"
203 xx_echo "Starting machine $name from $cdrom."
205 local qemu_command=""
206 local qemu_opts=""
207 local qemu_common_network_options="-device e1000,vlan=0 -net user \
208 -redir udp:8080::8080 -redir udp:8081::8081 \
209 -redir tcp:8080::8080 -redir tcp:8081::8081 \
210 -redir tcp:2223::2223"
211 local has_grub=false
212 if [ "$XX_ARCH" == "ia32" ]; then
213 qemu_command=qemu-system-i386
214 qemu_opts="$qemu_common_network_options -usb -boot d -cdrom $cdrom"
215 if $XX_USE_KVM; then
216 extra_opts="$extra_opts -enable-kvm"
218 has_grub=true
219 elif [ "$XX_ARCH" == "amd64" ]; then
220 qemu_command=qemu-system-x86_64
221 qemu_opts="$qemu_common_network_options -usb -boot d -cdrom $cdrom"
222 if $XX_USE_KVM; then
223 extra_opts="$extra_opts -enable-kvm"
225 has_grub=true
226 elif [ "$XX_ARCH" == "arm32/integratorcp" ]; then
227 qemu_command=qemu-system-arm
228 qemu_opts="-M integratorcp -usb -kernel $cdrom"
229 elif [ "$XX_ARCH" == "ppc32" ]; then
230 qemu_command=qemu-system-ppc
231 qemu_opts="$qemu_common_network_options -usb -boot d -cdrom $cdrom"
233 if [ -z "$qemu_command" ]; then
234 xx_fatal "Unable to find proper emulator."
237 xx_run_debug $qemu_command \
238 $extra_opts \
239 $qemu_opts \
240 -m "${XX_MEMORY_MB}" \
241 -daemonize -pidfile "$XX_TEMP/$name.pid" \
242 -monitor "unix:$XX_TEMP/$name.monitor,server,nowait"
243 sleep 1
245 if $has_grub; then
246 xx_do_qemu_command "$name" "sendkey ret"
249 XX_LAST_MACHINE=$name
251 XX_KNOWN_MACHINES="$XX_KNOWN_MACHINES $name"
253 if $wait_for_vterm; then
254 xx_echo2 "Waiting for OS to boot into GUI..."
255 if ! xx_do_wait_for_text "$name" `xx_do_compute_timeout 60` "to see a few survival tips"; then
256 xx_fatal "OS have not booted into a known state."
264 xx_stop_machine() {
265 local name=`xx_get_var name $XX_LAST_MACHINE "$@"`
267 xx_echo "Forcefully killing machine $name."
268 if [ -e "$XX_TEMP/$name.monitor" ]; then
269 xx_do_qemu_command "$name" "quit"
271 sleep 1
272 kill -9 `cat "$XX_TEMP/$name.pid" 2>/dev/null` 2>/dev/null
274 if [ "$name" = "$XX_LAST_MACHINE" ]; then
275 XX_LAST_MACHINE=""
280 ## Wait for text to appear on machine console.
282 # @param 1 Machine id.
283 # @param 2 UNIX end-time (date %s + TIME_OUT).
284 # @param 3 Text to match.
285 xx_do_wait_for_text() {
286 while true; do
287 xx_activity "Taking screenshot, looking for '$3'."
289 xx_do_screenshot "$1" "$XX_TEMP/$1-full.ppm" \
290 "$XX_TEMP/$1-term.png" "$XX_TEMP/$1-term.txt"
292 if grep -q "$3" <"$XX_TEMP/$1-term.txt"; then
293 return 0
296 if [ `date +%s` -gt $2 ]; then
297 return 1
300 sleep 1
301 done
304 xx_assert() {
305 local timeout=`xx_get_var timeout $XX_DEFAULT_TIMEOUT "$@"`
306 local machine=`xx_get_var machine $XX_LAST_MACHINE "$@"`
307 local error_msg=`xx_get_var error "" "$@"`
308 local text=`xx_get_def_var "$@"`
310 xx_echo "Checking that '$text' will appear on $machine within ${timeout}s."
312 if ! xx_do_wait_for_text "$machine" `xx_do_compute_timeout $timeout` "$text"; then
313 xx_fatal "Failed to recognize '$text' on $machine."
317 xx_die_on() {
318 local timeout=`xx_get_var timeout $XX_DEFAULT_TIMEOUT "$@"`
319 local machine=`xx_get_var machine $XX_LAST_MACHINE "$@"`
320 local error_msg=`xx_get_var message "" "$@"`
321 local text=`xx_get_def_var "$@"`
323 xx_echo "Checking that '$text' will not appear on $machine within ${timeout}s."
325 if xx_do_wait_for_text "$machine" `xx_do_compute_timeout $timeout` "$text"; then
326 xx_fatal "Prohibited text '$text' spotted on $machine."
330 xx_sleep() {
331 local amount=`xx_get_def_var "$@"`
333 xx_echo "Waiting for ${amount}s."
334 sleep $amount
338 xx_cls() {
339 local machine=`xx_get_var machine $XX_LAST_MACHINE "$@"`
341 xx_echo "Clearing the screen on $machine."
342 for i in `seq 1 35`; do
343 xx_do_qemu_command "$machine" "sendkey ret"
344 done
345 sleep 1
348 xx_do_ocr_prepare() {
349 local image_width=`identify -format '%w' "$1"`
350 local image_height=`identify -format '%h' "$1"`
351 local correct_size="$(( ( $image_width / 8 ) * 8 ))x$(( ( $image_height / 16 ) * 16 ))"
352 convert "$1" -crop "$correct_size" +repage -crop "8x16" +repage +adjoin txt:- \
353 | sed -e 's|[0-9]*,[0-9]*: ([^)]*)[ ]*#\([0-9A-Fa-f]\{6\}\).*|\1|' -e 's:^#.*:@:' -e 's#000000#0#g' -e 's#FFFFFF#F#' \
354 | sed -e ':a' -e 'N;s#\n##;s#^@##;/@$/{s#@$##p;d}' -e 't a'
357 xx_do_ocr() {
358 local column_count="$(( `identify -format '%w' $1` / 8 ))"
359 local row_count="$(( `identify -format '%h' $1` / 16 ))"
360 # What we do in this magick pipeline:
361 # - convert the image to text, i.e. each pixel's color is converted to 0 or F (black/white)
362 # - do actual OCR - known sequences of pixels are replaced with single letter
363 # - replace unrecognized sequences with question mark (the line shall contain one letter only)
364 # - paste the character to form one long line
365 # - fold the line to get back the original terminal
366 # - keep only the first lines
367 xx_do_ocr_prepare "$1" \
368 | sed -f "$XX_MY_HOME/ocr.sed" \
369 | sed '/../s#.*#?#' \
370 | paste -sd '' \
371 | fold -w "$column_count" \
372 | head -n "$row_count"
375 xx_do_screenshot() {
376 xx_do_qemu_command "$1" "screendump $2"
377 if [ -n "$3" ]; then
378 # Looping as the screenshot might take some time to save
379 local retries=3
380 while true; do
381 if convert "$2" -crop 640x480+4+24 +repage -colors 2 -monochrome "$3"; then
382 break
384 retries=$(( $retries - 1 ))
385 if [ $retries -le 0 ]; then
386 xx_fatal "Failed to convert screen to monochrome."
388 sleep 1
389 done
391 if [ -n "$4" ]; then
392 xx_do_ocr "$3" >"$4"
393 if $XX_DUMP_TERMINAL; then
394 local print_it=false
395 if [ -e "$4.previous" ]; then
396 if ! cmp --quiet "$4.previous" "$4"; then
397 print_it=true
399 else
400 print_it=true
403 if $print_it; then
404 xx_debug "Terminal content:"
405 sed 's#.*# | &#' <"$4" | xx_debug_filter
407 cat "$4" >"$4.previous"
413 ## Wait for text to appear on machine console.
415 # @param 1 Machine id.
416 # @param 2 UNIX end-time (date %s + TIME_OUT).
417 # @param 3 Text that shall not be matched.
418 # @param 4 Text that shall be matched before timeout.
419 # @param 5 Consider prompt as a success.
420 # @param 6 Check for standard Bdsh error messages.
421 # @retval 0 Expected text was matched.
422 # @retval 1 Prompt text was matched.
423 # @retval 2 Time-out, nothing matched.
424 # @retval 3 Unexpected text was matched.
425 # @retval 4 Standard Bdsh error message detected.
426 xx_do_complex_text_waiting() {
427 xx_debug "waiting: $1/$2 match='$4', no-match='$3'"
428 xx_debug "waiting: $1/$2 prompt_is_success=$5 check_for_bdsh_error=$6"
430 while true; do
431 xx_activity "Taking screenshot, checking for specific output."
433 xx_do_screenshot "$1" "$XX_TEMP/$1-full.ppm" \
434 "$XX_TEMP/$1-term.png" "$XX_TEMP/$1-term.txt"
436 if [ -n "$3" ] && grep -q "$3" <"$XX_TEMP/$1-term.txt"; then
437 return 3
440 if [ -n "$4" ] && grep -q "$4" <"$XX_TEMP/$1-term.txt" ; then
441 return 0
444 if $6 && grep -q -e 'Cannot spawn' -e 'Command failed' <"$XX_TEMP/$1-term.txt"; then
445 return 4
448 if $5 && grep -q '^/[-a-zA-Z0-9_/.]* # _[ ]*$' <"$XX_TEMP/$1-term.txt" ; then
449 return 0
452 if [ `date +%s` -gt $2 ]; then
453 return 2
456 sleep 1
457 done
460 xx_cmd() {
461 local cmd=`xx_get_def_var "$@"`
462 local timeout=`xx_get_var timeout $XX_DEFAULT_TIMEOUT "$@"`
463 local machine=`xx_get_var machine $XX_LAST_MACHINE "$@"`
464 local error_msg=`xx_get_var error "" "$@"`
465 local text=`xx_get_var assert "" "$@"`
466 local negtext=`xx_get_var die_on "" "$@"`
467 local text_is_empty=`if [ -n "$text" ]; then echo false; else echo true; fi`
469 xx_echo "Sending '$cmd' to $machine."
470 xx_do_type "$machine" "$cmd"
471 xx_do_qemu_command "$machine" "sendkey ret"
474 xx_do_complex_text_waiting "$machine" `xx_do_compute_timeout $timeout` \
475 "$negtext" "$text" $text_is_empty true
476 local res=$?
478 xx_debug "xx_do_complex_text_waiting = $res"
480 case $res in
481 0|1)
482 return 0
485 if $text_is_empty; then
486 xx_fatal "Command timed-out."
487 else
488 xx_fatal "Failed to match '$text'."
491 3|4)
492 if [ -n "$error_msg" ]; then
493 xx_fatal "$error_msg"
494 else
495 xx_fatal "Command failed."
499 xx_fatal "Internal error, we shall never reach this line."
501 esac
506 xx_do_print_help() {
507 echo "Usage: $1 [options] scenario-file [scenarios-file ...]"
508 cat <<'EOF_USAGE'
509 where [options] are:
511 --help Print this help and exit.
512 --headless Hide the screen of the virtual machine.
513 --root=DIR Find HelenOS image in the source directory.
514 --image=FILE File with main HelenOS image (specify --arch).
515 --arch=ARCH Architecture of the image file (see --image).
516 --no-kvm Do not try to run QEMU with KVM enabled.
517 --memory=INT Size of VM RAM in INT MB.
518 --fail-fast Exit with first error.
519 --debug Print (a lot of) debugging messages.
520 --activity Print messages about background activity.
521 --dump-terminal Print contents of the vterm (implies --debug).
522 --train-ocr For experts: train OCR for vterm contents.
524 EOF_USAGE
528 XX_MY_HOME=`which -- "$0" 2>/dev/null`
529 # Maybe, we are running Bash
530 [ -z "$XX_MY_HOME" ] && XX_MY_HOME=`which -- "$BASH_SOURCE" 2>/dev/null`
531 XX_MY_HOME=`dirname -- "$XX_MY_HOME"`
533 XX_DEBUG_ECHO=:
534 XX_ACTIVITY_ECHO=:
535 XX_DUMP_TERMINAL=false
536 XX_TRAIN_OCR=false
537 XX_HEADLESS=false
538 XX_USE_KVM=true
539 XX_TEMP="$PWD/tmp-vm/"
540 XX_HELENOS_ROOT="."
541 XX_AUTODETECT_HELENOS=false
542 XX_ARCH=""
543 XX_CDROM_FILE=""
544 XX_MEMORY_MB=""
545 XX_FAIL_FAST=false
547 XX_KNOWN_MACHINES=""
548 XX_DEFAULT_TIMEOUT=5
549 XX_LAST_MACHINE=default
552 # Replace with getopt eventually.
553 while [ $# -gt 0 ]; do
554 case "$1" in
555 --headless)
556 XX_HEADLESS=true
558 --train-ocr)
559 XX_TRAIN_OCR=true
561 --train-ocr=*)
562 XX_TRAIN_OCR=true
563 XX_TRAIN_OCR_FILE=`echo "$1" | cut '-d=' -f 2-`
565 --debug)
566 XX_DEBUG_ECHO=echo
568 --activity)
569 XX_ACTIVITY_ECHO=echo
571 --dump-terminal)
572 XX_DUMP_TERMINAL=true
573 XX_DEBUG_ECHO=echo
575 --root=*)
576 XX_HELENOS_ROOT=`echo "$1" | cut '-d=' -f 2-`
577 XX_AUTODETECT_HELENOS=true
579 --image=*)
580 XX_CDROM_FILE=`echo "$1" | cut '-d=' -f 2-`
581 XX_AUTODETECT_HELENOS=false
583 --arch=*)
584 XX_ARCH=`echo "$1" | cut '-d=' -f 2-`
585 XX_AUTODETECT_HELENOS=false
587 --memory=*)
588 XX_MEMORY_MB=`echo "$1" | cut '-d=' -f 2-`
589 if ! echo "$XX_MEMORY_MB" | grep -q '^[1-9][0-9]*$'; then
590 xx_fatal "--memory has to be provided as an integer."
593 --temp=*)
594 XX_TEMP=`echo "$1" | cut '-d=' -f 2-`
596 --no-kvm)
597 XX_USE_KVM=false
599 --fail-fast)
600 XX_FAIL_FAST=true
602 --help|-h|-?)
603 xx_do_print_help "$0"
604 exit 0
606 --*)
607 xx_fatal "Unknown option $1."
610 break
612 esac
613 shift
614 done
616 [ -z "$XX_MEMORY_MB" ] && XX_MEMORY_MB=256
618 mkdir -p "$XX_TEMP"
620 if $XX_AUTODETECT_HELENOS; then
621 if ! [ -r "$XX_HELENOS_ROOT/Makefile.config" ]; then
622 xx_fatal "Cannot open $XX_HELENOS_ROOT/Makefile.config."
624 XX_ARCH=`grep '^PLATFORM = ' "$XX_HELENOS_ROOT/Makefile.config" | cut '-d=' -f 2- | tr -d ' '`
625 XX_CDROM_FILE=$XX_HELENOS_ROOT/`cat "$XX_HELENOS_ROOT/defaults/$XX_ARCH/output"`
629 if $XX_TRAIN_OCR; then
630 if [ -z "$XX_TRAIN_OCR_FILE" ]; then
631 echo "Usage notes for --train-ocr command-line switch."
632 echo
633 echo "Prepare HelenOS image.iso with train-ocr.txt in its root."
634 echo
635 echo "Run this script with the ISO and with --train-ocr=out.sed"
636 echo "where out.sed is output file with OCR commands."
637 echo
638 echo "If the script finishes with no error you can replace the"
639 echo "old OCR scheme (ocr.sed) with the new one in out.sed."
640 echo
641 exit
644 # Set to false to debug the script below without running QEMU
645 # all the time
646 if true; then
647 xx_start_machine name=ocr vterm=false
648 xx_echo "Will display the training set on the VM screen"
649 xx_echo "(this will take a while)."
650 sleep 20
651 xx_do_type ocr "cat train-ocr.txt"
652 sleep 1
653 xx_do_qemu_command ocr "sendkey ret"
654 sleep 5
655 xx_do_screenshot ocr "$XX_TEMP/ocr-full.ppm" "$XX_TEMP/ocr-term.png"
656 xx_stop_machine
659 xx_echo "Doing OCR..."
660 xx_do_ocr_prepare "$XX_TEMP/ocr-term.png" \
662 prev1_line=""
663 prev2_line=""
664 prev3_line=""
665 counter=0
666 while read current_line; do
667 if [ $counter -gt 5 ]; then
668 while read current_line; do
669 if ! [ "$current_line" = "$prev1_line" -o "$current_line" = "$prev2_line" ]; then
670 break
672 done
673 alphabet=`sed -n 's#^\(.\)\(.\)\(\1\2\)\{4,\}##p' train-ocr.txt`
674 alphabet_size=`echo "$alphabet" | wc -c`
675 for i in `seq 1 $(( $alphabet_size - 1))`; do
676 symbol="`echo \"$alphabet\" | fold -w 1 | sed -n \"${i}p\" | sed 's#[*\.\&\\:\/\[]\|\]#\\\\&#g'`"
677 echo "s:$current_line:$symbol:"
678 read current_line
679 done
680 echo "$current_line" | tr 'F' '0' | sed 's#.*#s:&:_:#'
681 echo "$current_line" | tr '0F' '.' | sed 's#.*#s:&:?:#'
682 break
684 if [ "$current_line" = "$prev2_line" -a "$prev1_line" = "$prev3_line" -a "$prev1_line" != "$prev2_line" ]; then
685 counter=$(( $counter + 1 ))
686 else
687 counter=0
689 prev3_line="$prev2_line"
690 prev2_line="$prev1_line"
691 prev1_line="$current_line"
692 done >"$XX_TRAIN_OCR_FILE"
694 xx_echo "New OCR scheme is in $XX_TRAIN_OCR_FILE."
695 exit
699 XX_RESULT_SUMMARY="$XX_TEMP/summary.$$.txt"
701 date '+# Execution started on %Y-%m-%d %H:%M.' >"$XX_RESULT_SUMMARY"
702 echo '# =======================================' >>"$XX_RESULT_SUMMARY"
705 # Run it
706 for i in "$@"; do
707 echo "# Starting scenario $i..."
709 . $i
710 for i in $XX_KNOWN_MACHINES; do
711 xx_stop_machine name="$i"
712 done
714 if [ $? -eq 0 ]; then
715 res="OK"
716 else
717 res="FAIL"
719 echo "# Scenario $i terminated, $res."
720 printf '%-35s %4s\n' "$i" "$res" >>"$XX_RESULT_SUMMARY"
721 if $XX_FAIL_FAST && [ "$res" = "FAIL" ]; then
722 exit 1
724 done
727 date '+# Execution finished on %Y-%m-%d %H:%M.' >>"$XX_RESULT_SUMMARY"
729 # Display the results.
730 echo
732 cat "$XX_RESULT_SUMMARY"