. "$BST_LIB" # BST-TIMEOUT: 900 # ONE python3 start, and that is the whole cost. Every # # assertion below either reads the filesystem/dpkg with # # shell builtins or greps the single probe run's output. # # 99-python3-pathspec carries 900 for one start over a # # ten-path corpus; this probe does strictly less work after # # the start -- a dozen calls into a pure-Python function # # and three small file writes -- so the same bound is if # # anything generous. # # 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-calver 2022.6.26 (poky meta/recipes-devtools/python, # python3-calver_2022.6.26.bb, `inherit python_setuptools_build_meta ptest`) -- # THE SETUPTOOLS EXTENSION THAT GIVES A PACKAGE A CALENDAR VERSION, on the # wasm32-hwjs glibc image. # # ============================================================================ # WHY THIS PORT IS POSSIBLE AT ALL, AND WHAT THE .so CASES ARE REALLY SAYING. # ============================================================================ # # This image's python3 is ONE STATIC BINARY with an EMPTY lib-dynload and no # dynamic loader (BRINTOS-1476; 99-python3 asserts both directly). A module # carrying a compiled extension could therefore not be imported here -- not # "would be slower", could not be imported. calver is pure Python -- two source # files, one of them empty -- which is the entire reason it ports. # # So `module-no-so` and `pkg-no-so` are not boilerplate. They are a REGRESSION # TRIPWIRE on a version bump: calver grows no extension today, and the day a # release adds one these go red at INSTALL time rather than at `import calver` # on somebody else's ticket three rounds later. The assertion is made from BOTH # sides because the two fail differently -- a find catches a file the package # does not declare, the dpkg list catches a declared file that is not Python. # # ============================================================================ # THE EXTENSION IS DRIVEN, NOT MERELY IMPORTED -- AND THE CLOCK IS PINNED. # ============================================================================ # # calver's whole job is to stamp a package with `datetime.datetime.now()`. An # unpinned probe would therefore produce a different string every day and this # suite could assert nothing about it beyond its shape -- which is the one thing # a broken port would still get right. data/python3-calver-probe.py replaces # `calver.integration`'s own module-global `datetime` with a stub whose `now()` # returns a FIXED instant, 2026-03-07T14:05:09, and every generated version # below is then a constant. # # THE PIN'S DATE IS CHOSEN, NOT ARBITRARY: a SINGLE-DIGIT month and day. The # default format is `%Y.%m.%d` and both fields are zero-padded, so 2026-03-17 # would leave a plugin that had dropped the padding entirely green. # # NO setuptools IS INVOLVED, ON PURPOSE. `calver.integration.version(dist, # keyword, value)` is called by setuptools with a distribution object and # assigns to `dist.metadata.version`; the object is duck-typed, so the probe # supplies one. This image installs no setuptools, and the port's deliverable is # the plugin -- not setuptools' ability to find it. # # EVERY VALUE ASSERTED BELOW WAS MEASURED, on x86-64, against THIS EXACT PROBE # and calver at SRCREV 3268d8acf2c345f32a1c5f08ba25dc67f76cca81 with poky's # `0001-setup.py-hard-code-version.patch` applied -- the same source this # image's recipe builds (recorded 2026-08-31; kept in the ticket's design doc # and test-plan section BRINTOS-1836) -- then RE-MEASURED against this build's # own python3-calver_2022.6.26-r0_wasm32.deb, which agrees with the control on # every key but the module's path. They are not predictions and they are not # read off the source. What makes the control transferable is that all of it is # pure-Python and content-determined: `strftime` over a fixed datetime and a # handful of `isinstance` branches depend on the format string and the value, # not on pointer width, byte order or the platform string. Nothing # arch-dependent is asserted here at all. # # THE FIVE BEHAVIOURS THE TICKET CLOSES ON, one probe key each: # # use_calver=True the default format applied to the pinned date # use_calver="%Y.%m" a caller-supplied format, a different code arm # use_calver= the caller generates it and calver takes it verbatim # use_calver=False/None the version is LEFT ALONE -- see below # PKG-INFO present an already-dated sdist keeps its release date # # THE FALSY ARM IS THE ONE WORTH READING TWICE. `use_calver` is a keyword every # `setup()` in the process gets offered, so the plugin is called on projects # that never asked for it; a version assigned there would rewrite the version of # a package that did not opt in. The fixture's sentinel is the literal string # `unset`, and seeing it come back unchanged IS the assertion. # # `pin-roundtrip` closes a loop worth naming: poky pins this package's own # version for reproducibility by passing the LITERAL "2022.6.26" as the format # (`0001-setup.py-hard-code-version.patch`), and a format with no directives # strftimes to itself. So the plugin's behaviour and the recipe's PV are the # same code path, and `version` below reads the result of it out of the # installed metadata. # # ============================================================================ # WHAT IS NOT COVERED, ON PURPOSE. # ============================================================================ # # - A real `setup()` run. It would need setuptools on target, which this image # does not install, and it would measure setuptools' plugin discovery rather # than calver. The entry point is asserted from the installed metadata and # LOADED instead, which is the half of that discovery calver owns. # - python3-calver's ptest. The recipe inherits `ptest`, but `ptest` is not in # this distro's DISTRO_FEATURES, so the build produces NO python3-calver-ptest # package at all -- measured: the three .debs this recipe emits are the main # package, -dev and -dbg. There is nothing here to install or to skip. # - python3-calver-dev. Produced, and EMPTY on this build (measured: its data # archive carries `./` and nothing else), so the image does not name it. # - A `__version__` attribute. calver's `__init__.py` is EMPTY and carries # none -- the in-interpreter statement of the version is # `importlib.metadata.version("calver")`, which is what `version` asserts. # # THIS PORT CLOSES ON THIS SUITE: non-interactive, no manual browser pass, no # follow-on ticket under BRINTOS-780. P=python3-calver # THE CANARY, which asserts nothing about python3-calver. # # A suite's FIRST BST result line is lost between the guest's printf and the # host's read -- per-suite, deterministic, and NOT a defect in the host tool # (BRINTOS-1149 established that and filed it as linux-in-the-browser#177). The # harness computes its pass count as total-minus-failures, so THE DROPPED LINE # IS COUNTED AS A PASS. A nothing-case therefore stands in that position and the # eaten line costs nothing. It sits ahead of the `require` guard deliberately, # because a `require` that skips would emit its skip as the first line and be # eaten too. # # Remove this case when linux-in-the-browser#177 is fixed. bst_ok $P/suite-canary require python3 $P/python3-present SITE=/usr/lib/python3.12/site-packages CV=$SITE/calver DI=$SITE/calver-2022.6.26.dist-info PKGLIST=/var/lib/dpkg/info/python3-calver.list D=$BST_DATA PROBE=$D/python3-calver-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-calver.$$ mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # ------------------------------------------------------------ the payload --- # Where the module has to land. `python3 -c 'import calver'` further down is the # functional statement; these are the ones that say WHICH copy answered, so a # module that imported out of some other path is distinguishable from one that # imported out of the package this image installed. check $P/site-packages-dir test -d "$SITE" check $P/module-dir test -d "$CV" # THE PACKAGE IS TWO SOURCE FILES AND ONE OF THEM IS EMPTY. `__init__.py` holds # nothing at all -- no `__version__`, no re-export -- and every line of calver # is in `integration.py`. Both are named because they fail differently: a # missing `__init__.py` breaks the import, a missing `integration.py` leaves # `import calver` succeeding and every behaviour case below red. check $P/module-init test -f "$CV/__init__.py" check $P/module-integration test -f "$CV/integration.py" # THE DIST-INFO DIRECTORY CARRIES THE PINNED VERSION IN ITS NAME. This is a # second, independent statement of the version -- from the installed layout # rather than from the metadata the interpreter reads -- and the two disagreeing # would mean a mixed install that no single case could otherwise see. check $P/dist-info-dir test -d "$DI" check $P/dist-info-record test -f "$DI/RECORD" check $P/dist-info-metadata test -f "$DI/METADATA" # THE ENTRY POINT'S METADATA FILE. `entry_points.txt` is the whole of calver's # wiring into setuptools, and it is the one file a wheel build can silently drop # while producing a package that imports perfectly and does nothing. Asserted # here as a file, and LOADED through the probe further down. check $P/dist-info-entry-points test -f "$DI/entry_points.txt" check_out $P/entry-points-group "distutils.setup_keywords" cat "$DI/entry_points.txt" check_out $P/entry-points-keyword "use_calver = calver.integration:version" \ cat "$DI/entry_points.txt" # NO COMPILED EXTENSION, ANYWHERE UNDER THE MODULE. See the header: on this # image a .so could not be loaded at all, so this is the case that fails a # version bump which grew one -- loudly, here, instead of at `import` later. check_eq $P/module-no-so "" \ "$(find "$CV" -name '*.so' -o -name '*.so.*' 2>/dev/null)" # ...and the same assertion from the PACKAGE's side rather than the filesystem's. # # The list is copied to tmpfs first and never redirected from the rootfs: a # top-level stdin redirect on the virtiofs root makes dup2(2) return 513 and ash # EXITS, ending the suite silently (brintos/linux#73). cat "$PKGLIST" > "$W/pkgf" 2>/dev/null || : > "$W/pkgf" check $P/pkg-list-present test -s "$W/pkgf" check_eq $P/pkg-no-so "" \ "$(grep -E '\.so($|\.)' "$W/pkgf" 2>/dev/null)" # EVERY REGULAR FILE THE PACKAGE SHIPS UNDER site-packages IS PYTHON OR ITS # METADATA. The two cases above name the failure that is expected; this one # catches the shape nobody predicted -- a binary, a .pyd, a shared archive -- # without having to enumerate what it might be. # # THE `-f` TEST IS LOAD-BEARING. A dpkg .list names DIRECTORIES too, and without # a trailing slash, so a name-only filter reports every directory in the package # as an unexpected shape and this case would be red on a CORRECT package # (measured on python3-pathspec, BRINTOS-1834). _bad="" while IFS= read -r _f; do case "$_f" in "$SITE"/*) ;; *) continue ;; esac [ -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 package ------ # dpkg KNOWS the package. Every case above would look identical if some other # package had installed the same files, so this is the one that says # python3-calver is a real package this image installed rather than an # IMAGE_INSTALL line that resolved to nothing. # # The stanza is read from a tmpfs copy for the same reason as the file list. The # anchor is exact: `python3-calver` is a PREFIX of `python3-calver-ptest` and # `python3-calver-dev`, and an unanchored match would report one stanza as # another's. sed -n '/^Package: python3-calver$/,/^$/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 "2022.6.26-r0" cat "$W/st" # THE ONLY RUNTIME DEPENDENCY THE PACKAGE CARRIES. calver depends on nothing but # the interpreter core -- no third-party module, and not even a stdlib split # (measured off the built .deb: `Depends: python3-core`, and that is the whole # line). This case is what says the port waits on nothing else in this round, # and it goes red if a version bump grows a dependency the image does not carry. check_out $P/pkg-depends-core "python3-core" cat "$W/st" # ---------------------------------------------------------- the interpreter -- # THE IMPORT, ON ITS OWN, BEFORE THE PROBE. If calver cannot be imported then # every probe case below reds at once and each names a different symptom; this # case is what says the cause is the import. It is also the cheapest possible # statement of the deliverable. check $P/import python3 -c 'import calver' check $P/import-integration python3 -c 'from calver.integration import version' # ------------------------------------------------------------- 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 # ONE interpreter start for everything that follows. bst_probe bounds it and # keeps BOTH streams: a probe that dies early otherwise leaves every case below # reporting got:'' and naming a symptom each, with the actual traceback nowhere. # The bound is generous because a python3 start on this arm is tens of seconds # before any work begins. bst_probe $P/probe-run "$W/probe" 600 python3 "$PROBE" OUT="$W/probe" # Read a single key. The probe emits each key at most once, so head -n 1 is # defensive rather than load-bearing; the anchors keep `kw_true` and # `kw_true_fields` distinguishable. pv() { grep "^$1=" "$OUT" 2>/dev/null | head -n 1 | cut -d= -f2-; } # THE EVIDENCE PLANE. If the probe emitted nothing, every case below is reading # an absent file: each would red with got:'' and the set would read exactly like # a broken plugin, when what actually happened is that no measurement was taken. # The word `unscorable` is PARSED -- brintos_local_verify re-measures a suite # that prints it on a fresh guest rather than trusting a shared boot's numbers # (BRINTOS-1644) -- so it goes in this structural check, not a comment. # # The cases below still print their reds: withdraw, do not hide. This case is # what says what those reds measure. 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 # A traceback the probe caught itself, surfaced as its own case rather than as # twenty separate got:'' reds. check_eq $P/probe-no-fatal "" "$(pv fatal)" # THE VERSION THE RECIPE PINS, read through importlib.metadata rather than from # dpkg. A mismatch here means the recipe and what actually landed have gone out # of step, and every expectation in this file is then describing a different # calver. calver carries no `__version__`, so this is the only in-interpreter # statement of the version there is. check_eq $P/version "2022.6.26" "$(pv version)" check_eq $P/version-readable "" "$(pv version_error)" # The module answered out of the package this image installed, not from a stray # copy on sys.path. This is what makes the payload cases above and the behaviour # cases below statements about the SAME calver. check_eq $P/module-file "$CV/__init__.py" "$(pv module_file)" check_eq $P/integration-file "$CV/integration.py" "$(pv integration_file)" # ...and it answered out of a .py, not an extension. The third statement of the # .so negative, this one from inside the interpreter. check_eq $P/module-is-py "yes" "$(pv module_is_py)" # ------------------------------------------------------------- the API ------- # The public surface, named one function at a time: `version` missing and # `pkginfo_version` missing are different breakages and a single "the API is # there" case would report them identically. check_eq $P/api-version "yes" "$(pv api_version)" check_eq $P/api-get-version "yes" "$(pv api_get_version)" check_eq $P/api-pkginfo-version "yes" "$(pv api_pkginfo_version)" check_eq $P/api-get-pkginfo-contents "yes" "$(pv api_get_pkginfo_contents)" # THE DEFAULT FORMAT, asserted as the string rather than only through its # result. `kw-true` below going wrong could mean the format changed or the clock # did; this case separates the two. check_eq $P/default-format "%Y.%m.%d" "$(pv default_format)" # ------------------------------------------------------- the entry point ----- # calver does nothing by being importable. It works because setuptools finds # `use_calver` in the `distutils.setup_keywords` group and calls what it points # at, so a package whose entry-point metadata did not survive packaging imports # perfectly and is silently inert. `dist-info-entry-points` above reads the file; # these read it the way a consumer does. check_eq $P/ep-count "1" "$(pv ep_count)" check_eq $P/ep-value "calver.integration:version" "$(pv ep_value)" check_eq $P/ep-loads "yes" "$(pv ep_loads)" # ...and it resolves to the same object `import calver.integration` gives. A # second copy on sys.path would satisfy every other case here and fail only this. check_eq $P/ep-is-version "yes" "$(pv ep_is_version)" check_eq $P/ep-no-error "" "$(pv ep_error)" # -------------------------------------------------- the five behaviours ------ # See the header. The clock is frozen at 2026-03-07T14:05:09 from here on, so # every value below is a constant and not today's date. # THE ORDINARY DECLARATION: a project says only that it wants a calendar version # and gets `%Y.%m.%d` applied to the date. This is the case the ticket names. check_eq $P/kw-true "2026.03.07" "$(pv kw_true)" # ...and the same answer taken apart. `kw-true` going red says the string # differs; these say WHICH field moved, which is the difference between a wrong # format and a wrong clock. check_eq $P/version-fields "3" "$(pv kw_true_fields)" check_eq $P/comp-year "2026" "$(pv comp_year)" # The two zero-padded fields, which is why the pinned date has a single-digit # month and day: `3` and `7` here would mean the padding was lost, and no other # case in this file would notice. check_eq $P/comp-month "03" "$(pv comp_month)" check_eq $P/comp-day "07" "$(pv comp_day)" check_eq $P/comp-numeric "yes" "$(pv comp_numeric)" # The whole statement the ticket asks for: the generated string parses as a # calendar version whose components ARE the date the fixture pinned. check_eq $P/comp-matches-pin "yes" "$(pv comp_matches_pin)" # A CALLER-SUPPLIED FORMAT -- a different arm of `_get_version` from the one # above, and the arm the recipe's own build takes. check_eq $P/kw-format "2026.03" "$(pv kw_format)" # THE RECIPE'S PIN, ROUND-TRIPPED. poky pins this package's own version for # reproducibility by passing the literal "2022.6.26" as the format, and a format # with no directives strftimes to itself. So this case and `version` above are # the two ends of one code path: the plugin computes the string, the recipe # ships it, and the installed metadata reads it back. check_eq $P/pin-roundtrip "2022.6.26" "$(pv pin_roundtrip)" # A CALLABLE: the project generates its own version and calver takes it # verbatim, without applying a format to it. check_eq $P/kw-callable "2026.03.07.dev1" "$(pv kw_callable)" # THE FALSY AND UNUSABLE VALUES LEAVE THE VERSION ALONE. `use_calver` is offered # to every `setup()` in the process, so a plugin that assigned here would # rewrite the version of a package that never opted in. `unset` is the fixture's # sentinel: seeing it come back unchanged is the assertion. check_eq $P/kw-false "unset" "$(pv kw_false)" check_eq $P/kw-none "unset" "$(pv kw_none)" # An int is neither True, nor a str, nor callable -- the final `return` in # `_get_version`, and the arm a `truthy` test rather than an `is True` test # would get wrong. check_eq $P/kw-int "unset" "$(pv kw_int)" # ---------------------------------------------------------- PKG-INFO --------- # AN ALREADY-DATED SDIST KEEPS ITS RELEASE DATE. Building from an sdist re-runs # setup.py, and a naive plugin would stamp the rebuild with the date of the # REBUILD. calver reads PKG-INFO out of the working directory first and # generates only when there is nothing to read. Because the clock is frozen, the # release's version and "today's" are distinguishable here in a way they would # not be on a real build. check_eq $P/pkginfo-wins "2019.1.1" "$(pv pkginfo_wins)" check_eq $P/pkginfo-version "2019.1.1" "$(pv pkginfo_version)" # `UNKNOWN` is setuptools' placeholder for "no version was set", and calver # treats it as ABSENT rather than as a version. Without this arm every # unversioned sdist would be stamped `UNKNOWN` and no other case here would see # it. check_eq $P/pkginfo-unknown "2026.03.07" "$(pv pkginfo_unknown)" # No PKG-INFO at all -- the ordinary source-tree build, and the FileNotFound arm # of `pkginfo_version`. It generates. check_eq $P/pkginfo-absent "2026.03.07" "$(pv pkginfo_absent)" check_eq $P/pkginfo-none "yes" "$(pv pkginfo_none)" # The probe ran to the end. Every case above greps a key, so a probe that died # halfway would leave the remaining keys absent and each case would red with # got:'' -- this case is the one that says the cause was a truncated run. check_eq $P/probe-complete "yes" "$(pv done)" rm -rf "$W"