. "$BST_LIB" # BST-TIMEOUT: 1800 # TWO python3 starts, and they are the whole cost. # # # # One is the probe, which takes a single start for # # forty-odd measurements. The other is a bare `import # # magic' at the shell, which cannot be folded into the # # probe because the point of it is what a CALLER on this # # image gets -- the probe reports the same failure from # # inside an interpreter that has already started and # # already imported other things. # # # # The work inside each start is one 10 kB source parse and # # a handful of failed imports: microseconds beside an # # interpreter start on this arm, which is tens of seconds # # before the first line of the program runs. The file(1) # # calls in section 7 are the other cost -- a dozen execs, # # each re-reading the 8.5 MB magic.mgc through the # # malloc+read() loader, which is what 50-file.test budgets # # 300s for on its own. # # # # 1800 is 900s per python3 start, matching # # 99-python3-periphery's per-start budget and fifteen times # # the ~60s a python3 start has measured at on this arm. A # # suite-timeout red is a batch-wide cost, so if this proves # # slower on target the RIGHT fix is this number, not fewer # # cases. # # # # NOT MEASURED ON TARGET YET: this port's first on-target # # run is the batch verify, and the measured elapsed replaces # # this note in the test plan rather than in this comment, # # which would cost an image build to keep the shipped suite # # byte-identical. # # python3-file-magic 0.4.1 (file-magic on PyPI; meta-openembedded/meta-python # recipes-devtools/python/python3-file-magic_0.4.1.bb, `inherit pypi # setuptools3', PyPI sdist) on the wasm32-hwjs glibc image. # # ============================================================================ # WHAT THE PACKAGE IS. # ============================================================================ # # The `file' project's OWN Python binding -- one module, magic.py, 361 lines -- # and it is a ctypes interface to libmagic and nothing else. It carries no C of # its own: it opens the shared libmagic, declares the restype/argtypes of # fourteen `magic_*' entry points, and wraps them in a Magic class plus three # convenience functions (detect_from_filename, detect_from_content, # detect_from_fobj) that each return a FileMagic namedtuple of # (mime_type, encoding, name). # # ============================================================================ # THE HEADLINE: `import magic' CANNOT WORK ON THIS IMAGE, AND TWO SEPARATE # PLATFORM GAPS EACH SUFFICE TO STOP IT. # ============================================================================ # # GAP 1 -- NO _ctypes. This image's python3 is ONE STATIC BINARY built with # _ctypes CONFIGURED OUT: ffi_closure_alloc cannot be linked on this arm # (hwjs-cc#15 -- libffi packages the name, the coroutinize pass REFUSEs the # function, and wasmld FATALs the link of anything reaching it), and CPython # reaches it unconditionally outside Darwin. magic.py's line 7 is `import # ctypes', so the module dies there: # # ModuleNotFoundError: No module named '_ctypes' # # Working FFI is a separate milestone (linux-in-the-browser#180). # # GAP 2 -- NO SHARED libmagic. Even with _ctypes working, magic.py's line 24 is # # _libraries['magic'] = _init() # # at MODULE SCOPE, where _init() is ctypes.cdll.LoadLibrary(find_library( # 'magic')). This distro builds package libraries STATIC -- conf/distro/ # brintos.conf sets BRINTOS_PACKAGE_SHARED = "0" and appends --disable-shared # for class-target -- so the `file' recipe ships libmagic.a and there is NO # libmagic.so on this rootfs for find_library to answer with. `file' IS on that # file's BRINTOS_PACKAGE_SHARED_ALLOW list, so the day the flip is turned on is # the day this second gap closes; it is not on today, and section 6 asserts that # against the rootfs rather than assuming it. # # THE TWO ARE ASSERTED SEPARATELY ON PURPOSE. A reader who saw only the import # failure would conclude that FFI is the whole of what this port waits on, and # would be wrong by one gap: closing hwjs-cc#15 alone moves the failure from # line 7 to line 24 and changes the message rather than making the package work. # # ============================================================================ # WHY THIS SUITE IS NOT MERELY "THE IMPORT FAILS", AND WHY IT HAS NO BYPASS. # ============================================================================ # # 99-python3-periphery is the other ctypes-blocked port on this image, and it # reaches three of its seven interfaces by loading them from their file paths # under a stub parent package -- because those three import nothing but stdlib # that IS here. NOTHING ANALOGOUS EXISTS FOR THIS PORT. magic.py is a single # module whose module-scope CDLL load precedes every definition in it, so there # is no importable fraction and no sound subset: the partition is one module, # entirely blocked. A bypass here would have to fake the library handle, and a # suite built on a fake handle would measure the fake and stay green while the # port shipped dead. So the instrument is: # # - the SHIPPED FILE, read and parsed rather than executed (the probe), which # asserts that what landed is the real 0.4.1 API surface -- fourteen bound # libmagic entry points, the three detect_from_* functions, the Magic # methods, FileMagic's field order and the flag constants' values; and # # - libmagic ITSELF, through file(1), over the very corpus this binding would # be pointed at (section 7). That is the case this port most needs made: # the engine works on this arm, and what is missing is only the Python path # to it. Without section 7 a reader cannot tell "blocked on FFI" from # "libmagic does not work on wasm32", and those are very different # statements about the same rootfs. # # ============================================================================ # WHAT detect_from_filename WOULD RETURN, AND WHERE THAT NUMBER COMES FROM. # ============================================================================ # # magic.py builds its FileMagic from two libmagic calls: one on a handle opened # with MAGIC_MIME (= MIME_TYPE|MIME_ENCODING, 1040) and one with MAGIC_NONE (0). # _create_filemagic splits the first on '; ' and strips 'charset=' from the # second half. Those are EXACTLY `file -i' and `file -b'. So for the corpus # below, the counterfactual is derived rather than guessed -- and 50-file.test # already asserts both halves ON TARGET (BRINTOS-1844): # # elf.o ('application/x-object', 'binary', 'ELF 32-bit LSB relocatable, ...') # sample.gz ('application/gzip', 'binary', 'gzip compressed data, ...') # utf8.txt ('text/plain', 'utf-8', 'Unicode text, UTF-8 text') # ascii.txt ('text/plain', 'us-ascii', 'ASCII text') # empty ('inode/x-empty', 'binary', 'empty') # # MEASURED, on an x86-64 control running the real module (host file 5.45 -- the # same version this image builds) against the byte-identical magic.py: every one # of the five agrees with the derivation above. Two facts that control turned up # and that a reader would otherwise get wrong, both of them upstream behaviour # rather than this platform's: # # detect_from_content DOES NOT ALWAYS AGREE WITH detect_from_filename. For the # gzip fixture the buffer path cannot reach the stream trailer, so its `name' # is 'gzip compressed data, max compression, from Unix' -- without the # 'original size modulo 2^32 43' the filename path reports. For the empty # fixture the mime_type differs outright: 'application/x-empty' from the # buffer, 'inode/x-empty' from the path, because inode/* is a verdict about # the directory entry and a buffer has none. # # detect_from_fobj SIDES WITH THE PATH FOR gzip AND WITH THE BUFFER FOR empty. # It goes through magic_descriptor, which fstats -- so it sees the trailer, and # it also sees a zero-length regular file as application/x-empty. # # A MISSING PATH RAISES ValueError, NOT magic.error. libmagic returns its # error text where a type would go ("cannot open `/no/such' (No such file or # directory)"), that string contains no '; ', and _create_filemagic's split # fails -- so the exception a caller catches is ValueError carrying libmagic's # message. The `error' class the module defines is raised only by # MagicDetect's constructor. # # None of the three is assertable here; they are recorded because this suite is # where the next reader will look, and because they are what the ticket's own # description predicted the opposite of. # # ============================================================================ # THE RED WITNESSES. # ============================================================================ # # `pkg-import-refused', `ctypes-refused', `probe-magic-import' and # `no-shared-libmagic' assert the FAILURE, verbatim, in exactly the sense # 99-python3.test uses the term. They go red on the day the platform gap # closes. That inversion is deliberate: an exclusion nobody re-tests outlives # its cause, and the day `import magic' starts working is the day this file, the # roster entry and the image-recipe stanza should all be re-read together. # # The messages are asserted IN FULL rather than by exit status: an `import # magic' failing for some other reason -- the package not installed, a broken # stdlib -- would also be non-zero, and would read as this documented boundary # while being an unrelated defect. P=python3-file-magic bst_ok $P/suite-canary require python3 $P/python3-present require file $P/file-present SITE=/usr/lib/python3.12/site-packages DI=$SITE/file_magic-0.4.1.dist-info PKGLIST=/var/lib/dpkg/info/python3-file-magic.list PROBE=$BST_DATA/python3-file-magic-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-file-magic.$$ mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # --- 1. what shipped -------------------------------------------------------- # IT IS ONE MODULE FILE, NOT A PACKAGE DIRECTORY -- setup.py declares # `py_modules=['magic']'. Asserted both ways round: a release that grew a # package directory would still satisfy every source-level case in the probe. # # Asserted against THIS PACKAGE'S OWN FILE LIST, not the directory: python3-magic # (python-magic 0.4.27, 99-python3-magic) installs a `magic/' package directory # beside this magic.py, and a package directory wins the import. So on an image # carrying both, `import magic' is python-magic's, and every import case below # measures python-magic's refusal -- the same `_ctypes' message, from its line 21. check $P/site-packages-dir test -d "$SITE" check $P/module-file test -s "$SITE/magic.py" check_eq $P/no-package-dir "" \ "$(grep "^$SITE/magic/" /var/lib/dpkg/info/python3-file-magic.list 2>/dev/null)" # The byte-exact identity of what landed. The sha256 is of the sdist the # SRC_URI pins, and the installed copy was verified byte-identical to it off the # built .deb -- so this one case covers "the right source, unmodified". check_eq $P/module-sha256 \ "60d34c0fa8652ed26096a07c91e44811ac93249a003a2048b2f2cc054b91041d" \ "$(sha256sum "$SITE/magic.py" 2>/dev/null | cut -d' ' -f1)" check $P/pycache-dir test -d "$SITE/__pycache__" check $P/pycache-magic test -s "$SITE/__pycache__/magic.cpython-312.pyc" # --- 2. the dist-info ------------------------------------------------------- # THE DIRECTORY IS NAMED FOR THE PyPI DISTRIBUTION, NOT THE IMPORT NAME, and the # two differ here: `file_magic-0.4.1.dist-info' against `magic.py'. A reader # looking for a "file_magic" module on this rootfs will not find one. check $P/dist-info-dir test -d "$DI" check $P/dist-info-metadata test -f "$DI/METADATA" check_out $P/dist-info-name "Name: file-magic" cat "$DI/METADATA" check_out $P/dist-info-version "Version: 0.4.1" cat "$DI/METADATA" check_out $P/dist-info-summary "Python front end for libmagic" cat "$DI/METADATA" check_eq $P/dist-info-top-level "magic" "$(cat "$DI/top_level.txt" 2>/dev/null)" check_out $P/dist-info-purelib "Root-Is-Purelib: true" cat "$DI/WHEEL" check $P/dist-info-license test -s "$DI/LICENSE" # THE VERSION IN setup.py IS 0.4.0 AND THE ONE THAT SHIPS IS 0.4.1. The sdist's # PKG-INFO carries 0.4.1 and setuptools prefers it, so the recipe's filename, # the dist-info and METADATA all agree at 0.4.1 while setup.py disagrees with # all three. Recorded because it is the first thing a reader auditing the # version will trip on; nothing here depends on setup.py. # --- 3. the console script that cannot run ---------------------------------- # UPSTREAM PACKAGING DEFECT, NOT A PLATFORM GAP, and the one case in this suite # that would be red on x86-64 too. file-magic 0.4.1's entry_points.txt declares # # [console_scripts] # realpython = example.__main__:main # # and the sdist ships NO `example' module -- py_modules is ['magic'] alone. So # `inherit setuptools3' faithfully generates /usr/bin/realpython, it lands on # this image's PATH, and it fails on its own import line for a reason that has # nothing to do with ctypes. It is left in place rather than removed by a # bbappend: it is upstream's packaging, it fails cleanly and immediately, and it # is a #! script rather than a wasm module, so the image's exec census -- which # enumerates by wasm magic -- never attempts it and no census case can go red # for it. Asserted here so the dead entry point is on the record. # # We file nothing upstream for this: docs/conventions/no-public-upstream-reports. check $P/script-present test -f /usr/bin/realpython check $P/script-executable test -x /usr/bin/realpython check_out $P/script-imports-example "from example.__main__ import main" \ cat /usr/bin/realpython check $P/script-example-absent test ! -e "$SITE/example" # RED WITNESS: the script is dead on arrival, and this says so in its own words. check_eq $P/script-refused \ "ModuleNotFoundError: No module named 'example'" \ "$(/usr/bin/realpython 2>&1 | tail -1)" # --- 4. dpkg status and the dependency set ---------------------------------- sed -n '/^Package: python3-file-magic$/,/^$/p' /var/lib/dpkg/status > "$W/st" 2>/dev/null check $P/pkg-known test -s "$W/st" check_out $P/pkg-installed "Status: install ok installed" cat "$W/st" grep '^Depends:' "$W/st" > "$W/dep" 2>/dev/null || : > "$W/dep" check $P/pkg-depends-line test -s "$W/dep" # THE FOUR DEPENDENCIES, EACH ASSERTED SEPARATELY, measured off the built .deb: # Depends: file, python3-core, python3-ctypes, python3-threading # # `file' is the interesting one and it is what makes this port's dependency # graph honest: the binding needs the LIBRARY, and the only thing on this image # that provides libmagic is the `file' package (BRINTOS-1844), already here. # # python3-ctypes RESOLVES AND INSTALLS even though `import ctypes' fails, and # that pair is worth holding together: the pure-Python half of ctypes is what # python3-ctypes packages, so the dependency is satisfiable while the C # extension it needs is absent from the interpreter. A reader who saw only the # import failure would expect a broken dependency and find none. for _d in file python3-core python3-ctypes python3-threading; do check_out $P/pkg-depends-$_d "$_d" cat "$W/dep" done # ...and the package really is only those four. A fifth would mean the recipe # grew a dependency this suite has not reasoned about. check_eq $P/pkg-depends-exact \ "Depends: file, python3-core, python3-ctypes, python3-threading" \ "$(cat "$W/dep")" # --- 5. the ctypes boundary, from a shell ----------------------------------- # RED WITNESS. This is what a caller on this image actually gets. It FAILS IF # THE IMPORT EVER SUCCEEDS -- see the header. check_eq $P/pkg-import-refused \ "ModuleNotFoundError: No module named '_ctypes'" \ "$(python3 -c 'import magic' 2>&1 | tail -1)" # The CAUSE, asserted separately from the symptom, so a future change that broke # `import magic' for some OTHER reason is distinguishable from this one. check_eq $P/ctypes-refused \ "ModuleNotFoundError: No module named '_ctypes'" \ "$(python3 -c 'import ctypes' 2>&1 | tail -1)" # --- 6. GAP 2: there is no shared libmagic to load -------------------------- # The second, independent blocker, asserted against the rootfs because it is a # question about files rather than about the interpreter. find_library('magic') # has nothing to answer with here. # # RED WITNESS: a libmagic.so appearing on this rootfs means the package-shared # flip was turned on for `file', which is exactly when this port should be # re-measured. check_eq $P/no-shared-libmagic "" \ "$(ls -d /usr/lib/libmagic.so* 2>/dev/null)" # ...and the static archive IS what the `file' recipe ships, so the absence # above is the distro's static policy rather than a missing package. The .a # lives in the -staticdev package and need not be on a runtime image; what must # be here is the tool, which section 7 exercises. check $P/file-binary test -x /usr/bin/file check $P/magic-db test -s /usr/share/misc/magic.mgc # --- 7. libmagic ITSELF WORKS ON THIS ARM ----------------------------------- # The counterfactual, and the case this port most needs made. Every value below # is `file -i' and `file -b' over the same corpus 50-file.test built, and each is # one half of the FileMagic tuple detect_from_filename would return -- see the # header's table. 50-file.test already asserts these ON TARGET; they are # re-asserted here so that THIS suite, read alone, distinguishes "blocked on # FFI" from "libmagic is broken on wasm32". # # The corpus is written from literal bytes rather than pointed at the rootfs, # for 50-file.test's reason: a wasm32-hwjs rootfs contains no ELF at all, so a # test classifying an image binary would be asserting something about # HardwareJS packaging instead of about libmagic. printf '\177ELF\001\001\001\000\000\000\000\000\000\000\000\000\001\000\003\000\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\064\000\000\000\000\000\050\000\000\000\000\000' > "$W/elf.o" printf 'plain ascii text here\n' > "$W/ascii.txt" printf 'na\303\257ve caf\303\251 \342\200\224 \317\200\n' > "$W/utf8.txt" : > "$W/empty" # The gzip stream is a fixture rather than generated on target: its trailer # carries the original size and its header the compression level and OS byte, # all of which appear in the answer, so generating it here would make the # expected string depend on the guest's gzip rather than on libmagic. gz=$BST_DATA/file-sample.gz check $P/fixture-gz test -s "$gz" # The mime_type and encoding halves, as one string per fixture -- `file -b -i' # is exactly what magic.py splits on '; '. check_eq $P/mime-elf "application/x-object; charset=binary" "$(file -b -i "$W/elf.o")" check_eq $P/mime-gzip "application/gzip; charset=binary" "$(file -b -i "$gz")" check_eq $P/mime-utf8 "text/plain; charset=utf-8" "$(file -b -i "$W/utf8.txt")" check_eq $P/mime-ascii "text/plain; charset=us-ascii" "$(file -b -i "$W/ascii.txt")" check_eq $P/mime-empty "inode/x-empty; charset=binary" "$(file -b -i "$W/empty")" # The `name' half -- the MAGIC_NONE handle's answer. check_eq $P/name-elf "ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV)" "$(file -b "$W/elf.o")" check_eq $P/name-gzip "gzip compressed data, max compression, from Unix, original size modulo 2^32 43" "$(file -b "$gz")" check_eq $P/name-utf8 "Unicode text, UTF-8 text" "$(file -b "$W/utf8.txt")" check_eq $P/name-ascii "ASCII text" "$(file -b "$W/ascii.txt")" check_eq $P/name-empty "empty" "$(file -b "$W/empty")" # The ticket also names a PNG header. It is not in 50-file.test's corpus, so it # is built here and asserted through the same two calls -- an 8-byte signature # plus a 13-byte IHDR for a 1x1 8-bit RGB image, which is all libmagic reads. printf '\211PNG\r\n\032\n\000\000\000\015IHDR\000\000\000\001\000\000\000\001\010\002\000\000\000\220wS\336' > "$W/one.png" check_eq $P/mime-png "image/png; charset=binary" "$(file -b -i "$W/one.png")" check_eq $P/name-png "PNG image data, 1 x 1, 8-bit/color RGB, non-interlaced" "$(file -b "$W/one.png")" # CONTENT, NOT EXTENSION -- the property a half-loaded magic DB silently loses, # and the one that would make every value above meaningless if it were broken. cp "$gz" "$W/misnamed.txt" check_eq $P/content-not-suffix "application/gzip; charset=binary" \ "$(file -b -i "$W/misnamed.txt")" # --- 8. the probe ----------------------------------------------------------- if [ ! -f "$PROBE" ]; then bst_notok $P/probe-present "$PROBE did not ship" rm -rf "$W" exit 0 fi bst_ok $P/probe-present bst_probe $P/probe-run "$W/probe" 900 python3 "$PROBE" OUT="$W/probe" pv() { grep "^$1=" "$OUT" 2>/dev/null | head -n 1 | cut -d= -f2-; } if [ -s "$OUT" ]; then bst_ok $P/probe-output else bst_notok $P/probe-output "the probe emitted nothing -- python3 did not start, or died before its first line; every CASE below is unscorable" fi check_eq $P/probe-complete "yes" "$(pv probe_done)" check_eq $P/probe-count "42" "$(pv emitted_count)" # The probe read the SAME copy this suite has been asserting against. Without # this case every measurement below is a statement about whatever was first on # sys.path. check_eq $P/probe-site-dir "$SITE" "$(pv site_dir)" check_eq $P/probe-module-found "yes" "$(pv module_found)" check_eq $P/probe-module-sha256 \ "60d34c0fa8652ed26096a07c91e44811ac93249a003a2048b2f2cc054b91041d" \ "$(pv module_sha256)" check_eq $P/probe-module-bytes "10660" "$(pv module_bytes)" check_eq $P/probe-module-lines "361" "$(pv module_lines)" # ------------------------------------------- the boundary, from inside python -- # RED WITNESSES, all four. The first repeats the shell's answer from inside an # interpreter that has already started; the rest are the CAUSE, from the # interpreter's own side rather than by inference. check_eq $P/probe-magic-import \ "ModuleNotFoundError: No module named '_ctypes'" "$(pv magic_import)" check_eq $P/probe-ctypes-import \ "ModuleNotFoundError: No module named '_ctypes'" "$(pv ctypes_import)" check_eq $P/probe-ctypes-util \ "ModuleNotFoundError: No module named '_ctypes'" "$(pv ctypes_util_import)" # _ctypes is not a builtin either. On an arm where every extension module is # compiled into the one static binary, "builtin" is where a working _ctypes # WOULD appear -- so this is how a build that quietly re-enabled it becomes # visible from this suite rather than only from 99-python3.test. check_eq $P/probe-ctypes-not-builtin "absent" "$(pv ctypes_builtin)" # ...while the pure-Python ctypes package IS installed, which is what makes the # python3-ctypes dependency satisfiable. See section 4. check_eq $P/probe-ctypes-pkg-present "yes" "$(pv ctypes_pkg_present)" # EVERY OTHER MODULE magic.py IMPORTS IS PRESENT. A red here would mean the # image is missing more than ctypes and that this suite is understating the gap. for _m in threading collections os sys; do check_eq $P/stdlib-$_m "ok" "$(pv stdlib_$_m)" done # ------------------------------------------------ the shipped API, by source -- # What landed really is file-magic 0.4.1's API and not some other module named # magic.py. These are parsed, not executed -- see the probe's header for why # there is no alternative on this image. check_eq $P/probe-ast-parse "ok" "$(pv ast_parse)" # `import ctypes' IS THE FIRST IMPORT, which is why nothing in this module is # reachable: it fails before any other statement runs. check_eq $P/ast-ctypes-line "7" "$(pv ast_import_ctypes_line)" check_eq $P/ast-threading-line "8" "$(pv ast_import_threading_line)" check_eq $P/ast-imports "collections,ctypes,ctypes.util,threading" "$(pv ast_imports)" # THE LOAD AT MODULE SCOPE, at line 24 -- the single fact that says this module # cannot be partially used. It is not a lazy loader and there is no import-time # guard around it, so closing GAP 1 alone just moves the failure here. check_eq $P/ast-module-scope-init "yes" "$(pv ast_module_scope_init)" check_eq $P/ast-module-scope-init-line "24" "$(pv ast_module_scope_init_line)" # ...and the library it asks for by name, which is what ties this port to the # `file' recipe rather than to some other provider of a libmagic. check_eq $P/ast-find-library-arg "magic" "$(pv ast_find_library_arg)" check_eq $P/ast-funcs \ "_create_filemagic,_detect_make,_init,detect_from_content,detect_from_filename,detect_from_fobj,open" \ "$(pv ast_funcs)" check_eq $P/ast-classes "Magic,MagicDetect,error,magic_set" "$(pv ast_classes)" check_eq $P/ast-detect-funcs \ "detect_from_content,detect_from_filename,detect_from_fobj" \ "$(pv ast_detect_funcs)" check_eq $P/ast-magic-methods \ "buffer,check,close,compile,descriptor,errno,error,file,getparam,list,load,setflags,setparam" \ "$(pv ast_magic_methods)" # THE FOURTEEN libmagic ENTRY POINTS THE MODULE BINDS. This is the C ABI # surface that has to exist once ctypes works -- and every one of them is # already in the libmagic this image builds, which is why the port waits on FFI # and on the shared-library flip, and not on the `file' recipe. check_eq $P/ast-libmagic-symbols \ "magic_buffer,magic_check,magic_close,magic_compile,magic_descriptor,magic_errno,magic_error,magic_file,magic_getparam,magic_list,magic_load,magic_open,magic_setflags,magic_setparam" \ "$(pv ast_libmagic_symbols)" check_eq $P/ast-libmagic-symbol-count "14" "$(pv ast_libmagic_symbol_count)" # FileMagic's FIELD ORDER. All three detect_from_* helpers return this # namedtuple, and the order is NOT the order _create_filemagic passes its # keyword arguments in -- a caller unpacking positionally gets # (mime_type, encoding, name). check_eq $P/ast-filemagic-fields "mime_type,encoding,name" "$(pv ast_filemagic_fields)" # The flag constants, as literals. check_eq $P/const-none "0" "$(pv const_MAGIC_NONE)" check_eq $P/const-mime-type "16" "$(pv const_MAGIC_MIME_TYPE)" check_eq $P/const-mime-encoding "1024" "$(pv const_MAGIC_MIME_ENCODING)" check_eq $P/const-mime "1040" "$(pv const_MAGIC_MIME)" check_eq $P/const-continue "32" "$(pv const_MAGIC_CONTINUE)" check_eq $P/const-error "512" "$(pv const_MAGIC_ERROR)" check_eq $P/const-raw "256" "$(pv const_MAGIC_RAW)" check_eq $P/const-compress "4" "$(pv const_MAGIC_COMPRESS)" check_eq $P/const-symlink "2" "$(pv const_MAGIC_SYMLINK)" check_eq $P/const-devices "8" "$(pv const_MAGIC_DEVICES)" # MAGIC_MIME really is MIME_TYPE|MIME_ENCODING, computed by the probe rather # than restated here. It is the flag detect_from_* opens with, and the reason # one call yields both halves of the answer. check_eq $P/const-mime-composite "yes" "$(pv const_mime_is_composite)" rm -rf "$W"