. "$BST_LIB" # BST-TIMEOUT: 300 # # 88 cases on a green run (91 ids; the three `require` ids emit only when a tool # is absent). The four arms that can reach the USB layer -- the three mtp-* # tools and the probe -- are individually bounded at 60 s by bst_probe, so the # suite's own bound only has to cover the sum. 300 is comfortably above that and # well under the default's usefulness as a hang detector. # # libmtp 1.1.21 on wasm32-hwjs glibc -- the Media Transfer Protocol INITIATOR # library: the host side of the protocol phones and media players speak over # USB (meta-openembedded/meta-oe, recipes-connectivity/libmtp; one meta-brintos # bbappend, one LDFLAGS line). # # --------------------------------------------------------------------------- # THIS MACHINE HAS NO USB BUS, AND THE PORT SPLITS ALONG THAT LINE. # # The kernel HAS a USB core -- usb, hub, usbfs and ftdi_sio are registered -- # but there is no host controller, so /sys/bus/usb/devices is empty and /dev/bus # does not exist at all (measured under BRINTOS-1338, re-asserted below as this # suite's positive control). # # So libmtp divides in two: # # the compiled-in tables music-players.h (1417 device entries), the filetype # map and the property map. No bus, no allocation # beyond the library's own. FULLY exercised, from C, # by the probe. # # the device paths LIBMTP_Detect_Raw_Devices() and # LIBMTP_Open_Raw_Device(), and the three tools built # on them. Asserted to REFUSE, bounded, with the # documented diagnostic and exit status. # # --------------------------------------------------------------------------- # WHY THE TOOLS REPORT "Unknown error" RATHER THAN "no devices found", WHICH IS # NOT THE SAME ANSWER AND IS THE ONE THING HERE MOST LIKELY TO BE MISREAD. # # libmtp reaches the bus only through libusb1, which on this image is built with # the udev PACKAGECONFIG cleared -- eudev's do_compile is refused outright by # this toolchain, and nothing here provides udev in any case (BRINTOS-1576, # libusb1_%.bbappend). Without HAVE_LIBUDEV, find_usbfs_path() returns NULL # instead of assuming /dev/bus/usb, so libusb_init() returns LIBUSB_ERROR_OTHER # rather than reporting success over an empty device list (BRINTOS-1338, and # dfu-util reached the same wall under BRINTOS-1889). # # libmtp's init_usb() maps that to LIBMTP_ERROR_USB_LAYER (3), and # LIBMTP_Detect_Raw_Devices() then takes its `else if (ret != LIBMTP_ERROR_NONE)` # arm: it prints a PANIC line and returns 3 WITHOUT writing *devices or *numdevs. # Every mtp-* tool switches on that return and has a case for # NO_DEVICE_ATTACHED, CONNECTING and MEMORY_ALLOCATION but not for USB_LAYER, so # each lands in `default:` -- "Unknown error" / "Unknown connection error" on # stderr, exit 1. On a machine that HAD a bus and no device attached the same # code would print "No raw devices found." and exit 0. # # That is the honest answer for a machine with no usbfs, and it is asserted as # such -- with the three bus probes as its control, so that the day this # platform gains a host controller the refusal cases and the probe cases go red # TOGETHER and say why. # # --------------------------------------------------------------------------- # THE PROBE IS THE ONLY ARM THAT SEES PAST THE FIRST CALL. data/libmtp-probe is # cross-linked during the port against the archive and header this package # installs; src/libmtp_probe.c records how. It reads the device table, both # description maps, and the two device entry points' return contract -- and it # asserts what a FAILED Detect does to its out-parameters, which is nothing. bst_ok libmtp/suite-canary # first line: absorbs brintos/linux#96, which drops it require mtp-detect libmtp/mtp-detect-present require mtp-files libmtp/mtp-files-present require mtp-folders libmtp/mtp-folders-present W=${TMPDIR:-/tmp}/bst-libmtp.$$ rm -rf "$W" if mkdir -p "$W" 2>/dev/null; then bst_ok libmtp/workdir else bst_notok libmtp/workdir "cannot create working directory $W" exit 0 fi nl=' ' # has : ok iff contains the literal . # hasnt : the negative, for a split that must hold. # hasline : ok iff carries as a WHOLE line. has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "want:'$(_bst_1line "$2")' got:'$(_bst_1line "$3")'" ;; esac; } hasnt() { case "$3" in *"$2"*) bst_notok "$1" "present and must not be: $(_bst_1line "$2")" ;; *) bst_ok "$1" ;; esac; } hasline() { case "$nl$3$nl" in *"$nl$2$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "not listed: $2" ;; esac; } # run : run a command, keeping its stdout, stderr and status as # $W/.out, $W/.err and $W/.rc. Always exits 0 itself, so # bst_probe can bound it while the command's own status stays readable -- all # three tools are expected to FAIL here, and bst_probe scores a non-zero child # as a red. cat > "$W/run" <<'EOS' #!/bin/sh tag=$1; shift "$@" >"$W/$tag.out" 2>"$W/$tag.err" printf '%s\n' $? >"$W/$tag.rc" exit 0 EOS chmod +x "$W/run" export W # --- 1. the packaging --------------------------------------------------------- # Six packages are produced and five are installed. The split is asserted # against dpkg's own file lists as well as by stat: a file that reached the # image by some other route satisfies the stat and does not satisfy the claim. inc=/usr/include/libmtp.h pc=/usr/lib/pkgconfig/libmtp.pc lib=/usr/lib/libmtp.a check libmtp/bin-detect test -x /usr/bin/mtp-detect check libmtp/bin-files test -x /usr/bin/mtp-files check libmtp/bin-folders test -x /usr/bin/mtp-folders check libmtp/bin-connect test -x /usr/bin/mtp-connect check libmtp/header test -s "$inc" check libmtp/pc test -s "$pc" check libmtp/archive test -s "$lib" check_eq libmtp/which-detect "/usr/bin/mtp-detect" "$(command -v mtp-detect)" # mtp-getfile and four siblings are symlinks to mtp-connect (examples' # install-exec-hook). A dangling one would still satisfy `test -x` on the # target of a stale link, so the link is followed explicitly. check libmtp/symlink-getfile test -L /usr/bin/mtp-getfile check_eq libmtp/symlink-target "mtp-connect" "$(readlink /usr/bin/mtp-getfile)" check libmtp/symlink-resolves test -x /usr/bin/mtp-getfile # The library ships as an archive only: this distro appends --disable-shared at # class-target scope, so there is no libmtp.so to find. bst_lib_shape is the # distro-wide shape assertion, not a claim that no .so may ever exist. bst_lib_shape libmtp/lib-shape '/usr/lib/libmtp.so*' binlist=$(cat /var/lib/dpkg/info/libmtp-bin.list 2>/dev/null) if [ -n "$binlist" ]; then bst_ok libmtp/pkg-bin-list hasline libmtp/pkg-bin-detect /usr/bin/mtp-detect "$binlist" hasline libmtp/pkg-bin-files /usr/bin/mtp-files "$binlist" hasline libmtp/pkg-bin-folders /usr/bin/mtp-folders "$binlist" hasline libmtp/pkg-bin-getfile /usr/bin/mtp-getfile "$binlist" hasline libmtp/pkg-bin-hotplug /usr/bin/mtp-hotplug "$binlist" # mtp-probe is the udev helper and goes to libmtp-runtime, not to -bin. This # asserts the packaging SPLIT, not what the image installs: -runtime is off # IMAGE_INSTALL (no udevd here -- VIRTUAL-RUNTIME_dev_manager is # busybox-mdev) yet ships anyway, pulled in by upstream's # RRECOMMENDS:${BPN} += "libmtp-runtime ${PN}-bin" on a distro that does not # set NO_RECOMMENDATIONS. Its absence from the -bin list is the split # working rather than a missing program, either way. hasnt libmtp/pkg-bin-no-probe "/lib/udev/mtp-probe" "$binlist" else bst_notok libmtp/pkg-bin-list "no /var/lib/dpkg/info/libmtp-bin.list" fi devlist=$(cat /var/lib/dpkg/info/libmtp-dev.list 2>/dev/null) if [ -n "$devlist" ]; then bst_ok libmtp/pkg-dev-list hasline libmtp/pkg-dev-header "$inc" "$devlist" hasline libmtp/pkg-dev-pc "$pc" "$devlist" hasnt libmtp/pkg-dev-no-archive "/usr/lib/libmtp.a" "$devlist" else bst_notok libmtp/pkg-dev-list "no /var/lib/dpkg/info/libmtp-dev.list" fi stlist=$(cat /var/lib/dpkg/info/libmtp-staticdev.list 2>/dev/null) if [ -n "$stlist" ]; then bst_ok libmtp/pkg-staticdev-list hasline libmtp/pkg-staticdev-archive "$lib" "$stlist" else bst_notok libmtp/pkg-staticdev-list "no /var/lib/dpkg/info/libmtp-staticdev.list" fi # The udev data splits three ways and NONE of it is usable here -- there is no # udevd on this image. It is asserted because the split is what the recipe # claims and a version bump that moved a file between the three would otherwise # be silent: the hwdb goes to the main package, the rules to -common. hasline libmtp/pkg-hwdb /lib/udev/hwdb.d/69-libmtp.hwdb \ "$(cat /var/lib/dpkg/info/libmtp.list 2>/dev/null)" hasline libmtp/pkg-rules /lib/udev/rules.d/69-libmtp.rules \ "$(cat /var/lib/dpkg/info/libmtp-common.list 2>/dev/null)" # --- 2. the installed header and .pc ------------------------------------------ # One batched grep for every needle, then fork-free matching against its result. hdr=$(cat "$inc" 2>/dev/null) has libmtp/hdr-version '#define LIBMTP_VERSION_STRING "1.1.21"' "$hdr" has libmtp/hdr-detect 'LIBMTP_error_number_t LIBMTP_Detect_Raw_Devices(LIBMTP_raw_device_t **, int *);' "$hdr" has libmtp/hdr-open 'LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *);' "$hdr" has libmtp/hdr-init 'void LIBMTP_Init(void);' "$hdr" has libmtp/hdr-devlist 'int LIBMTP_Get_Supported_Devices_List(LIBMTP_device_entry_t ** const, int * const);' "$hdr" has libmtp/hdr-errenum 'LIBMTP_ERROR_NO_DEVICE_ATTACHED' "$hdr" pct=$(cat "$pc" 2>/dev/null) has libmtp/pc-version 'Version: 1.1.21' "$pct" has libmtp/pc-libs 'Libs: -L${libdir} -lmtp' "$pct" has libmtp/pc-libdir 'libdir=/usr/lib' "$pct" # Requires.private names libusb-1.0 -- the dependency a static consumer must # resolve itself, and the reason this port needed an LDFLAGS line at all. has libmtp/pc-requires 'Requires.private: libusb-1.0' "$pct" # --- 3. what is IN the archive ------------------------------------------------ # libmtp.a is an ar archive of LLVM IR BITCODE, so its string table carries # identifiers -- symbol names and each translation unit's source path -- but not # C string literals, which bitcode stores as encoded constant arrays. Symbols # and source paths are therefore assertable and message text is not. # # `grep -aowFf`: -F because a name is a literal, -f for ONE batched pass instead # of a fork per symbol, and -w because without it a name that is a PREFIX of # another reads as found. -a is load-bearing: the archive holds NUL bytes, so # without it grep reports the file as binary and prints no match at all -- which # reads as "symbol absent" on the two positive cases and as a vacuous pass on # the negative one. nd=$W/needles printf '%s\n' \ LIBMTP_Init \ LIBMTP_Detect_Raw_Devices \ LIBMTP_Open_Raw_Device \ LIBMTP_Open_Raw_Device_Uncached \ LIBMTP_Release_Device \ LIBMTP_Get_Supported_Devices_List \ LIBMTP_Get_Filetype_Description \ LIBMTP_Get_Property_Description \ LIBMTP_Get_Storage \ LIBMTP_Get_Folder_List \ LIBMTP_Get_Filelisting_With_Callback \ > "$nd" found=$(grep -aowFf "$nd" "$lib" 2>/dev/null | sort -u | tr '\n' ' ') want=11 got=$(printf '%s' "$found" | wc -w) check_eq libmtp/archive-symbols "$want" "$got" # The GLUE THAT WAS COMPILED, which is a stronger statement about the port than # any symbol match: libmtp ships two USB back-ends and configure picks one. has libmtp/archive-libusb1-glue 'libusb1-glue.c' "$(grep -aowF 'libusb1-glue.c' "$lib" 2>/dev/null | head -n 1)" hasnt libmtp/archive-no-openusb 'libopenusb1-glue.c' "$(grep -aowF 'libopenusb1-glue.c' "$lib" 2>/dev/null | head -n 1)" # --- 4. the bus, and the three tools without one ------------------------------ # The three probes are this section's control. They are stated as assertions, # not skips: if any of them flips, every refusal case below is no longer the # right answer and must go red beside it. check libmtp/bus-core-present test -d /sys/bus/usb check libmtp/bus-no-devices sh -c '[ -z "$(ls -A /sys/bus/usb/devices 2>/dev/null)" ]' check libmtp/bus-no-usbfs sh -c '[ ! -e /dev/bus ]' # BOUNDED, because a tool that blocked on a bus would otherwise take the whole # suite down with it and the runner would report every case above as a pass. # bst_probe scores the WRAPPER, which always exits 0; each tool's own status is # read from its .rc file afterwards. bst_probe libmtp/detect-bounded "$W/det.probe" 60 "$W/run" det mtp-detect det_rc=$(cat "$W/det.rc" 2>/dev/null) det_out=$(cat "$W/det.out" 2>/dev/null) det_err=$(cat "$W/det.err" 2>/dev/null) check_eq libmtp/detect-status "1" "$det_rc" has libmtp/detect-banner "libmtp version: 1.1.21" "$det_out" has libmtp/detect-listing "Listing raw device(s)" "$det_out" has libmtp/detect-libusb "Libusb1 init failed" "$det_err" # The PANIC line carries the numeric error code; 3 is LIBMTP_ERROR_USB_LAYER. # Its trailing "on line N" is deliberately NOT asserted -- it is __LINE__ in # libusb1-glue.c and would break on any upstream edit above it. has libmtp/detect-panic "LIBMTP PANIC: get_mtp_usb_device_list() error code: 3" "$det_err" has libmtp/detect-refuses "Unknown connection error." "$det_err" # And the negative that carries the whole distinction: this is NOT the # empty-bus answer. That path prints this line and exits 0. hasnt libmtp/detect-not-empty-bus "No raw devices found." "$det_out" bst_probe libmtp/files-bounded "$W/fil.probe" 60 "$W/run" fil mtp-files fil_rc=$(cat "$W/fil.rc" 2>/dev/null) fil_out=$(cat "$W/fil.out" 2>/dev/null) fil_err=$(cat "$W/fil.err" 2>/dev/null) check_eq libmtp/files-status "1" "$fil_rc" has libmtp/files-banner "libmtp version: 1.1.21" "$fil_out" has libmtp/files-libusb "Libusb1 init failed" "$fil_err" has libmtp/files-refuses "mtp-files: Unknown error" "$fil_err" hasnt libmtp/files-not-empty-bus "mtp-files: No Devices have been found" "$fil_out" bst_probe libmtp/folders-bounded "$W/fol.probe" 60 "$W/run" fol mtp-folders fol_rc=$(cat "$W/fol.rc" 2>/dev/null) fol_out=$(cat "$W/fol.out" 2>/dev/null) fol_err=$(cat "$W/fol.err" 2>/dev/null) check_eq libmtp/folders-status "1" "$fol_rc" # folders.c prints no version banner -- it announces the attempt instead. has libmtp/folders-announce "Attempting to connect device(s)" "$fol_out" has libmtp/folders-libusb "Libusb1 init failed" "$fol_err" has libmtp/folders-refuses "mtp-folders: Unknown error" "$fol_err" hasnt libmtp/folders-not-empty-bus "mtp-folders: no devices found" "$fol_out" # All three reach the SAME wall, and saying so is what makes the diagnosis one # fact about the platform rather than three about the tools. check_eq libmtp/refusal-is-uniform "$det_rc$fil_rc$fol_rc" "111" # --- 5. the probe ------------------------------------------------------------- # Installed 0644 into a read-only rootfs, so it is staged to tmpfs and the # executable bit set there. NEVER `test -x "$BST_DATA/libmtp-probe"` -- that # passes against a hand-placed copy and fails against every image that ever # built the package. check libmtp/probe-shipped test -f "$BST_DATA/libmtp-probe" probe=$W/libmtp-probe if cp "$BST_DATA/libmtp-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then bst_ok libmtp/probe-stage bst_probe libmtp/probe-runs "$W/probe.out" 60 "$probe" p=$(cat "$W/probe.out" 2>/dev/null) has libmtp/probe-version "version=1.1.21" "$p" has libmtp/probe-init "init=returned" "$p" has libmtp/probe-done "probe=done" "$p" # The compiled-in device table: 1417 entries in music-players.h at this # version. The COUNT is asserted as a floor rather than an equality -- it # grows with every upstream release and a floor still catches a table that # failed to link -- while the first and last entries pin the array's # identity and order. has libmtp/probe-devlist-rc "devlist_rc=0" "$p" has libmtp/probe-devlist-ptr "devlist_entries=nonnull" "$p" has libmtp/probe-devlist-vendor "devlist_first_vendor=Creative" "$p" has libmtp/probe-devlist-first "devlist_first_vid=041e" "$p" has libmtp/probe-devlist-last "devlist_last_vid=46f4" "$p" cnt=$(printf '%s\n' "$p" | sed -n 's/^devlist_count=//p') case "$cnt" in ''|*[!0-9]*) bst_notok libmtp/probe-devlist-count "no numeric devlist_count in probe output" ;; *) if [ "$cnt" -ge 1000 ]; then bst_ok libmtp/probe-devlist-count else bst_notok libmtp/probe-devlist-count "device table has only $cnt entries"; fi ;; esac # The two description maps, built by LIBMTP_Init and read without a bus. has libmtp/probe-filetype-mp3 "filetype_mp3=ISO MPEG-1 Audio Layer 3" "$p" has libmtp/probe-filetype-folder "filetype_folder=Folder" "$p" has libmtp/probe-filetype-unknown "filetype_unknown=Undefined filetype" "$p" has libmtp/probe-property-name "property_name=Name" "$p" # The device paths, from C. USB_LAYER is 3. has libmtp/probe-detect-rc "detect_rc=3" "$p" has libmtp/probe-detect-name "detect_rc_name=USB_LAYER" "$p" # THE OUT-PARAMETERS ARE NOT WRITTEN on this arm, and that is the whole # difference between "no devices" and "never answered": the # NO_DEVICE_ATTACHED path sets *devices=NULL and *numdevs=0, this one # returns without touching either. A caller that trusted them would read # uninitialised stack. has libmtp/probe-detect-devices "detect_devices=untouched" "$p" has libmtp/probe-detect-numdevs "detect_numdevs=untouched" "$p" # Open on a zeroed raw device is rejected rather than crashing: the library # calls init_usb() first and returns its error. (NULL is not passed -- # upstream dereferences the argument before validating it.) has libmtp/probe-open-rejected "open_zeroed=null" "$p" # And the negative that keeps the two answers apart, exactly as for the # tools: a machine with a bus and no device would report NO_DEVICE_ATTACHED. hasnt libmtp/probe-not-empty-bus "detect_rc_name=NO_DEVICE_ATTACHED" "$p" else bst_notok libmtp/probe-stage "cannot stage $BST_DATA/libmtp-probe" fi rm -rf "$W"