# BST-TIMEOUT: 300 # measured 19s; headroom is for the ZLE leg, which today # dies at once (linux-in-the-browser#162) and will take real # pty round-trips once it does not. Do not cut this to the # measurement: that would time the suite out the day #162 is # fixed, i.e. punish the platform for getting better. . "$BST_LIB" # zsh 5.8 -- the first interactive shell on this image that is neither bash nor # a busybox applet. # # WHAT THIS SUITE ASSERTS, AND WHY IT IS NOT "zsh --version". The reason anyone # installs zsh is a specific set of features no POSIX shell has: 1-indexed arrays # with range and negative subscripts, associative arrays, glob QUALIFIERS # (`*(.om[1])` instead of `ls | grep | head`), the parameter-expansion flags # (${(o)a}, ${(j:,:)a}, ${(s:,:)str}, ${(U)str}), and an arithmetic evaluator with # floats and bases. A port that started, printed a version and ran `echo` would # pass a naive suite while every one of those was broken. So the evidence here is # ANSWERS: 58 values the shell computes, each compared against a value recorded # from a native zsh 5.9 x86_64 control (Fedora 44) rather than from this arm -- # which is what makes a wrong answer here a finding rather than a re-statement of # whatever this build happens to do. The two versions differ only in ways none of # these cases reach; the design doc records the comparison. # # AND WHY HALF OF IT RUNS UNDER EXPECT. Everything zsh is chosen for as an # INTERACTIVE shell lives in ZLE, its line editor, which only runs when the shell # is interactive and stdin is a terminal. `zsh -c` never enters ZLE at all, so a # port whose line editor was entirely broken would pass all 58 cases above and # still be useless for the purpose it was ported for. data/selftest_zsh.exp # drives a real pty: history recall on ^P, cursor movement on ^A, TAB filename # completion. # # SPAWN BUDGET. exec on this arm is a fresh wasm module instantiation (~2-3 s), # so this file spends exactly five: --version, one `sh`, the script payload, the # expect payload, and one awk that joins all 58 answers at once. The loop that # reports them forks nothing. require zsh zsh/present tmp=${TMPDIR:-/tmp}/bst-zsh.$$ mkdir -p "$tmp" 2>/dev/null || tmp=/tmp check_out zsh/version "zsh 5.8" zsh --version # THE DIVERGENCE THIS PORT DELIBERATELY MADE, ASSERTED RATHER THAN TRUSTED. # meta-oe's recipe registers zsh as the "sh" alternative at priority 90 -- above # busybox (50) and toybox (60) -- so picking zsh into the image would silently # hand /bin/sh to zsh and re-point every #!/bin/sh script, the login shell and # this very runner. zsh_%.bbappend drops the priority to 10. If that regresses, # $ZSH_VERSION is set in /bin/sh and this case reds. check_eq zsh/sh-not-zsh none "$(sh -c 'echo ${ZSH_VERSION:-none}' 2>&1)" # ------------------------------------- the platform gap this port surfaced ----- # ASSERTED RED while linux-in-the-browser#161 is open, and the one case here that # is EXPECTED to fail: a foreground child forked by zsh is never reaped -- the # child runs, prints and exits into Z, and the parent parks forever at ~0% CPU # with nothing pending. bash and dash complete this identical construct on the # same boot; mksh hangs with zsh. Nothing about the zsh port can fix it, and it # goes green on its own when #161 closes. # # It is bounded rather than merely asserted because an unbounded red here would # cost the suite its whole budget instead of ~10s. # # The bound is hand-rolled, and `timeout` is deliberately NOT used: GNU timeout # waits for its child in exactly the sigsuspend idiom #161 breaks, so it parks # alongside the process it was supposed to bound -- measured here, and the reason # this case first cost a 600s suite timeout. Backgrounding from THIS shell works # because dash's own fork+wait is unaffected, and the parked zsh answers KILL. # BRINTOS-WORKAROUND: linux-in-the-browser#161 remove — the hand-rolled bound replaces # timeout(1) only because timeout parks in the same idiom; restore it when #161 closes. zsh -f -c 'print Z1;(print Z2);print Z3-ok' > "$tmp/fw" 2>&1 & fwpid=$! sleep 10 kill -9 "$fwpid" 2>/dev/null check_eq zsh/fork-wait "Z1 Z2 Z3-ok" "$(echo $(cat "$tmp/fw" 2>/dev/null))" # ----------------------------------------------------- the script payload ----- out=$(zsh -f "$BST_DATA/selftest.zsh" 2>&1); rc=$? printf '%s\n' "$out" > "$tmp/got" case "$out" in *ALL-ZSH-OK*) all=1 ;; *) all=0 ;; esac if [ "$rc" -eq 0 ] && [ "$all" -eq 1 ]; then bst_ok zsh/script-ok else bst_notok zsh/script-ok "rc=$rc $(_bst_1line "$out")"; fi # The expected table. Recorded from native zsh 5.9; a value here is an ANSWER, # so a diff is a real behavioural difference and not output drift. cat > "$tmp/want" <<'WANT' array-count 4 array-first alpha array-last delta array-slice beta,gamma array-append epsilon array-join alpha-beta-gamma-delta-epsilon array-nosplit 2 array-elemlen 9 hash-val 2 hash-count 3 hash-keys one,three,two hash-vals 1,2,3 hash-has 1 hash-nohas 0 hash-after four,three,two glob-txt 2 glob-plain 3 glob-dir 1 glob-rec 3 glob-neg 2 glob-ci 1 glob-alt 3 glob-empty 1 glob-largest a.txt glob-tail a.txt,b.txt glob-null 0 expand-upper HELLOWORLD expand-lower helloworld expand-cap HelloWorld expand-splitcount 3 expand-splitsecond b expand-rejoin a+b+c expand-sorted apple,fig,pear expand-rsorted pear,fig,apple expand-uniq a,b,c expand-numsort 9,10,100 expand-lpad 00042 expand-rpad 42... expand-len 11 expand-tail libfoo.so expand-head /usr/local/lib expand-root libfoo expand-ext so expand-subst Hell0W0rld expand-quote a\ b expand-words 2 arith-pow 1024 arith-mod 2 arith-float 1.5 arith-cmp 1 arith-hex 16#FF arith-base 11 arith-tern 111 arith-int 42 arith-fp 0.25 arith-bits 259 arith-loop 15 arith-sqrt 4 WANT # One pass, emitting "\t\t". A case the payload never printed # comes back as rather than vanishing from the count -- a payload that # died at line 30 would otherwise report as a short green suite. awk -v OFS='\t' ' NR == FNR { want[$1] = $2; order[++n] = $1; next } { got[$1] = $2; seen[$1] = 1 } END { for (i = 1; i <= n; i++) { c = order[i] print c, want[c], (c in seen ? got[c] : "") } } ' "$tmp/want" "$tmp/got" > "$tmp/verdicts" 2>/dev/null joined=0 while IFS=' ' read -r id want got; do [ -n "$id" ] || continue check_eq "zsh/$id" "$want" "$got" joined=$((joined + 1)) done < "$tmp/verdicts" check_rc zsh/join-nonempty 0 test "$joined" -gt 50 # ------------------------------------------------- the interactive (ZLE) leg --- # expect is on this image (45-expect.test) and its pty machinery is the thing # under test there, not here -- so an image without it SKIPs these rather than # reddening zsh for a missing driver. if command -v expect >/dev/null 2>&1; then zout=$(expect "$BST_DATA/selftest_zsh.exp" 2>&1); zrc=$? case "$zout" in *ALL-ZSH-ZLE-OK*) zall=1 ;; *) zall=0 ;; esac if [ "$zrc" -eq 0 ] && [ "$zall" -eq 1 ]; then bst_ok zsh/zle-all-ok else bst_notok zsh/zle-all-ok "rc=$zrc $(_bst_1line "$zout")"; fi for tag in Z1prompt Z2zle Z3keymap Z4history Z5edit Z6complete; do case "$zout" in *"Z-$tag OK"*) bst_ok "zsh/$tag" ;; *) bst_notok "zsh/$tag" "not OK in output (rc=$zrc)" ;; esac done else bst_skip zsh/zle-all-ok "no expect on this image to drive a pty" for tag in Z1prompt Z2zle Z3keymap Z4history Z5edit Z6complete; do bst_skip "zsh/$tag" "no expect on this image to drive a pty" done fi rm -rf "$tmp"