. "$BST_LIB" # BST-TIMEOUT: 1200 # THREE python3 starts and nothing else. # # # # They are: a bare `import', a second one whose STDERR is # # the assertion (`import-quiet'), and the probe, which # # takes ONE start for 110 measurements. # # # # 400s per start is the budget the neighbouring python3 # # suites use on this arm. Unlike 99-python3-editor next # # door, NOTHING here forks or execs: every case is a byte # # string parsed in memory, so the interpreter start IS the # # whole cost and there is no exec traffic to budget for. # # # # A suite-timeout red is a batch-wide cost, so if the # # starts prove slower than that on target the RIGHT fix is # # this number, not fewer cases. # # # # NOT MEASURED ON TARGET YET: this port's first on-target # # run is the batch verify, and the measured elapsed # # replaces this note in the test plan rather than in this # # comment, which would cost an image build to keep the # # shipped suite byte-identical. # # python3-javaobj-py3 0.4.4 (meta-openembedded meta-python/recipes-devtools/ # python, python3-javaobj-py3_0.4.4.bb, `inherit pypi setuptools3', PyPI sdist) # on the wasm32-hwjs glibc image. # # ============================================================================ # WHAT THE PACKAGE ACTUALLY IS, AND THEREFORE WHAT HAS TO BE TESTED. # ============================================================================ # # `javaobj' READS AND WRITES A BYTE FORMAT. It decodes a # `java.io.ObjectOutputStream' stream into Python objects -- `loads' from bytes, # `load' from a file object -- and, in the one direction it supports, encodes # them back with `dumps'. # # http://download.oracle.com/javase/6/docs/platform/serialization/spec/protocol.html # # So it is PURE PARSING: no socket, no subprocess, no device, no terminal, and # no JVM. That is what makes this port close on its suite with no BRINTOS-780 # follow-on -- and it is also what shapes the suite, because a parser is only # as good as the bytes you feed it. # # ============================================================================ # THE FIXTURES ARE WRITTEN FROM THE SPECIFICATION, NOT CAPTURED FROM A JVM. # ============================================================================ # # There is no Java on this image, so there is nothing here to produce a captured # stream with. A shipped blob would also be the wrong thing: it would make every # assertion below a comparison against a binary nobody can read, and a reader # checking the suite would have no way to tell a decoder bug from a bad fixture. # # The probe therefore BUILDS each stream record by record from five helpers -- # `utf', `classdesc', `prim_field', `obj_field', `prim_array' -- so the longest # literal a reader has to verify against the specification is one record. The # header is the specification's own constants, and `header-matches-constants' # asserts that the four bytes the probe writes are `StreamConstants.STREAM_MAGIC' # and `STREAM_VERSION' packed big-endian, rather than a hand-typed `aced0005' # that could agree with the file and disagree with the protocol. # # ============================================================================ # BIG-ENDIAN IS THE CASE MOST LIKELY TO CATCH A WRONG ASSUMPTION ON THIS ARM. # ============================================================================ # # The protocol is big-endian throughout and wasm32 is little-endian, so every # multi-byte read in this library is a byte-order conversion. The `be-*' cases # are eight one-field objects whose payloads are chosen so that a # little-endian read gives a DIFFERENT and obviously wrong answer: `00 00 00 2A' # is 42 and not 704643072, `01 00' is 256 and not 1, `40 04 ...' is 2.5 and not # a denormal. A port that got the byte order wrong fails those eight and # nothing else, which is what makes them worth stating separately from the # object cases that happen to contain the same bytes. # # ============================================================================ # FOUR THINGS A CALLER WILL GET WRONG. THESE ARE THE POINT OF THE SUITE. # ============================================================================ # # THERE IS NO `dump'. The ticket's own description says `dumps'/`dump' the # way `pickle' and `json' spell it. This package has `load', # `loads' and `dumps' and NO `dump' -- there is no way to # write straight to a file object. `has-dump' asserts the # absence, because a caller who codes against the stdlib's # symmetry gets an AttributeError at runtime. # # dumps() OF A PLAIN str IS NOT A TC_STRING. It is TC_BLOCKDATA: # `aced0005' `77' `02' `6869'. A JavaString that came back # from `loads' re-encodes as a real TC_STRING and is # byte-identical to the stream it came from # (`enc-javastring-hex'), but the ordinary Python `str' a # caller would naturally pass takes a different branch and # produces a stream a JVM reads as block data, not as a # String. The two cases sit side by side for that reason. # # WRITING IS "WIP CURRENTLY", AND THAT MEANS EVERY SCALAR IS REFUSED. # `dumps' handles None, str and the package's own beans. # `int', `float', `bool', `bytes' and `list' each raise # RuntimeError -- five cases, because a caller reading # "familiar to users of marshal, pickle and json" in the # package's own summary will assume otherwise. # # A MALFORMED STREAM RAISES, BUT NOT WITH ONE KIND OF EXCEPTION. # Nine error cases, and they come back as FIVE different # types: OSError for a bad header, RuntimeError for a # truncated or unknown record, IndexError for an empty stream # and for a dangling TC_REFERENCE, TypeError for a str input. # IndexError in particular is a bare `list index out of # range' escaping from the reader, not a diagnosis -- so # `except OSError' around a `loads' is not the guard it looks # like. The suite states each one so that the shape of the # contract is on the record rather than inferred. # # ============================================================================ # TWO UPSTREAM DEFECTS IN javaobj.modifiedutf8, MEASURED HERE. # ============================================================================ # # Both are pure Python, present on every platform this release runs on, and # NEITHER IS A brintOS PLATFORM GAP -- they are not attributed to a brintos.io # issue and they are not reds. The suite asserts what the code ACTUALLY DOES, # so these cases pass here and would start failing the day the library is # fixed, which is exactly the signal wanted. # # A SUPPLEMENTARY CHARACTER DECODES TO THE WRONG CODE POINT. The protocol # writes a character above the BMP as a CESU-8 surrogate # PAIR. `ED A0 BC ED B7 A2' is U+D83C U+DDE2, which # reassembles to U+1F1E2. This release returns U+F1E2 -- # the 0x10000 offset is dropped. `mutf8-surrogate-pair' # records what it returns and `mutf8-surrogate-expected' # records what the pair means, so the gap between them is # stated rather than buried. # # THE DECODER'S OWN ERROR PATH IS BROKEN. Three malformed inputs -- a real # 4-byte UTF-8 sequence, an invalid lead byte, and a lone # high surrogate -- are all correctly REJECTED, but the # `raise UnicodeDecodeError(...)' that rejects them passes a # generator where bytes are required, so what a caller # actually catches is TypeError. Three cases assert # TypeError, which is the honest statement of the contract as # shipped. # # ============================================================================ # THE LIBRARY LOGS, AND IT RECONFIGURES THE ROOT LOGGER TO DO IT. # ============================================================================ # # v1's unmarshaller logs an `==Oops state dump====' block with a hex dump of the # stream on every malformed input, and v1's MARSHALLER logs through the # module-level `logging.debug()', which calls `logging.basicConfig()' when the # root logger has no handler. So the first `dumps()' in a process installs a # StreamHandler bound to whatever `sys.stderr' was at that instant. # # That matters twice. It is a real side effect on any application that embeds # this library, and `marshaller-installs-root-handler' asserts it directly -- # with the root logger stripped, one `dumps' puts a handler back. And it is a # trap for THIS suite: the probe parses nothing but its own stdout, so it # configures logging into a buffer it owns before the first library call, and # `stdout-clean' asserts that not one byte of the library's output reached the # stream the runner reads. # # ============================================================================ # NO PATCH, NO BBAPPEND AND NO SITE ANSWER. # ============================================================================ # # The recipe is meta-openembedded's exactly as written. There is no configure # step, no compile step and no C, so there is nothing on this arm for an # override to reach. This image's python3 is ONE STATIC BINARY with an empty # lib-dynload and no dynamic loader (BRINTOS-1476), so a module carrying a # compiled extension could not be imported here at all -- `pkg-only-python' and # `pkg-no-so' are what say this one carries none. P=python3-javaobj-py3 # --------------------------------------------------------------------------- bst_ok $P/suite-canary require python3 $P/python3-present SITE=/usr/lib/python3.12/site-packages PKGDIR=$SITE/javaobj DI=$SITE/javaobj_py3-0.4.4.dist-info PKGLIST=/var/lib/dpkg/info/python3-javaobj-py3.list PROBE=$BST_DATA/python3-javaobj-py3-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-javaobj-suite.$$ mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # --- the module, as installed --------------------------------------------- # Unlike python3-editor next door this one is a PACKAGE, not a flat module: # setup.py declares packages=[...], so what lands is a directory with two # implementation subpackages under it. check $P/site-packages-dir test -d "$SITE" check $P/package-dir test -d "$PKGDIR" check $P/not-a-flat-module test ! -e "$SITE/javaobj.py" check $P/init-file test -s "$PKGDIR/__init__.py" check $P/constants-file test -s "$PKGDIR/constants.py" check $P/utils-file test -s "$PKGDIR/utils.py" check $P/modifiedutf8-file test -s "$PKGDIR/modifiedutf8.py" check $P/v1-subpackage test -s "$PKGDIR/v1/__init__.py" check $P/v2-subpackage test -s "$PKGDIR/v2/__init__.py" check $P/v1-unmarshaller test -s "$PKGDIR/v1/unmarshaller.py" check $P/v1-marshaller test -s "$PKGDIR/v1/marshaller.py" check $P/v2-core test -s "$PKGDIR/v2/core.py" check $P/pycache-init test -s "$PKGDIR/__pycache__/__init__.cpython-312.pyc" # --- the package, as dpkg has it ------------------------------------------ cat "$PKGLIST" > "$W/pkgf" 2>/dev/null || : > "$W/pkgf" check $P/pkg-list-present test -s "$W/pkgf" # The sdist ships a `tests/' tree beside the package; setup.py declares only # javaobj and its two subpackages, so none of it must reach the rootfs. # Asserted against THIS package's file list rather than `test ! -d $SITE/tests': # site-packages is shared, and python3-reedsolo 2.0.13 installs its own # top-level `tests' package into it, so the directory test measures a # neighbour and goes red on an image where javaobj is packaged correctly. check_eq $P/no-tests-package "" \ "$(grep -E "^$SITE/tests(/|\$)" "$W/pkgf" 2>/dev/null)" # This image's python3 has no dynamic loader at all (BRINTOS-1476), so a .so in # this list would be a file that could never be imported. check_eq $P/pkg-no-so "" \ "$(grep -E '\.so($|\.)' "$W/pkgf" 2>/dev/null)" # setup.py declares no entry_points and no scripts, so nothing lands in a bindir. check_eq $P/pkg-no-bin "" \ "$(grep -E '^/usr/(s?bin)/' "$W/pkgf" 2>/dev/null)" _bad="" while IFS= read -r _f; do [ -f "$_f" ] || continue case "$_f" in *.py|*.pyc) continue ;; */__pycache__/*) continue ;; */*.dist-info/*) continue ;; esac _bad="$_bad $_f" done < "$W/pkgf" check_eq $P/pkg-only-python "" "$_bad" # --- the wheel metadata ---------------------------------------------------- check $P/dist-info-dir test -d "$DI" check $P/dist-info-metadata test -f "$DI/METADATA" check_out $P/dist-info-name "Name: javaobj-py3" cat "$DI/METADATA" check_out $P/dist-info-version "Version: 0.4.4" cat "$DI/METADATA" check $P/dist-info-record test -s "$DI/RECORD" # Purelib and a py3-none-any tag are the machine-readable form of "there is # nothing arch-specific in here", which is the whole reason this port is cheap. check_out $P/dist-info-purelib "Root-Is-Purelib: true" cat "$DI/WHEEL" check_out $P/dist-info-tag-py3 "Tag: py3-none-any" cat "$DI/WHEEL" # The py2 tag rides along because upstream's setup.cfg carries # `[bdist_wheel] universal = 1' and setup.py still claims 2.7, so bdist_wheel # emits both halves of the universal pair. It is a statement about the source # distribution, not about this arm. What WOULD matter is a third tag carrying # an interpreter or platform, which is what `no-arch-tag' is for. check_out $P/dist-info-tag-py2 "Tag: py2-none-any" cat "$DI/WHEEL" check_eq $P/dist-info-no-arch-tag "" \ "$(grep '^Tag: ' "$DI/WHEEL" 2>/dev/null \ | grep -v -e '^Tag: py2-none-any$' -e '^Tag: py3-none-any$')" check_out $P/dist-info-top-level "javaobj" cat "$DI/top_level.txt" sed -n '/^Package: python3-javaobj-py3$/,/^$/p' /var/lib/dpkg/status > "$W/st" 2>/dev/null check $P/pkg-known test -s "$W/st" check_out $P/pkg-installed "Status: install ok installed" cat "$W/st" check_out $P/pkg-version "Version: 0.4.4-r0" cat "$W/st" # --- import ---------------------------------------------------------------- check $P/import python3 -c 'import javaobj' # An import that WORKS but complains is a port that is not finished. Asserted # on its own start so that the noise, if any, is the whole of the evidence. check_eq $P/import-quiet "" \ "$(python3 -c 'import javaobj' 2>&1)" # --- the probe ------------------------------------------------------------- if [ ! -f "$PROBE" ]; then bst_notok $P/probe-present "$PROBE did not ship" rm -rf "$W" exit 0 fi bst_ok $P/probe-present bst_probe $P/probe-run "$W/probe" 900 python3 "$PROBE" OUT="$W/probe" pv() { grep "^$1=" "$OUT" 2>/dev/null | head -n 1 | cut -d= -f2-; } if [ -s "$OUT" ]; then bst_ok $P/probe-output else bst_notok $P/probe-output "the probe emitted nothing -- python3 did not start, or died before its first line; every CASE below is unscorable" fi check_eq $P/probe-complete "yes" "$(pv probe_done)" check_eq $P/probe-count "110" "$(pv emitted_count)" # --- the module, as the interpreter sees it -------------------------------- check_eq $P/module-answered "$PKGDIR/__init__.py" "$(pv module_file)" check_eq $P/is-package "yes" "$(pv is_package)" check_eq $P/version-attr "0.4.4" "$(pv version_attr)" check_eq $P/version-info "0.4.4" "$(pv version_info)" check_eq $P/has-loads "yes" "$(pv has_loads)" check_eq $P/has-load "yes" "$(pv has_load)" check_eq $P/has-dumps "yes" "$(pv has_dumps)" # The one the ticket asked for and the package does not have. check_eq $P/has-dump "no" "$(pv has_dump)" check_eq $P/v1-has-dumps "yes" "$(pv v1_has_dumps)" check_eq $P/v2-has-loads "yes" "$(pv v2_has_loads)" # v2 is a READER. The package's own summary calls writing "WIP currently" and # this is where that shows: the rewritten implementation has no encoder at all. check_eq $P/v2-has-dumps "no" "$(pv v2_has_dumps)" check_eq $P/beans \ "DefaultObjectTransformer,JavaArray,JavaByteArray,JavaClass,JavaEnum,JavaObject,JavaObjectMarshaller,JavaObjectUnmarshaller,JavaString" \ "$(pv beans)" check_eq $P/javastring-is-str "yes" "$(pv javastring_is_str)" check_eq $P/javaarray-is-list "yes" "$(pv javaarray_is_list)" # JavaByteArray is the odd one out: it derives from JavaObject, not from list. check_eq $P/javabytearray-bases "JavaObject" "$(pv javabytearray_bases)" # --- the protocol constants ------------------------------------------------ check_eq $P/const-magic "0xaced" "$(pv const_magic)" check_eq $P/const-version "0x05" "$(pv const_version)" check_eq $P/const-base-handle "0x7e0000" "$(pv const_base_handle)" check_eq $P/const-tc \ "TC_NULL=0x70,TC_REFERENCE=0x71,TC_CLASSDESC=0x72,TC_OBJECT=0x73,TC_STRING=0x74,TC_ARRAY=0x75,TC_CLASS=0x76,TC_BLOCKDATA=0x77,TC_ENDBLOCKDATA=0x78,TC_RESET=0x79,TC_BLOCKDATALONG=0x7a,TC_EXCEPTION=0x7b,TC_LONGSTRING=0x7c,TC_PROXYCLASSDESC=0x7d,TC_ENUM=0x7e" \ "$(pv const_tc)" check_eq $P/const-typecodes \ "TYPE_BYTE=B,TYPE_CHAR=C,TYPE_DOUBLE=D,TYPE_FLOAT=F,TYPE_INTEGER=I,TYPE_LONG=J,TYPE_SHORT=S,TYPE_BOOLEAN=Z,TYPE_OBJECT=L,TYPE_ARRAY=[" \ "$(pv const_typecodes)" check_eq $P/const-flags \ "SC_WRITE_METHOD=0x01,SC_BLOCK_DATA=0x08,SC_SERIALIZABLE=0x02,SC_EXTERNALIZABLE=0x04,SC_ENUM=0x10" \ "$(pv const_flags)" # The header every fixture below starts with, and the assertion that it IS the # constants rather than a literal that happens to match. check_eq $P/header-hex "aced0005" "$(pv header_hex)" check_eq $P/header-matches-constants "yes" "$(pv header_matches_constants)" # --- decoding, record by record -------------------------------------------- check_eq $P/dec-string "hello" "$(pv dec_string)" check_eq $P/dec-string-type "JavaString" "$(pv dec_string_type)" check_eq $P/dec-null "None" "$(pv dec_null)" check_eq $P/dec-blockdata "abc" "$(pv dec_blockdata)" check_eq $P/dec-longstring "xyz" "$(pv dec_longstring)" check_eq $P/dec-object-type "JavaObject" "$(pv dec_object_type)" check_eq $P/dec-object-class "Point" "$(pv dec_object_class)" check_eq $P/dec-object-d "2.5" "$(pv dec_object_d)" check_eq $P/dec-object-i "42" "$(pv dec_object_i)" # --- big-endian ------------------------------------------------------------ # Eight payloads chosen so a little-endian read gives a different answer. check_eq $P/be-int "42" "$(pv be_int)" check_eq $P/be-long "256" "$(pv be_long)" check_eq $P/be-short "256" "$(pv be_short)" check_eq $P/be-double "2.5" "$(pv be_double)" check_eq $P/be-float "0.5" "$(pv be_float)" check_eq $P/be-char "A" "$(pv be_char)" check_eq $P/be-byte "-1" "$(pv be_byte)" check_eq $P/be-boolean "True" "$(pv be_boolean)" # --- arrays ---------------------------------------------------------------- check_eq $P/arr-int "1,2,3" "$(pv arr_int)" check_eq $P/arr-double "1.5,-2.25" "$(pv arr_double)" check_eq $P/arr-long "1,-1" "$(pv arr_long)" check_eq $P/arr-short "1,-1" "$(pv arr_short)" check_eq $P/arr-float "0.5" "$(pv arr_float)" check_eq $P/arr-bool "True,False" "$(pv arr_bool)" check_eq $P/arr-char "A,B" "$(pv arr_char)" check_eq $P/arr-empty "" "$(pv arr_empty)" check_eq $P/arr-int-type "JavaArray" "$(pv arr_int_type)" # `[B' does NOT come back as a list, and its values are SIGNED -- 0xFE is -2, # which is what a Java byte is and what a Python `bytes' would not give. check_eq $P/arr-byte-type "JavaByteArray" "$(pv arr_byte_type)" check_eq $P/arr-byte-values "1,-2,3" "$(pv arr_byte_values)" # --- nesting and back-references ------------------------------------------- check_eq $P/dec-nested-outer "Outer" "$(pv dec_nested_outer)" check_eq $P/dec-nested-inner-class "Inner" "$(pv dec_nested_inner_class)" check_eq $P/dec-nested-n "7" "$(pv dec_nested_n)" # TC_REFERENCE resolves to the SAME object, not to a copy of it: handle # 0x7E0002 is the string written earlier in the same stream. check_eq $P/dec-backref-values "same,same" "$(pv dec_backref_values)" check_eq $P/dec-backref-is-same-object "yes" "$(pv dec_backref_is_same_object)" # --- enums and strings ----------------------------------------------------- check_eq $P/dec-enum-type "JavaEnum" "$(pv dec_enum_type)" check_eq $P/dec-enum-class "Color" "$(pv dec_enum_class)" check_eq $P/dec-enum-constant "RED" "$(pv dec_enum_constant)" check_eq $P/dec-utf8-2byte-hex "68c3a96c6c6f" "$(pv dec_utf8_2byte_hex)" # The protocol's modified UTF-8 writes a NUL as C0 80, and it round-trips # through the decoder as a real U+0000 inside the string. check_eq $P/dec-mutf8-nul-hex "610062" "$(pv dec_mutf8_nul_hex)" # `load' takes a file object where `loads' takes bytes, and answers the same. check_eq $P/dec-load-fd "hello" "$(pv dec_load_fd)" # --- encoding, and the round trip in both directions ----------------------- check_eq $P/enc-none-hex "aced000570" "$(pv enc_none_hex)" check_eq $P/enc-javastring-hex "aced000574000568656c6c6f" "$(pv enc_javastring_hex)" # A PLAIN str is TC_BLOCKDATA (0x77), not TC_STRING (0x74). Same three # characters, different record, and a JVM reads the two differently. check_eq $P/enc-plain-str-hex "aced000577026869" "$(pv enc_plain_str_hex)" check_eq $P/enc-plain-str-roundtrip "hi" "$(pv enc_plain_str_roundtrip)" # dumps(loads(b)) == b, byte for byte, for the object whose field table makes # the encoding deterministic. check_eq $P/enc-object-hex \ "aced000573720005506f696e7401020304050607080200024400016449000169787040040000000000000000002a" \ "$(pv enc_object_hex)" check_eq $P/enc-object-byte-identical "yes" "$(pv enc_object_byte_identical)" # ... and the other direction, loads(dumps(x)). check_eq $P/enc-object-roundtrip-class "Point" "$(pv enc_object_roundtrip_class)" check_eq $P/enc-object-roundtrip-i "42" "$(pv enc_object_roundtrip_i)" check_eq $P/enc-object-roundtrip-d "2.5" "$(pv enc_object_roundtrip_d)" # Five scalars a caller would expect to work, and all five refused. check_eq $P/enc-int "!RuntimeError" "$(pv enc_int)" check_eq $P/enc-float "!RuntimeError" "$(pv enc_float)" check_eq $P/enc-bool "!RuntimeError" "$(pv enc_bool)" check_eq $P/enc-bytes "!RuntimeError" "$(pv enc_bytes)" check_eq $P/enc-list "!RuntimeError" "$(pv enc_list)" # --- error paths ----------------------------------------------------------- # Nine malformed streams, five exception types. Each RAISES rather than # returning a partial object, which is the property that matters; the type is # stated because it is not the one a caller would guess. check_eq $P/err-wrong-magic "!OSError" "$(pv err_wrong_magic)" check_eq $P/err-wrong-version "!OSError" "$(pv err_wrong_version)" check_eq $P/err-empty "!IndexError" "$(pv err_empty)" check_eq $P/err-header-only "!RuntimeError" "$(pv err_header_only)" check_eq $P/err-truncated-classdesc "!RuntimeError" "$(pv err_truncated_classdesc)" check_eq $P/err-unknown-typecode "!RuntimeError" "$(pv err_unknown_typecode)" check_eq $P/err-truncated-object "!RuntimeError" "$(pv err_truncated_object)" # A dangling handle is a bare `list index out of range' out of the reader, not # a diagnosis. Stated so that nobody writes `except OSError' and calls it safe. check_eq $P/err-dangling-reference "!IndexError" "$(pv err_dangling_reference)" check_eq $P/err-str-input "!TypeError" "$(pv err_str_input)" check_eq $P/err-wrong-magic-message \ "The stream is not java serialized object. Invalid stream header: DEAD0005" \ "$(pv err_wrong_magic_message)" check_eq $P/err-unknown-typecode-message \ "Unknown OpCode in the stream: 0x6F (at offset 0x4)" \ "$(pv err_unknown_typecode_message)" # --- logging --------------------------------------------------------------- # The stream the runner parses carried none of the library's output. check_eq $P/stdout-clean "yes" "$(pv stdout_clean)" check_eq $P/log-bytes-nonzero "yes" "$(pv log_bytes_nonzero)" check_eq $P/log-has-oops-dump "yes" "$(pv log_has_oops_dump)" # One dumps() puts a handler back on a stripped root logger: this library # reconfigures its host application's logging as a side effect of being used. check_eq $P/marshaller-installs-root-handler "yes" \ "$(pv marshaller_installs_root_handler)" # --- javaobj.modifiedutf8 -------------------------------------------------- # Emitted as `codepoint,codepoint/nchars'. check_eq $P/mutf8-ascii "0x61,0x62,0x63/3" "$(pv mutf8_ascii)" check_eq $P/mutf8-nul "0x0/1" "$(pv mutf8_nul)" check_eq $P/mutf8-2byte "0xe9/1" "$(pv mutf8_2byte)" check_eq $P/mutf8-3byte "0x20ac/1" "$(pv mutf8_3byte)" # UPSTREAM DEFECT, asserted as shipped: a CESU-8 surrogate pair for U+1F1E2 # decodes to U+F1E2 -- the 0x10000 offset is dropped. Not a platform gap, not a # red: the pair of cases states the value returned and the value the pair # means, so the day the library is fixed this case fails and says why. check_eq $P/mutf8-surrogate-pair "0xf1e2/1" "$(pv mutf8_surrogate_pair)" check_eq $P/mutf8-surrogate-expected "0x1f1e2" "$(pv mutf8_surrogate_expected)" # UPSTREAM DEFECT, asserted as shipped: all three malformed inputs ARE # rejected, but the `raise UnicodeDecodeError(...)' that rejects them is itself # broken -- it passes a generator where bytes are required -- so what a caller # catches is TypeError. check_eq $P/mutf8-4byte "!TypeError" "$(pv mutf8_4byte)" check_eq $P/mutf8-invalid-byte "!TypeError" "$(pv mutf8_invalid_byte)" check_eq $P/mutf8-lone-surrogate "!TypeError" "$(pv mutf8_lone_surrogate)" # --- gzip, and the numpy that is not here ---------------------------------- # A gzipped stream is decoded TRANSPARENTLY: utils.py sniffs the first two # bytes and wraps the file object in a GzipFile when it sees 1F 8B. That single # call site is the whole reason the recipe's RDEPENDS names # python3-compression, and these three cases are what make the declaration # load-bearing rather than decorative. check_eq $P/gzip-magic "1f8b" "$(pv gzip_magic)" check_eq $P/gzip-loads "hello" "$(pv gzip_loads)" check_eq $P/gzip-load-fd "hello" "$(pv gzip_load_fd)" # numpy is imported at two sites and BOTH are wrapped in `except ImportError', # so it is optional and the recipe correctly declares nothing for it. It is # also absent from this image and cannot be built for this arm at all -- its # own port died in numpy's setup.py C++ probe -- so `use_numpy_arrays=True' # silently falls back to the ordinary JavaArray path. # # Asserted as a PAIR on purpose. If numpy ever lands on this image both cases # flip together, and the pair says which of the two facts changed instead of # leaving a lone red that reads like a decoder bug. check_eq $P/numpy-importable "no" "$(pv numpy_importable)" check_eq $P/numpy-flag-string "hello" "$(pv numpy_flag_string)" check_eq $P/numpy-flag-array-type "JavaArray" "$(pv numpy_flag_array_type)" # --- the v2 reader --------------------------------------------------------- # A separate implementation of the same protocol in the same package. It agrees # on the values and disagrees on the bean types and on the error type, which is # why a caller cannot treat the two as interchangeable. check_eq $P/v2-string "hello" "$(pv v2_string)" check_eq $P/v2-object-type "JavaInstance" "$(pv v2_object_type)" check_eq $P/v2-null "None" "$(pv v2_null)" check_eq $P/v2-wrong-magic "!ValueError" "$(pv v2_wrong_magic)" rm -rf "$W"