# BST-TIMEOUT: 300 # ~60 cases; 8 upstream programs + ~30 ikslint/iksperf runs # # iksemel 1.5+git -- "fast and portable XML parser and Jabber protocol library". # # THE EVIDENCE HERE IS OF TWO KINDS, AND THEY COVER DIFFERENT THINGS. # # 1. UPSTREAM'S OWN RELEASE GATE, RUN ON TARGET. test/ ships eight conformance # programs that `make all` already builds and `make install` then discards; # the bbappend installs them instead (the qcbortest precedent -- an image with # no compiler has no other way to run a library's own tests). They are the # first eight cases below, one case each rather than one lump, so a failure # names the subsystem: the arena allocator, the node/attribute API, the # incremental SAX parser, DOM build/serialize, SHA1, MD5, the stanza filter, # and JID parsing. Each is silent + rc=0 on success and prints expected-vs-got # then exit(1) on failure, so its own diagnostic is what lands in the detail. # # 2. CROSS-ARM AGREEMENT ON A FIXED CORPUS. data/iksemel-corpus.xml is a 48 kB # deterministic document (src/iksemel_mkfixture.py) and data/iksemel-native.txt # is what an x86_64 build of the SAME SRCREV, configured the same way, reports # about it (src/iksemel_mknative.sh). The target is asked to reproduce those # numbers. This is what upstream's corpus does NOT cover: it uses only # in-memory buffers, so nothing in it reads a file, and the read path is # exactly where this arm differs. # # THE ONE ASSERTION THAT CANNOT BE FAKED BY A HOST BINARY is wasm32-stack-smaller: # ikstack occupancy for the same document is dominated by pointer-sized fields, so # a 4-byte-pointer build MUST occupy strictly less than the 8-byte control. Every # other number here is arch-independent by design, and would pass just as happily # against an x86_64 binary that had somehow been packaged into the image. # # WHAT IS NOT COVERED, DELIBERATELY: the plaintext socket transport # (src/io-posix.c, USE_DEFAULT_IO). It compiles and links, but driving it wants a # Jabber server to connect to, which the gate does not have. It is stated as a # gap in the test-plan entry rather than papered over with a connect-to-nothing # case that would only assert that the connection failed. . "$BST_LIB" require ikslint iksemel/present NAT="$BST_DATA/iksemel-native.txt" COR="$BST_DATA/iksemel-corpus.xml" [ -r "$NAT" ] || { bst_skip iksemel/payload "missing control $NAT"; exit 0; } [ -r "$COR" ] || { bst_skip iksemel/payload "missing corpus $COR"; exit 0; } # Read one key out of the x86_64 control. Values may contain spaces. nat() { sed -n "s/^$1 //p" "$NAT" | head -1; } W=${TMPDIR:-/tmp}/bst-iksemel.$$ mkdir -p "$W" 2>/dev/null || { bst_notok iksemel/workdir "cannot mkdir $W"; exit 0; } trap 'rm -rf "$W"' EXIT # --------------------------------------------------------------------------- # 1. Upstream's eight conformance programs. # # libexecdir is /usr/libexec on this distro, but the suite locates the directory # rather than hardcoding it: a layer that moves libexecdir should make these # cases skip with a clear reason, not fail as though the library were broken. IKST= for d in /usr/libexec/iksemel-tests /usr/lib/iksemel-tests /usr/libexec/iksemel; do [ -d "$d" ] && { IKST=$d; break; } done UPSTREAM_TESTS="tst-ikstack tst-iks tst-sax tst-dom tst-sha tst-md5 tst-filter tst-jid" if [ -z "$IKST" ]; then # Not a skip: the bbappend installs these, so their absence is a packaging # regression, and a skip here would hide exactly the deliverable this port # exists to add. for t in $UPSTREAM_TESTS; do bst_notok "iksemel/upstream-$t" "iksemel-tests dir not installed (looked in /usr/libexec, /usr/lib)" done else for t in $UPSTREAM_TESTS; do if [ ! -x "$IKST/$t" ]; then bst_notok "iksemel/upstream-$t" "not installed in $IKST" else out=$("$IKST/$t" 2>&1) && rc=0 || rc=$? if [ "$rc" -eq 0 ]; then bst_ok "iksemel/upstream-$t" else bst_notok "iksemel/upstream-$t" "rc=$rc $(_bst_1line "$out")" fi fi done fi # --------------------------------------------------------------------------- # 2. The corpus arrived intact. Everything below compares against numbers taken # from THIS document, so a truncated payload would produce a page of # mismatches whose real cause is the payload, not the parser. sz=$(wc -c < "$COR" | tr -d ' ') check_eq iksemel/corpus-bytes "$(nat IKS-CORPUS-BYTES)" "$sz" # --------------------------------------------------------------------------- # 3. ikslint: well-formedness and the document's shape. check iksemel/lint-wellformed ikslint "$COR" check_eq iksemel/lint-version "$(nat IKS-VER-lint)" "$(ikslint -V 2>&1 | head -1)" ls_out=$(ikslint -s "$COR" 2>&1) check_eq iksemel/lint-pairs "$(nat IKS-LINT-PAIRS)" \ "$(printf '%s' "$ls_out" | sed -n 's/^Tags: \([0-9]*\) pairs.*/\1/p')" check_eq iksemel/lint-single "$(nat IKS-LINT-SINGLE)" \ "$(printf '%s' "$ls_out" | sed -n 's/^Tags: [0-9]* pairs, \([0-9]*\) single.*/\1/p')" check_eq iksemel/lint-depth "$(nat IKS-LINT-DEPTH)" \ "$(printf '%s' "$ls_out" | sed -n 's/.*single, \([0-9]*\) max depth.*/\1/p')" check_eq iksemel/lint-cdata "$(nat IKS-LINT-CDATA)" \ "$(printf '%s' "$ls_out" | sed -n 's/^Total size of character data: \([0-9]*\) bytes.*/\1/p')" # The histogram walks the whole document and counts by name -- it is the check # that the parser did not merely survive the file but saw every element in it. hist=$(ikslint -t "$COR" 2>&1) check_eq iksemel/hist-unique "$(nat IKS-HIST-UNIQUE)" \ "$(printf '%s' "$hist" | sed -n 's/^Histogram of \([0-9]*\) unique tags.*/\1/p')" for tag in record name value note flag level catalog bottom; do got=$(printf '%s\n' "$hist" | sed -n "s/^<$tag> \([0-9]*\) times\..*/\1/p") check_eq "iksemel/hist-$tag" "$(nat "IKS-HIST-$tag")" "${got:-0}" done # --------------------------------------------------------------------------- # 4. The incremental SAX parser, fed at six read-block sizes from 1 byte up. # # THE TAG HOOK COUNT MUST NOT MOVE. Feeding the parser one byte at a time forces # every tag, attribute and entity in a 48 kB document to be reassembled from # saved state across a resume boundary; if the saved state is wrong -- which on # an arm with a 4-byte size_t is the failure to expect -- the count changes or # the parse dies. The CDATA hook count DOES move (character data is delivered in # chunks, so smaller blocks mean more calls), and it is pinned per block size # from the control rather than being waved through as "expected to vary". for b in 1 7 64 512 4096 65536; do o=$(iksperf -s -b "$b" "$COR" 2>&1) check_eq "iksemel/taghook-b$b" "$(nat "IKS-PERF-TAGHOOK-b$b")" \ "$(printf '%s' "$o" | sed -n 's/^SAX: tag hook called \([0-9]*\),.*/\1/p')" check_eq "iksemel/cdatahook-b$b" "$(nat "IKS-PERF-CDATAHOOK-b$b")" \ "$(printf '%s' "$o" | sed -n 's/.*cdata hook called \([0-9]*\) times.*/\1/p')" done # An independent cross-check between two different tools' counters: at -b 1 the # cdata hook fires once per character-data byte, so it must equal the total # ikslint reported. Two code paths agreeing is worth more than either alone. cd1=$(iksperf -s -b 1 "$COR" 2>&1 | sed -n 's/.*cdata hook called \([0-9]*\) times.*/\1/p') check_eq iksemel/cdatahook-b1-equals-lint-cdata "$(nat IKS-LINT-CDATA)" "$cd1" # --------------------------------------------------------------------------- # 5. SHA1 over the corpus, computed by iksemel's own src/sha.c. sha=$(iksperf -1 "$COR" 2>&1 | sed -n 's/^SHA: hash \[\([0-9a-f]*\)\].*/\1/p') check_eq iksemel/sha1 "$(nat IKS-PERF-SHA1)" "$sha" # And against a SECOND implementation on the target, when the image has one -- # this is what turns "matches the control" into "is actually SHA1". if command -v sha1sum >/dev/null 2>&1; then check_eq iksemel/sha1-vs-sha1sum "$(sha1sum "$COR" | cut -d' ' -f1)" "$sha" else bst_skip iksemel/sha1-vs-sha1sum "no sha1sum on this image" fi # --------------------------------------------------------------------------- # 6. THE WASM32 DISCRIMINATOR. ikstack occupancy for one document is dominated by # pointer-sized fields in the iks node and attribute structs, so a build with # 4-byte pointers MUST use strictly less arena than the 8-byte control for the # identical input. This is the one case here that an x86_64 binary packaged # into the image by mistake could not pass. dom=$(iksperf -d "$COR" 2>&1) alloc=$(printf '%s' "$dom" | sed -n 's/.*ikstack: \([0-9]*\) bytes allocated.*/\1/p') used=$(printf '%s' "$dom" | sed -n 's/.*allocated, \([0-9]*\) bytes used.*/\1/p') nat_used=$(nat IKS-STACK-USED) if [ -z "$used" ] || [ -z "$alloc" ]; then bst_notok iksemel/stack-reported "iksperf -d printed no ikstack line: $(_bst_1line "$dom")" elif [ "$used" -le 0 ] 2>/dev/null; then bst_notok iksemel/stack-reported "ikstack used=$used" else bst_ok iksemel/stack-reported fi if [ -n "$used" ] && [ -n "$nat_used" ] && [ "$used" -lt "$nat_used" ] 2>/dev/null; then bst_ok iksemel/wasm32-stack-smaller else bst_notok iksemel/wasm32-stack-smaller \ "used=$used not < x86_64 $nat_used -- 32-bit pointers must shrink the arena" fi # The arena must also still be a superset of what it handed out; a used > alloc # would mean iks_stack_stat is reading past its own bookkeeping. if [ -n "$used" ] && [ -n "$alloc" ] && [ "$used" -le "$alloc" ] 2>/dev/null; then bst_ok iksemel/stack-used-within-alloc else bst_notok iksemel/stack-used-within-alloc "used=$used alloc=$alloc" fi # --------------------------------------------------------------------------- # 7. Well-formedness verdicts on one violation per file. # # These pin OBSERVED behaviour on both arms, including where iksemel is LAX: # it does not enforce entity declaration, a single root element, or a construct # left open at EOF, so three of the five are rc=0 with no message. That is not a # brintOS defect and not something this port fixes -- it is upstream's parser, # and pinning it is what makes a real change in the error paths show up as a red. for n in bad-entity double-root mismatch stray-lt unclosed-attr; do f="$BST_DATA/iksemel-bad-$n.xml" if [ ! -r "$f" ]; then bst_notok "iksemel/bad-$n-rc" "missing payload $f" continue fi out=$(ikslint "$f" 2>&1) && rc=0 || rc=$? check_eq "iksemel/bad-$n-rc" "$(nat "IKS-BAD-$n-RC")" "$rc" # The fixture path is stripped on both sides: the payload sits at a different # absolute path here than it did on the build host. msg=$(printf '%s' "$out" | head -1 | sed "s| in file '.*'||") check_eq "iksemel/bad-$n-msg" "$(nat "IKS-BAD-$n-MSG")" "$msg" done # --------------------------------------------------------------------------- # 8. The TLS boundary the bbappend chose (PACKAGECONFIG = ""). # # gnutls is not ported, so iks_has_tls() is 0 and the library has no STARTTLS. # Asserted from the target through the only path that reports it without a # compiler, so enabling TLS later is a CHANGED ASSERTION, not a silent gain. if command -v iksroster >/dev/null 2>&1; then check_out iksemel/tls-absent-note "$(nat IKS-TLS-HELPNOTE)" iksroster --help rout=$(iksroster -s -b test@example.org 2>&1) && rrc=0 || rrc=$? check_eq iksemel/tls-secure-rc "$(nat IKS-TLS-SECURE-RC)" "$rrc" check_eq iksemel/tls-secure-msg "$(nat IKS-TLS-SECURE-MSG)" \ "$(printf '%s' "$rout" | sed -n '2p')" check_eq iksemel/roster-version "$(nat IKS-VER-roster)" "$(iksroster -V 2>&1 | head -1)" else for c in tls-absent-note tls-secure-rc tls-secure-msg roster-version; do bst_notok "iksemel/$c" "iksroster not installed (iksemel-bin missing from the image?)" done fi check_eq iksemel/perf-version "$(nat IKS-VER-perf)" "$(iksperf -V 2>&1 | head -1)" # --------------------------------------------------------------------------- # 9. THE READ PATH ACROSS TWO FILESYSTEMS. # # ikslint sizes its read buffer from st_blksize (tools/ikslint.c:161), so it is # one of the few tools on the image whose behaviour is a direct function of what # the filesystem reports. The payload lives on the virtiofs root; a tmpfs copy # is the control. libmaxminddb's port set this precedent after psmisc found the # virtiofs root refusing an operation tmpfs allowed -- the class is real here, # not hypothetical, and a parser that reads short on one of the two would show up # as a changed character-data total rather than as an error. cp "$COR" "$W/corpus.xml" 2>/dev/null if [ -r "$W/corpus.xml" ]; then a=$(ikslint -s "$COR" 2>&1 | sed -n 's/^Total size of character data: \([0-9]*\).*/\1/p') b=$(ikslint -s "$W/corpus.xml" 2>&1 | sed -n 's/^Total size of character data: \([0-9]*\).*/\1/p') check_eq iksemel/read-virtiofs-vs-tmpfs "$a" "$b" check_eq iksemel/read-tmpfs-cdata "$(nat IKS-LINT-CDATA)" "$b" else bst_notok iksemel/read-virtiofs-vs-tmpfs "could not copy corpus to $W" fi # --------------------------------------------------------------------------- # 10. Packaging: what a consumer of this port actually links against, which # nothing above exercises. # # THIS ARM IS STATIC ONLY. The distro builds no shared libraries for the guest, # so libtool produces libiksemel.a and no libiksemel.so -- which is why the # upstream conformance programs above are plain wasm modules rather than libtool # wrappers. Asserting a .so here would be asserting a distro property that is # false; asserting the .a is asserting the deliverable. if [ -f /usr/lib/libiksemel.a ]; then bst_ok iksemel/libiksemel-a-installed else bst_notok iksemel/libiksemel-a-installed "no /usr/lib/libiksemel.a" fi # The absence of a .so is pinned deliberately, not left unstated: if this distro # ever gains shared libraries, this case turns red and someone re-reads the port # rather than discovering the change through a link failure downstream. if ls /usr/lib/libiksemel.so* >/dev/null 2>&1; then bst_notok iksemel/no-shared-lib "a libiksemel.so appeared -- this arm was static-only when the port landed" else bst_ok iksemel/no-shared-lib fi check iksemel/pkgconfig-installed test -f /usr/lib/pkgconfig/iksemel.pc check iksemel/header-installed test -f /usr/include/iksemel.h