. "$BST_LIB" # BST-TIMEOUT: 1200 # THREE python3 starts, and they are the whole cost. # # # # They are: a bare `import', a second import whose STDERR is # # the assertion (`import-quiet'), and the probe, which takes # # ONE start for eighty-odd measurements. Every other case # # below reads the filesystem or dpkg with shell builtins, or # # greps the probe's output. # # # # There is NO fourth for a shipped executable: setup.cfg # # declares no `[options.entry_points]', so nothing lands in # # /usr/bin and there is nothing to reach through a shebang. # # `no-bin-script' asserts that absence rather than leaving # # it unstated. # # # # The work inside the probe start is ~2 KB written to and # # read back from a directory -- microseconds beside an # # interpreter start on this arm, which is tens of seconds # # before the first line of the program runs. Nothing here is # # quadratic in anything: the largest payload is 250 bytes. # # # # 1200 is 400s per start, matching 99-python3-pyparsing's # # budget for its own three and roughly six times the ~60s a # # python3 start has measured at on this arm. # # # # 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-multivolumefile 0.2.3 (meta-openembedded/meta-python # recipes-devtools/python/python3-multivolumefile_0.2.3.bb -- `inherit # python_setuptools_build_meta pypi', PyPI sdist) on the wasm32-hwjs glibc image. # # ============================================================================ # WHAT THE PACKAGE DOES. # ============================================================================ # # A FILE-LIKE OBJECT OVER A SET OF NUMBERED VOLUMES. Write to it and the bytes # are split across `name.0001', `name.0002' ... at a stated volume size; open it # for reading and the volumes are presented as one stream, seekable across the # boundaries. It is the wrapper an archiver uses to produce split archives # without knowing anything about splitting -- py7zr's, in practice. # # It is the one member of this round whose subject is the FILESYSTEM rather than # memory alone, which is why `rootfs-*' below repeats a round trip on the image's # own virtiofs root instead of measuring only the tmpfs work dir. # # ============================================================================ # WHY IT PORTS, AND WHAT IT WAITS ON. # ============================================================================ # # PURE PYTHON, AND THE DEPENDENCY SET IS COMPLETE AS WRITTEN. 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 installs cleanly and # cannot be imported -- which is exactly what BRINTOS-2046 records for the # extension-bearing members of this Epic. multivolumefile declares no # ext_modules, ships no `.so' (`pkg-no-so' asserts it off the shipped file list), # and its `Depends' name only sub-packages of the ported python3: python3-core, # python3-io and python3-mmap. So this port waits on no other package and carries # no red attributable to BRINTOS-2046. # # `mmap' is in that set and is never imported at runtime by anything this suite # reaches: `from mmap import mmap' at the top of `__init__.py' is there for a # TYPE ANNOTATION on `readinto' and `write'. It is a genuine import all the same # -- a missing python3-mmap would fail the bare `import' below -- so it is # asserted in the Depends set rather than argued away. # # ============================================================================ # THE THREE FINDINGS, ALL UPSTREAM'S AND NONE OF THEM THIS ARM'S. # ============================================================================ # # Each was MEASURED against the same 0.2.3 sources on an x86-64 control before it # was written down here, so a reader can tell a property of the package from a # property of the platform. None is filed: they are upstream 0.2.3's behaviour on # every platform, and this port neither introduces nor can fix them. # # 1. `read(n)' DOES NOT CROSS A VOLUME BOUNDARY. It reads from the current volume # only and returns short at every boundary -- 100, 100, 50, 0 for a 250-byte # set read in 150-byte asks. A caller who treated the first short return as # end of file would silently lose 150 of the 250 bytes. `readall()' is the # method that loops, which is why every round trip here goes through it. # `readinto' inherits the rule exactly. # # 2. A MISSING MIDDLE VOLUME DOES NOT RAISE. The reader is built by # `glob(basename + ".*")' + `sorted()' with no continuity check anywhere, so a # set whose `.0002' is gone opens happily and returns volume 1 concatenated # with volume 3 -- 150 bytes where 250 were written, indistinguishable from a # shorter archive. This port's own ticket predicted a raise; the measurement # says otherwise, and the measurement is what ships. # # 3. A WRITER'S `stat()' IS NOMINAL, NOT ACTUAL. `_init_writer' and `_add_volume' # record the VOLUME SIZE in each `_FileInfo' rather than the bytes written, so # a 250-byte payload in three 100-byte volumes reports 300 through a writer and # 250 through a reader. It over-reports rather than truncating, which is the # harmless direction, and `writer-stat-size' pins it so a later release that # fixed it is visible as a change. # # The same glob-and-sort that produces finding 2 also swallows an unrelated file # sharing the prefix -- `pollution-*' -- so both are recorded together. P=python3-multivolumefile # ============================================================================ # The suite proper. # ============================================================================ # # `suite-canary' is emitted FIRST and unconditionally. A suite that dies before # its first result line is indistinguishable from one that was never scheduled, # so this one line is what separates "ran and found nothing" from "did not run". bst_ok $P/suite-canary require python3 $P/python3-present SITE=/usr/lib/python3.12/site-packages PKG=$SITE/multivolumefile DI=$SITE/multivolumefile-0.2.3.dist-info PKGLIST=/var/lib/dpkg/info/python3-multivolumefile.list PROBE=$BST_DATA/python3-multivolumefile-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-mvf.$$ mkdir -p "$W" "$W/py" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # ---------------------------------------------------------------- packaging -- # WHERE THE PACKAGING SAYS THE MODULE LANDS. These run before any import, so a # red here says the .deb is wrong rather than that the module misbehaves. check $P/site-packages-dir test -d "$SITE" check $P/package-dir test -d "$PKG" check $P/package-init test -s "$PKG/__init__.py" # It is a PACKAGE, not a single module. A release that flattened it would still # import and still pass every behavioural case, so the shape is asserted directly. check $P/no-flat-module test ! -f "$SITE/multivolumefile.py" # The whole payload: two modules, the typing marker and the stub. `stat.py' holds # the synthetic `stat_result' the `stat()' cases below read. check $P/module-stat test -s "$PKG/stat.py" # THE TYPE STUB AND THE MARKER, which `[options.package_data]' ships as package # data. Neither affects a single runtime case, which is exactly why each needs # its own assertion -- a packaging change that dropped them would be invisible # otherwise. Note the asymmetry: `__init__.pyi' ships and there is NO `stat.pyi', # which is upstream's own shape. check $P/pyi-stub test -s "$PKG/__init__.pyi" check $P/py-typed test -f "$PKG/py.typed" check $P/no-stat-stub test ! -e "$PKG/stat.pyi" # NO EXECUTABLE. setup.cfg declares no `[options.entry_points]', so nothing lands # in /usr/bin. Asserted so that a future release which started shipping one is # noticed here. check $P/no-bin-script test ! -e /usr/bin/multivolumefile # --------------------------------------------------------------- dist-info --- # The version is read from METADATA rather than from the module, so a mismatch # between what the packaging claims and what imports is visible as a mismatch. # There is no `__version__' to read from the module in the first place -- # `has-version-attr' below is where that is recorded. check $P/dist-info-dir test -d "$DI" check $P/dist-info-metadata test -f "$DI/METADATA" check_out $P/dist-info-version "Version: 0.2.3" cat "$DI/METADATA" check_out $P/dist-info-name "Name: multivolumefile" cat "$DI/METADATA" check $P/dist-info-wheel test -f "$DI/WHEEL" check $P/dist-info-record test -f "$DI/RECORD" check_out $P/dist-info-toplevel "multivolumefile" cat "$DI/top_level.txt" # PURE PYTHON, stated by the packaging itself. `Root-Is-Purelib: true' is the # wheel's own claim that nothing in it is architecture-specific, which is the # property that makes this package portable to this arm at all; `Tag: py3-none-any' # is the same claim in the filename's coordinates. check_out $P/dist-info-purelib "Root-Is-Purelib: true" cat "$DI/WHEEL" check_out $P/dist-info-tag "Tag: py3-none-any" cat "$DI/WHEEL" check_out $P/dist-info-generator "Generator: bdist_wheel" cat "$DI/WHEEL" # ------------------------------------------------------------ the .deb file --- # The installed file list, read once into $W. The copy is what the loop below # redirects from: a stdin redirect the top-level shell performs on the virtiofs # root makes dup2(2) return 513 and ENDS the suite silently (brintos/linux#73), # and $W is on tmpfs. cat "$PKGLIST" > "$W/pkgf" 2>/dev/null || : > "$W/pkgf" check $P/pkg-list-present test -s "$W/pkgf" # NO COMPILED EXTENSION, asserted off the shipped file list rather than off the # source. This image's python3 has an empty lib-dynload and no dynamic loader # (BRINTOS-1476), so a single .so here would mean a package that installs # cleanly and cannot import -- the BRINTOS-2046 shape. check_eq $P/pkg-no-so "" \ "$(grep -c '\.so$' "$W/pkgf" 2>/dev/null | grep -v '^0$')" # Every payload path the package ships is under site-packages, and is a .py, a # .pyc, the stub, or the typing marker. Anything else is a file this port did not # expect to be shipping and wants looking at. _bad="" while IFS= read -r _f; do case "$_f" in ''|/usr/lib/python3.12/site-packages) continue ;; */) continue ;; esac [ -d "$_f" ] && continue case "$_f" in *.py|*.pyc|*.pyi|*/py.typed) ;; # dist-info metadata. Which of these the build backend writes is a property # of SETUPTOOLS rather than of this package, so the whole standard set is # allowed: pinning the exact list would red on a setuptools change that says # nothing about multivolumefile. `pkg-no-so' above carries the real claim. */METADATA|*/WHEEL|*/RECORD|*/INSTALLER|*/LICENSE|*/entry_points.txt|*/top_level.txt) ;; *) _bad="$_bad $_f" ;; esac done < "$W/pkgf" check_eq $P/pkg-only-python "" "$_bad" # ------------------------------------------------------------- dpkg status --- sed -n '/^Package: python3-multivolumefile$/,/^$/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.2.3-r0" cat "$W/st" check_out $P/pkg-arch "Architecture: wasm32" cat "$W/st" # THE DEPENDENCY SET IS THE PORT'S CLAIM THAT IT WAITS ON NOTHING. Every name # here is a sub-package of the ported python3 (BRINTOS-1476), so each is asserted # individually -- a bare "the Depends line exists" would pass on a stanza that # had lost one of them, and the loss would surface as an ImportError at the first # use that reached that stdlib module. grep '^Depends:' "$W/st" > "$W/dep" 2>/dev/null || : > "$W/dep" check $P/pkg-depends-line test -s "$W/dep" for _d in python3-core python3-io python3-mmap; do check_out $P/pkg-depends-$_d "$_d" cat "$W/dep" done # ------------------------------------------------------------------ import --- # THE FIRST OF THREE PYTHON STARTS. If the Depends set above were short by one # sub-package, this is the case that would say so. check $P/import python3 -c 'import multivolumefile' # THE SECOND. A package that imports but WARNS on the way in is a different # defect from one that fails, and it is invisible to the case above because the # exit status is 0 either way. The assertion is that stderr is EMPTY. check_eq $P/import-quiet "" \ "$(python3 -c 'import multivolumefile' 2>&1 >/dev/null)" # ------------------------------------------------- a directory on the ROOTFS --- # EVERY OTHER CASE RUNS ON TMPFS, and for this package that would leave its own # subject untested: a volume boundary is only meaningful against real files, and # the mount an archiver on this target would split across is the virtiofs root, # not $TMPDIR. # # /var/tmp is a symlink into a /var/volatile that nothing mounts on this image # and /root does not exist -- /home/root is the home directory (84-iozone3 # records the same three candidates for the same reason). An unwritable or # missing candidate is not a red: the probe reports `rootfs_dir_used=no' and the # rootfs cases below skip. RFS= for _d in /home/root /var/lib /usr/lib/brintos-tests; do [ -d "$_d" ] || continue if mkdir -p "$_d/bst-mvf.$$" 2>/dev/null; then RFS="$_d/bst-mvf.$$"; break; fi done check_eq $P/rootfs-dir-found "yes" "$([ -n "$RFS" ] && echo yes || echo no)" # ------------------------------------------------------------------- probe --- if [ ! -f "$PROBE" ]; then bst_notok $P/probe-present "probe missing at $PROBE" rm -rf "$W" ${RFS:+"$RFS"} exit 0 fi bst_ok $P/probe-present # THE THIRD START, and the one that carries the suite. Bounded at 900s inside the # 1200s suite budget so that a hung probe is reported as a probe overrun with the # last line it printed, rather than as a suite timeout that says nothing about # where it stopped. bst_probe $P/probe-run "$W/probe" 900 python3 "$PROBE" "$W/py" "$RFS" OUT="$W/probe" # pv : the value the probe emitted for , or empty. `head -n 1' because # a key is emitted once and a duplicate would otherwise concatenate; `cut -f2-' # because several values legitimately contain `='. pv() { grep "^$1=" "$OUT" 2>/dev/null | head -n 1 | cut -d= -f2-; } # The probe reached its end. Every case below reads a value the probe printed, so # a probe that stopped early would otherwise turn into a run of empty-string # mismatches that all name the wrong thing. check_eq $P/probe-complete "yes" "$(pv probe_done)" check_eq $P/probe-count "88" "$(pv emitted_count)" # ---------------------------------------------------------------- identity --- # WHICH COPY ANSWERED. Without this the behavioural cases below would be # statements about whatever was first on sys.path. check_eq $P/module-answered "$PKG/__init__.py" "$(pv module_file)" check_eq $P/is-package "yes" "$(pv is_package)" check_eq $P/dist-version "0.2.3" "$(pv dist_version)" # NO `__version__' ATTRIBUTE, and that is the package's shape rather than a gap # in this port: setup.cfg carries the version through setuptools-scm into the # packaging alone and nothing writes it back into the module. A caller reaching # for `mvf.__version__' gets an AttributeError, so `dist-version' above is the # only version this package answers with. check_eq $P/has-version-attr "no" "$(pv has_version_attr)" check_eq $P/all-names "MultiVolume,open,stat_result" "$(pv all_names)" # `open' is a FACTORY, not a distinct implementation. check_eq $P/open-returns "MultiVolume" "$(pv open_returns)" check_eq $P/is-rawiobase "yes" "$(pv is_rawiobase)" check_eq $P/is-contextmanager "yes" "$(pv is_contextmanager)" # ------------------------------------------------------- the volume boundary -- # THE FOUR SPLITS, asserted individually rather than sampled: a payload short of # one volume, an EXACT MULTIPLE, one byte over a boundary, and a single write # spanning three volumes. Each carries the file names AND the size of every # volume, so a short final volume is pinned by size rather than by existence. # # `exact-volumes' is the case that answers the question the package's own # documentation does not: at an exact multiple there is NO empty trailing volume. # `write()' allocates the next one only when the incoming bytes are strictly more # than the remaining room, so 200 bytes at a volume size of 100 is two volumes # and not three. check_eq $P/small-volumes "small.0001:40" "$(pv small_volumes)" check_eq $P/exact-volumes "exact.0001:100 exact.0002:100" "$(pv exact_volumes)" check_eq $P/over-volumes "over.0001:100 over.0002:100 over.0003:1" "$(pv over_volumes)" check_eq $P/span3-volumes "span3.0001:100 span3.0002:100 span3.0003:50" "$(pv span3_volumes)" # THE ROUND TRIP. Each payload byte is a function of its offset, so a set # reassembled in the wrong order would not compare equal -- which is what makes # `yes' here a statement about ordering as well as about content. check_eq $P/small-roundtrip "yes" "$(pv small_roundtrip)" check_eq $P/exact-roundtrip "yes" "$(pv exact_roundtrip)" check_eq $P/over-roundtrip "yes" "$(pv over_roundtrip)" check_eq $P/span3-roundtrip "yes" "$(pv span3_roundtrip)" check_eq $P/small-readall-len "40" "$(pv small_readall_len)" check_eq $P/exact-readall-len "200" "$(pv exact_readall_len)" check_eq $P/over-readall-len "201" "$(pv over_readall_len)" check_eq $P/span3-readall-len "250" "$(pv span3_readall_len)" # ------------------------------------------------ read() and the boundary --- # FINDING 1, from both sides. `read(n)' reads the CURRENT VOLUME ONLY: four asks # of 150 bytes over a 250-byte set return 100, 100, 50 and 0. A caller who # treated the first short return as end of file would lose 150 of the 250 bytes. check_eq $P/read-does-not-cross "100,100,50,0" "$(pv read150_lengths)" check_eq $P/read-final-tell "250" "$(pv read150_tell)" # SEEK, inside each volume and then across a boundary. Each seek is followed by a # READ compared against the payload, because a seek that landed in the right # volume at the wrong offset would still report the right position. check_eq $P/seek0-tell "0" "$(pv seek0_tell)" check_eq $P/seek-vol1-pos "37" "$(pv seek_in_vol1_pos)" check_eq $P/seek-vol1-read "yes" "$(pv seek_in_vol1_read)" check_eq $P/seek-vol2-pos "120" "$(pv seek_in_vol2_pos)" check_eq $P/seek-vol2-read "yes" "$(pv seek_in_vol2_read)" check_eq $P/seek-vol3-pos "225" "$(pv seek_in_vol3_pos)" check_eq $P/seek-vol3-read "yes" "$(pv seek_in_vol3_read)" # THE SAME FINDING REACHED FROM THE OTHER SIDE: a read starting 5 bytes before # the end of volume 1 and asking for 20 gets 5, not 20. check_eq $P/seek-across-pos "95" "$(pv seek_across_pos)" check_eq $P/seek-across-len "5" "$(pv seek_across_len)" check_eq $P/seek-across-bytes "yes" "$(pv seek_across_bytes)" # SEEK_CUR and SEEK_END take different arms of `seek()'; SEEK_END is computed off # the LAST position, which for a reader is the total size. check_eq $P/seek-cur-pos "5" "$(pv seek_cur_pos)" check_eq $P/seek-end-pos "220" "$(pv seek_end_pos)" check_eq $P/seek-end-read-len "30" "$(pv seek_end_read_len)" # THE SHORT READ AT THE VERY END returns what remains rather than raising, and # the read after it returns empty rather than raising. check_eq $P/tail-pos "245" "$(pv tail_pos)" check_eq $P/tail-len "5" "$(pv tail_len)" check_eq $P/after-eof-len "0" "$(pv after_eof_len)" # SEEK PAST THE END is neither refused nor clamped: the position is accepted as # given and the read there is empty, so `tell()' cannot be used to discover the # size after one of these. check_eq $P/seek-past-eof-pos "10000" "$(pv seek_past_eof_pos)" check_eq $P/seek-past-eof-read-len "0" "$(pv seek_past_eof_read_len)" check_eq $P/seek-past-eof-tell "10000" "$(pv seek_past_eof_tell)" # `readinto' is `read' underneath and inherits the boundary rule exactly. check_eq $P/readinto-n "100" "$(pv readinto_n)" check_eq $P/readinto-bytes "yes" "$(pv readinto_bytes)" # ------------------------------------------------------------------- stat() --- # FINDING 3. The two `stat()' answers disagree and which one a caller gets # depends on the mode it opened in: a WRITER sums the nominal volume size per # volume (300 for a 250-byte payload in three 100-byte volumes), a READER sums # the volumes' actual sizes (250). It over-reports rather than truncating, which # is the harmless direction. check_eq $P/writer-stat-size "300" "$(pv writer_stat_size)" check_eq $P/writer-tell "250" "$(pv writer_tell)" check_eq $P/writer-writable "True" "$(pv writer_writable)" check_eq $P/reader-stat-size "250" "$(pv reader_stat_size)" check_eq $P/reader-writable "False" "$(pv reader_writable)" check_eq $P/reader-seekable "True" "$(pv reader_seekable)" check_eq $P/reader-readable "True" "$(pv reader_readable)" check_eq $P/isatty "False" "$(pv isatty)" # `st_ino' is hard-coded 0 -- a synthetic stat over a SET of files has no single # inode to report -- while `st_mode' passes the first volume's through, so a # caller's S_ISREG test still answers. check_eq $P/stat-ino "0" "$(pv stat_ino)" check_eq $P/stat-isreg "yes" "$(pv stat_isreg)" check_eq $P/closed-after-close "True" "$(pv closed_after_close)" check_eq $P/readable-after-close "False" "$(pv readable_after_close)" # ------------------------------------------------- a volume set with a HOLE --- # FINDING 2, AND THE ONE THIS PORT'S OWN READING GOT WRONG BEFORE IT WAS # MEASURED. The ticket predicted that opening a set with a missing middle volume # would raise. It does not: there is no continuity check anywhere in # `_init_reader', so the set opens, `stat()' reports the smaller total, and # `readall()' returns volume 1 concatenated with volume 3 -- 150 bytes where 250 # were written, indistinguishable from a shorter archive. check_eq $P/gap-before "gap.0001:100 gap.0002:100 gap.0003:50" "$(pv gap_before)" check_eq $P/gap-after "gap.0001:100 gap.0003:50" "$(pv gap_after)" check_eq $P/gap-open-no-raise "none" "$(pv gap_open_exc)" check_eq $P/gap-stat-size "150" "$(pv gap_stat_size)" check_eq $P/gap-read-len "150" "$(pv gap_read_len)" check_eq $P/gap-is-v1-plus-v3 "yes" "$(pv gap_is_v1_plus_v3)" # The same glob-and-sort from the other side: a file merely SHARING THE PREFIX is # swallowed into the set and its bytes appended to the stream -- 255 where 250 # were written. Same cause, same disposition: upstream's, recorded, not filed. check_eq $P/pollution-stat-size "255" "$(pv pollution_stat_size)" check_eq $P/pollution-read-len "255" "$(pv pollution_read_len)" # ---------------------------------------------------------- naming options --- # `ext_digits', `hex' and `ext_start' change the SUFFIX the writer allocates, and # `_add_volume' asserts the last suffix against the format it is about to extend # -- so a set written under one of these can only be extended under the same one. # An archiver picking a different width is the whole reason the knobs exist. check_eq $P/hex-volumes "hexy.001:100 hexy.002:100 hexy.003:50" "$(pv hex_volumes)" check_eq $P/wide-volumes "wide.000007:100 wide.000008:50" "$(pv wide_volumes)" # ------------------------------------------------------------------ append --- # `ab' reopens an existing set at its end and extends it, allocating a new volume # because the last one it found was already full. It is the ONE mode that is not # seekable, and `tell()' answers the total of the existing volumes the moment it # opens. check_eq $P/append-before "ap.0001:100 ap.0002:50" "$(pv append_before)" check_eq $P/append-not-seekable "False" "$(pv append_seekable)" check_eq $P/append-open-tell "150" "$(pv append_open_tell)" check_eq $P/append-after "ap.0001:100 ap.0002:100 ap.0003:10" "$(pv append_after)" check_eq $P/append-total-len "210" "$(pv append_total_len)" check_eq $P/append-prefix-intact "yes" "$(pv append_prefix_intact)" check_eq $P/append-tail "yes" "$(pv append_tail)" # ------------------------------------------------ the methods that say `no' --- # `io.RawIOBase' promises more surface than this package implements, and what it # declines is declined LOUDLY rather than silently returning something wrong. # `fileno()' is a RuntimeError on purpose -- a set of files has no single # descriptor -- while the line-oriented and truncating methods are # NotImplementedError. Asserted by exception TYPE, never by message: a message is # upstream prose and would red on a rewording that changes no behaviour. check_eq $P/fileno-exc "RuntimeError" "$(pv fileno_exc)" check_eq $P/readline-exc "NotImplementedError" "$(pv readline_exc)" check_eq $P/readlines-exc "NotImplementedError" "$(pv readlines_exc)" check_eq $P/truncate-exc "NotImplementedError" "$(pv truncate_exc)" check_eq $P/writelines-exc "NotImplementedError" "$(pv writelines_exc)" # A MODE THE PACKAGE DOES NOT KNOW is refused at construction -- and `mode=None', # which is what `open()' DEFAULTS to, is one of them. A caller who omits the mode # gets NotImplementedError rather than a reader, so the default in the signature # is not a usable default and this case says so. check_eq $P/mode-none-exc "NotImplementedError" "$(pv mode_none_exc)" check_eq $P/mode-bogus-exc "NotImplementedError" "$(pv mode_bogus_exc)" # `xb' over an EXISTING set raises FileExistsError, which is the one guard the # writer does perform. check_eq $P/xb-existing-exc "FileExistsError" "$(pv xb_existing_exc)" # THE DEFAULT VOLUME SIZE is 10 MiB, applied when `volume' is not given in a # write mode. Asserted as a number because an archiver that forgot to pass one # would otherwise silently produce a single-volume set and pass every round trip # above. check_eq $P/default-volume-size "10485760" "$(pv default_volume_size)" check_eq $P/default-volumes "dflt.0001:50" "$(pv default_volumes)" # ------------------------------------------------------------ on the ROOTFS --- # EVERY CASE ABOVE RAN ON TMPFS. This package's subject is the filesystem, so one # full round trip is repeated on the image's own virtiofs root -- the mount an # archiver on this target would actually split across. A missing or unwritable # candidate directory is a SKIP rather than a red: it says nothing about the # package. if [ "$(pv rootfs_dir_used)" = "yes" ]; then check_eq $P/rootfs-volumes "mvf.0001:100 mvf.0002:100 mvf.0003:50" "$(pv rootfs_volumes)" check_eq $P/rootfs-roundtrip "yes" "$(pv rootfs_roundtrip)" check_eq $P/rootfs-stat-size "250" "$(pv rootfs_stat_size)" else bst_skip $P/rootfs-volumes "no writable rootfs directory: $(pv rootfs_error)" bst_skip $P/rootfs-roundtrip "no writable rootfs directory" bst_skip $P/rootfs-stat-size "no writable rootfs directory" fi rm -rf "$W" [ -n "$RFS" ] && rm -rf "$RFS" :