. "$BST_LIB" # liblinebreak 1.2 -- Wu Yongwei's implementation of the Unicode line breaking # algorithm, UAX #14 Revision 22 (Unicode 5.1.0), as meta-oe ships it from # recipes-support/liblinebreak. A renderer asks it, for every position in a # string, whether a line may be broken there: MUSTBREAK, ALLOWBREAK, NOBREAK, or # INSIDEACHAR for a position part-way through a multi-byte sequence. # # --------------------------------------------------------------------------- # WHY THIS SUITE CAN BE STRICT # # UAX #14 IS A FULLY SPECIFIED ALGORITHM OVER A FIXED PROPERTY TABLE. For a # given code point sequence the break opportunities are determined by the # standard and by the library's own data tables -- not by the word size, the # byte order, or the libc. So every value compared below is REQUIRED to be # identical on x86_64 and on wasm32, and a single differing line is a defect # rather than a platform difference to be reasoned about. There is no tolerance # band anywhere in this file. # # That is what makes data/linebreak-native.txt an oracle rather than a # convenience: it is the verbatim output of the SAME probe source, compiled # against a native x86_64 build of the SAME liblinebreak 1.2 sources. The probe # prints only small integers (0..3), hex byte strings and ASCII -- never a # pointer, a size_t or a sizeof -- so nothing in its output is permitted to # differ between the two machines. # # WHAT IS ACTUALLY BEING MEASURED. liblinebreak installs no executable, so # without a cross-linked consumer this suite could only assert that an archive # and two headers exist -- which would pass just as happily if every function in # the archive trapped on first call. data/linebreak-probe is that consumer: the # probe source cross-linked against the very archive this port installs, reached # through the way a real consumer reaches it. # # THE ANCHORED CASES (the L section) EXIST BECAUSE AN ORACLE CAN BE WRONG. The # bulk of this file compares target output against the native control, which # catches any disagreement between the two machines but would NOT catch a # control captured from an already-broken build. So a dozen results are also # asserted against literals read off UAX #14 itself -- CRLF is one break and not # two, NBSP forbids a break on both sides, ZWSP offers one, a break is allowed # after a space and never before it. Those hold whatever the control says. # --------------------------------------------------------------------------- # A root prefix, empty on the target and set only when this suite is validated # NATIVELY under dash before it ships. Writing /usr/lib and /usr/include needs # real root, so a native run stages them under a temporary tree and points this # at it. The runner never sets it, so on the image these are exactly the # absolute paths. R="${BST_LINEBREAK_ROOT:-}" ARCHIVE=$R/usr/lib/liblinebreak.a INCDIR=$R/usr/include PROBE=$BST_DATA/linebreak-probe NATIVE=$BST_DATA/linebreak-native.txt if [ ! -f "$ARCHIVE" ]; then bst_skip linebreak/present "liblinebreak not installed on this image" exit 0 fi bst_ok linebreak/present # --- the package is whole ---------------------------------------------------- check linebreak/hdr-linebreak test -f "$INCDIR/linebreak.h" check linebreak/hdr-linebreakdef test -f "$INCDIR/linebreakdef.h" check linebreak/staticlib test -f "$ARCHIVE" # ${PN} is empty by design: liblinebreak is an autotools package, so the # distro-wide --disable-shared reaches its libtool link and upstream's only # remaining outputs are the archive and the two headers. The deliverable is # liblinebreak-dev + liblinebreak-staticdev, the same shape as libexecinfo, # lockdev, nanopb-runtime, libb64 and libinih already in this image. If a shared # object ever appeared here it would mean the distro-wide switch had stopped # reaching this recipe, and the port would be shipping something that cannot be # loaded on a static-only image. if [ -n "$(find "$R/usr/lib" -maxdepth 1 -name 'liblinebreak.so*' 2>/dev/null)" ]; then bst_notok linebreak/no-shared "a liblinebreak shared object is installed on a static-only image" else bst_ok linebreak/no-shared fi # --- the probe and its control ship together --------------------------------- if [ ! -f "$PROBE" ]; then bst_notok linebreak/probe-present "$PROBE missing -- the port cannot be functionally verified" exit 0 fi bst_ok linebreak/probe-present if [ ! -f "$NATIVE" ]; then bst_notok linebreak/native-present "$NATIVE missing -- no oracle to compare against" exit 0 fi bst_ok linebreak/native-present # --- run the consumer once --------------------------------------------------- # brintos-selftests' do_install stages data/* at 0644, and the deployed rootfs is # read-only at browser runtime, so the probe cannot simply be executed where it # lies -- stage an executable copy under TMPDIR, as every probe-driven suite in # this package does. tmp=${TMPDIR:-/tmp}/bst-linebreak.$$ _p=$tmp.bin cp "$PROBE" "$_p" 2>/dev/null && chmod 0755 "$_p" 2>/dev/null if [ ! -x "$_p" ]; then bst_notok linebreak/probe-exec "could not stage an executable copy of the probe" rm -f "$_p" exit 0 fi bst_ok linebreak/probe-exec "$_p" > "$tmp.out" 2> "$tmp.err"; rc=$? rm -f "$_p" check_eq linebreak/probe-rc 0 "$rc" # A truncated run must be distinguishable from a run that legitimately produced # these lines, so the probe prints a terminator and this is checked before any # comparison. Without it a crash half way through would silently reduce the # number of cases rather than fail one. if [ "$(tail -n 1 "$tmp.out" 2>/dev/null)" = "PROBE-END" ]; then bst_ok linebreak/probe-complete else bst_notok linebreak/probe-complete "no PROBE-END terminator: $(_bst_1line "$(tail -n 3 "$tmp.out" 2>/dev/null)")" fi want_n=$(wc -l < "$NATIVE" | tr -d ' ') got_n=$(wc -l < "$tmp.out" | tr -d ' ') check_eq linebreak/line-count "$want_n" "$got_n" # --- the anchored cases: UAX #14 itself, not the control --------------------- # # Each literal below is read off the standard, so these hold even if the native # control were captured from a broken build. The break array is one digit per # code unit of the encoding named in the id, and brks[i] describes the break # opportunity AFTER unit i: 0 MUSTBREAK, 1 ALLOWBREAK, 2 NOBREAK, 3 INSIDEACHAR. lb_get() { awk -v k="$1" '$1==k { $1=""; sub(/^ /,""); print; exit }' "$tmp.out"; } # "Hello world": no break inside either word, one allowed after the space # (LB18 SP divide), and end of text is a mandatory break (LB3). check_eq linebreak/anchor-hello 22222122220 "$(lb_get B01-hello-32)" # "well-known": LB21 allows a break after the hyphen, nowhere else. check_eq linebreak/anchor-hyphen 2222122220 "$(lb_get B02-hyphen-32)" # "1,000.50": LB25 keeps a numeric run together across the infix separators. check_eq linebreak/anchor-number 22222220 "$(lb_get B03-number-32)" # "(a b)": LB30 forbids a break after opening punctuation and before closing. check_eq linebreak/anchor-parens 22120 "$(lb_get B04-parens-32)" # NBSP is GL (glue): LB12 forbids a break on BOTH sides. This is the case a # renderer is most visibly wrong about -- it is what stops "10 km" splitting. check_eq linebreak/anchor-nbsp 220 "$(lb_get B06-nbsp-32)" # ZWSP is ZW: LB8 offers a break AFTER it and nowhere else -- an invisible # break opportunity, the complement of NBSP. check_eq linebreak/anchor-zwsp 210 "$(lb_get B07-zwsp-32)" # WORD JOINER is WJ: LB11 forbids the break that would otherwise be allowed. check_eq linebreak/anchor-wj 220 "$(lb_get B08-wj-32)" # CRLF is ONE mandatory break, not two: LB5 forbids a break BETWEEN CR and LF. # Position 1 (the CR) must be NOBREAK and position 2 (the LF) MUSTBREAK. A # library that got this wrong would split every DOS line ending in half. check_eq linebreak/anchor-crlf 2200 "$(lb_get C03-crlf-32)" # A lone LF is a mandatory break (LB4); two of them are two. check_eq linebreak/anchor-lf 200 "$(lb_get C01-lf-32)" check_eq linebreak/anchor-lflf 2000 "$(lb_get C09-lflf-32)" # LINE SEPARATOR U+2028 and PARAGRAPH SEPARATOR U+2029 are BK: mandatory. check_eq linebreak/anchor-ls 200 "$(lb_get C05-ls-32)" check_eq linebreak/anchor-ps 200 "$(lb_get C06-ps-32)" # A combining mark binds to its base (LB9): no break between "e" and U+0301. check_eq linebreak/anchor-combining 220 "$(lb_get B11-combining-32)" # Ideographs may be broken between, but not before the ideographic comma, which # is CL (LB13 forbids a break before it). After it a break is allowed again, # because LB16 only glues CL to a non-starter and an ideograph is not one -- # which is why the third digit is 1 and not 2. check_eq linebreak/anchor-cjk 1210 "$(lb_get B09-cjk-32)" # The INSIDEACHAR contract, which is what a caller indexing its own byte buffer # depends on. U+1D11E is 4 bytes in UTF-8 and a surrogate pair in UTF-16, so the # same logical string yields arrays of three different lengths -- and every # position that is not the LAST unit of a character must read 3. check_eq linebreak/anchor-astral-8 233320 "$(lb_get B12-astral-8)" check_eq linebreak/anchor-astral-16 2320 "$(lb_get B12-astral-16)" check_eq linebreak/anchor-astral-32 220 "$(lb_get B12-astral-32)" # The four public constants, from the header rather than from the control. check_eq linebreak/anchor-mustbreak 0 "$(lb_get I05-mustbreak)" check_eq linebreak/anchor-allowbreak 1 "$(lb_get I06-allowbreak)" check_eq linebreak/anchor-nobreak 2 "$(lb_get I07-nobreak)" check_eq linebreak/anchor-insideachar 3 "$(lb_get I08-insideachar)" # The three typedefs a consumer declares its own buffers with. A width that # changed under a new arm would corrupt every caller silently, so these are # pinned even though they are the least surprising values in the file. check_eq linebreak/anchor-utf8-width 1 "$(lb_get I01-sizeof-utf8)" check_eq linebreak/anchor-utf16-width 2 "$(lb_get I02-sizeof-utf16)" check_eq linebreak/anchor-utf32-width 4 "$(lb_get I03-sizeof-utf32)" # Language tailoring is real, not decorative: lb_prop_lang_map carries six # languages, and German genuinely differs from English on guillemets. Asserting # that they DIFFER (rather than what each is) is the part that would catch the # tailoring being silently bypassed -- if the lang argument stopped reaching the # table, every language would return the default and this case would go red # while all eleven per-language comparisons below still passed. if [ "$(lb_get F01-lang-en)" = "$(lb_get F02-lang-de)" ]; then bst_notok linebreak/anchor-tailoring "en and de gave identical results: the lang argument is not reaching lb_prop_lang_map" else bst_ok linebreak/anchor-tailoring fi # An unknown tag and a NULL tag must both fall back to the default table rather # than mis-index it -- the crash-shaped failure of a lang lookup. check_eq linebreak/anchor-lang-fallback "$(lb_get F07-lang-null)" "$(lb_get F08-lang-unknown)" # --- the control comparison: one case per probe line ------------------------- # # Both files carry the same ids in the same order by construction (same probe # source, same sequence of printf calls), so this is a single lockstep pass with # no subprocess per line -- 185 awk invocations would be slow inside the guest. # A target run that ended early leaves the remaining reads empty, which fails # those cases rather than silently shortening the suite. exec 3< "$NATIVE" exec 4< "$tmp.out" while IFS= read -r wl <&3; do IFS= read -r gl <&4 || gl="" id=${wl%% *} if [ "$wl" = "$gl" ]; then bst_ok "linebreak/$id" else bst_notok "linebreak/$id" "want='$(_bst_1line "${wl#* }")' got='$(_bst_1line "${gl#* }")'" fi done exec 3<&- 4<&- # Nothing should reach stderr: the probe writes only to stdout, so anything here # came from the runtime (a trap message, a loader complaint) and is worth seeing # even when every comparison passed. if [ -s "$tmp.err" ]; then bst_notok linebreak/clean-stderr "$(_bst_1line "$(cat "$tmp.err")")" else bst_ok linebreak/clean-stderr fi rm -f "$tmp.out" "$tmp.err"