# BST-TIMEOUT: 600 # measured 97s on target (89 cases, each a fresh exec of a # # 19 MB static wasm image); the runner default of 60 trips it # # xmlstarlet (XMLStarlet 1.6.1) — the XML command line toolkit. The binary is # named `xml`, not `xmlstarlet`. # # Every expectation below was scored against a NATIVE x86_64 control built from # the same tarball, with the same meta-oe patches, against the SAME libxml2 # 2.12.10 and libxslt 1.1.43 this image links — so a red here is a platform # difference rather than a wrong expectation. Where upstream's answer is # surprising (the exit codes for a missing file, a bad stylesheet, a bad XPath) # the suite asserts the OBSERVED answer and says so, so the case goes red when # xmlstarlet changes rather than when a reader's intuition differs. . "$BST_LIB" require xml xmlstarlet/present t=${TMPDIR:-/tmp}/bst-xmlstarlet.$$ mkdir -p "$t" 2>/dev/null || { bst_notok xmlstarlet/tmpdir "cannot mkdir $t"; return 2>/dev/null || exit 0; } cat > "$t/b.xml" <<'EOF' Dune9.99 Germinal7.50 EOF printf '\n' > "$t/bad.xml" # --- the banner --------------------------------------------------------------- # Its first line is the package version, which is EMPTY unless the recipe passes # VERSION= to make: configure.ac takes the version from `git describe` and the # tarball is not a git repository. This case is the regression guard for the # bbappend line that fixes it — see xmlstarlet_%.bbappend. check_out xmlstarlet/version-self "1.6.1" xml --version check_out xmlstarlet/version-xml2 "libxml2 2.12.10" xml --version check_out xmlstarlet/version-xslt "libxslt 1.1.43" xml --version check_out xmlstarlet/usage "XMLStarlet Toolkit" xml --help # --- el: element structure ---------------------------------------------------- check_out xmlstarlet/el-path "catalog/book/title" xml el "$t/b.xml" check_out xmlstarlet/el-uniq "catalog/book/price" xml el -u "$t/b.xml" check_eq xmlstarlet/el-lines 7 "$(xml el "$t/b.xml" | wc -l | tr -d ' ')" check_eq xmlstarlet/el-uniq-lines 4 "$(xml el -u "$t/b.xml" | wc -l | tr -d ' ')" check_out xmlstarlet/el-attr "catalog/book/@id" xml el -a "$t/b.xml" # --- sel: XPath --------------------------------------------------------------- check_out xmlstarlet/sel-value "Germinal" xml sel -t -v '//book[@id="b2"]/title' -n "$t/b.xml" check_out xmlstarlet/sel-count "2" xml sel -t -v 'count(//book)' -n "$t/b.xml" check_out xmlstarlet/sel-attr "b1" xml sel -t -v '//book[1]/@id' -n "$t/b.xml" check_out xmlstarlet/sel-sum "17.49" xml sel -t -v 'sum(//price)' -n "$t/b.xml" check_out xmlstarlet/sel-copy "Dune" xml sel -t -c '//book[1]/title' -n "$t/b.xml" check_out xmlstarlet/sel-text "Dune" xml sel -T -t -v '//title' -n "$t/b.xml" check_eq xmlstarlet/sel-match "b1 Dune" "$(xml sel -t -m '//book' -v '@id' -o ' ' -v 'title' -n "$t/b.xml" | head -1)" check_eq xmlstarlet/sel-if "Germinal" "$(xml sel -t -m '//book' -i '@lang="fr"' -v 'title' -n "$t/b.xml")" check_eq xmlstarlet/sel-else "DEARCHEAP" "$(xml sel -t -m '//book' -i 'price>8' -o 'DEAR' --else -o 'CHEAP' -n "$t/b.xml")" check_eq xmlstarlet/sel-sort "Dune" "$(xml sel -t -m '//book' -s A:T:U 'title' -v 'title' -n "$t/b.xml" | head -1)" # string / number functions run inside libxslt's XPath engine check_out xmlstarlet/sel-substr "Dun" xml sel -t -v 'substring(//title[1],1,3)' -n "$t/b.xml" check_out xmlstarlet/sel-concat "Dune-en" xml sel -t -v 'concat(//title[1],"-",//book[1]/@lang)' -n "$t/b.xml" check_out xmlstarlet/sel-translate "DUNE" xml sel -t -v 'translate(//title[1],"une","UNE")' -n "$t/b.xml" check_out xmlstarlet/sel-round "10" xml sel -t -v 'round(//price[1])' -n "$t/b.xml" check_out xmlstarlet/sel-last "Germinal" xml sel -t -v '//book[last()]/title' -n "$t/b.xml" # a document read from stdin rather than a path check_eq xmlstarlet/sel-stdin "2" "$(cat "$t/b.xml" | xml sel -t -v 'count(//book)' -n -)" # an XPath that selects nothing is not an error — upstream exits 0 with no output out=$(xml sel -t -v '//nothing' -n "$t/b.xml" 2>&1); rc=$? if [ "$rc" -eq 0 ] && [ -z "$out" ]; then bst_ok xmlstarlet/sel-empty else bst_notok xmlstarlet/sel-empty "rc=$rc $(_bst_1line "$out")"; fi # --- sel: namespaces ---------------------------------------------------------- cat > "$t/ns.xml" <<'EOF' deep EOF check_out xmlstarlet/ns-elem "deep" xml sel -N k=http://example.org/k -t -v '//k:item' -n "$t/ns.xml" check_out xmlstarlet/ns-attr "7" xml sel -N k=http://example.org/k -t -v '//k:item/@k:n' -n "$t/ns.xml" # --- ed: editing -------------------------------------------------------------- check_out xmlstarlet/ed-update "1.23" xml ed -u '//book[@id="b1"]/price' -v 1.23 "$t/b.xml" check_out xmlstarlet/ed-ins-attr 'year="1965"' xml ed -i '//book[1]' -t attr -n year -v 1965 "$t/b.xml" check_out xmlstarlet/ed-subnode "hi" xml ed -s '/catalog' -t elem -n note -v hi "$t/b.xml" check_out xmlstarlet/ed-rename "|\n|g' | head -1 | grep -o '' | wc -l | tr -d ' ')" # -L rewrites the file in place: the only case in this suite that WRITES to the # filesystem, so it doubles as a small write probe of the guest's /tmp. cp "$t/b.xml" "$t/e.xml" check xmlstarlet/ed-inplace-rc xml ed -L -u '//book[@id="b1"]/title' -v 'DUNE' "$t/e.xml" check_eq xmlstarlet/ed-inplace-file "1" "$(grep -c '<title>DUNE' "$t/e.xml")" check_eq xmlstarlet/ed-inplace-other "1" "$(grep -c 'Germinal' "$t/e.xml")" # --- tr: XSLT ----------------------------------------------------------------- cat > "$t/s.xsl" <<'EOF' | EOF check_eq xmlstarlet/tr-text "Dune|Germinal|" "$(xml tr "$t/s.xsl" "$t/b.xml")" cat > "$t/p.xsl" <<'EOF' nobody hello EOF check_eq xmlstarlet/tr-param-default "hello nobody" "$(xml tr "$t/p.xsl" "$t/b.xml")" check_eq xmlstarlet/tr-param-override "hello world" "$(xml tr "$t/p.xsl" -s who=world "$t/b.xml")" # libexslt is linked into this binary as well as libxslt; without it these two # extension namespaces resolve to nothing and the transform silently empties. cat > "$t/e.xsl" <<'EOF' | EOF check_eq xmlstarlet/tr-exslt "xxx|9.99" "$(xml tr "$t/e.xsl" "$t/b.xml")" # an XSLT template that builds a document rather than text cat > "$t/x.xsl" <<'EOF' EOF check_out xmlstarlet/tr-doc "Germinal" xml tr "$t/x.xsl" "$t/b.xml" # --- val: the four validation modes ------------------------------------------- printf '\n' > "$t/w.xml" check_out xmlstarlet/val-wf-ok "valid" xml val "$t/b.xml" check_rc xmlstarlet/val-wf-bad 1 xml val "$t/bad.xml" out=$(xml val --err "$t/bad.xml" 2>&1); rc=$? case "$out" in *"Opening and ending tag mismatch"*) hit=1 ;; *) hit=0 ;; esac if [ "$rc" -eq 1 ] && [ "$hit" -eq 1 ]; then bst_ok xmlstarlet/val-err-detail else bst_notok xmlstarlet/val-err-detail "rc=$rc hit=$hit $(_bst_1line "$out")"; fi cat > "$t/c.dtd" <<'EOF' EOF check_out xmlstarlet/val-dtd-ok "valid" xml val -d "$t/c.dtd" "$t/b.xml" check_rc xmlstarlet/val-dtd-bad 1 xml val -d "$t/c.dtd" "$t/w.xml" cat > "$t/c.xsd" <<'EOF' EOF check_out xmlstarlet/val-xsd-ok "valid" xml val -s "$t/c.xsd" "$t/b.xml" check_rc xmlstarlet/val-xsd-bad 1 xml val -s "$t/c.xsd" "$t/w.xml" cat > "$t/c.rng" <<'EOF' EOF check_out xmlstarlet/val-rng-ok "valid" xml val -r "$t/c.rng" "$t/b.xml" check_rc xmlstarlet/val-rng-bad 1 xml val -r "$t/c.rng" "$t/w.xml" # --- fo: formatting ----------------------------------------------------------- check_eq xmlstarlet/fo-indent ' ' "$(xml fo -s 4 "$t/b.xml" | sed -n 3p)" check_eq xmlstarlet/fo-omit "" "$(xml fo -o "$t/b.xml" | head -1)" check_out xmlstarlet/fo-decl '' xml fo "$t/b.xml" check_rc xmlstarlet/fo-bad 2 xml fo "$t/bad.xml" # -R recovers a malformed document rather than refusing it out=$(xml fo -R "$t/bad.xml" 2>/dev/null); rc=$? case "$out" in *""*) hit=1 ;; *) hit=0 ;; esac if [ "$rc" -eq 0 ] && [ "$hit" -eq 1 ]; then bst_ok xmlstarlet/fo-recover else bst_notok xmlstarlet/fo-recover "rc=$rc hit=$hit $(_bst_1line "$out")"; fi # --- pyx / depyx -------------------------------------------------------------- check_out xmlstarlet/pyx-elem "(catalog" xml pyx "$t/b.xml" check_out xmlstarlet/pyx-attr "Aid b1" xml pyx "$t/b.xml" check_eq xmlstarlet/pyx-roundtrip "2" "$(xml pyx "$t/b.xml" | xml depyx | xml sel -t -v 'count(//book)' -n -)" # --- c14n --------------------------------------------------------------------- check_out xmlstarlet/c14n "Dune" xml c14n "$t/b.xml" check_out xmlstarlet/c14n-exc "Dune" xml c14n --exc-without-comments "$t/b.xml" # canonicalisation drops the XML declaration — that is the point of it out=$(xml c14n "$t/b.xml" 2>&1); rc=$? case "$out" in "')" check_eq xmlstarlet/unesc '' "$(xml unesc '<a & b>')" check_eq xmlstarlet/esc-roundtrip '' "$(xml unesc "$(xml esc '')")" # --- ls: the filesystem surface ----------------------------------------------- # `xml ls` walks a directory with opendir/readdir/lstat and renders it as XML, so # it is this port's one real syscall probe on the guest's filesystem. mkdir -p "$t/d" && printf 'hi\n' > "$t/d/f.txt" check_out xmlstarlet/ls-root "" xml ls "$t/d" check_out xmlstarlet/ls-name 'n="f.txt"' xml ls "$t/d" check_out xmlstarlet/ls-size 's="3"' xml ls "$t/d" check_eq xmlstarlet/ls-parses "1" "$(xml ls "$t/d" | xml sel -t -v 'count(//f)' -n -)" # --- encodings ---------------------------------------------------------------- # libxml2 handles UTF-8, UTF-16, ASCII and ISO-8859-1 with BUILT-IN converters and # reaches iconv only for anything else — which matters here, because glibc's # iconv_open dlopens a gconv module and this image has no dynamic loader. These # cases stay inside the built-in set deliberately; the target's answer for an # iconv-only encoding is recorded in the ticket rather than asserted here. printf '\ncaf\351\n' > "$t/l1.xml" printf '\ncaf\303\251\n' > "$t/u8.xml" # both decode to the same UTF-8 output: 0xC3 0xA9 utf8_cafe=$(printf 'caf\303\251') check_eq xmlstarlet/enc-latin1-in "$utf8_cafe" "$(xml sel -t -v '//t' -n "$t/l1.xml")" check_eq xmlstarlet/enc-utf8-in "$utf8_cafe" "$(xml sel -t -v '//t' -n "$t/u8.xml")" check_out xmlstarlet/enc-latin1-decl 'encoding="ISO-8859-1"' xml fo "$t/l1.xml" # re-encoding UTF-8 out to ISO-8859-1 exercises the built-in encoder in reverse check_eq xmlstarlet/enc-latin1-out "2" "$(xml fo -e ISO-8859-1 "$t/u8.xml" | wc -l | tr -d ' ')" check_out xmlstarlet/enc-latin1-out-decl 'encoding="ISO-8859-1"' xml fo -e ISO-8859-1 "$t/u8.xml" # --- exit codes, as upstream actually defines them ---------------------------- # Each of these was measured on the native control. They are asserted because # they are the contract a shell script around xmlstarlet depends on, and because # two of them are surprising: a missing FILE is 3, not 1 or 2. check_rc xmlstarlet/rc-missing-file 3 xml sel -t -v '//x' -n "$t/nope.xml" check_rc xmlstarlet/rc-el-missing 3 xml el "$t/nope.xml" check_rc xmlstarlet/rc-ed-missing 3 xml ed -u '//title' -v x "$t/nope.xml" check_rc xmlstarlet/rc-bad-xpath 4 xml sel -t -v '//[' -n "$t/b.xml" check_rc xmlstarlet/rc-pyx-malformed 4 xml pyx "$t/bad.xml" check_rc xmlstarlet/rc-missing-xsl 4 xml tr "$t/nope.xsl" "$t/b.xml" check_rc xmlstarlet/rc-unknown-cmd 2 xml bogus check_rc xmlstarlet/rc-no-args 2 xml printf '\n' > "$t/bs.xsl" check_rc xmlstarlet/rc-bad-xsl 5 xml tr "$t/bs.xsl" "$t/b.xml" # a document that is well-formed but absent from the filesystem is "invalid", # not an error, to `val` — a different answer from every other subcommand's 3 check_rc xmlstarlet/rc-val-missing 1 xml val "$t/nope.xml" # --- scale -------------------------------------------------------------------- # 2000 elements / ~44 kB, shipped as a payload rather than generated on target # (a 2000-iteration ash loop costs more than the case is worth). This is the only # case that exercises the parser at a size the fixtures above cannot. if [ -f "$BST_DATA/xmlstarlet-big.xml" ]; then big="$BST_DATA/xmlstarlet-big.xml" check_eq xmlstarlet/big-count "2000" "$(xml sel -t -v 'count(//n)' -n "$big")" check_eq xmlstarlet/big-last "v1999" "$(xml sel -t -v '//n[@id="1999"]' -n "$big")" check_eq xmlstarlet/big-pyx "2000" "$(xml pyx "$big" | xml depyx | xml sel -t -v 'count(//n)' -n -)" check xmlstarlet/big-val xml val "$big" check_eq xmlstarlet/big-edit "1" "$(xml ed -u '//n[@id="0"]' -v EDITED "$big" | grep -c '>EDITED<')" else bst_notok xmlstarlet/big-payload "missing $BST_DATA/xmlstarlet-big.xml" fi rm -rf "$t"