# BST-TIMEOUT: 600 # 27 upstream execs. The suite's own compute is 0.71 s for all # # 27 on an x86-64 control, so essentially the whole cost here is # # 27 fresh wasm module instantiations (~4.9 s apiece on # # 80-libmpc's measurement of the same shape). See below. . "$BST_LIB" # libcbor 0.11.0 -- PJK's CBOR (RFC 8949) encoder/decoder in C, from meta-oe # recipes-extended. See libcbor_%.bbappend for what the port adds and why. # # --------------------------------------------------------------------------- # THIS IS THE SECOND CBOR LIBRARY ON THIS IMAGE, AND THAT IS DELIBERATE # # 81-qcbor.test covers QCBOR, and the two are not spellings of one another -- # they sit at opposite ends of the design space for the same RFC, which is why # porting both says more than porting either twice: # # qcbor encodes into a buffer THE CALLER supplies and allocates nothing. # A decoded item is a view; the caller walks it. That is what makes # it usable in the constrained half of this image. # libcbor builds a REFERENCE-COUNTED OBJECT TREE on the heap -- cbor_item_t # with cbor_incref/cbor_decref -- so a document can be loaded, # mutated, re-serialised and pretty-printed like a DOM. It also # offers a streaming/callback decoder for when the tree is too much. # # So the assertions below are shaped by that difference: the allocator, the # refcount, the copy semantics and the pretty printer are all things qcbor has # no equivalent of, and they are where this port's risk actually lives on an arm # with a different pointer width than upstream's CI ever sees. # # --------------------------------------------------------------------------- # WHAT CLOSES THIS TICKET: UPSTREAM'S OWN SUITE, NOT A PROBE WE WROTE # # libcbor ships 26 cmocka test programmes under test/, and cmocka is already on # this image (77-cmocka.test), which is what makes running them here possible. # The bbappend switches WITH_TESTS on -- it defaults OFF, so OE has never built # them -- and stages what upstream's own `file(GLOB TESTS "*_test.c")` selects, # plus cpp_linkage_test. Every programme gets its own BST case below, so a red # names the area of the library that broke rather than just the suite. # # That matters more here than the count suggests. These are not smoke tests: the # 296 cmocka cases they contain on this arm include the not-well-formed corpus # (bad_inputs_test), the reference-counting and deep-copy contracts # (copy_test, 33 cases), the streaming decoder driven callback by callback # (cbor_stream_decode_test, 53 cases here), serialisation round-trips # (cbor_serialize_test, 46 cases), the UTF-8 validator (unicode_test) and the # allocator-failure paths through a mock allocator (test_allocator.c). A # library-only port on an image with no C compiler would otherwise be verifiable # only by looking at the files it installed. # # --------------------------------------------------------------------------- # WHY EVERY ONE OF THESE IS EXPECTED TO PASS # # All 27 pass on an x86-64 control build of this same commit (170bee2b, v0.11.0) # configured the way the target arm configures it -- static, Release, # WITH_TESTS=ON, WITH_EXAMPLES=OFF. CBOR's encoding is deterministic and its # expected byte strings are properties of the FORMAT, not of the machine, so # unlike a captured native control there is no class of row that may legitimately # differ between the arms. A red here is a real defect in the port, the arm, or # the library beneath it. # # The one dimension where this arm genuinely differs from upstream's CI is # POINTER WIDTH: wasm32 is ILP32 while libcbor is developed and fuzzed on # x86-64. That is exactly what memory_utils_test exercises -- the overflow # checks in _cbor_safe_signaling_add and friends exist because a size # computation that cannot overflow in 64 bits can in 32 -- and why # stack_over_limit_test and bad_inputs_test are worth watching first if this # suite ever goes red. # # --------------------------------------------------------------------------- # SPAWN BUDGET IS THE CONSTRAINT, WHICH IS WHY THE HARNESS RUNS ONCE # # 27 programmes is 27 fresh wasm module instantiations. The whole suite measures # 0.71 s natively; on this arm the instantiations alone dominate that by orders # of magnitude. So this file runs ptest-runner ONCE, reads back the PASS:/FAIL: # and CASES: lines it collected, and reports from a shell loop that spawns # nothing -- the parse is a `while read`, not a command substitution per case. ptestdir=/usr/lib/libcbor/ptest runner=/usr/bin/ptest-runner inc=/usr/include lib=/usr/lib/libcbor.a pc=/usr/lib/pkgconfig/libcbor.pc # Upstream's own counts, from the v0.11.0 tree the recipe fetches (SRCREV # 170bee2b): test/ globs to 26 *_test.c programmes, plus cpp_linkage_test = 27; # src/CMakeLists.txt lists 20 sources; the install ships cbor.h plus 21 headers # under cbor/ (15 in cbor/, 6 in cbor/internal/). want_progs=27 want_members=20 want_hdrs=21 # 296 cmocka cases across the 26 cmocka groups (cpp_linkage_test is not a cmocka # group and reports 0). Asserted as a TOTAL because a programme can exit 0 # having run NOTHING -- which is what a group compiled out by a configuration # change looks like, and it is indistinguishable from success by exit status # alone. This is the same guard 81-qcbor.test makes with its not-66-tests case. # # 296 AND NOT 299, AND THE DIFFERENCE IS size_t. Upstream's CMakeLists.txt runs # check_type_size("size_t") and defines -DEIGHT_BYTE_SIZE_T only when it is 8 or # more; on this ILP32 arm it is 4, so three cases guarded by that macro are not # compiled in -- bad_inputs_test's test_7 (7 -> 6) and cbor_stream_decode_test's # test_bstring_int64_decoding and test_string_int64_decoding (55 -> 53). Each # decodes a 4294967296-byte item, a length a 32-bit size_t cannot represent at # all, and upstream emits a WARNING at configure time saying exactly that. # # So an x86-64 control counts 299 and this arm counts 296 for the same SRCREV, # and the control is the WRONG ORACLE for this one number: 299 here would be a # permanent red that no fix to the port could clear. Verified by identity, not # by arithmetic -- the three cases absent on target are precisely the three # behind the macro, with every other programme's count matching the control. want_cases=296 # Below this file's BST-TIMEOUT on purpose, so an overrun is reported by # ptest-runner in its own voice instead of this whole file being killed around # it -- the arrangement 77-libpcre.test and 80-libmpc.test use. runner_t=500 nl=' ' tmp=${TMPDIR:-/tmp}/bst-libcbor.$$ mkdir -p "$tmp" 2>/dev/null || tmp=/tmp has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } # ----------------------------------------------------------- what shipped ---- # The -dev/-staticdev half. libcbor installs no binary and no shared object: the # distro is static-only, so libcbor.a IS the library. check_rc libcbor/ar 0 test -s "$lib" check_rc libcbor/hdr-cbor-h 0 test -s "$inc/cbor.h" check_rc libcbor/hdr-dir 0 test -d "$inc/cbor" check_rc libcbor/no-so 1 test -e /usr/lib/libcbor.so check_rc libcbor/no-la 1 test -e /usr/lib/libcbor.la # A pkg-config file IS asserted here, and its ABSENCE is what 80-libmpc.test # asserts for mpc -- the difference is read off each upstream's install rules # rather than assumed from the neighbour. libcbor's src/CMakeLists.txt # configure_file()s libcbor.pc and installs it; asserting the wrong one of these # two is how a perfectly good port goes red. check_rc libcbor/pc 0 test -s "$pc" check_out libcbor/pc-libs "-lcbor" cat "$pc" check_out libcbor/pc-version "0.11.0" cat "$pc" # The cmake package files, which are how a downstream find_package(libcbor) # resolves. Shipped by upstream's configure_package_config_file(). check_rc libcbor/cmake-config 0 test -s /usr/lib/cmake/libcbor/libcborConfig.cmake check_rc libcbor/cmake-version 0 test -s /usr/lib/cmake/libcbor/libcborConfigVersion.cmake # The public headers, counted by glob expansion rather than `ls | wc -l`, which # would fork twice. A -dev package that dropped, say, streaming.h would still # compile every consumer that uses only the tree API, so the gap would surface # later in someone else's build. set -- "$inc"/cbor/*.h "$inc"/cbor/internal/*.h check_eq libcbor/hdr-count "$want_hdrs" "$#" for h in arrays bytestrings callbacks common data encoding floats_ctrls ints \ maps serialization streaming strings tags; do check_rc "libcbor/hdr-$h" 0 test -s "$inc/cbor/$h.h" done # Two GENERATED headers, which is why they are named separately: cbor_export.h # comes from generate_export_header() and configuration.h from configure_file() # over configuration.h.in, so they exist only if the build reached those steps. check_rc libcbor/hdr-export 0 test -s "$inc/cbor/cbor_export.h" check_rc libcbor/hdr-config 0 test -s "$inc/cbor/configuration.h" # ------------------------------------------- the build configuration --------- # configuration.h is where libcbor's compile-time choices become visible on the # target, and it is the shipped header downstream code reads them from -- so # this is the one place the port's configuration can be pinned at all. Every # value is upstream's default; naming them means a future bbappend that changes # one is a red rather than a silent behaviour change under existing callers. cfg=$(cat "$inc/cbor/configuration.h" 2>/dev/null) has libcbor/cfg-major "#define CBOR_MAJOR_VERSION 0" "$cfg" has libcbor/cfg-minor "#define CBOR_MINOR_VERSION 11" "$cfg" has libcbor/cfg-patch "#define CBOR_PATCH_VERSION 0" "$cfg" # The pretty printer is ON, and pretty_printer_test (13 cases) is in the suite # precisely because of it -- it writes through FILE* streams, which is the only # stdio the library does at all. has libcbor/cfg-pretty "#define CBOR_PRETTY_PRINTER 1" "$cfg" has libcbor/cfg-growth "#define CBOR_BUFFER_GROWTH 2" "$cfg" # The decoder's nesting ceiling. stack_over_limit_test drives exactly this # constant, so a change here without a change there makes that test meaningless. has libcbor/cfg-stack "#define CBOR_MAX_STACK_SIZE 2048" "$cfg" # restrict survived -- upstream blanks it only for MSVC, and a build that # silently lost it would compile identically and optimise worse. has libcbor/cfg-restrict "#define CBOR_RESTRICT_SPECIFIER restrict" "$cfg" # ----------------------------------------------- the ptest payload ----------- check_rc libcbor/ptest-dir 0 test -d "$ptestdir" check_rc libcbor/ptest-script 0 test -x "$ptestdir/run-ptest" check_rc libcbor/ptest-progs 0 test -d "$ptestdir/progs" # A short count is the failure this asserts against: the bbappend derives its # list from upstream's own glob and bbfatals on a programme that was declared but # not built, so a disagreement here means something was lost after # do_install_ptest. set -- "$ptestdir"/progs/* check_eq libcbor/prog-count "$want_progs" "$#" # ------------------------------------------------------- run it, once -------- # The harness is preferred wherever it exists, because this port closes on the # suite run UNDER ptest-runner -- its discovery and its tally are part of what is # asserted. The fallback runs the identical script directly and SKIPS only the # cases that are about the harness itself; they have no subject on an image that # ships no ptest-runner. out=$tmp/runner.out if [ -x "$runner" ]; then bst_ok libcbor/ptest-runner-present "$runner" -t "$runner_t" libcbor > "$out" 2>&1 rc=$? check_eq libcbor/ptest-runner-rc 0 "$rc" begin=; end=; rerr= while read -r k v; do case "$k" in BEGIN:) begin=$v ;; END:) end=$v ;; ERROR:) rerr="$v" ;; esac done < "$out" check_eq libcbor/runner-begin "$ptestdir" "$begin" check_eq libcbor/runner-end "$ptestdir" "$end" check_eq libcbor/runner-no-error "" "$rerr" else bst_skip libcbor/ptest-runner-present "this image ships no ptest-runner" bst_skip libcbor/ptest-runner-rc "no ptest-runner to run" for c in runner-begin runner-end runner-no-error; do bst_skip "libcbor/$c" "harness cases have no subject without ptest-runner" done ( cd "$ptestdir" && sh ./run-ptest ) > "$out" 2>&1 fi # ------------------------------------- one case per upstream programme ------- # A single `while read` over the capture: no fork per case, and every programme # upstream declared gets its own BST case, so a red names the area that broke. # CASES: lines are run-ptest's own count of what each programme actually ran. npass=0 nfail=0 ncases=0 while read -r verdict name extra; do case "$verdict" in PASS:) bst_ok "libcbor/t-$name"; npass=$((npass + 1)) ;; FAIL:) bst_notok "libcbor/t-$name" "upstream test programme failed"; nfail=$((nfail + 1)) ;; CASES:) case "$extra" in ''|*[!0-9]*) : ;; *) ncases=$((ncases + extra)) ;; esac ;; esac done < "$out" # The tally is asserted separately from the individual cases, because the three # fail differently. A red above is one broken area. A short TOTAL means # programmes never ran at all -- a truncated capture, a harness timeout, a # payload that did not install. And a short CASE count means the programmes ran # but were HOLLOW: cmocka exits 0 having executed nothing when a group is # compiled out, so without this a configuration change that amputated half the # suite would read as a clean green run. check_eq libcbor/suite-total "$want_progs" "$((npass + nfail))" check_eq libcbor/suite-failures 0 "$nfail" check_eq libcbor/suite-cases "$want_cases" "$ncases" # cpp_linkage_test is called out on its own because what it asserts is not a # behaviour of the library but a property of the SHIPPED HEADER: that cbor.h's # extern "C" guards are correct, so a C++ caller links. It is the only member of # the suite a C compiler cannot check, and -dev is most of what this port # installs. The bbappend opts into the C++ driver for this one file. has libcbor/cpp-linkage "PASS: cpp_linkage_test" "$(cat "$out" 2>/dev/null)" # ============================================================================ # The archive: what is in it. # ============================================================================ # # Flattened to printable text once. GNU grep suppresses matches inside data it # classifies as binary and busybox tr does not handle [:print:] under -c, so the # octal range is the formulation both greps and both trs agree on -- derived # empirically by 69-libb64.test and reused since. flat=$tmp/lib.flat ( tr -c '\40-\176\n' ' ' < "$lib" > "$flat" 2>/dev/null ) a=$(cat "$flat" 2>/dev/null) # One translation unit per source in src/CMakeLists.txt's SOURCES. A missing # member is a partial compile the link would only notice if something referenced # it -- and on a static-only arm with no consumer in the image, nothing would. for m in cbor allocators streaming encoders builder_callbacks loaders \ memory_utils stack unicode encoding serialization arrays common \ floats_ctrls bytestrings callbacks strings maps tags ints; do has "libcbor/member-$m" "$m.c.o" "$a" done # A named sample across every layer of the API. Not a count: the archive carries # 236 external symbols natively and pinning that number would go false on any # version bump, whereas these are the entry points that define what the library # IS -- load and serialise, the refcount, the allocator seam, the tree # constructors, the streaming decoder, and the pretty printer. for s in cbor_load cbor_serialize cbor_serialize_alloc \ cbor_incref cbor_decref cbor_intermediate_decref \ cbor_set_allocs cbor_copy \ cbor_build_uint8 cbor_build_negint8 cbor_build_bool cbor_build_float4 \ cbor_build_bytestring cbor_build_string cbor_build_tag \ cbor_new_definite_array cbor_new_indefinite_array \ cbor_new_definite_map cbor_new_indefinite_map \ cbor_array_push cbor_array_get cbor_array_size \ cbor_map_add cbor_map_size \ cbor_typeof cbor_isa_uint cbor_isa_float_ctrl cbor_is_null \ cbor_stream_decode cbor_describe; do has "libcbor/sym-$s" "$s" "$a" done rm -rf "$tmp"