# BST-TIMEOUT: 600 # # ~20 xmllint runs, each of which parses a whole DocBook DTD (five modules plus # up to twenty ISO entity sets, ~450-540 kB per version) before it validates # anything, and ~25 xmlcatalog runs that are near-instant beside them. The # sibling 62-docbook-xsl suite, which loads a comparable amount of XML per run, # was measured at ~42 s for 28 cases on an idle host and declares 600; this file # does more runs of cheaper documents, so the same bound with the same ~3x load # margin is the right one rather than a nominal figure. # # WHAT THIS SUITE IS FOR. docbook-xml-dtd4 is pure data -- allarch, empty # do_compile, nothing linked -- so "the port works" cannot be shown by running # it. It is shown by a VALIDATOR reaching the DTDs through the XML catalog and # accepting a conforming document while rejecting a non-conforming one. Both # halves are needed: a validator that accepts everything passes a suite built # only from positive cases and is worthless, and a catalog that resolves nothing # fails identically to one that resolves wrongly unless the negative control # says which. Sections 6-8 are that structure. # # THIS IMAGE HAS A ROOT CATALOG AND STOCK YOCTO DOES NOT. oe-core's # xmlcatalog.bbclass wires the NATIVE sysroot only and says so in its own header # (Yocto #13271), so on a stock image /etc/xml/docbook-xml.xml lands with nothing # chaining it. Here brintos-xml-catalog ships /etc/xml/catalog, which chains it. # Section 4 asserts the sub-catalog on its own terms (XML_CATALOG_FILES pointed # straight at it, which is what works anywhere), and section 5 asserts the root # chain separately -- so the two facts stay distinguishable and a regression in # either one names itself. . "$BST_LIB" # THE CANARY, which asserts nothing about docbook-xml-dtd4. # # A suite's FIRST BST result line is lost between the guest's printf and the # host's read -- BRINTOS-1149, which established the loss as per-suite, # deterministic and platform-side, and taught brintos_local_verify.py to report # the deficit rather than removing it. The harness computes its pass count as # total-minus-failures, so THE DROPPED LINE IS COUNTED AS A PASS: a suite whose # first case is a genuine `not ok` loses exactly the line that mattered. Here # that slot would hold `tree-4.1.2`, i.e. "the payload is on the image at all". # # So a nothing-case stands in that position. Nothing was moved out of the slot; # a case was added in front of it. Same idiom and reason as 99-mailcap, # 54-ninja, 56-snappy, 82-double-conversion and 83-flatbuffers. # # It sits ahead of the `require` guards deliberately: a `require` that skips # emits its skip as the suite's first line, and that line would be eaten too. # # Remove this case when the loss itself is fixed -- BRINTOS-1149 closed on the # attribution, not on the platform defect underneath it. bst_ok docbook-dtd4/suite-canary require xmllint docbook-dtd4/xmllint-present require xmlcatalog docbook-dtd4/xmlcatalog-present DTD=/usr/share/xml/docbook/schema/dtd SUBCAT=/etc/xml/docbook-xml.xml ROOTCAT=/etc/xml/catalog VERSIONS="4.1.2 4.2 4.3 4.4 4.5" t=${TMPDIR:-/tmp}/bst-docbook-dtd4.$$ if ! mkdir -p "$t" 2>/dev/null; then bst_notok docbook-dtd4/tmpdir "cannot create $t" exit 0 fi # Nothing below chdirs and nothing redirects stdin from the rootfs: both # intermittently answer the kernel's restart sentinel as "Unknown error 513" and # end the whole suite where they happen (brintos/linux#73). Documents are written # into $t and named by absolute path. # --- 1. the payload the recipe installs -------------------------------------- # # Five versions, each a self-contained DTD tree. The ticket asks for 4.1.2 # through 4.5 and the recipe's do_install loop names exactly those, so a version # that silently stopped unpacking is a case of its own rather than a count. for v in $VERSIONS; do check docbook-dtd4/tree-$v test -d "$DTD/$v" check docbook-dtd4/driver-$v test -s "$DTD/$v/docbookx.dtd" check docbook-dtd4/subcatalog-$v test -s "$DTD/$v/catalog.xml" done # --- 2. every file the DTD's own entity declarations reference ---------------- # # A half-unpacked payload is the characteristic failure of a five-zip recipe, and # it is invisible to a presence check on the directory: docbookx.dtd is a driver # that pulls in five .mod modules, and dbcentx.mod pulls in up to twenty ISO # entity sets whose file names differ BETWEEN versions (4.1.2 ships iso-amsa.ent, # 4.5 ships isoamsa.ent). So the reference list is derived from each version's # own text rather than written out here -- a hardcoded list would be wrong for at # least one version and would go stale on the next. # # The count is asserted as well as the misses. An extraction that matched nothing # reports zero missing and would pass vacuously, which is the failure mode a # presence guard most easily hides: 26 is the smallest count any of the five # versions yields (4.1.2 and 4.2; 4.3-4.5 yield 27, htmltblx.mod being the extra). for v in $VERSIONS; do refs=$(cat "$DTD/$v"/*.dtd "$DTD/$v"/*.mod 2>/dev/null \ | awk -f "$BST_DATA/docbook-dtd4-strip-comments.awk" 2>/dev/null \ | grep -oE '"[A-Za-z0-9._-]+(/[A-Za-z0-9._-]+)*\.(mod|dtd|ent)"' \ | tr -d '"' | sort -u) n=$(printf '%s\n' "$refs" | grep -c '.') if [ "$n" -ge 26 ]; then bst_ok docbook-dtd4/refs-found-$v else bst_notok docbook-dtd4/refs-found-$v "extracted only $n references from $DTD/$v -- expected at least 26"; fi miss= for r in $refs; do [ -f "$DTD/$v/$r" ] || miss="$miss $r" done if [ -z "$miss" ]; then bst_ok docbook-dtd4/refs-present-$v else bst_notok docbook-dtd4/refs-present-$v "missing under $DTD/$v:$(_bst_1line "$miss")"; fi done # --- 3. the sub-catalog the recipe builds ------------------------------------- # # do_install creates /etc/xml/docbook-xml.xml with xmlcatalog from libxml2-native # and adds one nextCatalog per version. Each link is asserted separately: a # version dropped from the chain is perfectly invisible until something asks for # that version's DTD, and then it looks like a missing payload rather than a # missing catalog entry. check docbook-dtd4/catalog-file test -s "$SUBCAT" for v in $VERSIONS; do check_out docbook-dtd4/catalog-chains-$v "$DTD/$v/catalog.xml" cat "$SUBCAT" done # --- 4. resolution through the sub-catalog ------------------------------------ # # The ticket's pass criterion for the catalog: a public identifier must answer a # LOCAL path. Both spellings are covered, because a document may name either and # they take different routes through the catalog -- and the three older versions # ship a catalog.xml with no prefer="public", so their PUBLIC and SYSTEM entries # are consulted in a different order than 4.4/4.5's. for v in $VERSIONS; do check_out docbook-dtd4/resolve-public-$v "$DTD/$v/docbookx.dtd" \ xmlcatalog "$SUBCAT" "-//OASIS//DTD DocBook XML V$v//EN" check_out docbook-dtd4/resolve-system-$v "$DTD/$v/docbookx.dtd" \ xmlcatalog "$SUBCAT" "http://www.oasis-open.org/docbook/xml/$v/docbookx.dtd" # An answer naming a file that is not there is worse than no answer: it is the # shape a stale or mis-versioned catalog takes, and it fails later and further # away. So the answer is followed to the file. res=$(xmlcatalog "$SUBCAT" "-//OASIS//DTD DocBook XML V$v//EN" 2>/dev/null) check docbook-dtd4/resolve-target-$v test -f "${res#file://}" done # A public identifier this package does not ship must resolve to nothing. Without # it, a catalog that answered everything with one path would pass every case # above. uout=$(xmlcatalog "$SUBCAT" "-//OASIS//DTD DocBook XML V9.9//EN" 2>&1) case "$uout" in *"No entry"*) bst_ok docbook-dtd4/resolve-unknown-none ;; *) bst_notok docbook-dtd4/resolve-unknown-none "resolved to '$(_bst_1line "$uout")'" ;; esac # --- 5. the root catalog: this image's, not oe-core's ------------------------- # # Stock Yocto ships no /etc/xml/catalog (Yocto #13271, quoted in # xmlcatalog.bbclass's own header), so a caller there must name the sub-catalog # explicitly. brintos-xml-catalog fills that gap on this image, and section 6's # default-search-path case is the consequence. Asserted here rather than assumed, # because the whole difference between this image and a stock one is one file. check docbook-dtd4/root-catalog test -s "$ROOTCAT" check_out docbook-dtd4/root-catalog-chains "$SUBCAT" cat "$ROOTCAT" check_out docbook-dtd4/root-resolves-45 "$DTD/4.5/docbookx.dtd" \ xmlcatalog "$ROOTCAT" "-//OASIS//DTD DocBook XML V4.5//EN" check_out docbook-dtd4/root-resolves-412 "$DTD/4.1.2/docbookx.dtd" \ xmlcatalog "$ROOTCAT" "-//OASIS//DTD DocBook XML V4.1.2//EN" # --- 6. validation: a conforming document, against each version --------------- # # The port's reason to exist. --nonet is what makes a pass mean anything: with # it, an identifier the catalog does not rewrite is refused rather than fetched, # so no case below can be passing because something reached the network. (This # guest has none either way -- belt and braces, cheaply. Section 8 makes the # offline claim explicitly.) mkdoc() { # mkdoc > file printf '%s\n' '' printf '%s\n' "" printf '%s\n' "
BSTDBTITLE$2
" } GOOD='SECBODYSENTINEL text.' # An element outside its allowed content model: sect1 is a perfectly valid # element that may not appear inside para. The document stays WELL-FORMED, so a # rejection can only come from the DTD -- section 7 pins that distinction. BADCHILD='SECNx' # The other class of content-model violation: right children, wrong order. BADORDER='BODYSENTINEL text.SEC' for v in $VERSIONS; do mkdoc "$v" "$GOOD" > "$t/good-$v.xml" check docbook-dtd4/validate-$v \ env XML_CATALOG_FILES="$SUBCAT" xmllint --nonet --noout --valid "$t/good-$v.xml" done # The same document with no XML_CATALOG_FILES at all, i.e. through libxml2's # default search path. This is the case that is true HERE and false on a stock # Yocto image, and it is the one a downstream consumer (xmlto, xsltproc, any # Makefile) actually takes -- none of them set XML_CATALOG_FILES. check docbook-dtd4/validate-default-path \ xmllint --nonet --noout --valid "$t/good-4.5.xml" # --- 7. the negative cases: the validator must REJECT ------------------------- # # Without these every case in section 6 passes against a validator that accepts # anything, and the port would be certified by a suite that cannot fail. for v in $VERSIONS; do mkdoc "$v" "$BADCHILD" > "$t/badchild-$v.xml" bout=$(XML_CATALOG_FILES="$SUBCAT" xmllint --nonet --noout --valid "$t/badchild-$v.xml" 2>&1); brc=$? if [ "$brc" -ne 0 ]; then bst_ok docbook-dtd4/reject-badchild-$v else bst_notok docbook-dtd4/reject-badchild-$v "rc=0 -- sect1 inside para was accepted against V$v"; fi case "$bout" in *"Element sect1 is not declared in para list of possible children"*) bst_ok docbook-dtd4/reject-badchild-diag-$v ;; *) bst_notok docbook-dtd4/reject-badchild-diag-$v "$(_bst_1line "$bout")" ;; esac done mkdoc 4.5 "$BADORDER" > "$t/badorder.xml" oout=$(XML_CATALOG_FILES="$SUBCAT" xmllint --nonet --noout --valid "$t/badorder.xml" 2>&1); orc=$? if [ "$orc" -ne 0 ]; then bst_ok docbook-dtd4/reject-badorder else bst_notok docbook-dtd4/reject-badorder "rc=0 -- para before title in sect1 was accepted"; fi case "$oout" in *"Element sect1 content does not follow the DTD"*) bst_ok docbook-dtd4/reject-badorder-diag ;; *) bst_notok docbook-dtd4/reject-badorder-diag "$(_bst_1line "$oout")" ;; esac # The control that gives section 7 its meaning: the SAME invalid document parses # clean without --valid. So the rejections above are the DTD's verdict on the # content model, not a well-formedness error that would fail with no DTD at all. check docbook-dtd4/badchild-wellformed \ env XML_CATALOG_FILES="$SUBCAT" xmllint --nonet --noout "$t/badchild-4.5.xml" # --- 8. offline: the whole reason a local catalog exists ---------------------- # # The positive half is every --nonet pass above. The negative half is here: with # the catalog emptied, the identical document must FAIL, and must fail by # reaching for the network. Without this case section 6 would pass on an image # whose catalog does nothing, because the DTD could have been found some other # way -- and the failure would only appear on a machine with no network, which is # every machine this image runs on. printf '%s\n%s\n' '' \ '' > "$t/empty-catalog.xml" eout=$(XML_CATALOG_FILES="$t/empty-catalog.xml" \ xmllint --nonet --noout --valid "$t/good-4.5.xml" 2>&1); erc=$? if [ "$erc" -ne 0 ]; then bst_ok docbook-dtd4/nocatalog-fails else bst_notok docbook-dtd4/nocatalog-fails "rc=0 with the catalog emptied -- the DTD resolved by some path this suite does not control"; fi case "$eout" in *"Attempt to load network entity"*) bst_ok docbook-dtd4/nocatalog-diagnosed ;; *) bst_notok docbook-dtd4/nocatalog-diagnosed "$(_bst_1line "$eout")" ;; esac # The contrast, and the reason this suite does not validate by local path: the # DTD files themselves are fine when the catalog is not, so a path-driven # validation passes either way and would hide section 8 entirely. printf '%s\n' '' > "$t/bypath.xml" printf '%s\n' "" >> "$t/bypath.xml" printf '%s\n' "
BSTDBTITLE$GOOD
" >> "$t/bypath.xml" check docbook-dtd4/nocatalog-path-works \ env XML_CATALOG_FILES="$t/empty-catalog.xml" xmllint --nonet --noout --valid "$t/bypath.xml" # --- 9. the entity sets, which are half the payload by file count ------------- # # ent/ is ~20 files per version and nothing above touches it: docbookx.dtd loads # the modules, but the ISO entity sets are only read when a document actually # references one. A payload that unpacked the modules and lost ent/ therefore # passes every case above. These do reference them, and assert the SUBSTITUTED # text -- so a set that resolved to the wrong file fails too. for v in $VERSIONS; do mkdoc "$v" 'SECENT—SENT©2026' \ > "$t/ent-$v.xml" check_out docbook-dtd4/entities-$v 'ENT—SENT©2026' \ env XML_CATALOG_FILES="$SUBCAT" xmllint --nonet --valid --noent "$t/ent-$v.xml" done # And its control: with no DTD loaded the same entities are undefined, so the # expansions above came from ent/ and not from libxml2's built-in five. nout=$(XML_CATALOG_FILES="$SUBCAT" xmllint --nonet --noout --noent "$t/ent-4.5.xml" 2>&1) case "$nout" in *"Entity 'mdash' not defined"*) bst_ok docbook-dtd4/entities-need-dtd ;; *) bst_notok docbook-dtd4/entities-need-dtd "expected an undefined-entity error without --valid, got: $(_bst_1line "$nout")" ;; esac rm -rf "$t"