# BST-TIMEOUT: 300 # reads ~114 kB across four files; the cost here is execs, not bytes # # mailcap 2.1.54 -- /etc/mailcap, the RFC 1524 table that says which program # opens which MIME type, and the mime.types extension-to-type map that goes with # it. There is no consumer on this image yet (mime-support's run-mailcap is # BRINTOS-1190 and had not landed when this was written), so the deliverable is # the data, and this suite asserts that the data PARSES and RESOLVES rather than # that the files exist. # # WHAT IS ACTUALLY IN THE PACKAGE, MEASURED, NOT ASSUMED. /etc/mailcap is 272 # bytes and SIX entries, all of them /usr/bin/xdg-open, with exactly one named # field between them (copiousoutput on text/html) and NO continuation lines. # That is upstream's own file -- byte-identical to an x86-64 install of the same # release -- not a truncated payload. It follows that the shipped table cannot # exercise a mailcap parser's two divergence points, so those are covered # against data/mailcap-rfc1524.fixture instead and the real table is then read # by a parser shown to handle them. # # THE TABLE IS TESTED BY ITS REAL INSTALLED PATH, NOT THROUGH THE SYMLINK. # mailcap declares ALTERNATIVE mime.types on /etc/mime.types with no explicit # priority, so update-alternatives installs the file as /etc/mime.types.mailcap # and leaves /etc/mime.types a symlink to it -- and mime-support declares the # same alternative at priority 90 against this one's class default of 10, so # when that package lands the symlink moves to ITS table and this one stays # where it is. Everything below reads /etc/mime.types.mailcap; the symlink gets # one case of its own, whose expectation is DERIVED from what is installed. # # WRITTEN TO EXEC AS LITTLE AS POSSIBLE. On this arm a process's memory is not # returned when it exits and /bin/sh is a 16 MB wasm module, so a suite's cost # is dominated by how many programs it starts. Five execs cover the whole file. . "$BST_LIB" S=mailcap # THE CANARY, which is not an assertion about mailcap at all. # # brintos_local_verify.py loses a suite's FIRST BST result line before the host # parser ever sees it -- BRINTOS-1149, a defect in our own host-side tooling and # not in the runner, the guest or any package. The harness reports the deficit # (`declared N case(s), N-1 reached the console`) but 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` therefore loses the one line that mattered, # silently -- and for this suite that first case would be `present-mailcap`, i.e. # "the package's own table is on the image", which is the last red anyone would # want eaten. # # So a case that asserts nothing stands in that position and the eaten line costs # nothing. Nothing was moved out of the slot; a nothing-case was added in front # of it, and every real case below is still counted. Same idiom and same reason # as 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 1149 is fixed. bst_ok $S/suite-canary D=$BST_DATA tmp=${TMPDIR:-/tmp} W=$tmp/bst-mailcap.$$ require gawk $S/gawk-present require sha256sum $S/sha256sum-present require find $S/find-present MAILCAP=/etc/mailcap MTREAL=/etc/mime.types.mailcap MTLINK=/etc/mime.types NGINX=/etc/nginx/mime.types FIXTURE=$D/mailcap-rfc1524.fixture # --------------------------------------------------------------- payload shape # One find over every path this suite has an opinion about, including the two # that belong to mime-support: their presence is what selects the expectation # for the alternatives case below, so it has to be measured, not assumed. find "$MAILCAP" "$MTLINK" "$MTREAL" "$NGINX" \ /etc/mime.types.mime-support /usr/bin/run-mailcap /usr/lib/mime/mailcap \ -maxdepth 0 -printf '%p %y %s %l\n' 2>/dev/null > "$W.inv" sha256sum "$MAILCAP" "$MTREAL" "$NGINX" "$MTLINK" 2>/dev/null > "$W.sums" for pair in "mailcap:$MAILCAP" "mime-types:$MTREAL" "nginx:$NGINX" "fixture:$FIXTURE"; do nm=${pair%%:*}; f=${pair#*:} if [ -f "$f" ]; then bst_ok "$S/present-$nm" else bst_notok "$S/present-$nm" "$f is not a regular file"; fi done if [ ! -f "$MAILCAP" ] || [ ! -f "$MTREAL" ] || [ ! -f "$NGINX" ] || [ ! -f "$FIXTURE" ]; then bst_notok $S/parse-prereq "a payload file is missing; the parse batches cannot run" return 2>/dev/null || exit 0 fi # ------------------------------------------------------- RFC 1524: shipped table gawk -v PFX=MC \ -v QUERIES='audio/mpeg,image/png,application/pdf,application/msword,application/postscript,text/html,text/plain,application/json,video/mp4' \ -f "$D/mailcap-parse.awk" "$MAILCAP" > "$W.mc" . "$W.mc" check_eq $S/mailcap-entries 6 "$MC_ENTRIES" check_eq $S/mailcap-comments 3 "$MC_COMMENTS" check_eq $S/mailcap-blanks 4 "$MC_BLANKS" # Every field-1 is a well-formed type/subtype and every named field is one RFC # 1524 defines. A table that parses into entries but carries a field the spec # has no name for is broken in a way a count of entries cannot see. check_eq $S/mailcap-badtype 0 "$MC_BADTYPE" check_eq $S/mailcap-badfield 0 "$MC_BADFIELD" check_eq $S/mailcap-dangling 0 "$MC_DANGLING" check_eq $S/mailcap-types \ 'audio/*,image/*,application/msword,application/pdf,application/postscript,text/html' \ "$MC_TYPES" # The shipped table has no continuations. Asserted rather than left implicit: # if upstream ever adds one, the fixture-backed paths below start carrying the # real table too and that is worth knowing. check_eq $S/mailcap-nocont 0 "$MC_CONT" # The fifth type in that list is written 'application/postscript ;' in the file, # with a space before the separator. It appears there without the space because # the parser trims each field -- which is the whole of that case; a separate # assertion for it would only be the same statement said twice. # ------------------------------------------------ RFC 1524: resolving a spread # The entry INDEX is asserted alongside the command, so a table that resolves to # the right program via the wrong entry still fails. check_eq $S/resolve-audio-mpeg '1:/usr/bin/xdg-open %s' "$MC_R_audio_mpeg" check_eq $S/resolve-image-png '2:/usr/bin/xdg-open %s' "$MC_R_image_png" check_eq $S/resolve-application-msword '3:/usr/bin/xdg-open %s' "$MC_R_application_msword" check_eq $S/resolve-application-pdf '4:/usr/bin/xdg-open %s' "$MC_R_application_pdf" check_eq $S/resolve-application-ps '5:/usr/bin/xdg-open %s' "$MC_R_application_postscript" check_eq $S/resolve-text-html '6:/usr/bin/xdg-open %s' "$MC_R_text_html" check_eq $S/resolve-text-html-flag 'copiousoutput' "$MC_RF_text_html" # The negatives are the half that matters: there is no text/* and no # application/* wildcard in this table, so a parser that treats 'audio/*' as # matching anything passes every case above and fails these three. check_eq $S/resolve-text-plain-none 'none' "$MC_R_text_plain" check_eq $S/resolve-application-json-none 'none' "$MC_R_application_json" check_eq $S/resolve-video-mp4-none 'none' "$MC_R_video_mp4" # ------------------------------------- RFC 1524: continuation and \; escaping gawk -v PFX=FX -f "$D/mailcap-parse.awk" "$FIXTURE" > "$W.fx" . "$W.fx" check_eq $S/fixture-entries 4 "$FX_ENTRIES" check_eq $S/fixture-badtype 0 "$FX_BADTYPE" check_eq $S/fixture-badfield 0 "$FX_BADFIELD" check_eq $S/fixture-cont 2 "$FX_CONT" # Three physical lines joined into one view-command field, with the backslash # and the newline removed and nothing else. check_eq $S/cont-physlines 3 "$FX_PHYS_1" check_eq $S/cont-view '/bin/bst-view %s --flag --tail' "$FX_VIEW_1" check_eq $S/cont-flag 'needsterminal' "$FX_FLAGS_1" check_eq $S/cont-description 'joined across three lines' "$FX_NV_1_description" # The escaped '\;' must not split the field: a naive split(';') reports four # fields and a view command truncated at '%s'. check_eq $S/escape-nfields 3 "$FX_NF_2" check_eq $S/escape-view '/bin/bst-view %s ; /bin/bst-second %s' "$FX_VIEW_2" check_eq $S/escape-flag 'copiousoutput' "$FX_FLAGS_2" # Continuations that begin a new field: the leading blanks belong to the # separator, not to the value, so the trim is what makes these readable. check_eq $S/named-physlines 4 "$FX_PHYS_3" check_eq $S/named-order 'test,nametemplate,description' "$FX_NAMED_3" check_eq $S/named-test 'test -n "$DISPLAY"' "$FX_NV_3_test" check_eq $S/named-nametmpl '%s.bst' "$FX_NV_3_nametemplate" check_eq $S/flags-both 'needsterminal,copiousoutput' "$FX_FLAGS_4" # ------------------------------------------- mime.types, nginx table, integrity gawk -v LOOKUPS='pdf,html,png,tar,json,mp3,ogg,ics,wasm,bst-no-such-ext' \ -v RLOOKUPS='application/pdf,text/html,application/wasm,image/png,application/json' \ -v CORE="$MAILCAP,$MTLINK,$MTREAL,$NGINX" \ -f "$D/mailcap-mimetypes.awk" "$MTREAL" "$NGINX" "$W.sums" "$W.inv" > "$W.mt" . "$W.mt" # The inventory pass saw all four paths. This is the case that names the cause # when the find above produces nothing -- which is what a find without -printf # does, silently, leaving every size and type assertion below reading ''. check_eq $S/inventory-core 4 "$MT_INV_CORE" # These five numbers are upstream's own make-check (python3 test.py < mime.types) # re-derived on target by the same rules. Pinning them is what makes a truncated # or half-installed table fail: the file would still parse. check_eq $S/mimetypes-types 2151 "$MT_TYPES" check_eq $S/mimetypes-extensions 1393 "$MT_EXTS" check_eq $S/mimetypes-dup-ext 0 "$MT_DUPEXT" check_eq $S/mimetypes-dup-type 0 "$MT_DUPTYPE" check_eq $S/mimetypes-bad-top 0 "$MT_BADTOP" check_eq $S/mimetypes-toplevel \ 'application:1604,audio:171,chemical:1,font:6,image:89,message:24,model:41,multipart:16,text:95,video:102,x-conference:1,x-epoc:1' \ "$MT_TOPLEVEL" check_eq $S/ext-pdf 'application/pdf' "$MT_E_pdf" check_eq $S/ext-html 'text/html' "$MT_E_html" check_eq $S/ext-png 'image/png' "$MT_E_png" check_eq $S/ext-tar 'application/x-tar' "$MT_E_tar" check_eq $S/ext-json 'application/json' "$MT_E_json" check_eq $S/ext-mp3 'audio/mpeg' "$MT_E_mp3" check_eq $S/ext-ogg 'audio/ogg' "$MT_E_ogg" check_eq $S/ext-ics 'text/calendar' "$MT_E_ics" check_eq $S/ext-wasm 'application/wasm' "$MT_E_wasm" check_eq $S/ext-absent 'none' "$MT_E_bst_no_such_ext" # The reverse direction, only where the table is unambiguous -- and text/html is # carried as the case that shows the qualifier is real rather than decorative: # it maps to two extensions (html, htm) and the suite says so instead of # picking one. check_eq $S/type-application-pdf 'pdf' "$MT_T_application_pdf" check_eq $S/type-image-png 'png' "$MT_T_image_png" check_eq $S/type-application-json 'json' "$MT_T_application_json" check_eq $S/type-application-wasm 'wasm' "$MT_T_application_wasm" check_eq $S/type-text-html-ambig 'ambiguous:2' "$MT_T_text_html" # /etc/nginx/mime.types is GENERATED at build time from mime.types by # generate-nginx-mimetypes.sh, on the build host, so it is the one file here # that is not simply copied out of the tarball. Re-deriving it on target and # comparing line for line is the assertion that the generation ran and produced # the right thing for this arm. check_eq $S/nginx-derived-lines 1078 "$MT_NGX_DERIV" check_eq $S/nginx-file-lines 1080 "$MT_NGX_LINES" check_eq $S/nginx-derivation 0 "$MT_NGX_DIFF" # ------------------------------------------------ sizes and content, vs x86-64 # The three sha256 sums are of an INDEPENDENT x86-64 install of mailcap 2.1.54 -- # tarball unpacked, its own Makefile install run -- not of the .deb this image # built, which would have made the comparison tautological. mailcap does not # inherit allarch, so the wasm32 build genuinely ran its own install and its own # nginx-table generation; that the bytes agree is a result, not a tautology. check_eq $S/size-mailcap 272 "$MT_SZ__etc_mailcap" check_eq $S/size-mime-types 74869 "$MT_SZ__etc_mime_types_mailcap" check_eq $S/size-nginx 38661 "$MT_SZ__etc_nginx_mime_types" check_eq $S/sha-mailcap \ '9e701bfd119a566cd01950a1fa4049e3d0caf8cad684228ff04afb232aa911b8' "$MT_SHA__etc_mailcap" check_eq $S/sha-mime-types \ '6d89583307fd2941a32039b9d8f392195c6e9a2fc89c681e693a06a714b3a23d' "$MT_SHA__etc_mime_types_mailcap" check_eq $S/sha-nginx \ '845b3cf44e3d178299f218f6fffc4067a989d3f88895dc03bd931c7c9e7230c4' "$MT_SHA__etc_nginx_mime_types" # --------------------------------------------------------- the alternatives link # Both halves of the collision are asserted, and the EXPECTATION IS DERIVED from # what is installed rather than written down: mime-support wins at priority 90 # when it is there, mailcap holds the link at the class default of 10 when it is # not. Either way this package's own table is the file checked above, so the # suite stays true whichever package landed first. check_eq $S/alt-real-is-file 'f' "$MT_TY__etc_mime_types_mailcap" check_eq $S/alt-link-is-link 'l' "$MT_TY__etc_mime_types" if [ -f /etc/mime.types.mime-support ]; then alt_want=mime.types.mime-support alt_why='mime-support installed, ALTERNATIVE_PRIORITY 90' else alt_want=mime.types.mailcap alt_why='mailcap alone, ALTERNATIVE_PRIORITY 10 (the update-alternatives default)' fi check_eq $S/alt-winner "$alt_want" "$MT_LN__etc_mime_types" # And what the link RESOLVES to, not just what it says: sha256sum follows the # symlink, so this is the same table read the way anything on the image would # read it. It is only a statement about mailcap while mailcap holds the link. if [ "$alt_want" = mime.types.mailcap ]; then check_eq $S/alt-link-resolves \ '6d89583307fd2941a32039b9d8f392195c6e9a2fc89c681e693a06a714b3a23d' "$MT_SHA__etc_mime_types" else bst_skip $S/alt-link-resolves "$alt_why -- /etc/mime.types is not this package's table" fi # ------------------------------------------------------------------ run-mailcap # The ticket asks for this WHERE mime-support's run-mailcap is present, which is # the only assertion here that drives the tables with the tool that exists to # consume them: run-mailcap maps the EXTENSION through mime.types, then the TYPE # through /etc/mailcap, and prints the command it would run. Both cases below # therefore span both files at once. # # WHICH mime.types it reads is whoever holds the alternatives link -- ours today, # mime-support's once BRINTOS-1190 lands. The /etc/mailcap half is always this # package's, because mime-support installs its own table to /usr/lib/mime/mailcap # and never to /etc/mailcap, so there is no collision on that side. # # It is a skip rather than a silent omission while the tool is absent: a skip is # a coverage prompt the sweep can see. The branch is not untested for being # skipped -- it was developed and run against these exact tables on an x86-64 # host that does carry run-mailcap, which is how the first draft's use of an # extensionless filename (resolving to application/octet-stream, rc=3) was found. # # BRINTOS-1190 LANDED, SO run-mailcap IS NOW ON THE IMAGE -- AND IT RUNS. # The skip below became these two cases. They were briefly RED: when 1190 first # built, run-mailcap could not start at all on this arm because perl died in # glibc's allocator, 20/20 measured -- linux-in-the-browser#154, the platform # rather than either package. It measures 0/20 now, on a base that did not move # (a60034d50dbb); see the #154 block in 99-mime-support.test for the before/after # table and for what did change. Both cases are GREEN, and every data assertion # above passed unchanged throughout -- neither this table nor mime-support's was # ever implicated. # # THE CLASSIFIER IS THE POINT OF THIS BLOCK, and it is kept now that the reds it # was written for have gone: #154 is still open upstream, nothing here fixed it # deliberately, and a return would come back through this exact call. Left as a # bare check_out, both reds carried `perl: warning: Setting locale failed` in # their detail field: that preamble -- SEVEN lines, not the four this comment # used to claim -- is on EVERY perl run on this image including the ones that # succeed, and it filled the 100 characters a later reader attributes by, so a # platform block read as a locale bug. Stripping it and naming the issue is what # would make a returning red actionable. Same idiom, same wide pattern set as # 36-libperlio-gzip-perl and 99-mime-support; the set is wide by measurement, # not guesswork -- update-mime produced four different glibc spellings in 20 runs. # # BRINTOS-WORKAROUND: linux-in-the-browser#154 remove -- when #154 is CLOSED # upstream these two become the ordinary check_out assertions they were written # as, and this whole classifier goes. if command -v run-mailcap >/dev/null 2>&1; then : > "$W.probe.pdf"; : > "$W.probe.html" for pair in "pdf:$W.probe.pdf:/usr/bin/xdg-open" "copiousoutput:$W.probe.html:.html |"; do cs=${pair%%:*}; rest=${pair#*:}; cf=${rest%%:*}; cw=${rest#*:} co=$(run-mailcap --norun --action=view "$cf" 2>&1); cr=$? cd_=$(printf '%s\n' "$co" | awk '/^perl: warning/{s=1;next} /^[ \t]/&&s{next} {s=0;print}') [ -n "$cd_" ] || cd_=$co case "$cd_" in *mremap_chunk*|*munmap_chunk*|*"corrupted size"*|*"corrupted double-linked list"*|\ *"double free or corruption"*|*"free(): "*|*"malloc(): "*|*"realloc(): "*) bst_notok "$S/run-mailcap-$cs" \ "rc=$cr $(_bst_1line "$cd_") -- linux-in-the-browser#154, perl aborts in glibc allocator; this table is not implicated" ;; *) case "$co" in *"$cw"*) [ "$cr" -eq 0 ] && bst_ok "$S/run-mailcap-$cs" \ || bst_notok "$S/run-mailcap-$cs" "rc=$cr $(_bst_1line "$cd_")" ;; *) bst_notok "$S/run-mailcap-$cs" "rc=$cr want:'$cw' got:'$(_bst_1line "$cd_")'" ;; esac ;; esac done else bst_skip $S/run-mailcap "run-mailcap not installed on this image (mime-support: BRINTOS-1190)" fi rm -f "$W.inv" "$W.sums" "$W.mc" "$W.fx" "$W.mt" "$W.probe.pdf" "$W.probe.html"