# BST-TIMEOUT: 300 . "$BST_LIB" # libmimetic 0.9.8 -- a C++ MIME (RFC 2045/2046) parser and generator, ported as # -dev + -staticdev + -tests: 51 headers under five directories, libmimetic.a, # and the probe this suite is mostly about. There is NO pkg-config file, no cmake # package, no CLI and no script: upstream installs headers and one archive and # nothing else, which is unusually little even for a library, and is why the # payload section below is a header census rather than the .pc/cmake triangulation # 81-json-c and 81-jsoncpp can do. # # Numbered 81 beside the other parse-a-format-into-a-tree libraries. mimetic # overlaps none of them: they read JSON, CBOR, msgpack or XML, and this one reads # a MIME message -- headers, a boundary-delimited part tree, and the two RFC 2045 # transfer encodings. Nothing else on this image parses MIME. # # WHAT THE PORT DECIDED. # # 1. The C++ driver. mimetic is C++ end to end and the external-tcmode CXX is # the C driver, where the standard headers do not resolve -- so without the # bbappend's one-line opt-in the build stops in CONFIGURE, at # `checking STL required features... error: std::char_traits unsupported`, # before a compiler is ever asked for the library. # # 2. A shipped header corrected so that it compiles. This is the one divergence # a reviewer should read as a FIX rather than a policy choice, and section 2 # below is where it is asserted. rfc822/messageid.h takes uint32_t from # behind `#ifdef HAVE_STDINT_H`, and HAVE_STDINT_H lives in the # generated config.h, which is NOT in include_HEADERS and therefore never # installed. So the guard can never be true for a consumer, and # -- the umbrella header upstream tells you to use -- # does not compile: # # mimetic/rfc822/messageid.h:29:23: error: expected ')' before 'thread_id' # # Upstream's gap, not this arm's: measured identically with g++ 13 on x86-64 # at this same SRCREV. The recipe makes the include unconditional in the # installed copy and then PROVES the result with -fsyntax-only over the # umbrella header with no defines at all, which is the only place that claim # can be tested since the target has no compiler. # # WHAT THE PROBE IS FOR. mimetic installs no programme, upstream's examples/ are # EXTRA_PROGRAMS automake never builds for `all`, and meta-oe's own patch deletes # test/ from SUBDIRS -- so without /usr/libexec/libmimetic-tests/libmimetic-probe # this suite could only list files. BRINTOS-217 is the standing reason that is not # coverage for a parser: oniguruma packaged perfectly on wasm32-hwjs and then # handed every caller silently wrong match offsets. The probe parses three # committed fixtures and compares part boundaries, header field values and decoded # bodies BYTE FOR BYTE against constants compiled into it. # # Style follows 81-json-c and 81-jsoncpp: files are read once into shell variables # and matched with `case` globs, the archive is one batched pass, and the probe is # run ONCE -- on this runtime the wasm module's start-up dominates the cost of the # whole suite, and ~130 check_out spawns would cost more than the assertions. incd=/usr/include/mimetic lib=/usr/lib/libmimetic.a nl=' ' hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "absent or empty: $2"; fi; } has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } lacks() { case "$3" in *"$2"*) bst_notok "$1" "present but should not be: $2" ;; *) bst_ok "$1" ;; esac; } noglob_ok() { # ok iff nothing matched the glob already expanded into "$@" _id=$1; shift if [ ! -e "$1" ]; then bst_ok "$_id"; else bst_notok "$_id" "present: $1"; fi } # --------------------------------------------------------------------------- # 1. The shipped payload -- all 51 headers. # # Enumerated rather than counted, and enumerated in the five directories upstream # installs them to, because mimetic's Makefile.am overrides `includedir` per # subdirectory (INCLUDEDIR=$includedir/mimetic, then /codec, /os, /rfc822, # /parser). That is an unusual layout and a `make install` that lost one of those # overrides would put a whole subtree in the wrong place -- which a count would # not see and a consumer's #include would. for h in body circular_buffer contentdescription contentdisposition contentid \ contenttransferencoding contenttype fieldparam header libconfig \ message mimeentity mimeentitylist mimetic mimeversion streambufs \ strutils tokenizer tree utils version; do hasfile "libmimetic/header-$h" "$incd/$h.h" done for h in base64 code codec codec_base codec_chain other_codecs qp; do hasfile "libmimetic/codec-$h" "$incd/codec/$h.h" done for h in directory file file_iterator fileop mmfile os stdfile utils; do hasfile "libmimetic/os-$h" "$incd/os/$h.h" done for h in address addresslist body datetime field fieldvalue group header \ mailbox mailboxlist message messageid rfc822; do hasfile "libmimetic/rfc822-$h" "$incd/rfc822/$h.h" done for h in itparser itparserdecl; do hasfile "libmimetic/parser-$h" "$incd/parser/$h.h" done # The umbrella header is what upstream documents as the include. Asserting the # lines is asserting that it still reaches every subtree above -- a header that # ships while its umbrella has stopped naming it is present and unreachable. # The id carries no second slash: a case id is / and the runner # splits on the first, so `codec/codec.h` would read as a third field. umb=$(cat "$incd/mimetic.h" 2>/dev/null) for h in libconfig.h version.h body.h header.h message.h mimeentity.h \ mimeentitylist.h strutils.h tokenizer.h utils.h contenttype.h \ codec/codec.h os/os.h parser/itparser.h; do id=$(printf '%s' "$h" | tr / -) has "libmimetic/umbrella-$id" "#include " "$umb" done # --------------------------------------------------------------------------- # 2. The header fix, in the file that ships. # # Three cases: the guard is gone, the include is unconditional, and the # declaration it exists for is still there. The third is not redundant -- if a # later version drops the uint32_t overload the fix becomes pointless, and this # is where that shows up rather than in a silent no-op. mid=$(cat "$incd/rfc822/messageid.h" 2>/dev/null) lacks libmimetic/fix-guard-gone "#ifdef HAVE_STDINT_H" "$mid" has libmimetic/fix-include "#include " "$mid" has libmimetic/fix-declaration "MessageId(uint32_t thread_id" "$mid" # config.h is what the guard was waiting for, and NOT installing it is upstream's # shape rather than an accident of this port -- so it is pinned as an absence. If # a later version starts installing it, the fix above becomes redundant and this # case is where the next reader finds that out. noglob_ok libmimetic/no-config-h "$incd/config.h" # --------------------------------------------------------------------------- # 3. The archive. hasfile libmimetic/archive-present "$lib" check_eq libmimetic/archive-format '!' "$(head -c 7 "$lib" 2>/dev/null)" # Flatten the archive's printable runs to one token per line first: a raw # `grep -o` over binary data reports "Binary file matches" instead of the # matches, which would silently under-count and read as a partial compile. tmp=${TMPDIR:-/tmp} flat=$tmp/libmimetic-flat.$$ ( tr -c '[:print:]' '\n' < "$lib" > "$flat" 2>/dev/null ) a=$(cat "$flat" 2>/dev/null) # --- the compiled unit census ----------------------------------------------- # # THE EXHAUSTIVE CHECK, and it is the archive MEMBER list rather than json-c's # "every declared entry point is present". That substitution is deliberate: # mimetic is C++, its names are mangled, and a mangled name CONTAINS the # identifier -- so a json-c-style `grep -oFf declared-names` would report # `header` found because `_ZN7mimetic6HeaderD2Ev` is in the archive, and would go # on reporting it after the compile of header.cxx had failed entirely. It would # be a check that cannot fail. The member list cannot be satisfied that way: one # name per translation unit, and a partial compile drops the member. # # 32 sources across the four Makefile.am's (14 top-level, 2 codec, 5 os, 11 # rfc822), and 32 members in the archive. Only 29 BASENAMES are distinct: utils, # header and message each appear in two directories, and libtool renames the # second of each pair lt1-/lt2-/lt3-. The loop below therefore matches as a # SUBSTRING -- `utils.o` finds `lt1-utils.o` -- and the three renamed members are # named separately underneath, which is what keeps the substring form from # reporting a whole translation unit present because its namesake survived. The # lt numbering follows libtool's LIBADD order and was measured identical on # this arm and on native x86-64. nobj=0 for o in body contentdescription contentdisposition contentid \ contenttransferencoding contenttype fieldparam header message \ mimeentity mimeversion strutils utils version \ base64 qp \ file_iterator fileop mmfile stdfile \ address addresslist datetime field fieldvalue group mailbox \ mailboxlist messageid; do case "$a" in *"$o.o"*) nobj=$((nobj+1)) ;; *) bst_notok "libmimetic/object-$o" "$o.o not in $lib -- that translation unit is not in the archive" ;; esac done if [ "$nobj" -eq 29 ]; then bst_ok libmimetic/objects-complete else bst_notok libmimetic/objects-complete "found=$nobj want=29"; fi for o in lt1-utils lt2-header lt3-message; do has "libmimetic/object-$o" "$o.o" "$a" done # --- named symbols, one per layer ------------------------------------------- # # Itanium mangling writes a name as , so `12ContentType` can # only come from a symbol naming that exact class -- which is what makes these # assertions, where a bare `ContentType` would also match a mention of # ContentTypeSomething. One per layer so that a failure names the layer that # broke rather than only reporting a count. for s in 10MimeEntity 6Header 4Body 11ContentType 23ContentTransferEncoding \ 18ContentDisposition 9ContentId 18ContentDescription 11MimeVersion \ 10FieldParam 12Rfc822Header 5Field 10FieldValue 7Mailbox 11MailboxList \ 7Address 11AddressList 9MessageId 8DateTime 5Group 6Base64 2QP \ 6MMFile 7StdFile 6FileOp 14ifile_iterator 14IteratorParser; do has "libmimetic/sym-$s" "7mimetic$s" "$a" done # The floor guards the check itself: an extraction that silently matched nothing # would otherwise pass every case above vacuously. Measured at 1055 distinct # mimetic-namespace symbols on this arm; 800 leaves room for an inlining # decision to move without leaving room for half the library to go missing. nsym=$(grep -oE '_ZN?K?7mimetic[A-Za-z0-9_]+' "$flat" 2>/dev/null | sort -u | grep -c .) if [ "$nsym" -ge 800 ]; then bst_ok libmimetic/sym-floor else bst_notok libmimetic/sym-floor "distinct mimetic symbols=$nsym want>=800"; fi # version.cxx compiles the version macro in, so finding it here pins the compiled # code to the headers rather than to anything this suite writes down. has libmimetic/lib-version-string "0.9.8" "$a" rm -f "$flat" # --- static-only guards ------------------------------------------------------- # # Shared objects are refused distro-wide -- wasmld has no stable -shared ABI on # the HWJS-3 coro arm -- and mimetic is a libtool package, so a .so or a .la here # would mean the static selection was lost rather than merely that a file # appeared. mimetic-config: upstream ships none, and this pins that nothing in # the port invented one. set -- /usr/lib/libmimetic.so*; noglob_ok libmimetic/no-shared "$@" set -- /usr/lib/libmimetic.la; noglob_ok libmimetic/no-libtool "$@" set -- /usr/lib/pkgconfig/mimetic.pc; noglob_ok libmimetic/no-pc "$@" # NOT asserted here: the absence of upstream's eleven examples/ programmes. # automake builds them only on an explicit `make examples` and installs none of # them, so there is nothing for this port to suppress -- and their names are # `b64`, `qp`, `mm`, `search`, `structure`: generic enough that asserting # /usr/bin/ is empty would red on whatever OTHER package ships one, which # is a claim about the image rather than about this port. # --------------------------------------------------------------------------- # 4. The probe -- the library actually running. # # Built by the libmimetic recipe against the archive that same build produced and # shipped in libmimetic-tests, so it cannot drift from the library it tests. It is # installed 0755 in libexecdir and needs no staging: it opens its fixtures # read-only and writes nothing, so the read-only rootfs at browser runtime is not # in its way. `require' rather than a hard failure -- an image that takes # libmimetic-dev without libmimetic-tests legitimately has nothing to run here, # and that is a coverage prompt (add the package), not a broken port. MT=/usr/libexec/libmimetic-tests require "$MT/libmimetic-probe" libmimetic/probe-present for f in mm-simple.msg mm-multipart.msg mm-nested.msg; do hasfile "libmimetic/fixture-$f" "$MT/$f" done # Run ONCE and keep both the output and the status. out=$("$MT/libmimetic-probe" "$MT" 2>&1); prc=$? bst_ok libmimetic/probe-ran # --- a plain message: the RFC822 header layer -------------------------------- # The two To: addresses are the point of simple-to-count: the second is BARE # (carol@example.org, no display name), which is the form a naive comma splitter # gets wrong while still parsing the first correctly. for c in simple-open simple-ctype simple-charset simple-cte simple-mimeversion \ simple-subject simple-xfield simple-nofield simple-from-count \ simple-from-mailbox simple-from-label simple-to-count simple-parts \ simple-body; do has "libmimetic/run-$c" "MM-$c OK" "$out" done # --- the file layers ---------------------------------------------------------- # mp-file-is-stdfile is an ASSERTION ABOUT WHAT A CONSUMER GETS, and it is # deliberate. mimetic::File is MMFile when HAVE_MMAP is defined and StdFile when # it is not -- and HAVE_MMAP comes from the config.h that is never installed, so # every consumer of this image gets StdFile even though the library itself was # built against MMFile and MMFile is compiled into the archive. mp-mmap-* reaches # MMFile by naming it explicitly, which is the only way a consumer can, and is # worth reaching: mmap(2) under the wasm32-hwjs guest is a real port risk, and # meta-oe's MAP_FAILED patch -- without which this source does not compile -- # exists because the stock code tested mmap's return with `m_beg > 0`. That patch # makes it BUILD; these cases are what say it WORKS. for c in mp-file-is-stdfile mp-mmap-open mp-mmap-size mp-stdfile-agrees; do has "libmimetic/run-$c" "MM-$c OK" "$out" done # --- a three-part multipart: boundaries, and both transfer codecs ------------- # mp-p2-decoded is base64 over bytes a text-only path would corrupt -- a NUL, a # 0x01 and a 0xff -- compared byte for byte. mp-p3-decoded is quoted-printable # with two =XX escapes for one UTF-8 codepoint, a literal =3D, a soft line break # the encoder inserted mid-word, and a trailing space encoded as =20. # # mp-preamble and the part bodies carry the RFC 2046 5.1.1 rule that the newline # before a boundary delimiter belongs to the DELIMITER: a part body ends one byte # before the newline the fixture appears to give it, and the epilogue -- which no # boundary follows -- keeps its own. Every constant in the probe was measured on a # native x86-64 build of this same source first, so a divergence here is a port # defect rather than a guess being corrected. for c in mp-ctype mp-ismultipart mp-boundary mp-parts mp-preamble mp-epilogue \ mp-parts-present mp-p1-ctype mp-p1-index mp-p1-body \ mp-p2-ctype mp-p2-cte mp-p2-name mp-p2-disposition mp-p2-filename \ mp-p2-decoded mp-p3-ctype mp-p3-cte mp-p3-decoded; do has "libmimetic/run-$c" "MM-$c OK" "$out" done # --- nesting ------------------------------------------------------------------ # The recursion is the whole point of this group: a parser that stops at the # first boundary level reports 0 sub-parts for the multipart/alternative and # still looks green on every other case in this suite. for c in nested-open nested-ctype nested-boundary nested-parts \ nested-outer-present nested-inner-ctype nested-inner-boundary \ nested-inner-parts nested-inner-present nested-i1-ctype \ nested-i1-index nested-i1-body nested-i2-ctype nested-i2-body \ nested-o2-ctype nested-o2-body; do has "libmimetic/run-$c" "MM-$c OK" "$out" done # --- the codecs standalone ---------------------------------------------------- # Over the byte range the parser fixtures do not reach: every value 0x00..0xff # exactly once. codec-b64-head and codec-b64-len pin the ENCODED form as well as # the round-trip, because a round-trip alone passes when both halves are wrong in # the same way -- and the length pins the line WRAPPING, which is the half of RFC # 2045 4.3.2.4 a round-trip cannot see. The -literal cases decode from a literal # rather than from our own encoder's output, so an encoder/decoder pair that agree # with each other but not with the RFC fails here and nowhere else. for c in codec-b64-roundtrip codec-b64-head codec-b64-len codec-qp-roundtrip \ codec-b64-literal codec-qp-literal codec-qp-softbreak; do has "libmimetic/run-$c" "MM-$c OK" "$out" done # --- identity and the string layer under every field parse -------------------- # istring is the case-insensitive string RFC822 field names compare as; getting # it wrong makes hasField() answer by luck of capitalisation, which is the kind # of defect that passes every fixture written in one capitalisation. for c in lib-version util-istring-ci util-istring-ne; do has "libmimetic/run-$c" "MM-$c OK" "$out" done # The summary is the guard on the whole of section 4: the probe reports its own # tally, so a run that died a third of the way through -- taking two dozen `has` # cases red with it but leaving the rest of the suite green -- is named here as # one unambiguous failure rather than left to be inferred from the pattern. has libmimetic/probe-summary "MM-summary pass=63 fail=0" "$out" check_eq libmimetic/probe-rc 0 "$prc"