. "$BST_LIB" # BST-TIMEOUT: 900 # ~150 cases. All but four are file reads and string # # compares; the four are probe execs, each bounded at 600 s # # by bst_probe itself. The probe does no I/O beyond one # # /tmp file and no network at all. # # libvncserver 0.9.14 on wasm32-hwjs glibc -- the RFB (VNC) protocol library # (meta-openembedded/meta-oe, recipes-graphics/libvncserver; one meta-brintos # bbappend and one patch). # # =========================================================================== # WHY A PROTOCOL LIBRARY CAN BE EXERCISED END TO END WITH NO PEER. # =========================================================================== # # libvncserver and libvncclient are the two halves of the same wire format, and # this image ships both. The framebuffer-update path between them is # ONE-DIRECTIONAL: the server writes a message, the client reads it. So the # whole of the encoder and decoder surface -- seven encodings, the pixel-format # translation table, the cursor and CopyRect, and the server-to-client message # types -- is buffer-in, buffer-out, and needs no socket, no listening port and # no second process. # # data/libvncserver-probe holds both halves in ONE process and hands the # server's output to the client through a REGULAR FILE. That choice is the # probe's own precondition and it is about this arm: a socketpair holding both # halves in one process deadlocks the instant an update exceeds its buffer, and # with no pthread_create here (linux-in-the-browser#85) there is no second # thread to drain it. A file cannot block and has no capacity bound. # # One socketpair IS created, for one call. rfbNewClient() runs webSocketsCheck(), # which recv(MSG_PEEK)s the fd; ENOTSOCK on a regular file is not the EAGAIN it # expects, so it refuses the client outright. The record is therefore built over # a socketpair and its transport swapped to the file immediately afterwards. # # That is why this port closes on its suite and files no follow-on interactive # ticket under BRINTOS-780. # # =========================================================================== # WHAT THIS PORT SHIPS, AND WHY THERE IS NO SHARED LIBRARY. # =========================================================================== # # libvncserver-dev + libvncserver-staticdev. The meta-oe recipe leaves # BUILD_SHARED_LIBS at upstream's `${UNIX}' default of ON, and # conf/distro/brintos.conf's --disable-shared covers autotools only # (BRINTOS-1479), so the bbappend appends OFF. The deliverable is two archives # -- libvncserver.a and libvncclient.a -- the seven public headers under # /usr/include/rfb, the two .pc files and the LibVNCServer cmake package. # # The example and test programs upstream builds are NOT installed: its own # install() names the libraries, the headers, the cmake package and the .pc # files, and no executable. They are still BUILT, deliberately -- see below. # # =========================================================================== # FINDING 1: ONE VLA IN THE WEBSOCKETS HANDSHAKE REFUSED EVERY CONSUMER'S LINK. # =========================================================================== # # webSocketsGenSha1Key() builds the RFC 6455 accept-key input in # `char tmp[strlen(key) + sizeof(GUID) - 1]' and hands its address to # hash_sha1(), which on this image is the libgcrypt shim and so an opaque # cross-module sink. The coro arm refuses the function: # # [hwjs-coro] REFUSE fn=webSocketsGenSha1Key # reason=vla/dynamic-alloca-address-escape-opaque-sink # escaped=1 opaque_escape=1 ptrelem=0 across=0 dynalloca_total=1 # wasmld: FATAL (HWJS-3 coro arm): the coroutinize pass REFUSED function(s) # ... a refused def is a live ABI chimera. Stop-and-report. # # and that is a property of the ARCHIVE MEMBER, not of the websockets path: # nothing has to call it. On the stock recipe all seventeen upstream examples # and tests failed to link in turn, each naming this one function. Had the # examples been off, the archive would have packaged green and refused the first # consumer that ever appeared -- which is the lvgl case (BRINTOS-2016) exactly. # That is why WITH_EXAMPLES and WITH_TESTS stay at upstream's ON: they are the # only consumers the recipe itself builds. # # The coroutinize pass now gives that VLA heap storage. It cannot be asserted # from the archive -- a VLA leaves no member name behind -- so what asserts it # is the probe: the probe IS a consumer, and it links and runs. # # =========================================================================== # FINDING 2: -lgpg-error HAD TO BE NAMED, OR THE IMAGE IMPORTS ELEVEN TRAPS. # =========================================================================== # # CMakeLists.txt finds the crypto backend with a bare # `find_library(LIBGCRYPT_LIBRARIES gcrypt)' -- no pkg-config -- so the link # line carried -lgcrypt alone. On a shared platform libgcrypt.so's DT_NEEDED # closes the gap; on a static-only distro the consumer must name it, and eleven # libgpg-error entry points became env.* imports that trap at first call: # # wasmld: phantom import: env.gpgrt_lock_init (function) # wasmld: phantom import: env.gpg_err_code_from_syserror (function) # wasmld: FATAL (HWJS-3 coro arm): final image imports symbol(s) outside the # sanctioned surface ... TRAP at first call # # The bbappend presets the cache variable instead of patching the link line, # which reaches BOTH consumers: the link, and libvncserver.pc's Libs.private, # where get_link_libraries() writes the same value. Both are asserted below. # This is the cmake spelling of what libotr carries as LIBGCRYPT_LIBS; the # linker is right and nothing upstream is broken, so neither carries a marker. # # =========================================================================== # WHAT THE PROBE COVERS THAT NO FILE CHECK CAN, AND WHERE THE NUMBERS COME FROM. # =========================================================================== # # Four modes, one exec each, so that a fault in one section cannot take the # others with it: # # inventory the build's own feature macros and every wire struct size. RFC # 6143 fixes each of these, and a struct that grew a pad byte would # corrupt every message without failing to compile. # encodings raw, RRE, CoRRE, Hextile, zlib, ZRLE, Tight and Ultra, each # encoded from a 64x48 framebuffer and decoded back by the client # half. The frame is built so that every encoder meets its best # case and its worst in one update: 16 rows of gradient, 16 of flat # colour, 16 of deterministic LCG noise. The eight lossless ones # must come back EXACT -- not "close", exact -- and the ninth case # is the same Tight encoder at JPEG quality 5, which must not. # pixfmt the translation table, at 32/16/8 bpp and both byte orders. This # is not a round trip through the library and back: libvncserver # builds its tables as out = (in * outMax + inMax/2) / inMax, so # the value each channel MUST take is computed in the probe from # first principles and compared. A table that translated # self-consistently but wrongly fails here. # protocol Bell, ServerCutText and SetColourMapEntries constructed by the # server and parsed back by the client; the cursor shape, hotspot # and geometry through the client's own GotCursorShape; and # CopyRect through GotCopyRect, plus the assertion that the # server's framebuffer really moved those pixels. # # EVERY EXPECTED VALUE BELOW WAS MEASURED, on x86-64, against THIS EXACT PROBE # built from THIS libvncserver 0.9.14 source with the same PACKAGECONFIG set # (bar the crypto backend, which is OpenSSL on the reference host and libgcrypt # here -- it is not on any path this suite drives). The pixel counts, the # per-band JPEG errors (gradient 60, flat 15, noise 240 of 255) and the # per-format quantisation losses (0, 0, 4, 4, 42) are readings, not estimates. # # WHERE A BYTE COUNT IS NOT ASSERTED EXACTLY, THAT IS DELIBERATE. raw, RRE, # CoRRE and Hextile are format-deterministic and are pinned to the byte. zlib, # ZRLE, Tight and Ultra are not: their size depends on the zlib and # libjpeg-turbo the image was built against (1.3 and 80 on the reference host, # 1.3.1 and 62 here), so what is asserted is the property that matters -- that # they compressed at all, and by how much at worst. bst_ok libvncserver/suite-canary # first line: absorbs brintos/linux#96, which drops it tmp=${TMPDIR:-/tmp} nl=' ' hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "absent or empty: $2"; fi; } nofile() { _id=$1; shift; if [ ! -e "$1" ]; then bst_ok "$_id"; else bst_notok "$_id" "present: $1"; fi; } has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } hasline() { case "$nl$3$nl" in *"$nl$2$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "not present: $2" ;; esac; } inc=/usr/include/rfb lib=/usr/lib # Which library THIS arm shipped, and which arm it is. cmake builds one kind at # a time, so a converted libvncserver carries no archive and every case below # that named one would go red on a package that is fine. bst_ps_lib prefers the # archive where both shipped, so the static arm reads exactly the bytes it # always read and no case changes its answer there (BRINTOS-1930). srvlib=$(bst_ps_lib "$lib/libvncserver.a" "$lib/libvncserver.so" "$lib/libvncserver.so".*) cltlib=$(bst_ps_lib "$lib/libvncclient.a" "$lib/libvncclient.so" "$lib/libvncclient.so".*) : "${srvlib:=$lib/libvncserver.a}" : "${cltlib:=$lib/libvncclient.a}" # --- the shipped files --------------------------------------------------------- for h in rfb.h rfbclient.h rfbconfig.h rfbproto.h rfbregion.h keysym.h threading.h; do hasfile "libvncserver/file-header-${h%.h}" "$inc/$h" done hasfile libvncserver/file-archive-server "$srvlib" hasfile libvncserver/file-archive-client "$cltlib" hasfile libvncserver/file-pc-server "$lib/pkgconfig/libvncserver.pc" hasfile libvncserver/file-pc-client "$lib/pkgconfig/libvncclient.pc" hasfile libvncserver/file-cmake-config "$lib/cmake/LibVNCServer/LibVNCServerConfig.cmake" hasfile libvncserver/file-cmake-targets "$lib/cmake/LibVNCServer/LibVNCServerTargets.cmake" hasfile libvncserver/file-cmake-version "$lib/cmake/LibVNCServer/LibVNCServerConfigVersion.cmake" # --- what must NOT be here ----------------------------------------------------- # # No shared object: BUILD_SHARED_LIBS=OFF is the port's decision and the thing a # regression would undo first. Upstream's SOVERSION is 1, so the three names a # shared build would produce are all named. for s in libvncserver.so libvncserver.so.1 libvncserver.so.0.9.14 \ libvncclient.so libvncclient.so.1 libvncclient.so.0.9.14; do nofile "libvncserver/no-so-${s}" "$lib/$s" done # No example or test binary. Upstream builds seventeen of them and installs # none; one appearing in ${bindir} would mean the install rules had changed. for b in vncev simple example pnmshow storepasswd regiontest cargstest \ copyrecttest wstest; do nofile "libvncserver/no-binary-$b" "/usr/bin/$b" done # tjbench is the one name of the seventeen that COLLIDES with another package, # and a bare `nofile' on the path is therefore a statement about that package # rather than about this port. Upstream's test/tjbench.c is copied from # libjpeg-turbo, so libvncserver does build a `tjbench' -- but the # /usr/bin/tjbench this image carries is libjpeg-turbo's own, declared by # jpeg-tools (measured: jpeg-tools.list is the only dpkg list naming that path) # and asserted PRESENT on purpose by 57-libjpeg-turbo. What is asserted here is # OWNERSHIP instead: no libvncserver package may declare it. That keeps the # regression this case was written for -- an install rule that started shipping # the test binary -- while saying nothing about a file this port does not own. own= for l in /var/lib/dpkg/info/libvnc*.list; do [ -f "$l" ] || continue case "$nl$(cat "$l" 2>/dev/null)$nl" in *"$nl/usr/bin/tjbench$nl"*) own="$own $l" ;; esac done if [ -z "$own" ]; then bst_ok libvncserver/no-binary-tjbench else bst_notok libvncserver/no-binary-tjbench "declared by:$own"; fi # --- the packaging statement, read from dpkg ----------------------------------- # # Where the files land AGAINST WHAT THE PACKAGING SAYS, not only against the # filesystem: a file present but undeclared, and one declared but absent, are # different defects and only dpkg's own list tells them apart. DEVLIST=/var/lib/dpkg/info/libvncserver-dev.list STLIST=/var/lib/dpkg/info/libvncserver-staticdev.list if [ -f "$DEVLIST" ] && [ -f "$STLIST" ]; then bst_ok libvncserver/dpkg-lists-present devl=$(cat "$DEVLIST" 2>/dev/null) stl=$(cat "$STLIST" 2>/dev/null) hasline libvncserver/dpkg-dev-declares-rfb-h "$inc/rfb.h" "$devl" hasline libvncserver/dpkg-dev-declares-rfbclient-h "$inc/rfbclient.h" "$devl" hasline libvncserver/dpkg-dev-declares-rfbconfig-h "$inc/rfbconfig.h" "$devl" hasline libvncserver/dpkg-dev-declares-pc-server "$lib/pkgconfig/libvncserver.pc" "$devl" hasline libvncserver/dpkg-dev-declares-pc-client "$lib/pkgconfig/libvncclient.pc" "$devl" if bst_ps_converted "$lib/libvncserver.so" "$lib/libvncserver.so".*; then # Converted: no archive exists, so -staticdev is empty and these three cases # -- all of them about WHICH package the archive lands in -- are vacuous # rather than failing. Skipped with a reason rather than dropped: a case id # is a red-case attribution key, and a silently absent case reads as # coverage that was never lost. The trade, stated rather than glossed: on # this arm nothing here notices -staticdev going wrong, because there is # nothing it should contain. bst_skip libvncserver/dpkg-staticdev-declares-server "converted: ships no archive to declare" bst_skip libvncserver/dpkg-staticdev-declares-client "converted: ships no archive to declare" bst_skip libvncserver/dpkg-split-archives-not-in-dev "converted: ships no archive to split" else hasline libvncserver/dpkg-staticdev-declares-server "$lib/libvncserver.a" "$stl" # libvncclient.a is the interesting one. The meta-oe recipe declares # PACKAGE_BEFORE_PN = "libvncclient" with FILES = "${libdir}/libvncclient.*", # which on a SHARED build takes the .so and on a static one would take the # archive -- except that -staticdev precedes ${PACKAGE_BEFORE_PN} in PACKAGES, # so the archive lands in -staticdev with the other. Asserted, because it is # decided by list ORDER and nothing states it. hasline libvncserver/dpkg-staticdev-declares-client "$lib/libvncclient.a" "$stl" case "$nl$devl$nl" in *"$nl$lib/libvncserver.a$nl"*) bst_notok libvncserver/dpkg-split-archives-not-in-dev "libvncserver.a is declared by libvncserver-dev" ;; *) bst_ok libvncserver/dpkg-split-archives-not-in-dev ;; esac fi # every file libvncserver-staticdev declares exists. The list is read from a # TMPFS COPY: a `while ... done < ' at the top level of a suite # makes dup2(2) return 513 and ash exits, taking every case after it with it # (brintos/linux#73), and a `while' cannot be wrapped in a subshell because it # has to keep the variables it sets. STLOCAL=$(bst_local "$STLIST") missing= while IFS= read -r f; do [ -z "$f" ] && continue [ -e "$f" ] || missing="$missing $f" done < "$STLOCAL" if [ -z "$missing" ]; then bst_ok libvncserver/dpkg-staticdev-files-exist else bst_notok libvncserver/dpkg-staticdev-files-exist "declared and absent:$missing"; fi else bst_notok libvncserver/dpkg-lists-present "one of $DEVLIST / $STLIST is absent" for c in dpkg-dev-declares-rfb-h dpkg-dev-declares-rfbclient-h \ dpkg-dev-declares-rfbconfig-h dpkg-dev-declares-pc-server \ dpkg-dev-declares-pc-client dpkg-staticdev-declares-server \ dpkg-staticdev-declares-client dpkg-split-archives-not-in-dev \ dpkg-staticdev-files-exist; do bst_notok "libvncserver/$c" "no dpkg list to read" done fi # --- rfbconfig.h: the PACKAGECONFIG decision, as the build recorded it --------- # # rfbconfig.h is generated by cmake from the options the bbappend settles, so it # is the artifact a PACKAGECONFIG regression moves first. do_install already # refuses the package if these move; asserting them again here is what makes the # ON-TARGET image say it, rather than the build host. cfg=$(cat "$inc/rfbconfig.h" 2>&1) has libvncserver/cfg-version '#define LIBVNCSERVER_VERSION "0.9.14"' "$cfg" has libvncserver/cfg-zlib '#define LIBVNCSERVER_HAVE_LIBZ' "$cfg" has libvncserver/cfg-jpeg '#define LIBVNCSERVER_HAVE_LIBJPEG' "$cfg" has libvncserver/cfg-png '#define LIBVNCSERVER_HAVE_LIBPNG' "$cfg" has libvncserver/cfg-lzo '#define LIBVNCSERVER_HAVE_LZO' "$cfg" has libvncserver/cfg-gcrypt '#define LIBVNCSERVER_HAVE_LIBGCRYPT' "$cfg" has libvncserver/cfg-websockets '#define LIBVNCSERVER_WITH_WEBSOCKETS' "$cfg" has libvncserver/cfg-24bpp '#define LIBVNCSERVER_ALLOW24BPP' "$cfg" has libvncserver/cfg-no-pthread '/* #undef LIBVNCSERVER_HAVE_LIBPTHREAD */' "$cfg" has libvncserver/cfg-no-gnutls '/* #undef LIBVNCSERVER_HAVE_GNUTLS */' "$cfg" has libvncserver/cfg-no-openssl '/* #undef LIBVNCSERVER_HAVE_LIBSSL */' "$cfg" has libvncserver/cfg-no-ipv6 '/* #undef LIBVNCSERVER_IPv6 */' "$cfg" # --- the .pc files, line for line ---------------------------------------------- pcs=$(cat "$lib/pkgconfig/libvncserver.pc" 2>&1) pcc=$(cat "$lib/pkgconfig/libvncclient.pc" 2>&1) hasline libvncserver/pc-server-name 'Name: LibVNCServer' "$pcs" hasline libvncserver/pc-server-version 'Version: 0.9.14' "$pcs" hasline libvncserver/pc-client-name 'Name: LibVNCClient' "$pcc" hasline libvncserver/pc-client-version 'Version: 0.9.14' "$pcc" has libvncserver/pc-server-libs '-lvncserver' "$pcs" has libvncserver/pc-client-libs '-lvncclient' "$pcc" # FINDING 2, asserted on the OTHER side of the fix: the .pc has to name # -lgpg-error too, or the next static consumer of this package repeats the link # failure the bbappend just removed. has libvncserver/pc-server-private-gcrypt '-lgcrypt' "$pcs" has libvncserver/pc-server-private-gpg-error '-lgpg-error' "$pcs" has libvncserver/pc-client-private-gcrypt '-lgcrypt' "$pcc" has libvncserver/pc-client-private-gpg-error '-lgpg-error' "$pcc" if command -v pkg-config >/dev/null 2>&1; then for p in libvncserver libvncclient; do o=$(pkg-config --modversion $p 2>&1); r=$? if [ "$r" -eq 0 ] && [ "$o" = "0.9.14" ]; then bst_ok "libvncserver/pkg-config-modversion-$p" else bst_notok "libvncserver/pkg-config-modversion-$p" "rc=$r out=$o"; fi done o=$(pkg-config --static --libs libvncserver 2>&1) case "$o" in *-lgpg-error*) bst_ok libvncserver/pkg-config-static-libs-gpg-error ;; *) bst_notok libvncserver/pkg-config-static-libs-gpg-error "out=$o" ;; esac else bst_skip libvncserver/pkg-config-modversion-libvncserver "pkg-config not installed on this image" bst_skip libvncserver/pkg-config-modversion-libvncclient "pkg-config not installed on this image" bst_skip libvncserver/pkg-config-static-libs-gpg-error "pkg-config not installed on this image" fi # --- the archives -------------------------------------------------------------- # # Both are LLVM IR bitcode, so the string table carries identifiers and source # paths but not C string literals -- the shape 99-lvgl, 99-libftdi and 99-libusb1 # all record. Symbol names and translation-unit paths are what can be asserted, # and they are the stronger statement anyway. flat=$tmp/libvncserver-flat.$$ ( cat "$srvlib" "$cltlib" 2>/dev/null \ | tr -c '\40-\176\n' '\n' > "$flat" ) hasfile libvncserver/archive-flattened "$flat" # The encoder and decoder translation units, by the debug path each object # records. These are the files the probe drives, named one by one, so that an # encoding silently dropped from the build is a case here rather than a # surprise in the probe section. # # The client's tight.c, zrle.c and ultra.c are deliberately NOT in this list. # They are TEMPLATES: rfbproto.c #includes each one three times with BPP set to # 8, 16 and 32, so no object ever records their path. Measured on this image's # own archives -- 33 translation units, and those three are not among them. tus=$(grep -oE '/usr/src/debug/libvncserver/0\.9\.14/libvnc(server|client)/[A-Za-z0-9_/.-]+\.c' "$flat" 2>/dev/null | sort -u) ntu=$(printf '%s' "$tus" | grep -c .) if [ "$ntu" -ge 30 ]; then bst_ok libvncserver/tu-count else bst_notok libvncserver/tu-count "distinct translation units=$ntu, want >= 30"; fi for t in libvncserver/rfbserver.c libvncserver/translate.c libvncserver/cursor.c \ libvncserver/rre.c libvncserver/corre.c libvncserver/hextile.c \ libvncserver/zlib.c libvncserver/zrle.c libvncserver/tight.c \ libvncserver/ultra.c libvncserver/websockets.c libvncserver/ws_decode.c \ libvncclient/rfbproto.c libvncclient/vncviewer.c libvncclient/cursor.c \ libvncclient/sockets.c libvncclient/listen.c; do hasline "libvncserver/tu-$(echo "$t" | tr / -)" "/usr/src/debug/libvncserver/0.9.14/$t" "$tus" done # COMPLETENESS: every public entry point the probe reaches must be defined in # one of the two archives. -w is load-bearing -- without it a name that is a # strict PREFIX of a real symbol passes vacuously. namef=$tmp/libvncserver-names.$$ printf '%s\n' \ rfbGetScreen rfbNewClient rfbSetTranslateFunction rfbSendFramebufferUpdate \ rfbSendUpdateBuf rfbSendBell rfbSendServerCutText rfbSendSetColourMapEntries \ rfbMakeXCursor rfbSetCursor rfbDoCopyRect rfbDoCopyRegion \ rfbScheduleCopyRegion rfbShowCursor rfbHideCursor \ rfbInitServerWithoutPthreadsButWithZRLE \ rfbProcessEvents rfbShutdownServer rfbRegisterProtocolExtension \ webSocketsCheck \ sraRgnCreate sraRgnCreateRect sraRgnDestroy sraRgnAnd sraRgnOr \ rfbGetClient HandleRFBServerMessage SetFormatAndEncodings \ SendFramebufferUpdateRequest rfbClientCleanup ConnectToRFBServer \ rfbInitClient WaitForMessage \ > "$namef" found=$(grep -owFf "$namef" "$flat" 2>/dev/null | sort -u) ndecl=$(grep -c . "$namef"); nfound=$(printf '%s' "$found" | grep -c .) if [ "$ndecl" -ge 30 ] && [ "$nfound" -eq "$ndecl" ]; then bst_ok libvncserver/sym-complete else bst_notok libvncserver/sym-complete "named=$ndecl found=$nfound"; fi for s in rfbGetScreen rfbNewClient rfbSendFramebufferUpdate rfbSetTranslateFunction \ rfbMakeXCursor rfbDoCopyRect rfbGetClient HandleRFBServerMessage; do hasline "libvncserver/sym-$s" "$s" "$found" done # FINDING 1's own function CANNOT be asserted here and that is worth saying: # webSocketsGenSha1Key is `static', so it carries no external linkage name and # the flattened archive does not contain the string at all (measured -- it is # the one name of thirty-three that is absent). What stands in for it is # webSocketsCheck, its only caller and the entry point of the same file, above; # and what actually asserts the patch is the probe, which is a consumer, since # an unpatched build cannot produce a linked consumer at all. # # `rfbInitServer' is likewise not a symbol: rfb.h #defines it to one of four # mangled names chosen by the pthread and ZRLE build options. The name in the # list above -- rfbInitServerWithoutPthreadsButWithZRLE -- is this arm's answer # to both, so it asserts the threading decision of section 1 from the other end, # in the archive, where a PACKAGECONFIG change would show up as a link failure # for every consumer rather than as a comment. hasline libvncserver/sym-init-server-no-pthreads-with-zrle \ rfbInitServerWithoutPthreadsButWithZRLE \ "$(grep -owF rfbInitServerWithoutPthreadsButWithZRLE "$flat" 2>/dev/null | sort -u)" # --- the probe ----------------------------------------------------------------- hasfile libvncserver/probe-shipped "$BST_DATA/libvncserver-probe" probe=$tmp/bst-libvncserver-probe.$$ if cp "$BST_DATA/libvncserver-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then bst_ok libvncserver/probe-staged staged=1 else bst_notok libvncserver/probe-staged "cannot stage $BST_DATA/libvncserver-probe" staged=0 fi # kv kv() { _g=$(sed -n "s/^$2 //p" "$4" 2>/dev/null | head -n 1) if [ "$_g" = "$3" ]; then bst_ok "libvncserver/$1" else bst_notok "libvncserver/$1" "$2=${_g:-}, measured $3 on x86-64"; fi } # kvle : an upper bound, for a value whose # exact figure depends on the zlib / libjpeg-turbo this image was built with. kvle() { _g=$(sed -n "s/^$2 //p" "$4" 2>/dev/null | head -n 1) case "$_g" in ''|*[!0-9-]*) bst_notok "libvncserver/$1" "$2 absent or not a number: ${_g:-}"; return ;; esac if [ "$_g" -le "$3" ]; then bst_ok "libvncserver/$1" else bst_notok "libvncserver/$1" "$2=$_g, want <= $3"; fi } # kvrange kvrange() { _g=$(sed -n "s/^$2 //p" "$5" 2>/dev/null | head -n 1) case "$_g" in ''|*[!0-9-]*) bst_notok "libvncserver/$1" "$2 absent or not a number: ${_g:-}"; return ;; esac if [ "$_g" -ge "$3" ] && [ "$_g" -le "$4" ]; then bst_ok "libvncserver/$1" else bst_notok "libvncserver/$1" "$2=$_g, want $3..$4"; fi } dead() { for _c in $1; do bst_notok "libvncserver/$_c" "$2"; done; } # ---- mode: inventory ---------------------------------------------------------- INV_CASES="inv-version inv-zlib inv-jpeg inv-png inv-lzo inv-gcrypt inv-no-gnutls inv-no-openssl inv-websockets inv-24bpp inv-no-pthread inv-no-ipv6 inv-sz-protocolversion inv-sz-serverinit inv-sz-pixelformat inv-sz-fbupdate inv-sz-fbupdaterect inv-sz-colourmap inv-sz-bell inv-sz-cuttext inv-sz-copyrect inv-sz-rre inv-sz-xcursorcolors inv-enc-raw inv-enc-copyrect inv-enc-rre inv-enc-corre inv-enc-hextile inv-enc-zlib inv-enc-tight inv-enc-ultra inv-enc-zrle inv-end" oi=$tmp/libvncserver-inventory.$$ if [ "$staged" -eq 1 ]; then bst_probe libvncserver/probe-inventory-runs "$oi" 600 "$probe" inventory rc=$? else bst_notok libvncserver/probe-inventory-runs "probe was never staged"; rc=127 fi if [ "$rc" -eq 0 ] || [ -s "$oi" ]; then kv inv-version VNC-I-version 0.9.14 "$oi" kv inv-zlib VNC-I-have-zlib 1 "$oi" kv inv-jpeg VNC-I-have-jpeg 1 "$oi" kv inv-png VNC-I-have-png 1 "$oi" kv inv-lzo VNC-I-have-lzo 1 "$oi" # The reference host had no libgcrypt and fell through to OpenSSL; this image # is the other way round, and the library's own order is gcrypt > openssl > # builtin. Both halves are asserted so the pair cannot drift together. kv inv-gcrypt VNC-I-have-gcrypt 1 "$oi" kv inv-no-gnutls VNC-I-have-gnutls 0 "$oi" kv inv-no-openssl VNC-I-have-openssl 0 "$oi" kv inv-websockets VNC-I-websockets 1 "$oi" kv inv-24bpp VNC-I-allow24bpp 1 "$oi" kv inv-no-pthread VNC-I-pthread 0 "$oi" kv inv-no-ipv6 VNC-I-ipv6 0 "$oi" kv inv-sz-protocolversion VNC-I-sz-ProtocolVersionMsg 12 "$oi" kv inv-sz-serverinit VNC-I-sz-ServerInitMsg 24 "$oi" kv inv-sz-pixelformat VNC-I-sz-PixelFormat 16 "$oi" kv inv-sz-fbupdate VNC-I-sz-FramebufferUpdateMsg 4 "$oi" kv inv-sz-fbupdaterect VNC-I-sz-FramebufferUpdateRectHeader 12 "$oi" kv inv-sz-colourmap VNC-I-sz-SetColourMapEntriesMsg 6 "$oi" kv inv-sz-bell VNC-I-sz-BellMsg 1 "$oi" kv inv-sz-cuttext VNC-I-sz-ServerCutTextMsg 8 "$oi" kv inv-sz-copyrect VNC-I-sz-CopyRect 4 "$oi" kv inv-sz-rre VNC-I-sz-RREHeader 4 "$oi" kv inv-sz-xcursorcolors VNC-I-sz-XCursorColors 6 "$oi" kv inv-enc-raw VNC-I-enc-raw 0 "$oi" kv inv-enc-copyrect VNC-I-enc-copyrect 1 "$oi" kv inv-enc-rre VNC-I-enc-rre 2 "$oi" kv inv-enc-corre VNC-I-enc-corre 4 "$oi" kv inv-enc-hextile VNC-I-enc-hextile 5 "$oi" kv inv-enc-zlib VNC-I-enc-zlib 6 "$oi" kv inv-enc-tight VNC-I-enc-tight 7 "$oi" kv inv-enc-ultra VNC-I-enc-ultra 9 "$oi" kv inv-enc-zrle VNC-I-enc-zrle 16 "$oi" kv inv-end VNC-END inventory "$oi" else dead "$INV_CASES" "the inventory probe produced no transcript (rc=$rc)" fi # ---- mode: encodings ---------------------------------------------------------- LOSSLESS="raw rre corre hextile zlib zrle tight ultra" oe=$tmp/libvncserver-encodings.$$ if [ "$staged" -eq 1 ]; then bst_probe libvncserver/probe-encodings-runs "$oe" 600 "$probe" encodings rc=$? else bst_notok libvncserver/probe-encodings-runs "probe was never staged"; rc=127 fi ENC_CASES="enc-cases enc-end" for e in $LOSSLESS; do ENC_CASES="$ENC_CASES enc-$e-decoded enc-$e-exact enc-$e-maxerr enc-$e-rects" done ENC_CASES="$ENC_CASES enc-raw-bytes enc-rre-bytes enc-corre-bytes enc-hextile-bytes enc-zlib-compressed enc-zrle-compressed enc-tight-compressed enc-ultra-compressed enc-tightjpeg-decoded enc-tightjpeg-lossy enc-tightjpeg-bytes enc-tightjpeg-flat-tight enc-tightjpeg-gradient-loose enc-tightjpeg-noise-loosest enc-tightjpeg-band-order" if [ "$rc" -eq 0 ] || [ -s "$oe" ]; then kv enc-cases VNC-E-cases 9 "$oe" # The eight lossless encodings, each asserted three ways: the decode # SUCCEEDED, not one of the 3072 pixels differs, and the worst single channel # error is zero. "diffpixels 0" alone would pass a decoder that wrote the # source buffer back unchanged; "maxerr 0" alone would pass one that decoded # nothing into an already-correct buffer. The pair with `decoded' does not. for e in $LOSSLESS; do kv "enc-$e-decoded" "VNC-E-$e-decoded" 1 "$oe" kv "enc-$e-exact" "VNC-E-$e-diffpixels" 0 "$oe" kv "enc-$e-maxerr" "VNC-E-$e-maxerr" 0 "$oe" done # Rect counts are a property of the format, not of the data. CoRRE splits the # 64x48 frame into 2 rects at its 48x48 maximum; every other encoding here # sends one. for e in raw rre hextile zlib zrle tight ultra; do kv "enc-$e-rects" "VNC-E-$e-rects" 1 "$oe" done kv enc-corre-rects VNC-E-corre-rects 2 "$oe" # Format-deterministic byte counts: 4 (update header) + 12 (rect header) + # payload. raw and RRE both send 3072 32-bit pixels = 12288. kv enc-raw-bytes VNC-E-raw-bytes 12304 "$oe" kv enc-rre-bytes VNC-E-rre-bytes 12304 "$oe" kv enc-corre-bytes VNC-E-corre-bytes 12316 "$oe" kv enc-hextile-bytes VNC-E-hextile-bytes 8224 "$oe" # The four that go through zlib or libjpeg-turbo: the assertion is that they # compressed, with a ceiling generous enough to survive a library bump. kvrange enc-zlib-compressed VNC-E-zlib-bytes 3000 9000 "$oe" kvrange enc-zrle-compressed VNC-E-zrle-bytes 3000 9000 "$oe" kvrange enc-tight-compressed VNC-E-tight-bytes 3000 9000 "$oe" kvrange enc-ultra-compressed VNC-E-ultra-bytes 3000 11000 "$oe" # The lossy case. It must decode, it must NOT be exact, and its damage must # fall the way JPEG's does: least on flat colour, more on a gradient, most on # noise. That ordering is the statement worth making -- a single figure over # the whole frame cannot tell a working JPEG from a broken one. kv enc-tightjpeg-decoded VNC-E-tightjpeg-decoded 1 "$oe" kvrange enc-tightjpeg-lossy VNC-E-tightjpeg-diffpixels 2000 3072 "$oe" kvrange enc-tightjpeg-bytes VNC-E-tightjpeg-bytes 300 2500 "$oe" kvrange enc-tightjpeg-flat-tight VNC-E-tightjpeg-maxerr-flat 1 40 "$oe" kvrange enc-tightjpeg-gradient-loose VNC-E-tightjpeg-maxerr-gradient 5 120 "$oe" kvrange enc-tightjpeg-noise-loosest VNC-E-tightjpeg-maxerr-noise 120 255 "$oe" _f=$(sed -n 's/^VNC-E-tightjpeg-maxerr-flat //p' "$oe" 2>/dev/null | head -n 1) _g=$(sed -n 's/^VNC-E-tightjpeg-maxerr-gradient //p' "$oe" 2>/dev/null | head -n 1) _n=$(sed -n 's/^VNC-E-tightjpeg-maxerr-noise //p' "$oe" 2>/dev/null | head -n 1) case "$_f$_g$_n" in ''|*[!0-9]*) bst_notok libvncserver/enc-tightjpeg-band-order "one of the three band figures is absent: flat=$_f gradient=$_g noise=$_n" ;; *) if [ "$_f" -lt "$_g" ] && [ "$_g" -lt "$_n" ]; then bst_ok libvncserver/enc-tightjpeg-band-order else bst_notok libvncserver/enc-tightjpeg-band-order "flat=$_f gradient=$_g noise=$_n, want flat < gradient < noise" fi ;; esac kv enc-end VNC-END encodings "$oe" else dead "$ENC_CASES" "the encodings probe produced no transcript (rc=$rc)" fi # ---- mode: pixfmt ------------------------------------------------------------- FMTS="32le 32be 16le 16be 8le" op=$tmp/libvncserver-pixfmt.$$ if [ "$staged" -eq 1 ]; then bst_probe libvncserver/probe-pixfmt-runs "$op" 600 "$probe" pixfmt rc=$? else bst_notok libvncserver/probe-pixfmt-runs "probe was never staged"; rc=127 fi FMT_CASES="fmt-cases fmt-end" for f in $FMTS; do FMT_CASES="$FMT_CASES fmt-$f-decoded fmt-$f-exact fmt-$f-bytes fmt-$f-loss" done if [ "$rc" -eq 0 ] || [ -s "$op" ]; then kv fmt-cases VNC-P-cases 5 "$op" for f in $FMTS; do kv "fmt-$f-decoded" "VNC-P-$f-decoded" 1 "$op" # Every one of the 3072 pixels equals the value the RFB conversion formula # says it must -- computed in the probe, not read back out of the library. kv "fmt-$f-exact" "VNC-P-$f-wrongpixels" 0 "$op" done kv fmt-32le-bytes VNC-P-32le-bytes 12304 "$op" kv fmt-32be-bytes VNC-P-32be-bytes 12304 "$op" kv fmt-16le-bytes VNC-P-16le-bytes 6160 "$op" kv fmt-16be-bytes VNC-P-16be-bytes 6160 "$op" kv fmt-8le-bytes VNC-P-8le-bytes 3088 "$op" # The quantisation loss each depth costs, expanded back to 8 bits per channel. # Exact at 32 bpp; 4 of 255 at 5-6-5; 42 of 255 at 3-3-2, which is the blue # channel's two bits. These are integer arithmetic, so a difference is a real # divergence and not rounding. kv fmt-32le-loss VNC-P-32le-maxloss 0 "$op" kv fmt-32be-loss VNC-P-32be-maxloss 0 "$op" kv fmt-16le-loss VNC-P-16le-maxloss 4 "$op" kv fmt-16be-loss VNC-P-16be-maxloss 4 "$op" kv fmt-8le-loss VNC-P-8le-maxloss 42 "$op" kv fmt-end VNC-END pixfmt "$op" else dead "$FMT_CASES" "the pixfmt probe produced no transcript (rc=$rc)" fi # ---- mode: protocol ----------------------------------------------------------- PROTO_CASES="msg-bell-bytes msg-bell-parsed msg-bell-hook msg-cuttext-bytes msg-cuttext-parsed msg-cuttext-hook msg-cuttext-value msg-colourmap-bytes msg-colourmap-parsed msg-copyrect-parsed msg-copyrect-hook msg-copyrect-srcx msg-copyrect-srcy msg-copyrect-w msg-copyrect-h msg-copyrect-dstx msg-copyrect-dsty msg-copyrect-fbmoved msg-cursor-make msg-cursor-parsed msg-cursor-hook msg-cursor-xhot msg-cursor-yhot msg-cursor-w msg-cursor-h msg-cursor-bpp msg-end" om=$tmp/libvncserver-protocol.$$ if [ "$staged" -eq 1 ]; then bst_probe libvncserver/probe-protocol-runs "$om" 600 "$probe" protocol rc=$? else bst_notok libvncserver/probe-protocol-runs "probe was never staged"; rc=127 fi if [ "$rc" -eq 0 ] || [ -s "$om" ]; then # Bell is the whole message: one byte, type 2. Nothing else in RFB is smaller, # and it is the cleanest statement that the construct-and-parse loop is closed. kv msg-bell-bytes VNC-M-bell-bytes 1 "$om" kv msg-bell-parsed VNC-M-bell-parsed 1 "$om" kv msg-bell-hook VNC-M-bell-hook 1 "$om" # ServerCutText: 8-byte header + the 20-byte payload, and the client's # GotXCutText gets the text back byte for byte. kv msg-cuttext-bytes VNC-M-cuttext-bytes 28 "$om" kv msg-cuttext-parsed VNC-M-cuttext-parsed 1 "$om" kv msg-cuttext-hook VNC-M-cuttext-hook 1 "$om" kv msg-cuttext-value VNC-M-cuttext-value brintOS-RFB-cut-text "$om" # SetColourMapEntries: 6-byte header + 16 entries of three uint16 = 1638+6. # It is reachable only for a NON-true-colour client, so the probe switches # format for this one case. kv msg-colourmap-bytes VNC-M-colourmap-bytes 1644 "$om" kv msg-colourmap-parsed VNC-M-colourmap-parsed 1 "$om" # CopyRect. rfbDoCopyRect(x1,y1,x2,y2,dx,dy) names the DESTINATION and the # displacement, so the source is the destination shifted by (-dx,-dy): # (8,6)-(24,18) displaced by (8,6) reads from (0,0)-(16,12). Both the geometry # the client's own GotCopyRect reports AND the server framebuffer's contents # are asserted, because either alone can be right while the other is wrong. kv msg-copyrect-parsed VNC-M-copyrect-parsed 1 "$om" kv msg-copyrect-hook VNC-M-copyrect-hook 1 "$om" kv msg-copyrect-srcx VNC-M-copyrect-srcx 0 "$om" kv msg-copyrect-srcy VNC-M-copyrect-srcy 0 "$om" kv msg-copyrect-w VNC-M-copyrect-w 16 "$om" kv msg-copyrect-h VNC-M-copyrect-h 12 "$om" kv msg-copyrect-dstx VNC-M-copyrect-dstx 8 "$om" kv msg-copyrect-dsty VNC-M-copyrect-dsty 6 "$om" kv msg-copyrect-fbmoved VNC-M-copyrect-fbmoved 1 "$om" # The cursor, as an XCursor pseudo-rect: a 4x4 shape with the hotspot at # (1,2), through rfbMakeXCursor on the server and HandleCursorShape on the # client. bpp 4 is bytes per pixel in the client's own rcSource. kv msg-cursor-make VNC-M-cursor-make 1 "$om" kv msg-cursor-parsed VNC-M-cursor-parsed 1 "$om" kv msg-cursor-hook VNC-M-cursor-hook 1 "$om" kv msg-cursor-xhot VNC-M-cursor-xhot 1 "$om" kv msg-cursor-yhot VNC-M-cursor-yhot 2 "$om" kv msg-cursor-w VNC-M-cursor-w 4 "$om" kv msg-cursor-h VNC-M-cursor-h 4 "$om" kv msg-cursor-bpp VNC-M-cursor-bpp 4 "$om" kv msg-end VNC-END protocol "$om" else dead "$PROTO_CASES" "the protocol probe produced no transcript (rc=$rc)" fi rm -f "$flat" "$namef" "$probe" "$STLOCAL" \ "$oi" "$oi.err" "$oe" "$oe.err" "$op" "$op.err" "$om" "$om.err"