run_tests.sh 49.9 KB
Newer Older
1
#!/usr/bin/env bash
2
3
4

# Emulator tests
# This also shows the emulation state on various cameras
5
6
# usage: 
#   ./run_test.sh                   # test all models
7
8
#   ./run_test.sh 5D3 EOSM EOSM3    # test only specific models (uppercase arguments = camera models)
#   ./run_test.sh 5D3 drysh menu    # run only the selected tests/models (lowercase arguments = test names)
9

10
11
# Caveat: this assumes no other qemu-system-arm or
# arm-none-eabi-gdb processes are running during the tests
12

13
14
EOS_CAMS=( 5D 5D2 5D3 5D4 6D 6D2 7D 7D2M
           40D 50D 60D 70D 77D 80D
15
           400D 450D 500D 550D 600D 650D 700D 750D 760D 800D
16
           100D 200D 1000D 1100D 1200D 1300D EOSM EOSM2 )
17

18
POWERSHOT_CAMS=( EOSM3 EOSM10 EOSM5 A1100 )
19

20
EOS_SECONDARY_CORES=( 5D3eeko 5D4AE 7D2S )
21

22
# cameras able to run Canon GUI (menu tests)
alex@thinkpad's avatar
alex@thinkpad committed
23
GUI_CAMS=( 5D2 5D3 6D 40D 50D 60D 70D
alex@thinkpad's avatar
alex@thinkpad committed
24
           450D 500D 550D 600D 650D 700D
alex@thinkpad's avatar
alex@thinkpad committed
25
           100D 1000D 1100D 1200D 1300D EOSM EOSM2 )
alex@thinkpad's avatar
alex@thinkpad committed
26

27
# cameras with a SD card
28
SD_CAMS=( 5D3 5D4 6D 6D2 60D 70D 77D 80D
29
          450D 500D 550D 600D 650D 700D 750D 760D 800D
30
          100D 200D 1000D 1100D 1200D 1300D EOSM EOSM2 )
alex@thinkpad's avatar
alex@thinkpad committed
31

32
# cameras with a CF card
33
CF_CAMS=( 5D 5D2 5D3 5D4 7D 7D2M 40D 50D 400D )
34

35
36
# cameras able to run the FA_CaptureTestImage test (full-res silent picture backend)
FRSP_CAMS=( 5D3 500D 550D 50D 60D 1100D 1200D )
37

38
39
ML_PATH=${ML_PATH:=../magic-lantern}

40
41
42
43
44
45
46
47
48
49
50
51
52
53
# cameras not yet in mainline
declare -A CAM_BRANCH
CAM_BRANCH=( 
    [5D]=vxworks    [40D]=vxworks   [450D]=vxworks  [1000D]=vxworks
    [1200D]=1200D                   [1300D]=1300D
    [100D]=100D_merge_fw101         [70D]=70D_merge_fw112
#   [EOSM2]=EOSM2.103_wip
    [80D]=digic6-dumper     [750D]=digic6-dumper    [760D]=digic6-dumper
    [7D2]=digic6-dumper     [5D4]=digic6-dumper     [5DS]=digic6-dumper
    [77D]=digic6-dumper     [6D2]=digic6-dumper
    [200D]=digic6-dumper    [800D]=digic6-dumper
    [M50]=digic6-dumper
)

54
55
56
57
58
59
# newer openbsd netcat requires -N (since 1.111)
# older openbsd netcat does not have -N (prints error if we attempt to use it)
# try to autodetect which one should be used, and let the user override it
NC=${NC:=$(nc -h |& grep -q -- -N && echo "nc -N" || echo "nc")}
echo "Using netcat: $NC"

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
function has_upper_args {
    for arg in "$@"; do
        if [ "$arg" == "${arg^^}" ]; then
            return 0
        fi
    done
    return 1
}

function has_lower_args {
    for arg in "$@"; do
        if [ "$arg" == "${arg,,}" ]; then
            return 0
        fi
    done
    return 1
}

if (( $# > 0 )) && has_upper_args "$@"; then
    # uppercase arguments present? test only these models
80
81
82
83
84
85
86
    # fixme: nicer way to do the same? (intersection between arguments and the lists of supported models)
    REQ_CAMS=( $* )
    EOS_CAMS=($(join <(printf %s\\n "${REQ_CAMS[@]}" | sort -u) <(printf %s\\n "${EOS_CAMS[@]}" | sort -u) | sort -n))
    POWERSHOT_CAMS=($(join <(printf %s\\n "${REQ_CAMS[@]}" | sort -u) <(printf %s\\n "${POWERSHOT_CAMS[@]}" | sort -u) | sort -n))
    GUI_CAMS=($(join <(printf %s\\n "${REQ_CAMS[@]}" | sort -u) <(printf %s\\n "${GUI_CAMS[@]}" | sort -u) | sort -n))
    SD_CAMS=($(join <(printf %s\\n "${REQ_CAMS[@]}" | sort -u) <(printf %s\\n "${SD_CAMS[@]}" | sort -u) | sort -n))
    CF_CAMS=($(join <(printf %s\\n "${REQ_CAMS[@]}" | sort -u) <(printf %s\\n "${CF_CAMS[@]}" | sort -u) | sort -n))
87
    FRSP_CAMS=($(join <(printf %s\\n "${FRSP_CAMS[@]}" | sort -u) <(printf %s\\n "${FRSP_CAMS[@]}" | sort -u) | sort -n))
88
    EOS_SECONDARY_CORES=($(join <(printf %s\\n "${REQ_CAMS[@]}" | sort -u) <(printf %s\\n "${EOS_SECONDARY_CORES[@]}" | sort -u) | sort -n))
89
90
fi

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# lowercase args are test names
function test_selected {
    local test=$1
    shift
    if has_lower_args ${BASH_ARGV[@]}; then
        # lowercase args present?
        for arg in ${BASH_ARGV[@]}; do
            if [ "$arg" == "$test" ]; then
                # current test selected
                return 0
            fi
        done

        # this test is not among the lowercase args? skip it
        return 1
    fi

    # no lowercase args? run all tests
    return 0
}

112
declare -A MENU_SEQUENCE
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
MENU_SEQUENCE[5D2]="f1 i i i m up up up space m m w w 9 9 9 0 0 0 0 0 p p l i i p p m m l"
MENU_SEQUENCE[5D3]="f1 i i i f1 i m left down down down space m m p p q space m right right space pgup m down space ] ] ] m q 9 q left space pgdn pgdn m down space i ] m l p p i i q down space space q right up 0 9 9 0 l 9 9 0 0 0 0 q"
MENU_SEQUENCE[6D]="m left left up space space down space m right down space down space up up space down space m i i i q right space right space space space m m left space ] m up space right m left left space right right right m q 9 9 9 p p" # LV works too, but shows a RAM dump instead of raw buffer => nondeterministic screenshots
MENU_SEQUENCE[40D]="m left down down down space up space up space m left space space left up up space space down space m m i i p p space w space space w p" # mode switch not working; LiveView works, but has to be enabled from menu first
MENU_SEQUENCE[50D]="f1 i i i m up space space m w w 0 0 0 l i i w pgdn ] ] i pgup [ w l 0 0 p p" # screen flickers
MENU_SEQUENCE[60D]="f1 i i i i m left left up space m m i i q up right space right i down down left left left space right right i 9 up space left left left i 0 q 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 q up q m m 9 l q l"
MENU_SEQUENCE[70D]="m left down space down space down down down space down space m p p i i i i i q up up up space right right i right space ] ] m right space i m down space left m q 9 q left space right right right m q 9 q q 9 q q l p p i q down p"
MENU_SEQUENCE[450D]="f1 m up up space m left left i p p w down down space m down space down space m right right space 9 m j m space w space space"
MENU_SEQUENCE[500D]="f1 m i i right right up m right right space 0 0 0 0 0 0 0 0 0 0 0 0 l i p l"
MENU_SEQUENCE[550D]="m i i right right down down down space space p p l i q p" # info screen not working, could not test mode switch
MENU_SEQUENCE[600D]="i i m right right right right up space down down space up up space down space m p p q up right space right i up right space right i 9 down left space right right right i q 9 q 9 q l i i q down space space q 0" # starts with sensor cleaning animation; no info screen unless we enable it
MENU_SEQUENCE[650D]="f1 m right right p p l p p i i 9 q space" # starts in movie mode, no lens; mode switch only works in LiveView, but can't get out
MENU_SEQUENCE[700D]="f1 m right down space right space p p l p p i i q up space" # same as 650D
MENU_SEQUENCE[100D]="f1 i i i m left left left up up left left left left left left left up up space up space p p 9 q down space left i up up up space left left i right space right i 9 q 9 l p p i i q q l q"
MENU_SEQUENCE[1000D]="f1 i w w i p p m space space up up space m left i m m right left space 9 9 0 0 0 m j m space i i space"
MENU_SEQUENCE[1100D]="f1 i i m i i left m p p down right space right right space up right space 9 q 9 q 9 l i i q space right space q l" # drive mode not working
MENU_SEQUENCE[1200D]="f1 i i m i i space m m p p down right space right right space up right space 9 q 9 q 9 l i q space [ space q l" # drive mode not working
MENU_SEQUENCE[1300D]="f1 i i m i i up up up space m up up space right space down space i m space space m left up space down space m left right space right left space q right space right i q up p p 9 q 9 q 9 l i i q space l"
MENU_SEQUENCE[EOSM]="m up up up space m up space m up space m left down down down space space p p 0 9 9 9 m m i i p p space" # starts in LiveView; Q button not working
MENU_SEQUENCE[EOSM2]="m space space space up up space m up space m up space m up space m right space space m m m i q q 9 0 space i i m" # starts in LiveView
133

134
135
FMT_SEQ="space right space f1 space"
FMT_SEQ_5D3="space space right space f1 space space"
136
137
138
# these are customized for my ROM dumps (keys required to select the Format menu)
# TODO: some generic way to navigate to Format menu?
declare -A FORMAT_SEQUENCE
139
FORMAT_SEQUENCE[5D2]="m right right right right $FMT_SEQ"
140
FORMAT_SEQUENCE[5D3]="m left left left left $FMT_SEQ_5D3"
alex@thinkpad's avatar
alex@thinkpad committed
141
FORMAT_SEQUENCE[6D]="m left left left $FMT_SEQ"
alex@thinkpad's avatar
alex@thinkpad committed
142
FORMAT_SEQUENCE[40D]="m left left left left $FMT_SEQ"
143
FORMAT_SEQUENCE[50D]="m down down $FMT_SEQ"
alex@thinkpad's avatar
alex@thinkpad committed
144
FORMAT_SEQUENCE[60D]="m left left left left $FMT_SEQ"
alex@thinkpad's avatar
alex@thinkpad committed
145
FORMAT_SEQUENCE[70D]="m left left left $FMT_SEQ"
alex@thinkpad's avatar
alex@thinkpad committed
146
FORMAT_SEQUENCE[450D]="m left left $FMT_SEQ"                # fixme: locks up
147
148
149
FORMAT_SEQUENCE[500D]="m $FMT_SEQ"
FORMAT_SEQUENCE[550D]="m $FMT_SEQ"
FORMAT_SEQUENCE[600D]="m right right right $FMT_SEQ"
150
FORMAT_SEQUENCE[650D]="m $FMT_SEQ"                          # fixme: free space wrong before format
151
FORMAT_SEQUENCE[700D]="m left left left $FMT_SEQ"           # fixme: free space wrong before format
152
FORMAT_SEQUENCE[100D]="m $FMT_SEQ"                          # fixme: free space wrong before format
alex@thinkpad's avatar
alex@thinkpad committed
153
FORMAT_SEQUENCE[1000D]="m left left $FMT_SEQ"               # fixme: locks up
154
155
FORMAT_SEQUENCE[1100D]="m right right down $FMT_SEQ"
FORMAT_SEQUENCE[1200D]="m left left $FMT_SEQ"
alex@thinkpad's avatar
alex@thinkpad committed
156
FORMAT_SEQUENCE[1300D]="m left left down down $FMT_SEQ"
alex@thinkpad's avatar
alex@thinkpad committed
157
FORMAT_SEQUENCE[EOSM]="m left left left $FMT_SEQ"
158
FORMAT_SEQUENCE[EOSM2]="m left left left up $FMT_SEQ"
159

alex@thinkpad's avatar
alex@thinkpad committed
160
161
162
163
164
165
166
167
168
169
function set_gui_timeout {
    if [ $CAM == "100D" ]; then
        # 100D appears slower, for some reason
        GUI_TIMEOUT=10
    else
        # 500D needs less than 2 seconds; let's be a bit conservative
        GUI_TIMEOUT=5
    fi
}

170
# this script runs from qemu/tests/ so we have to go up one level
171
172
cd ..

173
174
175
# We will use mtools to alter and check the SD/CF image contents.
. ./mtools_setup.sh

176
echo "Compiling..."
177
./run_canon_fw.sh help &> build.log || { cat build.log; exit 1; }
178
179
180
181

# don't recompile each time (for speed)
export MAKE="echo skipping make"

182
183
184
185
186
187
188
189
# Most of the tests require custom SD/CF card imags.
# The one that comes with our QEMU install script is perfect.
echo
echo "Setting up temporary SD/CF card images..."

function sd_restore {
  trap '' SIGINT
  echo
190
191
  echo "Cleaning up..."
  cleanup
192
193
194
195
196
197
198
199
200
201
202
203
204
  echo "Restoring your SD/CF card images..."
  mv sd-user.img sd.img
  mv cf-user.img cf.img
  trap - SIGINT
}

# disable CTRL-C while moving the files
trap '' SIGINT
mv sd.img sd-user.img
mv cf.img cf-user.img
trap sd_restore EXIT
trap - SIGINT

205
cp -v $ML_PATH/contrib/qemu/sd.img.xz .
206
207
208
unxz -k sd.img.xz
cp sd.img cf.img

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# checksum of reference SD/CF images
sd_checksum=`md5sum sd.img`
cf_checksum=`md5sum cf.img`

# optional argument: -q = quiet
function sd_cf_restore_if_modified {
    # compute the checksums of SD and CF images in parallel
    local checksums="$( md5sum sd.img & md5sum cf.img & )"
    sd_new_checksum=$(echo "$checksums" | grep sd.img)
    cf_new_checksum=$(echo "$checksums" | grep cf.img)

    if [ "$sd_new_checksum" != "$sd_checksum" ]; then
        [ "$1" == "-q" ] || echo -n " SD modified "
        rm sd.img
        unxz -k sd.img.xz
    fi
    if [ "$cf_new_checksum" != "$cf_checksum" ]; then
        [ "$1" == "-q" ] || echo -n " CF modified "
        rm cf.img
        cp sd.img cf.img
    fi
}

232
233
234
235
# send a keypress and wait for the screen to match a checksum
# save the screenshot, regardless of match result
# vncexpect key md5 timeout capture
function vncexpect {
236
237

    # remove output file, just in case
238
    rm -f $4
239
240
241

    # do everything in one command, for speed
    local vnc_log=$(vncdotool -s $VNC_DISP -v -v -t $3 key $1 pause 0.5 expect-md5 $2 capture $4 2>&1)
242

243
244
245
246
247
248
249
250
251
252
253
254
    if [ $? == 0 ] && [ -f $4 ]; then

        # looks OK?
        if [ "$2" == "$(md5sum $4 | cut -d ' ' -f 1)" ]; then
            echo -n "."
            return 0
        else
            # doesn't always work - race condition?
            echo -ne "\e[31m¿\e[0m"
            return 1
        fi

255
    else
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

        # something went wrong

        # if the above timed out, it may have skipped the "capture" part - retry it now
        if [ ! -f $4 ]; then
            vncdotool -s $VNC_DISP capture $4 &> /dev/null

            if [ ! -f $4 ]; then
                # screenshot not taken - QEMU crashed?
                echo -n "X"
                return 2
            fi

            # it might have even worked!
            # why does it always happen on the first screenshot from 1000D?!
            if [ "$2" == "$(md5sum $4 | cut -d ' ' -f 1)" ]; then
                echo -n ","
                #echo "$vnc_log"
                return 0
            fi
        fi

        echo -ne "\e[31m!\e[0m"

        # print some debugging info
        if false; then
            echo "$2  expected"
            md5sum $4
            echo "$vnc_log"
        fi

287
        return 1
288

289
290
291
    fi
}

292
293
294
295
296
297
298
# just stop QEMU (current instance only) without shutting down DryOS
# to be used when emulation is not expected to run the GUI
function stop_qemu_expect_running {
    echo "quit" | $NC -U $QEMU_MONITOR &> /dev/null \
        && sleep 0.5 \
        || echo -e "\e[31mQEMU not running\e[0m"
}
299

300
301
302
303
304
function stop_qemu_expect_not_running {
    echo "quit" | $NC -U $QEMU_MONITOR &> /dev/null \
        && echo -e "\e[31mQEMU still running\e[0m" \
        && sleep 0.5
}
305

306
307
308
309
310
311
# shutdown DryOS cleanly and stop qemu (current instance only)
# if shutdown was not clean, print a warning
# to be used when emulation goes as far as booting the GUI
function shutdown_qemu {
    echo "system_powerdown" | $NC -U $QEMU_MONITOR &> /dev/null \
        || echo -en "\e[31mQEMU not running\e[0m " \
312
        && sleep 2 && stop_qemu_expect_not_running
313
314
315
316
317
318
319
320
321
}

# kill all instances of qemu-system-arm / arm-none-eabi-gdb
# only used if graceful shutdown didn't work (see above)
# fixme: only kill the QEMU processes started by us (how?)
function kill_qemu {

    if pidof qemu-system-arm > /dev/null; then
        echo -e "\e[31mQEMU still running"
322
323
    fi

324
325
326
    if killall -TERM -w qemu-system-arm 2>/dev/null; then
        sleep 1
    fi
327
328
329

    if pidof arm-none-eabi-gdb > /dev/null; then
        echo -e "\e[31mGDB still running\e[0m"
330
331
332
333

        # for some reason, -TERM may hang up here
        # but should be unreachable normally
        killall -9 -w arm-none-eabi-gdb 2>/dev/null
334
    fi
335
336
}

337
338
339
# just to be sure
kill_qemu expect_not_running

340
341
export QEMU_JOB_ID=0

342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# tried GNU parallel (kinda works, trouble with functions using arrays)
# tried Rust parallel (unable to call bash functions)
# this one is in plain bash, so bash functions just work :)
# https://stackoverflow.com/a/12436838
# https://stackoverflow.com/a/33048123
function job_limit {
    local max_number=$((0 + ${1:-0}))
    while true; do
        local current_number=$(jobs -pr | wc -l)
        if [[ $current_number -lt $max_number ]]; then
            break
        fi
        sleep 0.5
    done
}

# experiment - keep the "load" close to number of CPUs
# will throttle if other jobs are running on the same machine
# and use the full power if the machine is idle
function job_limit_auto {
    if [[ $(jobs -pr | wc -l) -gt 1 ]]; then
        # delay checking if we started more than one background job
        # trick to avoid undetected overloads and also to avoid slowdowns when there's nothing to do
        sleep 0.2
    fi
    local max_number=$(nproc)
    while true; do
        # check how many processes are running on the system before deciding to start a new job
        local procs_running=$(cat /proc/stat | grep procs_running | cut -d ' ' -f 2)
        local procs_blocked=$(cat /proc/stat | grep procs_blocked | cut -d ' ' -f 2)
        local current_number=$((procs_running + procs_blocked))
        if [[ $current_number -lt $max_number ]]; then
            break
        fi
        sleep 0.5
    done
}

380
381
382
383
384
# generic helper for running tests
# arguments: 
# - test function name (without the test_ prefix)
# - CAM (camera name)
# - test name override (optional; derived from test function name by default)
385
# QEMU_JOB_ID must be a small positive integer, unique for each job that might run in parallel
386
function run_test {
387
388
389
390
    if [ "$QEMU_JOB_ID" == "0" ]; then
        echo "You forgot to increment QEMU_JOB_ID ;)"
        exit 1
    fi
391
392
393
394

    # global variables used in tests
    TEST=${3:-${1//_/-}}
    CAM=$2
395
396
397
    GDB_PORT=$((1234+$QEMU_JOB_ID))
    VNC_DISP=":$((12345+QEMU_JOB_ID))"
    QEMU_MONITOR=qemu.monitor$QEMU_JOB_ID
398

399
400
401
402
403
    if ! test_selected $TEST; then
        # can we skip this test?
        return
    fi

404
405
406
407
408
409
410
    # make sure the test subdirectory is present
    # and remove any previous logs or screenshots from this test
    mkdir -p tests/$CAM/
    rm -f tests/$CAM/$TEST*.log
    rm -f tests/$CAM/$TEST*.png
    rm -f tests/$CAM/$TEST*.ppm

411
    # run the test function and buffer its output
412
    ( printf "%7s: " $CAM && test_$1 ) 2>&1 | sponge
413
414
415

    # local cleanup
    rm -f $QEMU_MONITOR
416
417
418
419
}

# called after running each set of tests
function cleanup {
420
    QEMU_JOB_ID=0
421
    wait
422
    kill_qemu expect_not_running
423
    sd_cf_restore_if_modified -q
424
    rm -f qemu.monitor[0-9]*
425
426
}

427

428
# All cameras booting the GUI should be able to navigate Canon menu:
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446

# arguments: delay after each key, timeout waiting for a given screen, key sequence (a single argument)
function send_menu_sequence {
    # note: these should also work via qemu.monitor
    # (slightly different keycodes and only PPM screenshots available)
    local count=0;
    for key in $3; do
        # send the key and wait until the expected screen comes up
        local PNG=$TEST$((count++)).png
        local MD5=$(cat tests/$CAM/$TEST.md5 2>/dev/null | grep $PNG | cut -d ' ' -f 1)
        if [ -z "$MD5" ]; then
            vncdotool -s $VNC_DISP key $key pause $1 capture tests/$CAM/$PNG; echo -n '?'
        else
            vncexpect $key $MD5 $2 tests/$CAM/$PNG
        fi
    done
}

447
448
449
450
451
452
453
454
455
456
457
function test_menu {

    if [ -f $CAM/patches.gdb ]; then
        (
            ./run_canon_fw.sh $CAM,firmware="boot=0" -snapshot -vnc $VNC_DISP -d debugmsg -S -gdb tcp::$GDB_PORT &
            arm-none-eabi-gdb -ex "set \$TCP_PORT=$GDB_PORT" -x $CAM/patches.gdb -ex quit &
        ) &> tests/$CAM/$TEST.log
    else
        (
            ./run_canon_fw.sh $CAM,firmware="boot=0" -snapshot -vnc $VNC_DISP -d debugmsg &
        ) &> tests/$CAM/$TEST.log
458
459
    fi

460
461
462
    set_gui_timeout
    sleep $GUI_TIMEOUT

463
    send_menu_sequence 0.5 3 "${MENU_SEQUENCE[$CAM]}"
464
465
466
467
468
469

    shutdown_qemu

    tests/check_grep.sh tests/$CAM/$TEST.log -q "GUICMD_LOCK_OFF" || return
    tests/check_grep.sh tests/$CAM/$TEST.log -q "SHUTDOWN" || return
    tests/check_grep.sh tests/$CAM/$TEST.log -q "\[MPU\] Shutdown requested." || return
470
471
472

    # this usually happens before "Shutdown requested", but in some cases it happens after (fixme)
    #tests/check_grep.sh tests/$CAM/$TEST.log -q "Terminate : Success" || return
473

474
475
    echo -n ' '
    tests/check_md5.sh tests/$CAM/ $TEST
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
}

echo
echo "Testing Canon menu..."
for CAM in ${GUI_CAMS[*]}; do
    ((QEMU_JOB_ID++))
    run_test menu $CAM &
    job_limit_auto
done; cleanup


# All GUI cameras should be able to format the virtual card:
function test_format {

    if [ -f $CAM/patches.gdb ]; then
        (
            ./run_canon_fw.sh $CAM,firmware="boot=0" -snapshot -vnc $VNC_DISP -S -gdb tcp::$GDB_PORT &
            arm-none-eabi-gdb -ex "set \$TCP_PORT=$GDB_PORT" -x $CAM/patches.gdb -ex quit &
        ) &> tests/$CAM/$TEST.log
    else
        (
            ./run_canon_fw.sh $CAM,firmware="boot=0" -snapshot -vnc $VNC_DISP &
        ) &> tests/$CAM/$TEST.log
499
500
    fi

501
502
503
    set_gui_timeout
    sleep $GUI_TIMEOUT

504
    send_menu_sequence 0.5 2 "${FORMAT_SEQUENCE[$CAM]}"
505
506
507

    shutdown_qemu

508
509
    echo -n ' '
    tests/check_md5.sh tests/$CAM/ $TEST
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
}

echo
echo "Testing card formatting..."
for CAM in ${GUI_CAMS[*]}; do
    ((QEMU_JOB_ID++))
    run_test format $CAM &
    job_limit_auto
done; cleanup


# All cameras should run under GDB and start a few tasks
function test_gdb {

    if [ ! -f $CAM/debugmsg.gdb ]; then
        echo -e "\e[33m$CAM/debugmsg.gdb not present\e[0m"
        return
    fi
528
529

    (
530
531
532
        ./run_canon_fw.sh $CAM,firmware="boot=0" -snapshot -display none -S -gdb tcp::$GDB_PORT &
        arm-none-eabi-gdb -ex "set \$TCP_PORT=$GDB_PORT" -x $CAM/debugmsg.gdb -ex quit &
    ) &> tests/$CAM/$TEST.log
533

534
535
536
    # wait for some tasks to start
    touch tests/$CAM/$TEST.log
    ( timeout 2 tail -f -n100000 tests/$CAM/$TEST.log & ) | grep --binary-files=text -qP "task_create\("
537

538
539
540
    # let it run for 10 seconds
    # (logs will be used later to find interrupt IDs)
    sleep 10
541
542
543
544
545
546
    stop_qemu_expect_running

    tac tests/$CAM/$TEST.log > tmp
    tests/check_grep.sh tmp -Em1 "task_create\("
    echo -n "         "
    tests/check_grep.sh tmp -Em1 "register_interrupt\([^n]"
547
}
548

549
echo
550
551
echo "Testing GDB scripts..."
for CAM in ${EOS_CAMS[*]} ${EOS_SECONDARY_CORES[*]} ${POWERSHOT_CAMS[*]}; do
552
    ((QEMU_JOB_ID++))
553
554
    run_test gdb $CAM &
    job_limit_auto
555
556
done; cleanup

557

558
559
560
561
# Interrupts are generally non-deterministic; however, we can use
# the -icount option in QEMU to get a deterministic execution trace.
# We'll let the emulation run until no new functions are discovered
# (that is, until the autogenerated IDC script no longer grows).
562
563
564
565
566
567

# Eeko is also a good target for testing call/return trace, because:
# - simple Thumb code from the bootloader (unlike DIGIC 6 cameras,
#   where the bootloader is plain ARM)
# - the overall structure is different enough to cause issues

568
function test_calls_main {
569
570
571
572
573

    # log all function calls/returns and export to IDC
    # log task switches as well
    # use -icount to get a deterministic execution
    # fixme: execution under gdb is not deterministic, even with -icount
574
    # ansi2txt used here (only once) because it's very slow
575
    ./run_canon_fw.sh $CAM,firmware="boot=0" -snapshot -icount 5 \
576
        -display none -d calls,notail,idc,tasks \
577
578
        -serial file:tests/$CAM/$TEST-uart.log \
        |& ansi2txt > tests/$CAM/$TEST-raw.log &
579

580
581
582
    # wait for uart log to appear
    while [ ! -f tests/$CAM/$TEST-uart.log ]; do sleep 1; done
    sleep 1
583
584

    # check for boot message
585
586
    if grep -qE "([KR].* (READY|AECU)|Dry|BootL)" tests/$CAM/$TEST-uart.log; then
      msg=`grep --text -oEm1 "([KR].* (READY|AECU)|Dry|BootL)[a-zA-Z >]*" tests/$CAM/$TEST-uart.log`
587
588
589
590
591
      printf "%-16s" "$msg"
    else
      echo -en "\e[33mBad output\e[0m      "
    fi
    echo -n ' '
592
593

    # wait until it the IDC no longer grows, up to 5 minutes
594
    ./wait_log.sh $CAM/calls.idc 300 5 -q "^}" 
595
596
    echo
    echo -n "                          "
597

598
    sleep 1
599
    stop_qemu_expect_running
600

601
602
603
604
605
    # as the emulation is not stopped exactly at the same time,
    # we have to trim it somewhere in order to get repeatable results.
    # in FROMUTILITY it was easy - after some point, the log contents
    # was repeating over and over, so remove_end_loop.py was doing a fine job.
    # in the main firmware, things are not always repeating with a simple pattern.
606
    # stopping when IDC no longer grows is not reliable either - it depends a lot on the PC speed
607
    # let's trim until matching the MD5 of $TEST-basic.idc
608
609
    # this assumes the needles (expected test results) were created on a slower PC and/or using a smaller timeout

610
    cat $CAM/calls.idc | grep -o "MakeFunction(.*)" \
611
        > tests/$CAM/$TEST-basic.idc
612

613
    # compute the MD5 of $TEST-basic.idc, line by line, until it matches its reference MD5
614
    # man, this python3 is quite painful with all these unicode errors...
615
    cd tests/$CAM/
616
    cat $TEST.md5 | grep $TEST-basic.idc | python3 -c 'if True:
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
        import os, sys
        from hashlib import md5
        ref_md5, filename = sys.stdin.readline().split()
        m = md5()
        trimmed = b""
        with open(filename, "rb") as input:
            for l in input.readlines():
                trimmed += l
                m.update(l)
                if m.hexdigest() == ref_md5:
                    break
        if trimmed:
            with open(filename, "wb") as output:
                output.write(trimmed)
    '
632
633
    cd $OLDPWD

634
    # trim the log file after the last function call identified in the IDC
635
    last_call=`tail -1 tests/$CAM/$TEST-basic.idc | grep -om1 "0x[^,]*"`
636
637
    last_call_thumb=`printf "0x%X\n" $((last_call+1))`

638
    # extract call/return lines, task switches and interrupts
639
    cat tests/$CAM/$TEST-raw.log \
640
        | grep -E "call |return |Task switch |interrupt " \
641
        | sed -n "1,/call $last_call\|call $last_call_thumb/ p" \
642
        > tests/$CAM/$TEST.log
643

644
645
646
    # extract only the basic info (call address indented, return address)
    # useful for checking when the log format changes
    # fixme: how to transform "return 0xVALUE to 0xADDR" into "return to 0xADDR" with shell scripting?
647
648
    cat tests/$CAM/$TEST.log | grep -oP " *call 0x[0-9A-F]+| to 0x[0-9A-F]+" \
        > tests/$CAM/$TEST-basic.log
649
650

    # also copy the IDC file for checking its MD5
651
    # this works on old CF models too (40D), even if some nondeterminism is present
652
    # the IDC needs trimming, too, as it doesn't always stop at the same line
653
654
    cat $CAM/calls.idc | sed -n "1,/MakeFunction($last_call/ p" > tests/$CAM/$TEST.idc
    cat $CAM/calls.idc | tail -n 2 >> tests/$CAM/$TEST.idc
655
656
657

    # extract only the call address from IDC
    # useful for checking when some additional info changes (e.g. comments)
658
659
    cat tests/$CAM/$TEST.idc | grep -o "MakeFunction(.*)" \
        > tests/$CAM/$TEST-basic.idc
660
661

    # count some stats
662
663
664
665
666
667
668
669
    calls=`cat tests/$CAM/$TEST.log | grep -c "call "`
    retns=`cat tests/$CAM/$TEST.log | grep "return " | grep -vc "interrupt"`
    ints=`cat tests/$CAM/$TEST.log | grep -E "interrupt .*at " | grep -vc "return"`
    reti=`cat tests/$CAM/$TEST.log | grep -c "return from interrupt"`
    nints=`cat tests/$CAM/$TEST.log | grep -E " interrupt .*at " | grep -vc "return"`
    nreti=`cat tests/$CAM/$TEST.log | grep -c " return from interrupt"`
    tsksw=`cat tests/$CAM/$TEST.log | grep -c "Task switch to "`
    tasks=`cat tests/$CAM/$TEST.log | grep -oP "(?<=Task switch to )[^:]*" | sort | uniq | head -3 |  tr '\n' ' '`
670
671
672
    if (( ints == 0 )); then
      echo -en "\e[33mno interrupts\e[0m "
    else
673
      echo -n "$ints ints"
674
      if (( reti == 0 )); then
675
        echo -e ", \e[31mno reti\e[0m"
676
        return
677
      fi
678
      if (( nints != 0 )); then echo -n " ($nints nested)"; fi
679
680
      echo -n ", $reti reti"
      if (( nreti != 0 )); then echo -n " ($nreti nested)"; fi
681
      if (( ints - nints > reti + 1 )); then
682
          echo -en " \e[33mtoo few reti\e[0m "
683
684
      fi
      if (( reti > ints )); then
685
          echo -e " \e[31mtoo many reti\e[0m"
686
          return
687
      fi
688
      if (( tsksw > 0 )); then
689
         echo -n ", $tsksw task switches ( $tasks)"
690
      else
691
         echo -en ", \e[33mno task switches\e[0m"
692
      fi
693
694
    fi

695
696
697
    # if we are checking only the IDC, say so
    echo
    echo -n "                          "
698
    grep -q '.log' tests/$CAM/$TEST.md5 \
699
700
        && echo -n "$calls calls, $retns returns " \
        || echo -n "IDC "
701

702
703
    tests/check_md5.sh tests/$CAM/ $TEST || cat tests/$CAM/$TEST.md5.log
}
704

705
echo
706
707
echo "Testing call/return trace on main firmware..."
for CAM in ${EOS_SECONDARY_CORES[*]} ${EOS_CAMS[*]}; do
708
    ((QEMU_JOB_ID++))
709
710
    run_test calls_main $CAM &
    job_limit_auto
711
712
done; cleanup

713
714
715
716

# We should get a valid call/return trace on the bootloader,
# which loads FROMUTILITY if autoexec.bin is not present on a bootable card.
# There are no timed interrupts here, so the process should be deterministic.
717
# On SD models, this process is deterministic without any trickery.
718
719
# On CF models, emulation is not deterministic even with -icount (fixme),
# so we'll only check the IDC for these models.
720
721
722
# The results are assumed to be correct, but they were not thoroughly checked.
# Feel free to report bugs, corner cases and so on.

723
function test_calls_from {
724

725
    # log all function calls/returns and export to IDC
726
    ./run_canon_fw.sh $CAM,firmware="boot=1" -snapshot \
727
        -display none -d calls,notail,idc \
728
729
        -serial file:tests/$CAM/$TEST-uart.log \
        &> tests/$CAM/$TEST-raw.log &
730
731

    # wait until the FROMUTILITY menu appears
732
    touch tests/$CAM/$TEST-uart.log
733
    (timeout 20 tail -f -n100000 tests/$CAM/$TEST-uart.log & ) \
734
735
736
        | grep -q "FROMUTIL"
    sleep 0.5

737
    stop_qemu_expect_running
738
739
740

    # extract call/return lines
    # remove infinite loop at the end, if any
741
    ansi2txt < tests/$CAM/$TEST-raw.log \
742
743
        | grep -E "call |return " \
        | python remove_end_loop.py \
744
        > tests/$CAM/$TEST.log
745

746
    # count some stats
747
748
    calls=`cat tests/$CAM/$TEST.log | grep -c "call "`
    retns=`cat tests/$CAM/$TEST.log | grep "return " | grep -vc "interrupt"`
749

750
751
752
    # extract only the basic info (call address indented, return address)
    # useful for checking when the log format changes
    # fixme: how to transform "return 0xVALUE to 0xADDR" into "return to 0xADDR" with shell scripting?
753
754
    cat tests/$CAM/$TEST.log | grep -oP " *call 0x[0-9A-F]+| to 0x[0-9A-F]+" \
        > tests/$CAM/$TEST-basic.log
755
756

    # also copy the IDC file for checking its MD5
757
    # this works on CF models too, even if some nondeterminism is present
758
    cp $CAM/calls.idc tests/$CAM/$TEST.idc
759

760
761
    # extract only the call address from IDC
    # useful for checking when some additional info changes (e.g. comments)
762
763
    cat tests/$CAM/$TEST.idc | grep -o "MakeFunction(.*)" \
        > tests/$CAM/$TEST-basic.idc
764

765
766
767
    if grep -q "FROMUTIL" tests/$CAM/$TEST-uart.log; then
        grep -oEm1 "FROMUTIL[^*]*" tests/$CAM/$TEST-uart.log | tr -d '\n'
    elif grep -q "AUTOEXEC.BIN not found" tests/$CAM/$TEST-uart.log; then
768
        echo -en "\e[33mFROMUTILITY not executed  \e[0m"
769
    else
770
        echo -e "\e[31mFAILED!\e[0m"
771
        return
772
    fi
773
774

    # if we are checking only the IDC, say so
775
    grep -q '.log' tests/$CAM/$TEST.md5 \
776
777
778
        && echo -n "$calls calls, $retns returns " \
        || echo -n "IDC "

779
780
    tests/check_md5.sh tests/$CAM/ $TEST || cat tests/$CAM/$TEST.md5.log
}
781

782
echo
783
echo "Testing call/return trace on fromutility..."
784

785
786
787
# remove autoexec.bin from card images (to get the FROMUTILITY menu)
mdel -i $MSD ::/autoexec.bin
mdel -i $MCF ::/autoexec.bin
788

789
# run the tests
790
for CAM in ${EOS_CAMS[*]}; do
791
    ((QEMU_JOB_ID++))
792
793
    run_test calls_from $CAM &
    job_limit_auto
794
done; cleanup
795

796

797
798
799
800
# Models able to display some Canon GUI should capture a still picture as well.
# This requires a full-res silent picture at qemu/<camera>/VRAM/PH-QR/RAW-000.DNG.
# Currently working on 500D, 550D, 50D, 60D, 1200D, and to a lesser extent, on 5D3 and 1100D.
function test_frsp {
801

802
    # compile it from ML dir, for each camera
803
    FRSP_PATH=$ML_PATH/minimal/qemu-frsp
804
    rm -f $FRSP_PATH/autoexec.bin
805
    [ "${CAM_BRANCH[$CAM]}" != "" ] && (cd $FRSP_PATH; hg up qemu -C; hg merge ${CAM_BRANCH[$CAM]}; cd $OLDPWD) &>> tests/$CAM/$TEST-build.log
806
807
    make MODEL=$CAM -C $FRSP_PATH clean         &>> tests/$CAM/$TEST-build.log
    make MODEL=$CAM -C $FRSP_PATH CONFIG_QEMU=y &>> tests/$CAM/$TEST-build.log
808
809
    (cd $FRSP_PATH; hg up qemu -C; cd $OLDPWD)  &>> tests/$CAM/$TEST-build.log

810
811
    if [ ! -f $FRSP_PATH/autoexec.bin ]; then
        echo -e "\e[31mCompile error\e[0m"
812
        return
813
814
    fi

815
816
817
    # copy autoexec.bin to card images
    mcopy -o -i $MSD $FRSP_PATH/autoexec.bin ::
    mcopy -o -i $MCF $FRSP_PATH/autoexec.bin ::
818

819
820
821
822
823
824
825
826
827
828
829
830
831
    # run the photo capture test
    # wait for FA_CaptureTestImage Fin
    (
      ./wait_log.sh tests/$CAM/$TEST-uart.log 20 5 -q --text "FA_CaptureTestImage Fin" 2>/dev/null
      sleep 1
      echo screendump tests/$CAM/$TEST.ppm
      echo quit
    ) | (
      ./run_canon_fw.sh $CAM,firmware="boot=1" -snapshot \
        -display none -monitor stdio \
        -d debugmsg \
        -serial file:tests/$CAM/$TEST-uart.log \
    ) &> tests/$CAM/$TEST.log
832

833
834
    tests/check_grep.sh tests/$CAM/$TEST-uart.log \
        -qm1 "FA_CreateTestImage Fin"  || return
835

836
837
    tests/check_grep.sh tests/$CAM/$TEST-uart.log \
        -qm1 "FA_CaptureTestImage Fin" || return
838

839
840
    tests/check_grep.sh tests/$CAM/$TEST-uart.log \
        -qm1 "FA_DeleteTestImage Fin"  || return
841

842
843
    if [ -f tests/$CAM/$TEST.md5 ]; then
        tests/check_md5.sh tests/$CAM/ $TEST
844
    else
845
        echo "OK (no display)"
846
    fi
847
848
849
}

echo
850
851
852
echo "Testing FA_CaptureTestImage..."
# this requires a custom build; cannot run in parallel
for CAM in ${FRSP_CAMS[*]}; do
853
    ((QEMU_JOB_ID++))
854
    run_test frsp $CAM
855
done; cleanup
856
857


858
# All EOS cameras should emulate the bootloader
859
860
# and jump to main firmware.
# Also list blocks copied to RAM during startup, if any.
861
862
function test_boot {

863
    # sorry, couldn't get the monitor working together with log redirection...
864
    (./run_canon_fw.sh $CAM,firmware="boot=0" -snapshot \
865
        -display none \
866
        -serial file:tests/$CAM/$TEST-uart.log \
867
        -d romcpy,int \
868
        &> tests/$CAM/$TEST.log \
869
870
871
    ) &

    # wait until some READY-like message is printed on the UART
872
    touch tests/$CAM/$TEST-uart.log
873
    ( timeout 2 tail -f -n100000 tests/$CAM/$TEST-uart.log & ) \
874
        | grep -qE "([KR].* (READY|AECU).*|Intercom.*|Dry>)"
875

876
    stop_qemu_expect_running
877
878

    # fixme: duplicate regex
879
    tests/check_grep.sh tests/$CAM/$TEST-uart.log \
880
881
882
        -oE "([KR].* (READY|AECU).*|Intercom.*|Dry>)"

    # print ROMCPY messages before the first interrupt
883
    ansi2txt < tests/$CAM/$TEST.log \
884
        | grep -m1 -B 100000 -E "Taking exception|forced shutdown" \
885
        | grep -oE "\[ROMCPY\].*" \
886
        | sed -e "s/\[ROMCPY\]/        /"
887
888
889
890
891
}

echo
echo "Testing bootloaders..."
for CAM in ${EOS_CAMS[*]} ${EOS_SECONDARY_CORES[*]}; do
892
    ((QEMU_JOB_ID++))
893
894
    run_test boot $CAM &
    job_limit_auto
895
done; cleanup
896

897

898
899
900
# some basic tests for PowerShot models
function test_boot_powershot {
    echo ""
901

902
903
904
905
906
907
    (
        ./run_canon_fw.sh $CAM -snapshot \
            -display none -d romcpy,int -S -gdb tcp::$GDB_PORT \
            -serial file:tests/$CAM/$TEST-uart.log &
        arm-none-eabi-gdb -ex "set \$TCP_PORT=$GDB_PORT" -x $CAM/debugmsg.gdb -ex quit &
    ) &> tests/$CAM/$TEST.log
908

909
910
911
912
    # wait for TurnOnDisplay, up to 10 seconds
    touch tests/$CAM/$TEST.log
    ( timeout 10 tail -f -n100000 tests/$CAM/$TEST.log & ) | grep -q "TurnOnDisplay"
    stop_qemu_expect_running
913

914
915
916
    printf "    SD boot: "; tests/check_grep.sh tests/$CAM/$TEST-uart.log -om1 "StartDiskboot"
    printf "    Display: "; tests/check_grep.sh tests/$CAM/$TEST.log -om1 "TurnOnDisplay"
    printf "    ROMcopy: "; tests/check_grep.sh tests/$CAM/$TEST.log -oPm1 "(?<=ROMCPY\]) "
917

918
919
920
921
922
923
924
    # print ROMCPY messages before the first interrupt
    # exception: EOS M5 copies interesting stuff after the first interrupt
    ansi2txt < tests/$CAM/$TEST.log \
        | ( [ $CAM == "EOSM5" ] && cat || grep -m1 -B 100000 -E "Taking exception|terminating on signal") \
        | grep -oE "\[ROMCPY\].*" \
        | sed -e "s/\[ROMCPY\]/     /"
}
925

926
927
928
929
930
931
932
echo
echo "Testing PowerShot models..."
for CAM in ${POWERSHOT_CAMS[*]}; do
    ((QEMU_JOB_ID++))
    run_test boot_powershot $CAM boot &
    job_limit_auto
done; cleanup
933

934
935
936
937
938
939
940

# All EOS cameras should run the portable display test:
function test_disp {
    (sleep 5; echo screendump tests/$CAM/$TEST.ppm; echo quit) \
      | ./run_canon_fw.sh $CAM,firmware="boot=1" -snapshot -display none -monitor stdio &> tests/$CAM/$TEST.log
    
    tests/check_md5.sh tests/$CAM/ $TEST
941
}
942

alex@thinkpad's avatar
alex@thinkpad committed
943
echo
944
echo "Testing display from bootloader..."
945
for CAM in ${EOS_CAMS[*]}; do
946
    ((QEMU_JOB_ID++))
947
948
    run_test disp $CAM &
    job_limit_auto
949
950
951
done; cleanup


952
953
954
955
956
# M3 uses a special CHDK build from Ant123
# using it for the other PowerShot models
# just to test whether they are booting from the card
function test_disp_chdk {
    echo ""
alex@thinkpad's avatar
alex@thinkpad committed
957

958
959
960
961
    M3_DISKBOOT_BIN=tests/test-progs/M3/DISKBOOT.BIN
    if [ ! -f $M3_DISKBOOT_BIN ]; then
        mkdir -p `dirname $M3_DISKBOOT_BIN`
        wget -q -O $M3_DISKBOOT_BIN http://a1ex.magiclantern.fm/bleeding-edge/M3/qemu/DISKBOOT.BIN
alex@thinkpad's avatar
alex@thinkpad committed
962
    fi
alex@thinkpad's avatar
alex@thinkpad committed
963

964
965
    # Our SD image appears to be already bootable for CHDK (?!)
    mcopy -o -i $MSD $M3_DISKBOOT_BIN ::
alex@thinkpad's avatar
alex@thinkpad committed
966

967
968
969
970
971
972
973
974
975
976
    # run the emulation
    (
      sleep 10
      echo screendump tests/$CAM/$TEST.ppm
      echo quit
    ) | (
      ./run_canon_fw.sh $CAM -snapshot \
            -display none -monitor stdio \
            -serial file:tests/$CAM/$TEST-uart.log \
    ) &> tests/$CAM/$TEST.log
alex@thinkpad's avatar
alex@thinkpad committed
977

978
979
980
981
    printf "    SD boot: "; tests/check_grep.sh tests/$CAM/$TEST-uart.log -om1 "StartDiskboot"
    printf "    RAMboot: "; tests/check_grep.sh tests/$CAM/$TEST-uart.log -om1 "Start Program on RAM"
    printf "    Display: "; tests/check_md5.sh tests/$CAM/ $TEST
}
982

983
984
985
986
987
988
989
echo
echo "Testing CHDK display..."
# this will require custom builds; let's not run it in parallel yet
for CAM in ${POWERSHOT_CAMS[*]}; do
    ((QEMU_JOB_ID++))
    run_test disp_chdk $CAM disp
done; cleanup
alex@thinkpad's avatar
alex@thinkpad committed
990

991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032

# All EOS cameras should run the Dry-shell console over UART
# fixme: only works on D4 and D5 models
function test_drysh {

    # skip VxWorks models for now
    if grep -q VxWorks $CAM/ROM1.BIN; then
        echo -e "\e[33mskipping\e[0m"
        return
    fi

    # most models require "akashimorino" to enable the Dry-shell
    # a few don't (500D, 5D3eeko), but sending it anyway shouldn't hurt

    (
        sleep 5; echo "akashimorino";
        sleep 0.5; echo "drysh";
        sleep 0.5; echo "vers";
        sleep 0.5; echo "?";
        sleep 0.5; echo "task";
        sleep 0.5; stop_qemu_expect_running;
    ) | (
        if [ -f $CAM/patches.gdb ]; then
            (
                arm-none-eabi-gdb -ex "set \$TCP_PORT=$GDB_PORT" -x $CAM/patches.gdb -ex quit 1>&2 &
                ./run_canon_fw.sh $CAM,firmware="boot=0" -snapshot \
                    -display none -serial stdio -S -gdb tcp::$GDB_PORT
            )  > tests/$CAM/$TEST.log \
              2> tests/$CAM/$TEST-emu.log
        else
            ./run_canon_fw.sh $CAM,firmware="boot=0" -snapshot \
                -display none -serial stdio \
                 > tests/$CAM/$TEST.log \
                2> tests/$CAM/$TEST-emu.log
        fi
    )

    tests/check_grep.sh tests/$CAM/$TEST.log \
        -oEm1 "Dry-shell .*\..*| xd .*" || return

    tests/check_grep.sh tests/$CAM/$TEST.log \
        -q "akashimorino"
1033
}
alex@thinkpad's avatar
alex@thinkpad committed
1034

1035
echo
1036
1037
echo "Testing Dry-shell over UART..."
for CAM in 5D3eeko ${EOS_CAMS[*]}; do
1038
    ((QEMU_JOB_ID++))
1039
    run_test drysh $CAM &
1040
    job_limit_auto
1041
1042
1043
done; cleanup


1044
1045
1046
1047
1048
1049
# Most EOS cameras should be able to create the DCIM directory if missing.
function test_dcim {

    # remove the DCIM directory from the card images
    mdeltree -i $MSD ::/DCIM &> /dev/null
    mdeltree -i $MCF ::/DCIM &> /dev/null
1050
1051

    if [ -f $CAM/patches.gdb ]; then
1052
        (
1053
            ./run_canon_fw.sh $CAM,firmware="boot=0" -display none -S -gdb tcp::$GDB_PORT &
1054
1055
            arm-none-eabi-gdb -ex "set \$TCP_PORT=$GDB_PORT" -x $CAM/patches.gdb -ex quit &
        ) &> tests/$CAM/$TEST.log
1056
1057
        sleep 5
        stop_qemu_expect_running
1058
    else
1059
1060
1061
1062
1063
1064
1065
1066
        (sleep 5; echo quit) \
            | ./run_canon_fw.sh $CAM,firmware="boot=0" -display none -monitor stdio &> tests/$CAM/$TEST.log
    fi
    
    if (mdir -b -i $MSD | grep -q DCIM) || (mdir -b -i $MCF | grep -q DCIM); then
        echo "OK"
    else
        echo -e "\e[31mFAILED!\e[0m"
1067
    fi
1068
}
1069
1070

echo
1071
1072
echo "Testing file I/O (DCIM directory)..."
# Currently works only on models that can boot Canon GUI,
1073
# also on single-core DIGIC 6 models, and on DIGIC 7 too.
1074
# we need to check the card contents; cannot run in parallel
1075
for CAM in ${GUI_CAMS[*]} 80D 750D 760D 77D 200D 6D2 800D; do
1076
    ((QEMU_JOB_ID++))
1077
    run_test dcim $CAM
1078
done; cleanup
1079