. "$BST_LIB" # BST-TIMEOUT: 1200 # THREE python3 starts, and they are the whole cost. Every # # other assertion below reads the filesystem or dpkg with # # shell builtins, or greps a line out of the transcript the # # third start left behind. # # # # The three are: a bare `import', a second one whose STDERR # # is the assertion (`import-quiet'), and the probe, which # # takes ONE start for ninety-eight measurements. # # # # The work inside each start is `struct.unpack' and # # `re.match' over headers under 64 bytes, plus three 1 MB # # buffers the `readsize-*' cases need in order to show that # # only the first 31 bytes of one is read. Microseconds # # beside an interpreter start on this arm, which is tens of # # seconds before the first line of the program runs. # # # # 1200 is 400s per start, the same per-start budget # # 99-python3-iso8601 sets for its three, and roughly six # # times the ~60s a python3 start has measured at here. # # Nothing in this suite is quadratic in anything, so the # # start is the only thing the number has to cover. # # # # A suite-timeout red is a batch-wide cost, so if the three # # 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-imagesize 1.4.1 (poky/meta # recipes-devtools/python/python3-imagesize_1.4.1.bb, `inherit setuptools3 # pypi', PyPI sdist) on the wasm32-hwjs glibc image. # # ============================================================================ # WHAT THE PACKAGE DOES. # ============================================================================ # # `imagesize.get(path)' returns `(width, height)' for an image file, and # `imagesize.getDPI(path)' returns `(xDPI, yDPI)'. It does that WITHOUT DECODING # THE IMAGE and without an imaging library: it reads the first 31 bytes, matches # them against eight container signatures, and unpacks the size out of whichever # header it recognised. Sphinx uses it to size figures at build time. # # Three exported names, one 376-line module, no configure step, no compiled # extension and no C anywhere in the package. # # THE `readsize-*' CASES ARE THE POINT OF THE LIBRARY. Each hands `get' a # fixture with a 1 MB tail and counts what it actually consumed: 31 bytes for # PNG, 31 for GIF, 39 for JPEG. A release that started reading files whole would # return every right answer and pass every other case in this suite, so the # property is asserted directly rather than inferred. # # ============================================================================ # `get' RETURNS (-1, -1) FOR A FILE IT DOES NOT RECOGNISE. IT DOES NOT RAISE. # ============================================================================ # # This is the suite's most useful single statement, and it is the opposite of # what a caller reasonably assumes. The dispatch is a chain of `elif's with NO # trailing `else', so when nothing matches, `width' and `height' keep the `-1' # they were initialised to and `get' returns them as if they were a size. # # Four inputs take that path, and each is asserted separately because they reach # the same answer for different reasons: # # unknown-bytes 64 NUL bytes -- no signature matches # empty-file a zero-byte file -- every `size >= N' guard fails # text-file plain text -- no signature matches # truncated-png a real PNG cut to 10 bytes, below the 24 its branch needs # truncated-gif a real GIF cut to 8 bytes, below the 10 its branch needs # # So a caller who writes `w, h = get(f)' and uses the result has no way to tell # a 3-byte text file from an image, and `netpbm-P7' shows the same hole inside a # format family the library does support -- PAM's `P7' is not in the `123456' # set the Netpbm branch tests, so it falls off the end too. # # The refusals that DO happen are asserted beside them, so the line between the # two groups is explicit rather than implied: a truncated JPEG, an SVG with no # `width', a WebP in an unknown codec and a big-endian TIFF whose size field has # the wrong data type all raise `ValueError'; a missing file raises # `FileNotFoundError' from `open' before any parsing starts. # # ============================================================================ # THREE ANSWERS THAT ARE WRONG RATHER THAN ABSENT. # ============================================================================ # # These are upstream defects in 1.4.1, they are ours to report rather than to # fix, and each is asserted with the literal wrong value so a future release # that repairs one turns this suite red at the case that names it. # # gif-overflow A GIF's size is unpacked with `' and raises `FileNotFoundError' (`dpi-bytesio'). # # And `get' CLOSES what it was handed. The `finally' calls `fhandle.close()' # unconditionally, including on a buffer the caller still owns, so the BytesIO # is unusable afterwards -- `bytesio-closed-after' asserts that, because a # caller who reads the same buffer twice gets a `ValueError' from the second # read and no hint that this library is why. # # ============================================================================ # SVG IS THE ONLY CONTAINER WHOSE ANSWER IS A FLOAT. # ============================================================================ # # Every other branch unpacks integers out of a binary header. SVG's two values # go through `_convertToPx', which returns `float', so `get' on an SVG returns # `(90.0, 60.0)' where the docstring promises `Tuple[int, int]'. The probe keeps # ints and floats distinguishable in its output on purpose -- `90' and `90.0' # are the same number and not the same result -- and `svg-mm' asserts # `95.99999999999999', which is what `25.4mm' actually computes to and is not # 96.0. # # `svg-attrs-past-window' is the other SVG limit worth knowing: only the first # 1024 bytes are searched, so a valid SVG with a long comment ahead of its # attributes is refused outright. # # ============================================================================ # THE PACKAGE IMPORTS `xml.etree' AND NEVER USES IT. # ============================================================================ # # `from xml.etree import ElementTree' is line 5 of imagesize.py, and # `ElementTree' appears nowhere else in the file -- the SVG path is two # `re.search' calls. The import is still at module scope, which is why # `python3-xml' is a real RDEPENDS and not a spurious one: drop that split and # `import imagesize' fails outright rather than degrading on SVG alone. # # `has-elementtree-global' asserts the dead name is present in the module's # globals and `elementtree-importable' asserts the split is on this image, so a # reader is told which of the two facts a future failure moved. # # ============================================================================ # WHY THE FIXTURES ARE BUILT AND NOT SHIPPED. # ============================================================================ # # Upstream's own corpus is 1.6 MB of real images and is NOT installed (setup.py # declares `packages=["imagesize"]', so `test/' is not packaged -- `no-test-dir' # asserts that). The probe builds every fixture in memory instead, which is # possible precisely because this library reads only headers. # # The sizes are upstream's: its suite asserts 802x670 for its PNG, JPEG, JPEG # 2000, GIF and big-endian TIFF, and 800x600 for its little-endian TIFF, and the # headers built here report the same numbers. Building them also lets a case # vary ONE header field at a time, which is what the signed GIF width, the VP8X # canvas and the BigTIFF offset field each need and what a fixed corpus cannot # give. # # ============================================================================ P=python3-imagesize # The canary is first so that a suite which dies before its own first assertion # is distinguishable from one that never started. bst_ok $P/suite-canary require python3 $P/python3-present SITE=/usr/lib/python3.12/site-packages PKG=$SITE/imagesize DI=$SITE/imagesize-1.4.1.dist-info PKGLIST=/var/lib/dpkg/info/python3-imagesize.list PROBE=$BST_DATA/python3-imagesize-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-imagesize.$$ mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # ---------------------------------------------------------------- layout ----- # IT IS A PACKAGE, NOT A MODULE: site-packages holds the DIRECTORY imagesize/ # with the implementation in a submodule of the same name. A release that # flattened it to one file would still import and still pass every behavioural # case below, so the shape is asserted on its own. check $P/site-packages-dir test -d "$SITE" check $P/package-dir test -d "$PKG" check $P/no-flat-module test ! -f "$SITE/imagesize.py" check $P/init-file test -s "$PKG/__init__.py" check $P/submodule-file test -s "$PKG/imagesize.py" # Upstream's 1.6 MB image corpus is not packaged -- see the header. This is a # size assertion as much as a layout one. check $P/no-test-dir test ! -d "$SITE/test" check $P/no-py-typed test ! -e "$PKG/py.typed" check $P/pycache-init test -s "$PKG/__pycache__/__init__.cpython-312.pyc" check $P/pycache-mod test -s "$PKG/__pycache__/imagesize.cpython-312.pyc" # ------------------------------------------------------------- the .deb ------ cat "$PKGLIST" > "$W/pkgf" 2>/dev/null || : > "$W/pkgf" check $P/pkg-list-present test -s "$W/pkgf" # PURE PYTHON, AND NOT MERELY "NO .so OF ITS OWN". 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. # imagesize ships no C -- no .c, no .pyx, no ext_modules in setup.py -- so this # is a REGRESSION TRIPWIRE on a version bump rather than a statement about today. check_eq $P/pkg-no-so "" \ "$(grep -E '\.so($|\.)' "$W/pkgf" 2>/dev/null)" # ...and nothing but Python in the payload, which is the same claim from the # other side: a data file or a script appearing here would be a packaging change # nobody asked for. _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" check $P/dist-info-dir test -d "$DI" check $P/dist-info-metadata test -f "$DI/METADATA" check_out $P/dist-info-version "Version: 1.4.1" cat "$DI/METADATA" check_out $P/dist-info-name "Name: imagesize" cat "$DI/METADATA" check $P/dist-info-license test -s "$DI/LICENSE.rst" check $P/dist-info-record test -s "$DI/RECORD" # `setuptools3' BUILT THIS, WHICH IS VISIBLE IN THE WHEEL METADATA and is the # half of the recipe's `inherit' line that leaves a trace on the rootfs. The # `py2-none-any' tag is upstream's `universal = 1' in setup.cfg -- a 2026 package # still declaring a Python 2 wheel tag, which is worth pinning so that a # repackaging shows up here rather than silently. check_out $P/dist-info-generator "Generator: bdist_wheel" cat "$DI/WHEEL" 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" check_out $P/dist-info-tag-py2 "Tag: py2-none-any" cat "$DI/WHEEL" check_out $P/dist-info-top-level "imagesize" cat "$DI/top_level.txt" sed -n '/^Package: python3-imagesize$/,/^$/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: 1.4.1-r0" cat "$W/st" # BOTH RDEPENDS ARE SUB-PACKAGES OF THE PORTED python3 (BRINTOS-1476) and both # are already on this image. `python3-core' comes from setuptools3-base and # carries io, os, re and struct; `python3-xml' is declared by the recipe and is # needed for the module-scope `xml.etree' import alone (see the header). The # line is asserted exactly, so a lean image that dropped `python3-xml' would say # so here once rather than present as a broken library. grep '^Depends:' "$W/st" > "$W/dep" 2>/dev/null || : > "$W/dep" check $P/pkg-depends-line test -s "$W/dep" check_eq $P/pkg-depends-exact \ "Depends: python3-core, python3-xml" \ "$(cat "$W/dep")" # ---------------------------------------------------------------- import ----- check $P/import python3 -c 'import imagesize' # A DeprecationWarning or a stray print on import would be invisible to the case # above, which scores only the exit status. check_eq $P/import-quiet "" \ "$(python3 -c 'import imagesize' 2>&1)" # ----------------------------------------------------------------- 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 # The probe's own two bookends. `probe-complete' says it reached its last line # and `probe-count' says it emitted every key it meant to: a probe that died # two-thirds of the way through would otherwise leave the missing cases scoring # against an empty string, which `check_eq' would read as a wrong answer rather # than as an absent one. check_eq $P/probe-complete "yes" "$(pv probe_done)" check_eq $P/probe-count "98" "$(pv emitted_count)" # -------------------------------------------------------------- identity ----- # WHICH COPY ANSWERED. Every behavioural case below is a statement about the # package named here. check_eq $P/package-answered "$PKG/__init__.py" "$(pv package_file)" check_eq $P/submodule-answered "$PKG/imagesize.py" "$(pv submodule_file)" check_eq $P/is-package "yes" "$(pv is_package)" check_eq $P/version-attr "1.4.1" "$(pv version_attr)" check_eq $P/dist-version "1.4.1" "$(pv dist_version)" # THE EXPORTED SURFACE IS THREE NAMES, and `__version__' is one of them -- # unusual, and it means `from imagesize import *' rebinds the caller's # `__version__'. The list is the contract, so it is asserted in order. check_eq $P/all-names "get,getDPI,__version__" "$(pv all_names)" check_eq $P/all-count "3" "$(pv all_count)" check_eq $P/has-get "yes" "$(pv has_get)" check_eq $P/has-getdpi "yes" "$(pv has_getDPI)" check_eq $P/has-version "yes" "$(pv has___version__)" # The dead `xml.etree' import and the split it makes load-bearing -- see header. check_eq $P/has-elementtree-global "yes" "$(pv has_elementtree_global)" check_eq $P/elementtree-importable "yes" "$(pv elementtree_importable)" # ------------------------------------------------------- get(): containers --- # Upstream's own corpus asserts 802x670 for these five; the headers are built by # the probe and report the same numbers. check_eq $P/gif87a "802x670" "$(pv gif87a)" check_eq $P/gif89a "802x670" "$(pv gif89a)" check_eq $P/png "802x670" "$(pv png)" check_eq $P/jpeg-baseline "802x670" "$(pv jpeg_baseline)" check_eq $P/jp2 "802x670" "$(pv jp2)" # INTERLACING AND PROGRESSIVE SCAN DO NOT MOVE THE SIZE FIELD, which is why this # library can ignore both. An interlaced PNG differs from `png' above in one # IHDR byte after the size; a progressive JPEG is SOF2 rather than SOF0, and the # scan loop accepts any marker in 0xc0-0xcf except the three that are not SOFs. check_eq $P/png-interlaced "802x670" "$(pv png_interlaced)" check_eq $P/jpeg-progressive "802x670" "$(pv jpeg_progressive)" # A SECOND, OLDER PNG SHAPE. When the four bytes at offset 12 are not `IHDR' the # library falls back to reading the size straight from offset 8. Nothing in the # wild writes that any more, but the branch is live and untested upstream. check_eq $P/png-old-form "802x670" "$(pv png_old_form)" # AN EXIF BLOCK AHEAD OF THE SOF IS THE COMMON REAL-WORLD JPEG, and it is the # case the marker-walking loop exists for: the size is not at a fixed offset, so # the loop steps segment by segment past APP1 (and past APP0 when both are # present) until it reaches a SOF marker. check_eq $P/jpeg-exif-before-sof "802x670" "$(pv jpeg_exif_before_sof)" check_eq $P/jpeg-jfif-and-exif "802x670" "$(pv jpeg_jfif_and_exif)" # BOTH TIFF BYTE ORDERS, and both size field types. See `tiff-le-byte-type' # below for why the two branches are not symmetric. check_eq $P/tiff-be-long "802x670" "$(pv tiff_be_long)" check_eq $P/tiff-be-short "802x670" "$(pv tiff_be_short)" check_eq $P/tiff-le-long "800x600" "$(pv tiff_le_long)" check_eq $P/tiff-le-short "800x600" "$(pv tiff_le_short)" check_eq $P/bigtiff-le "800x600" "$(pv bigtiff_le)" # THE TWO TIFF BRANCHES DO NOT VALIDATE ALIKE. The big-endian branch checks the # field's data type and refuses anything that is not SHORT or LONG; the # little-endian branch reads the value field whatever the type claims, so a # BYTE-typed entry it should reject is read as a LONG and answers. That also # means `tiff-le-short' above is right by accident -- on a little-endian TIFF a # SHORT lands in the low half of the field, which is where the LONG read looks. check_eq $P/tiff-be-byte-type "!ValueError" "$(pv tiff_be_byte_type)" check_eq $P/tiff-le-byte-type "800x600" "$(pv tiff_le_byte_type)" # --------------------------------------------------------- get(): the bugs --- # Asserted at their WRONG values -- see the header. Each is an upstream defect # in 1.4.1; a release that fixes one turns the case that names it red, which is # the intent. check_eq $P/gif-overflow "-25536x100" "$(pv gif_overflow)" check_eq $P/webp-vp8x "801x669" "$(pv webp_vp8x)" check_eq $P/svg-pt "1152.0x1152.0" "$(pv svg_pt)" check_eq $P/svg-pt-is-pc "yes" "$(pv px_pt_equals_pc)" check_eq $P/px-pt "1152.0" "$(pv px_pt)" check_eq $P/px-pc "96.0" "$(pv px_pc)" check_eq $P/bigtiff-bad-offsetsize "!UnboundLocalError" "$(pv bigtiff_bad_offsetsize)" # ------------------------------------------------------------- get(): SVG ---- # THE ONLY CONTAINER WHOSE ANSWER IS A FLOAT. `svg-cm', `svg-inch' and `svg-pc' # are three spellings of the same 96 px, which is the whole of the unit table # that is correct; `svg-mm' is the same length again and does NOT come out at # 96.0, because 25.4 * 96 / 2.54 / 10 is not exact in binary floating point. check_eq $P/svg-unitless "90.0x90.0" "$(pv svg_unitless)" check_eq $P/svg-px "90.0x90.0" "$(pv svg_px)" check_eq $P/svg-cm "96.0x96.0" "$(pv svg_cm)" check_eq $P/svg-mm "95.99999999999999x95.99999999999999" "$(pv svg_mm)" check_eq $P/svg-inch "96.0x96.0" "$(pv svg_inch)" check_eq $P/svg-pc "96.0x96.0" "$(pv svg_pc)" check_eq $P/svg-fraction "90.5x90.5" "$(pv svg_fraction)" check_eq $P/svg-no-decl "90.0x60.0" "$(pv svg_no_decl)" # `[^-]width="' MEANS THE ATTRIBUTE NEEDS A CHARACTER IN FRONT OF IT, and that # character is what stops `data-width' being read as `width'. Both halves: the # decoy is skipped, and a newline is an acceptable preceding character. check_eq $P/svg-decoy-attr "90.0x60.0" "$(pv svg_decoy_attr)" check_eq $P/svg-newline-before-attr "90.0x60.0" "$(pv svg_newline_before_attr)" # Only the first 1024 bytes are searched, so a valid SVG whose attributes sit # past a long leading comment is refused rather than parsed. check_eq $P/svg-attrs-past-window "!ValueError" "$(pv svg_attrs_past_window)" # The unit table's edges, read off the helper directly. check_eq $P/px-unknown-unit "ValueError" "$(pv px_unknown_unit)" check_eq $P/px-empty "TypeError" "$(pv px_empty)" # ---------------------------------------------------------- get(): Netpbm ---- # All six magic numbers, ASCII (P1-P3) and binary (P4-P6) alike, plus the # comment-skipping the header parser has to do. check_eq $P/netpbm-p1 "802x670" "$(pv netpbm_P1)" check_eq $P/netpbm-p2 "802x670" "$(pv netpbm_P2)" check_eq $P/netpbm-p3 "802x670" "$(pv netpbm_P3)" check_eq $P/netpbm-p4 "802x670" "$(pv netpbm_P4)" check_eq $P/netpbm-p5 "802x670" "$(pv netpbm_P5)" check_eq $P/netpbm-p6 "802x670" "$(pv netpbm_P6)" check_eq $P/netpbm-comment "802x670" "$(pv netpbm_comment)" # P7 IS PAM and is not in the `123456' set, so it takes the silent (-1, -1) path # rather than being refused -- the same hole as `unknown-bytes', inside a family # the library otherwise supports. check_eq $P/netpbm-p7 "-1x-1" "$(pv netpbm_P7)" # A lone `P' and a P6 with no dimensions DO raise, because they enter the branch # and then run out of file inside it. check_eq $P/netpbm-bare-p "!ValueError" "$(pv netpbm_bare_P)" check_eq $P/netpbm-no-dims "!ValueError" "$(pv netpbm_no_dims)" # ------------------------------------------------------------ get(): WebP ---- check_eq $P/webp-vp8 "802x670" "$(pv webp_vp8)" check_eq $P/webp-vp8l "802x670" "$(pv webp_vp8l)" check_eq $P/webp-bad-codec "!ValueError" "$(pv webp_bad_codec)" # ------------------------------------------- get(): the silent (-1,-1) path -- # The headline finding. See the header for why each of these five is listed # separately. check_eq $P/unknown-bytes "-1x-1" "$(pv unknown_bytes)" check_eq $P/empty-file "-1x-1" "$(pv empty_file)" check_eq $P/text-file "-1x-1" "$(pv text_file)" check_eq $P/truncated-png "-1x-1" "$(pv truncated_png)" check_eq $P/truncated-gif "-1x-1" "$(pv truncated_gif)" # ...and the refusals, so the boundary is explicit. check_eq $P/truncated-jpeg "!ValueError" "$(pv truncated_jpeg)" check_eq $P/svg-no-width "!ValueError" "$(pv svg_no_width)" check_eq $P/missing-file "!FileNotFoundError" "$(pv missing_file)" # ------------------------------------------------------- get(): file-like ----- check_eq $P/bytesio-png "802x670" "$(pv bytesio_png)" check_eq $P/bytesio-closed-after "yes" "$(pv bytesio_closed_after)" check_eq $P/open-handle "!TypeError" "$(pv open_handle)" check_eq $P/bytes-path "802x670" "$(pv bytes_path)" # THE HEADER IS ALL THAT IS READ -- the property the library exists for, and the # only one here that no other case would catch. check_eq $P/readsize-png-answer "802x670" "$(pv readsize_png_answer)" check_eq $P/readsize-png-bytes "31" "$(pv readsize_png_bytes)" check_eq $P/readsize-png-total "1000049" "$(pv readsize_png_total)" check_eq $P/readsize-gif-answer "802x670" "$(pv readsize_gif_answer)" check_eq $P/readsize-gif-bytes "31" "$(pv readsize_gif_bytes)" check_eq $P/readsize-gif-total "1000035" "$(pv readsize_gif_total)" check_eq $P/readsize-jpeg-answer "802x670" "$(pv readsize_jpeg_answer)" check_eq $P/readsize-jpeg-bytes "39" "$(pv readsize_jpeg_bytes)" check_eq $P/readsize-jpeg-total "1000017" "$(pv readsize_jpeg_total)" # ----------------------------------------------------------------- getDPI ---- check_eq $P/dpi-jfif-inch "300x300" "$(pv dpi_jfif_inch)" check_eq $P/dpi-jfif-cm "300x300" "$(pv dpi_jfif_cm)" check_eq $P/dpi-png-metre "72x72" "$(pv dpi_png_metre)" check_eq $P/dpi-bytes-path "72x72" "$(pv dpi_bytes_path)" # THE ASPECT-RATIO UNIT IS REPORTED AS A RESOLUTION -- see the header. These two # sit beside `dpi-png-metre' above deliberately: `dpi-png-unitless' returns the # identical 72x72 from a file that declares no resolution at all. check_eq $P/dpi-jfif-ratio "1x1" "$(pv dpi_jfif_ratio)" check_eq $P/dpi-png-unitless "72x72" "$(pv dpi_png_unitless)" # The honest absences. check_eq $P/dpi-jpeg-no-app0 "-1x-1" "$(pv dpi_jpeg_no_app0)" check_eq $P/dpi-gif "-1x-1" "$(pv dpi_gif)" check_eq $P/dpi-unknown "-1x-1" "$(pv dpi_unknown)" # `getDPI' HAS NO FILE-LIKE PATH, unlike `get'. check_eq $P/dpi-bytesio "!FileNotFoundError" "$(pv dpi_bytesio)" # ------------------------------------------------------- the unit helpers ----- # `_UNIT_INCH' AND `_UNIT_UM' ARE THE SAME CONSTANT -- both 6, the later # assignment winning -- so the JPEG 2000 resolution box's inch unit takes the # micrometre branch and a density of 10 becomes 254000. Dead as written, and # asserted because the NAME suggests an inch case exists. check_eq $P/unit-um "6" "$(pv unit_um)" check_eq $P/unit-inch "6" "$(pv unit_inch)" check_eq $P/unit-collide "yes" "$(pv unit_collide)" check_eq $P/convert-dpi-unit6 "254000" "$(pv convert_dpi_unit6)" # An unrecognised unit code is returned unconverted rather than refused. check_eq $P/convert-dpi-unit99 "10" "$(pv convert_dpi_unit99)" rm -rf "$W"