. "$BST_LIB" # LibCYAML (meta-oe recipes-support/cyaml) -- Michael Drake's schema-based YAML # loader and saver, sitting on the libyaml already covered by 73-libyaml.test. # Where libyaml hands a caller an event stream to walk, cyaml takes a # declarative schema -- field name, type, offset, size -- and loads a document # straight into the caller's C structs, allocating sequences and strings as it # goes, then writes those same structs back out as YAML. # # --------------------------------------------------------------------------- # TWO CONSUMERS, BECAUSE THEY PROVE DIFFERENT THINGS # # data/cyaml-units is UPSTREAM'S OWN 539-case unit suite, cross-linked from # the same sources this port builds. It reaches deeper into the library than # anything written for a port could -- the internal utf8 helpers, every # schema type against its error paths, the copy and free walkers -- and its # expectations are LITERALS COMPILED INTO THE BINARY, so unlike a # control-comparison it cannot be made vacuously green by a bad oracle. Its # per-test verdicts are still compared name-by-name against # data/cyaml-units-native.txt, a run of the identical source on x86_64: that # catches the other failure, a target binary in which tests silently stop # running rather than fail. # # data/cyaml-probe is a consumer of the INSTALLED PACKAGE. It is compiled # against /usr/include/cyaml and linked against /usr/lib/libcyaml.a exactly # as a downstream recipe would reach them, so it is the artifact that fails # if this port's hand-written do_install ships the wrong archive, drops a # header, or installs something that cannot be included. cyaml-units cannot # see any of that -- it links the tree it was built from. # # WHAT IS COMPARED AND WHAT IS NOT. Everything the probe prints in `cmp` mode # is schema-driven decode, emitter output bytes and error codes: functions of # the library alone, fixed by the code and the YAML spec rather than by word # size, byte order or libc. Those are compared line for line against # data/cyaml-native.txt with NO tolerance band -- one differing line is a # defect. The probe's `rt` mode is file-backed load and save, which is where # this platform's gaps live; those are scored against what a working platform # must do and attributed when they are not met, rather than diffed against a # control that would always disagree. # # THE ANCHORED CASES EXIST BECAUSE AN ORACLE CAN BE WRONG. A control captured # from an already-broken build would make every compared case agree with it. # So the values a specification or the C standard fixes -- the IEEE-754 bit # patterns of two exactly-representable literals, the integer extremes at each # width, the bitfield composition, the UTF-8 bytes -- are ALSO asserted against # literals here. Those hold whatever the control says. # --------------------------------------------------------------------------- # A root prefix, empty on the target and set only when this suite is validated # NATIVELY under dash before it ships. Writing /usr/lib and /usr/include needs # real root, so a native run stages them under a temporary tree and points this # at it. The runner never sets it, so on the image these are absolute paths. R="${BST_CYAML_ROOT:-}" ARCHIVE=$R/usr/lib/libcyaml.a INCDIR=$R/usr/include/cyaml PC=$R/usr/lib/pkgconfig/libcyaml.pc PROBE=$BST_DATA/cyaml-probe NATIVE=$BST_DATA/cyaml-native.txt UNITS=$BST_DATA/cyaml-units UNITS_NATIVE=$BST_DATA/cyaml-units-native.txt BASIC=$BST_DATA/cyaml-basic.yaml if [ ! -f "$ARCHIVE" ]; then bst_skip cyaml/present "cyaml not installed on this image" exit 0 fi bst_ok cyaml/present # --- the package is whole ---------------------------------------------------- check cyaml/hdr-cyaml test -f "$INCDIR/cyaml.h" check cyaml/hdr-private test -f "$INCDIR/private.h" check cyaml/staticlib test -f "$ARCHIVE" # The bbappend builds and installs the static half only, because libcyaml's # hand-rolled Makefile has a -shared rule that the distro-wide shared-library # disable never reaches and wasmld refuses. A .so appearing here would mean the # override had been lost and the port was shipping something the image cannot # load. if [ -n "$(find "$R/usr/lib" -maxdepth 1 -name 'libcyaml.so*' 2>/dev/null)" ]; then bst_notok cyaml/no-shared "a libcyaml shared object is installed on a static-only image" else bst_ok cyaml/no-shared fi # The pkg-config file is how a downstream recipe finds this package at all, so # it is part of the deliverable rather than a nicety. check cyaml/pkgconfig test -f "$PC" if [ -f "$PC" ]; then # Both libraries: a consumer of libcyaml.a must also link libyaml, and a .pc # that named only -lcyaml would leave every downstream link with a screenful # of undefined yaml_* references. check_out cyaml/pkgconfig-lcyaml "-lcyaml" cat "$PC" check_out cyaml/pkgconfig-lyaml "-lyaml" cat "$PC" # UPSTREAM RECIPE DEFECT, PINNED DELIBERATELY. meta-oe's cyaml_1.4.1.bb pins # SRCREV 07ff8654, which is 49 commits PAST the v1.4.1 tag on upstream main, # where the in-tree version is already 2.0.0-devel. So the package database # says 1.4.1 and this file -- the thing a consumer's version check actually # reads -- says 2.0.0. Nothing to do with this arm. Pinned so that if meta-oe # ever corrects the PV or the SRCREV, this case goes red and points the reader # straight at the change. check_out cyaml/upstream-pc-version "Version: 2.0.0" cat "$PC" fi # --- upstream's own unit suite ----------------------------------------------- if [ ! -f "$UNITS" ]; then bst_notok cyaml/units-present "$UNITS missing -- the library cannot be proven to run" else bst_ok cyaml/units-present if [ ! -f "$UNITS_NATIVE" ]; then bst_notok cyaml/units-native-present "$UNITS_NATIVE missing -- no control for the unit run" else bst_ok cyaml/units-native-present # brintos-selftests' do_install stages data/* at 0644 and the deployed rootfs # is read-only at browser runtime, so neither binary can be executed where it # lies -- stage an executable copy under TMPDIR, as every probe-driven suite # here does. utmp=${TMPDIR:-/tmp}/bst-cyaml-u.$$ _u=$utmp.bin cp "$UNITS" "$_u" 2>/dev/null && chmod 0755 "$_u" 2>/dev/null if [ ! -x "$_u" ]; then bst_notok cyaml/units-exec "could not stage an executable copy of the unit suite" else bst_ok cyaml/units-exec # The suite's file tests read test/data/basic.yaml and write into build/, # both RELATIVE to the working directory. Give it a private tree with # those two paths and run it there; without them the eleven file cases # would fail for want of a directory rather than for want of a working # library. urun=$utmp.d mkdir -p "$urun/test/data" "$urun/build" 2>/dev/null cp "$BASIC" "$urun/test/data/basic.yaml" 2>/dev/null if [ ! -f "$urun/test/data/basic.yaml" ]; then bst_notok cyaml/units-fixture "could not stage test/data/basic.yaml" else bst_ok cyaml/units-fixture fi ( cd "$urun" && "$_u" ) > "$utmp.out" 2> "$utmp.err"; urc=$? # The runner reports 0 iff every case passed, so this is the whole suite's # verdict in one case -- kept separate from the per-case comparison below # because a binary that died before running anything would leave that # comparison with nothing to disagree with. check_eq cyaml/units-rc 0 "$urc" # Upstream's own summary line. Asserting the DENOMINATOR as well as the # verdict is what catches a run in which cases stopped being registered. usum=$(grep -E '^(PASS|FAIL): [0-9]+ of [0-9]+ tests passed' "$utmp.err" | tail -n 1) check_eq cyaml/units-summary "PASS: 539 of 539 tests passed." "$usum" # Normalise this run's verdicts into the control's shape, then walk the # two in lockstep. Same binary, same registration order by construction, # so a single pass with no subprocess per case -- 539 awk invocations # would be slow inside the guest. A run that ended early leaves the # remaining reads empty, which fails those cases rather than silently # shortening the suite. grep -E '^ (PASS|FAIL|TODO): ' "$utmp.err" \ | sed -e 's/^ //' -e 's/^\([A-Z]*\): \([^ ]*\).*$/\1 \2/' > "$utmp.v" want_u=$(wc -l < "$UNITS_NATIVE" | tr -d ' ') got_u=$(wc -l < "$utmp.v" | tr -d ' ') check_eq cyaml/units-count "$want_u" "$got_u" exec 5< "$UNITS_NATIVE" exec 6< "$utmp.v" while IFS= read -r wl <&5; do IFS= read -r gl <&6 || gl="" wv=${wl%% *}; wn=${wl#* } gv=${gl%% *}; gn=${gl#* } if [ "$wn" = "$gn" ] && [ "$wv" = "$gv" ]; then bst_ok "cyaml/u-$wn" elif [ "$wn" = "$gn" ]; then bst_notok "cyaml/u-$wn" "native=$wv target=$gv" else bst_notok "cyaml/u-$wn" "case absent or out of order (target reported '$(_bst_1line "$gl")')" fi done exec 5<&- 6<&- rm -rf "$urun" rm -f "$_u" "$utmp.out" "$utmp.err" "$utmp.v" fi fi fi # --- the installed-package consumer ------------------------------------------ if [ ! -f "$PROBE" ]; then bst_notok cyaml/probe-present "$PROBE missing -- the installed package cannot be verified" exit 0 fi bst_ok cyaml/probe-present if [ ! -f "$NATIVE" ]; then bst_notok cyaml/native-present "$NATIVE missing -- no oracle to compare against" exit 0 fi bst_ok cyaml/native-present tmp=${TMPDIR:-/tmp}/bst-cyaml.$$ _p=$tmp.bin cp "$PROBE" "$_p" 2>/dev/null && chmod 0755 "$_p" 2>/dev/null if [ ! -x "$_p" ]; then bst_notok cyaml/probe-exec "could not stage an executable copy of the probe" rm -f "$_p" exit 0 fi bst_ok cyaml/probe-exec "$_p" cmp > "$tmp.out" 2> "$tmp.err"; rc=$? check_eq cyaml/probe-rc 0 "$rc" # A truncated run must be distinguishable from a run that legitimately produced # these lines, so the terminator is checked before any comparison. Without it a # crash half way through would silently reduce the number of cases rather than # fail one. if [ "$(tail -n 1 "$tmp.out" 2>/dev/null)" = "PROBE-END" ]; then bst_ok cyaml/probe-complete else bst_notok cyaml/probe-complete "no PROBE-END terminator: $(_bst_1line "$(tail -n 3 "$tmp.out" 2>/dev/null)")" fi want_n=$(wc -l < "$NATIVE" | tr -d ' ') got_n=$(wc -l < "$tmp.out" | tr -d ' ') check_eq cyaml/line-count "$want_n" "$got_n" cy_get() { awk -v k="$1" '$1==k { $1=""; sub(/^ /,""); print; exit }' "$tmp.out"; } # --- anchored: the C standard and IEEE 754, not the control ------------------ # 0.15625 and -2.5 are exactly representable, so a correct decode has ONE right # answer per width whatever the host. Asserting the BIT PATTERN rather than a # printed value is what makes this meaningful: a strtod that lost precision, or # a float path that went through double and back, would still print something # that looked right at %g. 0.15625 = 1.01b x 2^-3 -> 0x3FC4000000000000. check_eq cyaml/anchor-dbl-bits 3fc4000000000000 "$(cy_get B11-dbl-bits)" check_eq cyaml/anchor-flt-bits c0200000 "$(cy_get B12-flt-bits)" # The integer extremes at every width the schema supports. INT64_MIN is the one # a "parse the digits then negate" implementation gets wrong, and the unsigned # maxima are where a target that quietly used a 32-bit accumulator shows up. check_eq cyaml/anchor-i8 -128 "$(cy_get B01-i8)" check_eq cyaml/anchor-i16 -32768 "$(cy_get B02-i16)" check_eq cyaml/anchor-i32 -2147483648 "$(cy_get B03-i32)" check_eq cyaml/anchor-i64 -9223372036854775808 "$(cy_get B04-i64)" check_eq cyaml/anchor-u8 255 "$(cy_get B05-u8)" check_eq cyaml/anchor-u16 65535 "$(cy_get B06-u16)" check_eq cyaml/anchor-u32 4294967295 "$(cy_get B07-u32)" check_eq cyaml/anchor-u64 18446744073709551615 "$(cy_get B08-u64)" # The bitfield is composed from three named sub-values at three offsets: # lo=5 at bit 0, mid=0xaa at bit 4, hi=0xc at bit 12 -> 0xcaa5. A wrong shift # gives a wrong number rather than an error, which nothing else here would see. check_eq cyaml/anchor-bits 0000caa5 "$(cy_get C06-bits)" # Flags are the OR of every listed entry, not the last one seen. read|exec = 5. check_eq cyaml/anchor-flags 5 "$(cy_get C02-perm)" check_eq cyaml/anchor-flag-read 1 "$(cy_get C03-perm-read)" check_eq cyaml/anchor-flag-wr 0 "$(cy_get C04-perm-write)" check_eq cyaml/anchor-flag-exec 1 "$(cy_get C05-perm-exec)" # A UTF-8 sequence loaded out of the document as bytes: "été" is # c3a9 74 c3a9. A locale-dependent re-encode on the way in would change these # and would be invisible in any comparison of rendered text. check_eq cyaml/anchor-utf8-tag "5 c3a974c3a9" "$(cy_get D02-tag-2)" # Argument validation must be REPORTED, not trapped on: a library that # dereferenced a NULL config would take the whole process with it, so the fact # that these lines exist at all is half the assertion. check_eq cyaml/anchor-null-config "24 Bad parameter: NULL config" "$(cy_get F20-null-config)" check_eq cyaml/anchor-null-schema "25 Bad parameter: NULL schema" "$(cy_get F21-null-schema)" check_eq cyaml/anchor-null-data "16 Bad parameter: NULL data" "$(cy_get F22-null-data)" # TWO UPSTREAM BEHAVIOURS, PINNED DELIBERATELY -- neither is a port defect and # neither is this platform's: # # cyaml__read_bool starts at true and only flips to false for one of five # recognised strings, so ANY other scalar -- "perhaps", "2", a misspelt # "flase" -- loads silently as true and returns OK. A caller cannot tell a # typo from an intended value. # # An empty input is "no document" rather than "a broken document": OK is # returned and the caller's pointer is left unset, so code that treats OK as # "data is populated" dereferences NULL. # # Both are upstream libcyaml's on every architecture. Pinned rather than # reported, so a future bump that fixes either turns these red and points the # reader at the change. check_eq cyaml/upstream-bool-lenient "0 Success" "$(cy_get F08-bool-unknown-is-true)" check_eq cyaml/upstream-empty-is-ok "0 Success" "$(cy_get F12-empty-is-ok)" # --- the control comparison: one case per probe line ------------------------- exec 3< "$NATIVE" exec 4< "$tmp.out" while IFS= read -r wl <&3; do IFS= read -r gl <&4 || gl="" id=${wl%% *} if [ "$wl" = "$gl" ]; then bst_ok "cyaml/$id" else bst_notok "cyaml/$id" "want='$(_bst_1line "${wl#* }")' got='$(_bst_1line "${gl#* }")'" fi done exec 3<&- 4<&- if [ -s "$tmp.err" ]; then bst_notok cyaml/clean-stderr "$(_bst_1line "$(cat "$tmp.err")")" else bst_ok cyaml/clean-stderr fi # --- RT: the kernel-facing layer --------------------------------------------- # # Not compared against the control -- see the header. Each case asserts what a # working platform must do. A red here is a platform gap: it is attributed at # the verify gate and recorded in the design doc, and the ticket accepts it as a # legitimate result rather than a reason to withhold the port. rtdir=${TMPDIR:-/tmp} # One invocation per section, because on this platform an RT case can TRAP # rather than return an error and a trap ends the whole process. Run as one # batch the first gap would hide every gap after it, which is the opposite of # what this ticket wants -- surfacing the gaps IS part of the deliverable. Each # section also declares how many cases it owes, so a section that dies reports # short rather than simply making the suite smaller. rt_section() { _sec=$1; _want=$2 "$_p" rt "$rtdir" "$_sec" > "$tmp.rt" 2> "$tmp.rterr"; _rc=$? _got=$(grep -c '^RT[0-9]' "$tmp.rt" 2>/dev/null || echo 0) while IFS= read -r line; do case "$line" in RT[0-9]*) ;; *) continue ;; esac rid=${line%% *} rval=${line#* } if [ "$rval" = "ok" ]; then bst_ok "cyaml/$rid" else bst_notok "cyaml/$rid" "$(_bst_1line "$rval")" fi done < "$tmp.rt" # The verdict is `exited 0 AND reported every case it owes`. rt mode's only # exit path is fflush + _exit(0) after the terminator, so a zero status # already proves the probe ran past the end; the count catches the case where # it ran past the end having skipped work. if [ "$_rc" = 0 ] && [ "$_got" = "$_want" ]; then bst_ok "cyaml/rt-$_sec-complete" else bst_notok "cyaml/rt-$_sec-complete" \ "rc=$_rc, $_got/$_want cases reported, died after '$(_bst_1line "$(grep '^RT[0-9]' "$tmp.rt" 2>/dev/null | tail -n 1)")': $(_bst_1line "$(cat "$tmp.rterr" 2>/dev/null)")" fi check_eq "cyaml/rt-$_sec-count" "$_want" "$_got" if [ -s "$tmp.rterr" ]; then bst_notok "cyaml/rt-$_sec-stderr" "$(_bst_1line "$(cat "$tmp.rterr")")" else bst_ok "cyaml/rt-$_sec-stderr" fi } rt_section file 6 # RT01-RT06: save_file, read-back, round trip, determinism, # and the FILE_OPEN error path rm -f "$_p" "$tmp.out" "$tmp.err" "$tmp.rt" "$tmp.rterr"