# BST-TIMEOUT: 600 # 50 cases. Every invocation costs ~1s measured on target # # (the drivers fail at open today); the bound is headroom # # for the day the platform gaps close and they write for real. . "$BST_LIB" # Tokyo Cabinet 1.4.48 -- Mikio Hirabayashi's DBM, as meta-oe ships it from # recipes-support. A key/value store in five flavours (hash, B+tree, # fixed-length array, table, and the "abstract" facade over them), plus a # utility layer of lists/maps/trees and a codec toolbox. # # --------------------------------------------------------------------------- # WHY THIS SUITE IS SHAPED THE WAY IT IS # # The ticket closes this port on UPSTREAM'S OWN tc*test drivers, which ship in # ${bindir} with the stock recipe. So this file does not re-implement any # assertion -- it drives upstream's drivers and classifies what they print. # # TOKYO CABINET IS AN mmap DATABASE, AND THAT IS THE WHOLE STORY ON THIS ARM. # tchdbopenimpl() does exactly two things before it can touch a database file, # and this platform refuses both: # # 1. tclock() -- a whole-file fcntl(F_SETLKW, F_WRLCK) on the database fd # (tcutil.c:7160, called from tchdb.c:3346 and tcfdb.c:1752). POSIX # advisory locking returns EINVAL here (linux#16), so EVERY DEFAULT OPEN # FAILS before a byte is read. Upstream's own `-nl` flag (HDBONOLCK, and # the BDB/FDB/TDB equivalents) skips the lock file entirely -- every # driver accepts it, which is what makes the second gap reachable. # # 2. mmap(0, xmsiz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0) -- tchdb.c:3449, # and the same shape at tcfdb.c:1829. MAP_SHARED fails here whenever # PROT_WRITE is requested (linux#30, found by the gdbm port), so no # database can be CREATED or WRITTEN on this arm at all. # # THE SUITE THEREFORE ASSERTS BOTH ARMS SEPARATELY, and that pairing is the # most informative thing this port produces: # # /write-locking -> lock error (linux#16 -- never reaches the mmap) # /write-nolock -> mmap error (linux#30 -- the real ceiling) # # Reading the two together says precisely where the platform stands: fix #16 # alone and the first arm turns into the second, not into a green. A suite # running only the default arm would report "lock error" and hide the second, # independent gap entirely. # # THE CASE ORDER IS DELIBERATE AND LOAD-BEARING: codecs run BEFORE the database # arms. Every tc*test driver calls tcsysinfo(), which reads /proc/meminfo and # /proc/cpuinfo -- and reading those traps the KERNEL on this arm ("null # function or function signature mismatch" in seq_read_iter, linux#50), which # has been observed to take the whole guest down mid-run. The codec cases touch # no /proc and cannot trigger it, so putting them first means a guest lost to # linux#50 costs only the database arms, whose result is already known and # attributed. Ordered the other way, one kernel trap silently deletes the # entire green half of this port's evidence -- which is exactly what happened # on the first run of this suite. # # READ AND REMOVE ARE GUARDED BEHIND THEIR WRITE, for the same reason and one # more: with no database created, `tchtest read` is being handed a file that # was never written, and reporting a red for that would blame the platform for # a state this suite created. They are reported `skip` naming the write that # did not happen, and they run for real the moment the write succeeds. # # THE VERDICT IS COMPUTED FROM THIS RUN, NEVER HARD-CODED. Each arm reads the # ecode string the driver itself printed (tcerrmsg(): "lock error", "mmap # error"); a zero exit is reported ok with no reference to any issue. So the # day either gap lands, this file goes green by itself with no edit here -- and # a driver that starts failing for a THIRD reason is reported verbatim rather # than absorbed into one of the two known excuses. # # THE CODEC ARM IS NOT DECORATION. tcucodec drives the library's compression and # encoding paths, which are pure computation over a buffer and touch neither # mmap nor fcntl -- so they are the part of Tokyo Cabinet that DOES work here, # and they exercise both of the recipe's declared dependencies (zlib via # `zlib`, bzip2 via `bzip`). Without them a fully-blocked report would be # indistinguishable from a package that never built. # --------------------------------------------------------------------------- # A root prefix, empty on the target and set only when this suite is validated # NATIVELY under dash before it ships. The runner never sets it, so on the # image these are exactly the absolute paths. R="${BST_TC_ROOT:-}" BIN="$R/usr/bin" ISSUE_LOCK="blocked by brintos.io/brintos/linux/issues/16 (fcntl(F_SETLKW) returns EINVAL -- POSIX advisory locking unsupported). tclock() takes a whole-file F_WRLCK before the database is touched, so the default open cannot succeed; -nl skips it" ISSUE_MMAP="blocked by brintos.io/brintos/linux/issues/30 (mmap(MAP_SHARED) fails whenever PROT_WRITE is requested). A writer maps the database PROT_READ|PROT_WRITE MAP_SHARED at open, so no database can be created or written on this arm" if [ ! -x "$BIN/tchtest" ]; then bst_skip tokyocabinet/present "tokyocabinet not installed on this image" exit 0 fi bst_ok tokyocabinet/present work="${TMPDIR:-/tmp}/bst-tokyocabinet.$$" mkdir -p "$work" 2>/dev/null # --- 1. the package is whole (no subprocesses) -------------------------------- # # All sixteen programmes upstream builds on this arm. A missing one means the # COMMANDFILES override in the bbappend drifted, which would not fail the build # and would not fail any other case here. for p in tcamgr tcamttest tcbmgr tcbmttest tcbtest tcfmgr tcfmttest tcftest \ tchmgr tchmttest tchtest tctmgr tctmttest tcttest tcucodec tcumttest; do check "tokyocabinet/bin-$p" test -x "$BIN/$p" done check tokyocabinet/header test -f "$R/usr/include/tcutil.h" check tokyocabinet/staticlib test -s "$R/usr/lib/libtokyocabinet.a" # The two programmes the toolchain refuses to link (a VLA/alloca address # escaping to an opaque sink in their shared 500-line procmisc -- hwjs-cc#15) # are excluded by name in the bbappend. Their REAPPEARANCE would mean that # override stopped taking effect, which no other case would notice. if [ -e "$BIN/tcutest" ] || [ -e "$BIN/tcatest" ]; then bst_notok tokyocabinet/refused-pair-absent \ "tcutest/tcatest are installed, but hwjs-cc#15 refuses their link -- the COMMANDFILES override is not taking effect" else bst_ok tokyocabinet/refused-pair-absent fi # This distro is static-only for the guest; configure honours --disable-shared # by leaving MYLIBRARYFILES at the archive alone. A .so here would mean that # stopped holding. if [ -e "$R/usr/lib/libtokyocabinet.so" ]; then bst_notok tokyocabinet/no-shared "a shared libtokyocabinet is installed on a static-only distro" else bst_ok tokyocabinet/no-shared fi check_out tokyocabinet/version "1.4.48" "$BIN/tcamgr" version # --- 2. the codec toolbox (first: see the ordering note above) ---------------- # # Pure computation over a buffer: no mmap, no fcntl, no /proc. Each case encodes # a fixed payload and decodes it back, and passes only if the round-trip is # byte-identical to the input -- which is what distinguishes a working codec # from one that silently returns an empty result. src="$work/payload.txt" i=0 : > "$src" while [ "$i" -lt 64 ]; do printf 'tokyocabinet round-trip payload line %s -- 0123456789 ,./;[]<>?:{}| \n' "$i" >> "$src" i=$((i + 1)) done for codec in url base quote mime hex pack tcbs zlib bzip xml cstr ucs; do if "$BIN/tcucodec" "$codec" "$src" > "$work/enc" 2>"$work/err" && "$BIN/tcucodec" "$codec" -d "$work/enc" > "$work/dec" 2>>"$work/err" && cmp -s "$src" "$work/dec"; then bst_ok "tokyocabinet/codec-$codec" else bst_notok "tokyocabinet/codec-$codec" "round-trip differs or failed: $(_bst_1line "$(cat "$work/err" 2>/dev/null)")" fi done # cipher is symmetric: encrypting twice under one key returns the plaintext. if "$BIN/tcucodec" cipher -key brintos "$src" > "$work/enc" 2>/dev/null && "$BIN/tcucodec" cipher -key brintos "$work/enc" > "$work/dec" 2>/dev/null && cmp -s "$src" "$work/dec"; then bst_ok tokyocabinet/codec-cipher else bst_notok tokyocabinet/codec-cipher "symmetric cipher round-trip did not return the plaintext" fi # hash is a pure function of its input, so the same payload must hash the same # way twice -- a cheap guard against an uninitialised-buffer read. _h1=$("$BIN/tcucodec" hash "$src" 2>/dev/null) _h2=$("$BIN/tcucodec" hash "$src" 2>/dev/null) if [ -n "$_h1" ] && [ "$_h1" = "$_h2" ]; then bst_ok tokyocabinet/codec-hash else bst_notok tokyocabinet/codec-hash "hash empty or not reproducible: '$_h1' vs '$_h2'" fi # --- 3. the database drivers -------------------------------------------------- # # tc_db -- run one upstream driver invocation and classify it by # what the driver itself printed. Exactly one result line per call; returns 0 # only when the driver succeeded, which is what gates the read/remove arms. tc_db() { _id=$1; shift _out=$( "$@" 2>&1 ); _rc=$? if [ "$_rc" -eq 0 ]; then bst_ok "tokyocabinet/$_id" return 0 fi case "$_out" in *"lock error"*) bst_notok "tokyocabinet/$_id" "$ISSUE_LOCK -- $(_bst_1line "$_out")" ;; *"mmap error"*) bst_notok "tokyocabinet/$_id" "$ISSUE_MMAP -- $(_bst_1line "$_out")" ;; *) bst_notok "tokyocabinet/$_id" "rc=$_rc $(_bst_1line "$_out")" ;; esac return 1 } # tc_db_after -- only meaningful once the write # arm created a database; otherwise skip, naming the write that did not happen. tc_db_after() { _id=$1; _dep=$2; shift 2 if [ "$_dep" = "ok" ]; then tc_db "$_id" "$@" else bst_skip "tokyocabinet/$_id" "not attempted: tokyocabinet/$_id needs the write arm above, which created no database on this arm" fi } # Hash -- the flavour B+tree and table are built on, so a gap here is a gap in # all three. if tc_db hdb-write-locking "$BIN/tchtest" write "$work/casket.tch" 1000 500 5 5; then _w=ok; else _w=no; fi if tc_db hdb-write-nolock "$BIN/tchtest" write -nl "$work/nl.tch" 1000 500 5 5; then _n=ok; else _n=no; fi tc_db_after hdb-read-nolock "$_n" "$BIN/tchtest" read -nl "$work/nl.tch" tc_db_after hdb-remove-nolock "$_n" "$BIN/tchtest" remove -nl "$work/nl.tch" # B+tree -- ordered keys and cursors, layered on the hash file above. tc_db bdb-write-locking "$BIN/tcbtest" write "$work/casket.tcb" 1000 5 5 500 5 5 if tc_db bdb-write-nolock "$BIN/tcbtest" write -nl "$work/nl.tcb" 1000 5 5 500 5 5; then _n=ok; else _n=no; fi tc_db_after bdb-read-nolock "$_n" "$BIN/tcbtest" read -nl "$work/nl.tcb" # Table -- the column-store flavour, with its own indexes. tc_db tdb-write-locking "$BIN/tcttest" write "$work/casket.tct" 1000 500 5 5 if tc_db tdb-write-nolock "$BIN/tcttest" write -nl "$work/nl.tct" 1000 500 5 5; then _n=ok; else _n=no; fi tc_db_after tdb-read-nolock "$_n" "$BIN/tcttest" read -nl "$work/nl.tct" # Fixed-length array -- the one flavour with its own open path (tcfdb.c), which # repeats both refusals independently of tchdb.c. tc_db fdb-write-locking "$BIN/tcftest" write "$work/casket.tcf" 1000 50 if tc_db fdb-write-nolock "$BIN/tcftest" write -nl "$work/nl.tcf" 1000 50; then _n=ok; else _n=no; fi tc_db_after fdb-read-nolock "$_n" "$BIN/tcftest" read -nl "$work/nl.tcf" # The command-line manager over the same path, so a future fix is confirmed # through the user-facing tool and not only through the test driver. if tc_db hdb-mgr-create "$BIN/tchmgr" create "$work/mgr.tch" 500; then _m=ok; else _m=no; fi tc_db_after hdb-mgr-put "$_m" "$BIN/tchmgr" put "$work/mgr.tch" alpha bravo rm -rf "$work"