# BST-TIMEOUT: 300 # MEASURED: elapsed 49 s for 73 cases at a 9628 MiB peak # # (2026-08-19, local functional verify, otherwise idle # # host). The DEFAULT bound is 60, which leaves 18% headroom # # -- not enough for a loaded host, and a `suite-timeout` # # here would read as libdbi hanging on a directory read. # # The ticket gates and the nightly pass 300 explicitly, so # # this marker is what a developer running the suite bare # # gets. Most of the 49 s is the probe: a 3 MB wasm module # # instantiated once, plus the grep over libdbi.a. . "$BST_LIB" # libdbi 0.9.0 -- the database-independent abstraction layer (BRINTOS-970), # meta-oe recipes-dbs. What ships is /usr/lib/libdbi.a, the three headers under # /usr/include/dbi, /usr/lib/pkgconfig/dbi.pc, the empty driver directory # /usr/lib/dbd, and /usr/libexec/libdbi-tests/libdbi-probe. # # --------------------------------------------------------------------------- # THIS IS A DRIVER MANAGER WITH NO DRIVERS, AND THE SUITE IS BUILT AROUND THAT # RATHER THAN IN SPITE OF IT. # # libdbi is not a database and has no database code in it: it is an API plus a # loader that pulls a per-database driver out of one directory at run time. # Every driver is a shared object, -shared does not link on this arm # (brintos.io/brintos/hwjs-cc/issues/59), and libdbi-drivers is not packaged # for it -- so there is no driver here and there will not be one until that # clears. That is not a gap in the port; it is the state the ticket names. # # What a driver-free image CAN establish is sharper than a successful connect # would be, and it is three things: # # 1. THE FRAMEWORK INITIALISES AND REPORTS ZERO. dbi_initialize() answers # the driver count, and -1 on failure -- so "no drivers" and "libdbi is # broken" are two different answers and the suite pins both. The 0 comes # from the shipped /usr/lib/dbd; the -1 from a directory that is not # there. Without the directory the library ships uninitialisable, which is # what the port's bbappend fixes and what the init-missing case below # would otherwise be measuring by accident. # # 2. THE REFUSALS RETURN THEIR DOCUMENTED CODES. dbi_conn_new() answers NULL # when no driver of that name is loaded, which is the handle a caller on # this image actually holds -- so every option, capability and # error-reporting entry point is asserted against that NULL. The point is # not the individual return values but that a caller can keep going after # a failed open instead of crashing. # # 3. THE VALUE-CONVERSION HELPERS ARE CORRECT. These are libdbi's own, # exported in dbd.h for drivers to call -- quoting, the datetime parser, # the binary encoder/decoder. A driver would be the only caller and there # is none, so they are driven directly on fixtures. Every fixture value # was taken from a NATIVE build of the same 0.9.0 source on the build # host, not from reading the code, so a mismatch here is this arm # disagreeing with x86-64 rather than the suite disagreeing with itself. # # ONE ENTRY POINT IS DELIBERATELY NEVER CALLED. dbi_conn_error() -- the one # error-reporting function a caller reaches after a failed open -- dereferences # its Conn argument with no NULL guard (src/dbi_main.c reads conn->full_errmsg # before any check), so calling it on the NULL that dbi_conn_new() just # returned segfaults. That is upstream's bug on every platform, not this arm's, # and it is why the reporting coverage below goes through dbi_conn_set_error() # and the driver-side getters instead. A case that crashed the probe would take # the rest of the file's cases with it. # # NO CASE HERE IS EXPECTED TO BE RED. The platform limit -- no loadable module # -- is asserted as the behaviour it produces, not left as a failure: a # driverless enumeration is `n=0` and a green case, and the bogus-module case # below asserts that a file with the driver extension is REFUSED rather than # loaded. If a red appears it is a regression, not the known gap. # # THE PROBE SHIPS EXECUTABLE, unlike the data/ payloads most suites drive. It # is built by libdbi's own bbappend against the archive that build produced and # packaged as libdbi-tests under ${libexecdir}, so it is already 0755 and needs # no staging copy onto tmpfs -- and it cannot go stale against a library that # moved under it. PROBE=/usr/libexec/libdbi-tests/libdbi-probe # --- the package is whole ---------------------------------------------------- check libdbi/staticlib test -s /usr/lib/libdbi.a check libdbi/header-dbi test -s /usr/include/dbi/dbi.h check libdbi/header-dbd test -s /usr/include/dbi/dbd.h check libdbi/header-dbi-dev test -s /usr/include/dbi/dbi-dev.h check libdbi/pkgconfig test -s /usr/lib/pkgconfig/dbi.pc # A real ar archive carrying the driver-manager entry points. An archive that # is present but truncated would pass the size check above and fail every # consumer. check_eq libdbi/staticlib-ar '!' "$(head -c 7 /usr/lib/libdbi.a)" if grep -q dbi_initialize_r /usr/lib/libdbi.a 2>/dev/null; then bst_ok libdbi/staticlib-symbols else bst_notok libdbi/staticlib-symbols "libdbi.a carries no dbi_initialize_r" fi # THE DRIVER DIRECTORY IS THE PORT'S ONE PACKAGING CHANGE, so it is asserted # from the rootfs as well as through the library. It must be a directory and it # must be empty: a stray file there would be handed to dlopen on every # initialise by every consumer on this image. check libdbi/driverdir test -d /usr/lib/dbd check_eq libdbi/driverdir-empty '' "$(ls -A /usr/lib/dbd 2>&1)" # dbi.pc is what a consumer's build reads. The version is asserted because a # .pc that lost its Version: field configures consumers that then link against # an ABI they never checked. check_out libdbi/pc-version 'Version: 0.9.0' cat /usr/lib/pkgconfig/dbi.pc check_out libdbi/pc-libs '-ldbi' cat /usr/lib/pkgconfig/dbi.pc # --- run the probe ----------------------------------------------------------- check libdbi/probe-shipped test -s "$PROBE" if [ -x "$PROBE" ]; then out=$("$PROBE" 2>&1); prc=$? bst_ok libdbi/probe-exec else out=''; prc=127 bst_notok libdbi/probe-exec "$PROBE is not executable" fi # The probe's exit status counts ITS OWN failures -- a directory it could not # create, a file it could not write -- and not libdbi's answers, which are # scored case by case below. So 0 here means the probe ran to the end and # everything after this line is measuring the library rather than the harness. check_eq libdbi/probe-rc 0 "$prc" check_eq libdbi/probe-internal-failures 'LD-internal-failures 0' \ "$(printf '%s\n' "$out" | grep '^LD-internal-failures ')" # A helper for the many `LD- ` lines. Kept to one awk per case # rather than one per field: the guest is slow and the fork budget is what # decides whether this file finishes inside its bound. ld() { printf '%s\n' "$out" | sed -n "s/^LD-$1 //p"; } # --- identity ---------------------------------------------------------------- check_eq libdbi/version 'libdbi v0.9.0' "$(ld version)" check_eq libdbi/version-numeric '900' "$(ld version-numeric)" # --- the framework initialises against the SHIPPED directory ----------------- # rc=0 is the whole point of the bbappend: the framework came up and found no # driver. inst=nonnull says the instance handle was allocated -- the two are # separate because dbi_initialize_r() allocates the instance BEFORE it opens # the directory, so a -1 run also hands back a non-NULL instance (which it then # leaks; upstream's bug, recorded in the port's design doc and not asserted # here because a leak is not observable from the API). check_eq libdbi/init-default 'rc=0 inst=nonnull' "$(ld init-default)" # Zero drivers, asserted as a value rather than inferred from an absence. check_eq libdbi/drivers-none 'n=0' "$(ld drivers)" # With no driver loaded, opening one by name and making a connection from it # both answer NULL. These are the two calls a real consumer makes first. check_eq libdbi/driver-open-unknown 'null' "$(ld driver-open-unknown)" check_eq libdbi/conn-new-unknown 'null' "$(ld conn-new-unknown)" # Verbosity is per-instance and returns the PREVIOUS value; 1 is what # dbi_initialize_r() sets. The pair is what makes it a read-back rather than a # write nobody checked. check_eq libdbi/verbosity-default '1' "$(ld verbosity-prev)" check_eq libdbi/verbosity-readback '0' "$(ld verbosity-readback)" # The instance tears down without taking the process with it. The probe's rc=0 # above is what carries this; the case names it so the report says so. check_eq libdbi/shutdown 'ok' "$(ld shutdown)" # --- the three initialise outcomes, told apart ------------------------------- # A directory that is not there is -1. This is the answer the whole image gave # before the port shipped /usr/lib/dbd, and it is asserted here so the 0 above # cannot be mistaken for a default. check_eq libdbi/init-missing 'rc=-1' "$(ld init-missing)" # One that is there and empty is 0, from a path the suite created itself -- # which is what says the 0 above came from the directory and not from # somewhere else in the library. check_eq libdbi/init-empty 'rc=0' "$(ld init-empty)" # THE BOGUS-MODULE CASE IS THE PLATFORM ASSERTION. The directory now holds a # regular file whose name ends in the driver extension and whose contents are # not a shared object. libdbi stats it, matches the extension, hands it to # dlopen and prints dlerror() to stderr on failure (_get_driver). Getting rc=0 # here therefore means the file was SEEN and REFUSED, not skipped -- which is # the difference between "this arm loads no module" and "there was nothing to # load". A future arm that could load modules would still answer 0 for this # file (it is not a valid module) but would say something different about why. check_eq libdbi/init-bogus-module 'rc=0' "$(ld init-bogus-module)" # The loader must SAY something. An empty diagnostic would mean the refusal # went unreported, which is the one outcome a caller could not act on. if printf '%s\n' "$out" | grep -q 'Failed to load driver'; then bst_ok libdbi/bogus-module-reported else bst_notok libdbi/bogus-module-reported \ "no loader diagnostic between the begin/end markers: $(_bst_1line "$out")" fi # --- the connection that cannot be opened ------------------------------------ # dbi_conn_new() answered NULL above, so this is the handle a caller is holding. # Each of these guards NULL and has a documented answer. check_eq libdbi/null-conn-open 'null' "$(ld null-conn-open)" check_eq libdbi/null-set-option 'rc=-1' "$(ld null-set-option)" check_eq libdbi/null-set-option-numeric 'rc=-1' "$(ld null-set-option-numeric)" check_eq libdbi/null-get-option 'null' "$(ld null-get-option)" check_eq libdbi/null-require-option 'null' "$(ld null-require-option)" check_eq libdbi/null-get-option-numeric 'rc=0' "$(ld null-get-option-numeric)" check_eq libdbi/null-require-option-numeric 'rc=0' "$(ld null-require-option-numeric)" check_eq libdbi/null-get-option-list 'null' "$(ld null-get-option-list)" check_eq libdbi/null-connect 'rc=-1' "$(ld null-connect)" check_eq libdbi/null-cap-get 'rc=0' "$(ld null-cap-get)" check_eq libdbi/null-get-driver 'null' "$(ld null-get-driver)" check_eq libdbi/null-set-error 'rc=0' "$(ld null-set-error)" check_eq libdbi/null-quote-string-copy 'n=0' "$(ld null-quote-string-copy)" check_eq libdbi/null-quote-binary-copy 'n=0' "$(ld null-quote-binary-copy)" check_eq libdbi/null-disjoin 'rc=0' "$(ld null-disjoin)" # The DRIVER-side getters answer differently and the difference matters to a # caller: each returns the literal string "ERROR" (my_ERROR in src/dbi_main.c) # rather than NULL, so code that printed the result of a failed lookup would # print that word rather than crash on a NULL. Asserted exactly, because # "non-empty" would pass for a dangling pointer just as well. check_eq libdbi/null-driver-name '[ERROR]' "$(ld null-driver-name)" check_eq libdbi/null-driver-filename '[ERROR]' "$(ld null-driver-filename)" check_eq libdbi/null-driver-description '[ERROR]' "$(ld null-driver-description)" check_eq libdbi/null-driver-version '[ERROR]' "$(ld null-driver-version)" check_eq libdbi/null-driver-cap 'rc=0' "$(ld null-driver-cap)" check_eq libdbi/null-driver-reserved 'rc=0' "$(ld null-driver-reserved)" check_eq libdbi/null-driver-specific 'null' "$(ld null-driver-specific)" check_eq libdbi/null-driver-instance 'null' "$(ld null-driver-instance)" check_eq libdbi/null-driver-encoding 'null' "$(ld null-driver-encoding-iana)" # --- the quoting helper ------------------------------------------------------ # _dbd_escape_chars is what every driver's quote_string is built on. The # fixture carries all three of the characters it is asked to escape and the # returned length counts the backslashes it added. check_eq libdbi/escape-len 'n=23 out=O\'"'"'Brien said \"hi\"\\n' "$(ld escape)" # Nothing to escape: the helper must copy verbatim and NUL-terminate. This is # the case that would catch an off-by-one in the terminator. check_eq libdbi/escape-noop 'n=5 out=plain' "$(ld escape-noop)" # An embedded NUL: orig_size bounds the walk, not strlen. A helper that stopped # at the NUL would return 3 and quietly truncate every binary column a driver # ever quoted. check_eq libdbi/escape-binary 'n=5' "$(ld escape-binary)" # --- the datetime parser ----------------------------------------------------- # _dbd_parse_datetime ends in timegm(), so every value is UTC and independent # of the target's timezone -- which this image has none of anyway. Fixtures # measured against a native build of the same source. check_eq libdbi/dt-date '1787097600' "$(ld dt-date)" # 2026-08-19 00:00:00Z check_eq libdbi/dt-datetime '1787142896' "$(ld dt-datetime)" # 2026-08-19 12:34:56Z check_eq libdbi/dt-time '45296' "$(ld dt-time)" # 12:34:56 since epoch check_eq libdbi/dt-epoch '0' "$(ld dt-epoch)" # The timezone suffix is applied in the right DIRECTION, which is the half of # this parser most likely to be wrong and is invisible without both signs: # +02:00 subtracts 7200 and -05:00 adds 18000 relative to dt-datetime. check_eq libdbi/dt-tz-plus '1787135696' "$(ld dt-tz-plus)" check_eq libdbi/dt-tz-minus '1787160896' "$(ld dt-tz-minus)" # A NULL input is 0 rather than a crash -- drivers pass whatever the database # handed them. check_eq libdbi/dt-null '0' "$(ld dt-null)" # PAST 2038, WHICH IS AN ARM QUESTION AND NOT A LIBDBI ONE, AND THE THREE WIDTH # CASES BELOW ARE WHY IT IS ASSERTED WITH THEM RATHER THAN ALONE. # # time_t is 8 bytes here and long is 4 -- wasm32 is ILP32 -- so a caller that # stores a _dbd_parse_datetime() result in a long truncates it and every date # past 2038 wraps negative. This suite's own probe made exactly that mistake on # its first target run and dt-2038 came back as -2115947649, which is # 2179019647 - 2^32, while sizeof-time_t read 8 throughout. The parser was # right; the cast printing it was wrong. Asserting the value with the widths # beside it is what tells those two apart: dt-2038 wrong AND sizeof-time_t 4 # means the arm's time_t narrowed, which is a platform change; dt-2038 wrong # with the widths unchanged means whatever printed it truncated. # # The widths are also worth pinning on their own account: 4/8 is what a # consumer's own code has to be written against on this image, and it is the # opposite way round from every 64-bit host, where long is 8 and the mistake is # invisible. check_eq libdbi/dt-2038 '2179019647' "$(ld dt-2038)" check_eq libdbi/sizeof-time_t '8' "$(ld sizeof-time_t)" check_eq libdbi/sizeof-long '4' "$(ld sizeof-long)" check_eq libdbi/sizeof-long-long '8' "$(ld sizeof-long-long)" # --- the binary encoder ------------------------------------------------------ # SQLite's encoder, vendored into libdbi: it exists to make arbitrary bytes # survive a C string, so the assertion that matters is that the output holds no # NUL. The fixture carries a NUL, a 0x01 and a high byte -- the three inputs # the encoder treats specially. check_eq libdbi/bin-encode 'n=6 nul=0' "$(ld bin-encode)" check_eq libdbi/bin-encode-hex '025ffefffd78' "$(ld bin-encode-hex)" # Round trip, asserted on both the length and the bytes: an encoder and decoder # that were wrong in the same way would agree with each other, which is why the # encoded form is pinned byte-for-byte above as well. check_eq libdbi/bin-decode 'n=5 eq=1' "$(ld bin-decode)" # --- the attribute isolator -------------------------------------------------- # _isolate_attrib is how a driver pulls the size flags out of a field's # attribute word, so a wrong answer names a wrong integer width. check_eq libdbi/isolate-size4 '16' "$(ld isolate-size4)" check_eq libdbi/isolate-size8 '32' "$(ld isolate-size8)" check_eq libdbi/isolate-none '0' "$(ld isolate-none)" check_eq libdbi/isolate-decimal '4' "$(ld isolate-decimal)"