# BST-TIMEOUT: 300 # the payload is 32% larger than 2.40.0 Final, whose measured # # elapsed was 62s; 300 leaves the same headroom # # cim-schema-exper 2.50.0 -- the DMTF CIM EXPERIMENTAL schema, the MOF class # definitions not yet in a released schema. There is no CIM server on this image, # so the deliverable IS the payload: this suite asserts the payload is complete, # internally consistent, and byte-identical to the same recipe version installed # on x86-64. It cannot assert that a CIM server accepts the schema, and does not # claim to. # # THIS SCHEMA SHIPS ALONGSIDE THE FINAL ONE, WHICH IS THE WHOLE REASON THE TWO # RECIPES NEEDED WORK. Upstream ships them mutually exclusive -- each RCONFLICTS # the other -- and the entire overlap that justifies it is ONE symlink: both # create ${datadir}/mof/cim-current. Their payload trees and licence directories # were already disjoint. meta-brintos gives cim-current one owner (final, the # released schema), gives this tree cim-experimental instead, and clears both # RCONFLICTS. So this suite carries the co-installation as a first-class subject: # the two trees must be present, disjoint, and each intact. # # EVERY WALK IS SCOPED TO THIS VERSION'S OWN TREE, NEVER TO /usr/share/mof. # A find over the shared parent sees the other schema's 1441 files and counts # them as strays, which is precisely how a co-installed pair breaks each other's # suite rather than each other's payload. The shared parent is asserted, once and # deliberately, in its own section below. # # WRITTEN TO EXEC AS LITTLE AS POSSIBLE, AND THAT IS NOT A STYLE CHOICE. On this # arm a process's memory is not returned when it exits and /bin/sh alone is a # 16 MB wasm module, so the guest grows by tens of MB per exec. Every batch below # does its whole job in ONE program and hands back shell assignments. # # THE 1896 FILENAMES NEVER GO ON A COMMAND LINE. The guest's argument buffer # (__wasm32_args_buf) is 8192 bytes and naming every MOF costs six figures, and # that does not fail the exec -- the exec never returns and the whole suite is # lost with the guest (brintos/hardwarejs#15; 99-cim-schema-final.test lost two # boots to exactly this). So the one find below writes the relative paths to a # file and gawk opens them itself. Any future case that wants to walk the tree # must do the same: a `find`-substitution or a `"$V"/*/*.mof` glob wedges the # boot rather than erroring, which is the failure mode that reads as a flake. . "$BST_LIB" P=/usr/share/mof V=$P/cimv2.50.0 DOC=/usr/share/doc/cim-schema-2.50.0 OTHER=$P/cimv2.40.0 S=cim-schema-exper D=$BST_DATA tmp=${TMPDIR:-/tmp} W=$tmp/bst-cimx.$$ require gawk $S/gawk-present require sha256sum $S/sha256sum-present if [ -d "$P" ]; then bst_ok $S/root; else bst_notok $S/root "$P is not a directory"; return 2>/dev/null || exit 0 fi if [ -d "$V" ]; then bst_ok $S/version-dir; else bst_notok $S/version-dir "$V is not a directory"; return 2>/dev/null || exit 0 fi # ---------------------------------------------------------------- payload shape # ONE find over this version's tree and its licence directory -- NOT over $P. # %p is absolute because the two roots have no common relative frame; gawk strips # the prefix where it needs a relative path. find "$V" "$DOC" -printf '%y %m %s %p|%l\n' > "$W.ls" 2>/dev/null # An empty listing would leave the zero-valued cases below reading green on # nothing at all, so say outright that the walk produced something. if [ -s "$W.ls" ]; then bst_ok $S/payload-walk; else bst_notok $S/payload-walk "find over $V and $DOC produced no output"; fi gawk -v ref="$D/cim-schema-exper-inventory.txt" -v V="$V" -v DOC="$DOC" \ -v filesout="$W.files" ' function q(k, v) { printf "%s='"'"'%s'"'"'\n", k, v } { split($0, a, "|"); link = a[2] ty = $1; mode = $2; sz = $3 path = $4; for (i = 5; i <= NF; i++) path = path " " $i # a space in a path sub(/\|.*$/, "", path) if (ty == "d") { dirs++; if (mode != "755") badmode++ } else if (ty == "l") { links++; tgt[path] = link } else if (ty == "f") { if (index(path, V "/") == 1) { rel = substr(path, length(V) + 2) n++; bytes += sz; if (sz == 0) empty++ live[rel] = sz print rel > filesout k = index(rel, "/") if (k == 0) top++; else st[substr(rel, 1, k - 1)]++ } else if (index(path, DOC "/") == 1) { docn++ docrel[substr(path, length(DOC) + 2)] = sz } else { stray++; if (!strayex) strayex = path } if (mode != "644") badmode++ } } END { close(filesout) while ((getline l < ref) > 0) { if (l ~ /^#/) continue i = length(l); while (i > 0 && substr(l, i, 1) != " ") i-- rp = substr(l, 1, i - 1); rs = substr(l, i + 1) + 0 refn++ if (!(rp in live)) { diffs++; if (!ex) ex = "missing " rp } else if (live[rp] != rs) { diffs++; if (!ex) ex = rp " is " live[rp] " bytes, want " rs } seen[rp] = 1 } for (f in live) if (!(f in seen)) { diffs++; if (!ex) ex = "extra " f } for (s in st) q("CIM_ST_" toupper(s), st[s]) q("CIM_FILES", n + 0); q("CIM_BYTES", bytes + 0); q("CIM_EMPTY", empty + 0) q("CIM_TOP", top + 0); q("CIM_SUBTREES", length(st)) q("CIM_DIRS", dirs + 0); q("CIM_LINKS", links + 0); q("CIM_BADMODE", badmode + 0) q("CIM_STRAY", stray + 0); q("CIM_STRAY_EX", strayex) q("CIM_SCHEMA_TGT", tgt[V "/CIM_Schema.mof"]) q("CIM_DOCN", docn + 0); q("CIM_LICENSE_SZ", docrel["LICENSE"] + 0) q("CIM_INV_DIFFS", diffs + 0); q("CIM_INV_REF", refn + 0); q("CIM_INV_EX", ex) }' "$W.ls" > "$W.f1" . "$W.f1" check_eq $S/payload-files 1896 "$CIM_FILES" check_eq $S/payload-bytes 6659499 "$CIM_BYTES" check_eq $S/payload-empty 0 "$CIM_EMPTY" check_eq $S/payload-toplevel 3 "$CIM_TOP" check_eq $S/payload-subtrees 14 "$CIM_SUBTREES" # Each schema subtree by name with its file count -- a directory that exists and # is empty is the partial install `test -d` cannot see. Every tally differs from # 2.40.0 Final's, so these also pin that the EXPERIMENTAL tree is what landed. check_eq $S/subtree-Application 153 "$CIM_ST_APPLICATION" check_eq $S/subtree-Core 223 "$CIM_ST_CORE" check_eq $S/subtree-Database 32 "$CIM_ST_DATABASE" check_eq $S/subtree-Device 461 "$CIM_ST_DEVICE" check_eq $S/subtree-Event 36 "$CIM_ST_EVENT" check_eq $S/subtree-IPsecPolicy 25 "$CIM_ST_IPSECPOLICY" check_eq $S/subtree-Interop 39 "$CIM_ST_INTEROP" check_eq $S/subtree-Metrics 25 "$CIM_ST_METRICS" check_eq $S/subtree-Network 364 "$CIM_ST_NETWORK" check_eq $S/subtree-Physical 48 "$CIM_ST_PHYSICAL" check_eq $S/subtree-Policy 70 "$CIM_ST_POLICY" check_eq $S/subtree-Support 61 "$CIM_ST_SUPPORT" check_eq $S/subtree-System 236 "$CIM_ST_SYSTEM" check_eq $S/subtree-User 120 "$CIM_ST_USER" # 16 = cimv2.50.0 + its 14 subtrees + the doc directory. Every file 0644, every # directory 0755, and nothing installed outside the two trees the recipe names. check_eq $S/payload-dirs 16 "$CIM_DIRS" check_eq $S/payload-modes 0 "$CIM_BADMODE" check_eq $S/payload-stray 0 "$CIM_STRAY" # ------------------------------------------- the payload against the reference # cim-schema-exper compiles nothing, so the wasm32 package and an x86-64 install # of the same recipe version must be the same bytes. The reference was produced # independently on x86-64 -- upstream release zip + the recipe's own do_install # transform -- not from this package. The path+size half came off the find above. check_eq $S/inventory-x86-64-ref 1896 "$CIM_INV_REF" if [ "${CIM_INV_DIFFS:-1}" = 0 ]; then bst_ok $S/inventory-x86-64; else bst_notok $S/inventory-x86-64 "$CIM_INV_DIFFS difference(s) vs the x86-64 reference; first: $CIM_INV_EX"; fi # and the same bytes, not merely the same sizes ( cd "$V" && grep -v '^#' "$D/cim-schema-exper-sha256.txt" | sha256sum -c --quiet ) > "$W.sha" 2>&1 if [ -s "$W.sha" ]; then bst_notok $S/sha256-x86-64 "$(gawk 'END{print NR" mismatch(es)"}' "$W.sha"); first: $(gawk 'NR==1{print}' "$W.sha")" else bst_ok $S/sha256-x86-64 fi # ------------------------------------------ the schema's own manifest of itself # cim_schema_2.50.0.mof is a list of 1895 `#pragma include ("path")` lines, one # per class file. Every include must resolve onto a file that is present, and # every file except the master must be included exactly once -- so a partial # unpack fails here even if the reference manifests were absent. The same pass # reads every file's class declaration and checks for the CR bytes do_install # strips. gawk -v list="$W.files" -v V="$V" ' function q(k, v) { printf "%s='"'"'%s'"'"'\n", k, v } BEGIN { while ((getline rel < list) > 0) { if (rel == "") continue nfiles++; have[rel] = 1 base = rel; sub(/.*\//, "", base); sub(/\.mof$/, "", base) master = (rel == "cim_schema_2.50.0.mof") path = V "/" rel cls = "" while ((getline line < path) > 0) { if (index(line, "\r")) crlf[rel] = 1 if (master) { if (line ~ /^[ \t]*#pragma[ \t]+include/) { if (match(line, /\("[^"]+"\)/)) { inc_p = substr(line, RSTART + 2, RLENGTH - 4) if (inc_p in inc) dup++ inc[inc_p]++; ninc++ } else badinc++ continue } if (line ~ /^[ \t]*#pragma[ \t]+locale/) { nloc++; continue } } if (cls == "" && line ~ /^[ \t]*class[ \t]+[A-Za-z0-9_]+/) { match(line, /class[ \t]+[A-Za-z0-9_]+/) c = substr(line, RSTART + 5, RLENGTH - 5); gsub(/^[ \t]+/, "", c) cls = c; nclass++ if (c != base) { mism++; mlist = mlist (mlist ? " " : "") rel } } } close(path) if (cls == "") { noclass++; nclist = nclist (nclist ? " " : "") rel } } close(list) for (inc_p in inc) if (!(inc_p in have)) { dangling++; if (!dex) dex = inc_p } for (f in have) if (!(f in inc) && f != "cim_schema_2.50.0.mof") { notinc++; if (!nex) nex = f } q("CIM_NFILES", nfiles + 0) q("CIM_INC", ninc + 0); q("CIM_INC_DISTINCT", length(inc)); q("CIM_INC_DUP", dup + 0) q("CIM_INC_BAD", badinc + 0); q("CIM_LOCALE", nloc + 0) q("CIM_DANGLING", dangling + 0); q("CIM_DANGLING_EX", dex) q("CIM_NOTINC", notinc + 0); q("CIM_NOTINC_EX", nex) q("CIM_CLASS", nclass + 0); q("CIM_CLASS_MISM", mism + 0); q("CIM_CLASS_MISM_LIST", mlist) q("CIM_NOCLASS", noclass + 0); q("CIM_NOCLASS_LIST", nclist) q("CIM_CRLF", length(crlf)) }' > "$W.f2" . "$W.f2" # The parse pass has to see exactly the regular files the find did, or every # count below is short. CIM_Schema.mof is a symlink, so it is not in the list and # is not double-counted here; the symlink case below is what covers it. check_eq $S/parse-files-seen 1896 "$CIM_NFILES" check_eq $S/include-count 1895 "$CIM_INC" check_eq $S/include-distinct 1895 "$CIM_INC_DISTINCT" check_eq $S/include-duplicated 0 "$CIM_INC_DUP" check_eq $S/include-malformed 0 "$CIM_INC_BAD" check_eq $S/pragma-locale 1 "$CIM_LOCALE" if [ "${CIM_DANGLING:-1}" = 0 ]; then bst_ok $S/include-resolves; else bst_notok $S/include-resolves "$CIM_DANGLING include(s) name a file that is not installed; first: $CIM_DANGLING_EX"; fi if [ "${CIM_NOTINC:-1}" = 0 ]; then bst_ok $S/include-covers-tree; else bst_notok $S/include-covers-tree "$CIM_NOTINC file(s) not referenced by the master MOF; first: $CIM_NOTINC_EX"; fi # 1893 class files + the master + the two qualifier files = 1896. check_eq $S/class-files 1893 "$CIM_CLASS" # THE EXPERIMENTAL SCHEMA BREAKS THE ONE-CLASS-PER-FILE-NAMED-FOR-IT CONVENTION # TWICE, AND BOTH ARE UPSTREAM. CIM_ElectricalSwtich.mof is a misspelt FILENAME # holding the correctly-spelt class, and CIM_OpticalDiskDiagnosticTest.mof holds # CIM_OpticalDriveDiagnosticTest. 2.40.0 Final has neither, so this is asserted # as an exact list rather than as a count: a THIRD mismatch, or either of these # two disappearing, is a repacked or substituted file and reddens this case. check_eq $S/class-name-mismatch-count 2 "$CIM_CLASS_MISM" check_eq $S/class-name-mismatch-which \ "Device/CIM_ElectricalSwtich.mof System/CIM_OpticalDiskDiagnosticTest.mof" \ "$(printf '%s\n' $CIM_CLASS_MISM_LIST | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//')" check_eq $S/classless-files 3 "$CIM_NOCLASS" check_eq $S/classless-which \ "cim_schema_2.50.0.mof qualifiers.mof qualifiers_optional.mof" \ "$(printf '%s\n' $CIM_NOCLASS_LIST | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//')" # do_install strips CR from every *.mof, and unlike 2.40.0 Final -- where the # strip is a no-op -- this release genuinely carries them: three files in Event/ # hold CR bytes in the release zip. So this case asserts the transform FIRED and # stayed fired, which is a stronger statement than the same case makes for Final. check_eq $S/no-cr-bytes 0 "$CIM_CRLF" # ------------------------------------------------- reading through the symlinks # CIM_Schema.mof is created by do_install, not by the tarball, and is the kind of # thing a rootfs packer can flatten into a copy or drop entirely. A symlink whose # target is right and which cannot be read through is the failure a `readlink` # assertion misses, so both links are read rather than inspected. check_eq $S/symlink-count 1 "$CIM_LINKS" check_eq $S/CIM_Schema-target cim_schema_2.50.0.mof "$CIM_SCHEMA_TGT" sha256sum "$V/cim_schema_2.50.0.mof" "$V/CIM_Schema.mof" \ "$P/cim-experimental/qualifiers.mof" "$V/qualifiers.mof" \ "$DOC/LICENSE" > "$W.sums" 2>&1 gawk ' function q(k, v) { printf "%s='"'"'%s'"'"'\n", k, v } { h[$2] = $1 } END { q("CIM_H_MASTER", h["'"$V"'/cim_schema_2.50.0.mof"]) q("CIM_H_ALIAS", h["'"$V"'/CIM_Schema.mof"]) q("CIM_H_QDIR", h["'"$P"'/cim-experimental/qualifiers.mof"]) q("CIM_H_QUAL", h["'"$V"'/qualifiers.mof"]) q("CIM_H_LIC", h["'"$DOC"'/LICENSE"]) }' "$W.sums" > "$W.f3" . "$W.f3" # CIM_Schema.mof is the stable name a consumer compiles against; it must be the # 2.50.0 master, byte for byte, reached through the link. check_eq $S/master-sha256 \ 784461eea5c8d1b261b270aba3d9533b88a0bbc71a8e5640300e5d6e6ca42af2 "$CIM_H_MASTER" check_eq $S/CIM_Schema-reads-master "$CIM_H_MASTER" "$CIM_H_ALIAS" # and cim-experimental/ must be a way into the versioned tree, not a dangling link check_eq $S/cim-experimental-reads-tree "$CIM_H_QUAL" "$CIM_H_QDIR" # qualifiers.mof is byte-identical across 2.40.0 Final and 2.50.0 Experimental -- # the qualifier declarations did not change between them. Pinning the hash here # and in 99-cim-schema-final.test therefore pins the SAME value twice on purpose: # if one tree's copy ever diverges, exactly one of the two cases reddens and says # which tree moved. check_eq $S/qualifiers-sha256 \ 11e34ea60136b75969ad250202aee8270d0248c22f5022b9734b68550e5e7b70 "$CIM_H_QUAL" # ------------------------------------------------------------------- packaging # This recipe puts the licence in the RUNTIME package -- `FILES:${PN}` claims # `${datadir}/doc/*` and `FILES:${PN}-doc` is set empty -- so the file arriving # is the evidence that the payload landed in a package the image installs rather # than in a -doc package it does not. That is the ticket's stated risk, and it is # the one assertion here that fails if the packaging is wrong and everything else # is right. Its own doc directory is version-named, so it does not collide with # the final schema's. check_eq $S/doc-files 1 "$CIM_DOCN" check_eq $S/license-size 2073 "$CIM_LICENSE_SZ" check_eq $S/license-sha256 \ 7864f0e2d5f202bfcc5987d19de661142865fd5e7a20aa0d3e08cf766c93d36b "$CIM_H_LIC" # ------------------------------------------------- co-installation with -final # The ticket's stated risk, and the only way these two recipes can break each # other. Upstream declares them mutually exclusive over ONE symlink; meta-brintos # gives that symlink an owner and clears both RCONFLICTS. These cases are what # stops that resolution from silently regressing -- in either direction, since a # rebuild that restored the stock recipe would put cim-current back here. if [ -d "$OTHER" ]; then bst_ok $S/final-installed-alongside; else bst_notok $S/final-installed-alongside "$OTHER is absent -- the two schemas are meant to ship together"; fi # Exactly four entries under the shared parent: the two version trees and the two # entry-point symlinks, each pointing at its own tree. cim-current belongs to the # RELEASED schema; a cim-current found pointing at cimv2.50.0 means the ownership # flipped and -final's consumers now compile against the experimental classes. find "$P" -maxdepth 1 -mindepth 1 -printf '%y %P %l\n' | LC_ALL=C sort > "$W.top" check_eq $S/mof-top-level "d cimv2.40.0 |d cimv2.50.0 |l cim-current cimv2.40.0|l cim-experimental cimv2.50.0" \ "$(gawk '{printf "%s%s", (NR>1?"|":""), $0} END{print ""}' "$W.top")" # and neither tree was unpacked over the other. The two schemas share 1439 of # their 1441 relative class-file names -- they are two versions of ONE schema, so # relative-name overlap is expected and enormous, and is NOT what "disjoint" # means here. What makes them disjoint is that each tree is rooted at its own # version directory. So the check that a collision would actually break is # whether each root still holds ITS OWN schema: the counts below, and the final # schema's master MOF still hashing to the 2.40.0 master rather than to anything # this package installed. find "$V" "$OTHER" -mindepth 1 \( -type f -o -type l \) > "$W.both" 2>/dev/null gawk -v a="$V/" -v b="$OTHER/" ' function q(k, v) { printf "%s='"'"'%s'"'"'\n", k, v } { if (index($0, a) == 1) na++ else if (index($0, b) == 1) nb++ } END { q("CIM_A", na + 0); q("CIM_B", nb + 0) }' "$W.both" > "$W.f4" . "$W.f4" check_eq $S/exper-tree-entries 1897 "$CIM_A" check_eq $S/final-tree-entries 1442 "$CIM_B" # The final schema's master, read from ITS tree. cim-schema-final's own suite # pins this same value; asserting it from here too is what catches an install # order in which this package's 1896 files landed on top of that one's 1441. check_eq $S/final-master-untouched \ 1feb44aebb7cb573afdd114756c7b70289f6b8f655b3119d095d07d3bffe21c2 \ "$(sha256sum "$OTHER/cim_schema_2.40.0.mof" 2>/dev/null | gawk '{print $1}')" rm -f "$W".*