# BST-TIMEOUT: 2400 # 97 upstream cases = 194 sltest execs; see the design doc . "$BST_LIB" # slang / S-Lang 2.3.3 (poky recipes-extended/slang) -- an interpreted extension # language AND a terminal/screen-management library, the pair behind jed, most and # slrn. Ported here as a static library plus the slsh interpreter, and closed on # upstream's own test suite, which OpenEmbedded packages as a ptest. # # WHAT THIS FILE ASSERTS. The port's acceptance gate is upstream's own suite, run # on target under ptest-runner exactly as the ticket requires: no interactive # session, no hand-driven check. run-ptest is `make -C test runtests`, so what # executes is upstream's src/test corpus driven by its own runtests.sh. # # IT IS A LANGUAGE SUITE, NOT A TERMINAL ONE, and that is the point. src/test is # the S-Lang INTERPRETER's regression corpus: arrays and array arithmetic, # associative arrays, structs, namespaces, exceptions, references and closures, # sscanf/pack binary formatting, bstrings, integer overflow, long long, IEEE # NaN/Inf, math, sorting, regexp, and the POSIX io/dir/process wrappers. So a # green here is a statement about this arm's arithmetic and its syscall surface, # not merely about whether the library links. # # HALF THE CORPUS EXERCISES THE BYTECODE PATH. runtests runs 50 scripts as .sl and # 47 of them AGAIN as .slc; sltest byte-compiles the script and re-loads it # (SLang_byte_compile_file), so those 47 cases are slang's own bytecode writer and # loader rather than a duplicate of the .sl half. They also WRITE the .slc files # into this directory at run time, which is why a read-only ptest dir would show up # here as a wall of reds rather than as a packaging fault. # # AND EVERY CASE RUNS TWICE. runtests.sh runs each file once plain and once -utf8, # and marks the file failed if EITHER mode failed -- so 97 verdicts over 194 sltest # invocations. On an arm where every exec is a fresh wasm module instantiation of a # 12 MB binary, that exec count is the whole reason this suite declares its own # BST-TIMEOUT. # # THE ORACLE IS A NATIVE x86_64 CONTROL of the identical 2.3.3 tarball (sha256 # checked against the recipe) with the identical 5-patch oe series taken from the # recipe's own SRC_URI, autoconf'd the way do_configure:prepend does it, built # through the same STATIC arm, and driven through the same `make -C src/test # runtests`. It reports 97 cases / 97 PASS / 0 FAIL. That is what makes the target # result classifiable rather than arguable, and it is why the cases below are # ENUMERATED BY NAME rather than reduced to a count: a suite that quietly stopped # running the .slc half would still report a clean zero failures. # # ONE oe PATCH IS LOAD-BEARING FOR THIS ARM SPECIFICALLY. array_test.patch widens # array.sl's expected exception from IndexError to IndexError,InvalidParmError # because "IndexError will only be thrown for 64 bit machines". wasm32 is 32-bit, # so this port is a consumer of that patch -- array.sl passing here is partly that # patch being correct about 32-bit. # # EIGHT CASES ARE EXPECTED RED ON THIS ARM, AND THEY ARE LEFT ASSERTING PASS ON # PURPOSE. Four test files fail, each in both its .sl and its .slc arm, and each # for one platform gap that is filed and open: # # stdio.sl stdio.slc popen(3) is ENOSYS -- glibc routes popen through # __posix_spawn, unwired here brintos/linux#14 # signal.sl signal.slc a process signalling itself never runs the handler # (SIGHUP raised, count=0) brintos/linux#21 # posdir.sl posdir.slc on the virtiofs root utimensat() writes the mtime # value into atime too, so stat_file disagrees with the # utime() that just succeeded brintos/linux#63 # proc.sl proc.slc getpriority(2)/setpriority(2) are ENOSYS # brintos/linux#62 # # They are NOT masked, skipped or re-expected as failures, per the BRINTOS-389 # precedent: an expected-FAIL assertion has to be edited by hand the day the # platform is fixed, and nobody goes looking. Left asserting PASS, each case # turns green by itself when its issue lands, and that is the signal we want. # # Three aggregate cases are red for exactly the same reason and carry no # independent information: slang/ptest-runner-rc (1), slang/suite-nofail # (TOTAL: 1 FAIL: 1) and slang/runner-no-error (ptest-runner logs "ERROR: Exit # status is 2" -- 2 is GNU make's own failure status from run-ptest's # `make -C test runtests`, not runtests.sh's count of failed files). All three # clear when the four issues above do. # # So the measured expectation on this arm today is 121 PASS / 11 FAIL of 132, # against 132/132 once #14, #21, #62 and #63 land. The 89 green upstream cases # are the port's actual statement. require slsh slang/present ptestdir=/usr/lib/slang/ptest testdir=$ptestdir/test runner=/usr/bin/ptest-runner tmp=${TMPDIR:-/tmp}/bst-slang.$$ mkdir -p "$tmp" 2>/dev/null || tmp=/tmp # The 97 cases the native control ran, in the order runtests drives them: the 50 # TEST_SCRIPTS as .sl, then the 47 TEST_SCRIPTS_SLC again as .slc. SL_CASES="argv.sl syntax.sl scircuit.sl eqs.sl sscanf.sl loops.sl arith.sl array.sl strops.sl bstring.sl pack.sl stdio.sl assoc.sl selfload.sl struct.sl nspace.sl path.sl ifeval.sl anytype.sl arrmult.sl time.sl utf8.sl except.sl bugs.sl list.sl regexp.sl method.sl deref.sl naninf.sl overflow.sl sort.sl longlong.sl signal.sl dollar.sl req.sl docfun.sl debug.sl qualif.sl compare.sl break.sl multline.sl stack.sl misc.sl posixio.sl posdir.sl proc.sl math.sl autoload.sl nspace2.sl prep.sl argv.slc syntax.slc scircuit.slc eqs.slc sscanf.slc loops.slc arith.slc array.slc strops.slc bstring.slc pack.slc stdio.slc assoc.slc selfload.slc struct.slc nspace.slc path.slc ifeval.slc anytype.slc arrmult.slc time.slc utf8.slc except.slc bugs.slc list.slc regexp.slc method.slc deref.slc naninf.slc overflow.slc sort.slc longlong.slc signal.slc dollar.slc req.slc docfun.slc debug.slc qualif.slc compare.slc break.slc multline.slc stack.slc misc.slc posixio.slc posdir.slc proc.slc math.slc" # ----------------------------------------------------------------- payload --- # check_rc with `test` is an ash builtin and forks nothing, unlike check/check_out # whose command substitution does -- so the presence half of this file is free on # an arm where every exec is a fresh wasm module instantiation. check_rc slang/lib-static 0 test -f /usr/lib/libslang.a check_rc slang/header 0 test -f /usr/include/slang.h check_rc slang/header-curses 0 test -f /usr/include/slcurses.h check_rc slang/pkgconfig 0 test -f /usr/lib/pkgconfig/slang.pc check_rc slang/slsh-bin 0 test -x /usr/bin/slsh check_rc slang/slshrc 0 test -f /etc/slsh.rc # The ${datadir}/slsh tree install-static carries via install_common. autoload.sl # is named because slsh.rc's startup hook evalfile()s it: without it every slsh # invocation dies with "Unable to locate autoload.sl on load path" before running # any user code, which is exactly how an in-tree (uninstalled) build behaves. check_rc slang/share-dir 0 test -d /usr/share/slsh check_rc slang/share-autoload 0 test -f /usr/share/slsh/autoload.sl check_rc slang/share-require 0 test -f /usr/share/slsh/require.sl check_rc slang/share-rline 0 test -d /usr/share/slsh/rline check_rc slang/share-help 0 test -d /usr/share/slsh/help # The ptest payload. sltest is the TARGET-built driver; the seded Makefile is what # makes `make runtests` use it instead of trying to recompile it on a target with # no compiler, so a Makefile that still carried the $(CC) rule would fail here. check_rc slang/ptest-runptest 0 test -x "$ptestdir/run-ptest" check_rc slang/ptest-makefile 0 test -f "$testdir/Makefile" check_rc slang/ptest-sltest 0 test -x "$testdir/sltest" check_rc slang/ptest-runtests 0 test -x "$testdir/runtests.sh" # The two fixtures do_install_ptest re-points at the ptest dir. req.sl loads # require.sl by relative path and docfun.sl reads slangfun.txt; the recipe seds # both paths, so a missing file here costs exactly those cases and nothing else. check_rc slang/ptest-require 0 test -f "$testdir/require.sl" check_rc slang/ptest-slangfun 0 test -f "$testdir/slangfun.txt" # The suite writes .slc bytecode and lastrun/ stamps next to the scripts, so the # ptest dir must be writable. Asserted directly rather than inferred from a wall of # case failures, which is what a read-only rootfs would otherwise look like. if ( : > "$testdir/.bst-write-probe" ) 2>/dev/null; then rm -f "$testdir/.bst-write-probe" bst_ok slang/ptest-dir-writable else bst_notok slang/ptest-dir-writable \ "$testdir is not writable: sltest cannot emit the .slc files the 47 bytecode cases need" fi # --------------------------------------------- the interpreter, on its own --- # ONE spawn, deliberately: each value below is checked against the native control, # and together they are evidence INDEPENDENT of the suite -- so a suite that cannot # start still leaves a statement about whether slang executes at all. Folding nine # assertions into a single slsh invocation is the whole point on this arm; nine # separate -e calls would cost nine module instantiations for the same information. ev=$tmp/ev slsh -n -q -e ' printf("R-ver %s\n", _slang_version_string); printf("R-arith %d\n", 3*4+5); variable a=[1:10]; printf("R-arraysum %d\n", int(sum(a))); variable h=Assoc_Type[Integer_Type]; h["k"]=42; printf("R-assoc %d\n", h["k"]); printf("R-str %s\n", strup("abc")); printf("R-sqrt %.6f\n", sqrt(2.0)); variable s=struct{x,y}; s.x=7; printf("R-struct %d\n", s.x); printf("R-sscanf %d\n", sscanf("12 34", "%d %d", &a, &h)); printf("R-re %d\n", string_match("hello42","\\([0-9]+\\)",1)); ' > "$ev" 2>&1 # ONE awk pass turns the tagged output into "\t\t" triples, joining # each tag against the value the native control produced. The reporting loop then # forks nothing -- check_eq compares two strings the shell already holds. (Doing # this as nine `$(awk ...)` command substitutions instead would cost nine forks and # nine subshells to extract nine numbers from a file already in hand.) awk -v OFS='\t' ' NR==FNR { id[$1] = $2; want[$1] = $3; order[++ne] = $1; next } /^R-/ { got[$1] = $2 } END { for (i = 1; i <= ne; i++) { k = order[i] print id[k], want[k], (k in got) ? got[k] : "NOT-PRINTED" } } ' - "$ev" > "$tmp/lang.verdicts" 2>/dev/null <<'EOF' R-ver slang/lang-version 2.3.3 R-arith slang/lang-arith 17 R-arraysum slang/lang-arraysum 55 R-assoc slang/lang-assoc 42 R-str slang/lang-strup ABC R-sqrt slang/lang-sqrt 1.414214 R-struct slang/lang-struct 7 R-sscanf slang/lang-sscanf 2 R-re slang/lang-regexp 6 EOF while IFS=' ' read -r id want got; do [ -n "$id" ] || continue check_eq "$id" "$want" "$got" done < "$tmp/lang.verdicts" # The slsh wrapper's own identity, as opposed to the library's. Separate from the # library version above because the two version independently (slsh 0.9.4-0 ships # with S-Lang 2.3.3), so a wrapper swapped underneath us would show up here. check_out slang/slsh-version "slsh version 0.9.4-0; S-Lang version: 2.3.3" slsh --version # ------------------------------------------- the static-only arm, asserted --- # No libslang.so on a distro that builds static only. One appearing here would mean # the bbappend's `static`/`install-static` redirect silently stopped holding -- and # on this arm it could not have linked, so it would mean the arm itself changed. if [ -e /usr/lib/libslang.so ] || [ -e /usr/lib/libslang.so.2 ]; then bst_notok slang/static-only "a shared libslang shipped on an arm with no shared-object format" else bst_ok slang/static-only fi # THE MODULES ARE DELIBERATELY ABSENT, ASSERTED AS A POSITIVE. `install-static` is # upstream's own "static version of the library and slsh; NO MODULES" arm: # modules/ builds .so plugins that slsh dlopen()s, which cannot exist here. So # FILES:${PN}'s ${libdir}/slang/v2/modules/ entry matches nothing, and that is # correct rather than a failed install. Asserted this way round so that a modules # directory APPEARING -- which would mean the elf arm got built after all -- is # what turns the case red, and so a reader comparing FILES against the image does # not read the absence as a packaging bug. if [ -d /usr/lib/slang/v2/modules ]; then bst_notok slang/no-modules-on-static-arm \ "/usr/lib/slang/v2/modules exists: the dlopen()ed module .so's were built, so the recipe is no longer on upstream's install-static arm" else bst_ok slang/no-modules-on-static-arm fi # --------------------------------------------- upstream's own test suite ----- # THE GATE. ptest-runner is the harness the ticket closes on. -t is set well past # ptest-runner's own 300 s DEFAULT_TIMEOUT; it does not bound anything on this arm # anyway (relative timers do not fire, brintos/linux#48), so a tight value would be # an assertion about nothing. What actually holds the run is this file's own # BST-TIMEOUT marker, which the brintos-selftest runner enforces off /proc/uptime. if [ ! -x "$runner" ]; then bst_notok slang/ptest-suite "no ptest-runner on the image -- the suite cannot be run" for c in $SL_CASES; do bst_notok "slang/case-$c" "suite not run: no ptest-runner" done for c in suite-count suite-nofail; do bst_notok "slang/$c" "suite not run: no ptest-runner" done rm -rf "$tmp" return 2>/dev/null || exit 0 fi out=$tmp/runner.out "$runner" -t 20000 slang > "$out" 2>&1 rc=$? check_eq slang/ptest-runner-rc 0 "$rc" # ONE awk pass emitting "\t\t" per case, joined against the # control's list. Exact field comparison rather than a substring search, because # `PASS: argv.sl` is a substring of `PASS: argv.slc` -- a shell `case` glob would # score the .sl case off the .slc line and quietly mark a missing case green. printf '%s\n' $SL_CASES > "$tmp/expected" awk -v OFS='\t' ' NR==FNR { want[++ne] = $1; next } $1 == "PASS:" { v[$2] = "PASS"; ran++; next } $1 == "FAIL:" { v[$2] = "FAIL"; ran++; next } $1 == "TOTAL:" { tally = $0; next } $1 == "TIMEOUT:" { rerr = rerr $0 " "; next } $1 == "ERROR:" { rerr = rerr $0 " "; next } END { for (i = 1; i <= ne; i++) { n = want[i] print "slang/case-" n, "PASS", (n in v) ? v[n] : "NOT-REPORTED" } # A run that stopped a third of the way in leaves every case it DID reach # green, so the count is asserted separately from the verdicts. print "slang/suite-count", ne "", ran "" # ptest-runner counts PTESTS, not cases: run-ptest exits on runtests.sh, whose # status is the number of failed files. So this line is the suite verdict AND # the harness framing in one, and it is checked in addition to the per-case # loop because the two fail differently -- a case renamed upstream shows up # above, a suite that never started shows up here. print "slang/suite-nofail", "TOTAL: 1 FAIL: 0", tally "" # ptest-runner reports its own fork/execv/openpty failures, and its -t expiry, # as ERROR/TIMEOUT lines while still exiting 0 for some of them. print "slang/runner-no-error", "", rerr "" } ' "$tmp/expected" "$out" > "$tmp/verdicts" 2>/dev/null # Report, fork-free: check_eq on pre-computed triples. n=0 while IFS=' ' read -r id want got; do [ -n "$id" ] || continue check_eq "$id" "$want" "$got" n=$((n + 1)) done < "$tmp/verdicts" # A join that emitted nothing is itself a failure, and one that emitted half its # cases would otherwise read as a short but green suite. check_rc slang/join-nonempty 0 test "$n" -gt 97 rm -rf "$tmp"