. "$BST_LIB" # BST-TIMEOUT: 1200 # TWO python3 starts, and they are the whole of the cost. # # Every other case here reads the filesystem or dpkg with # # shell builtins, or greps the second start's output. # # # # The two are: a bare `import' (one case, so an import # # failure is legible without reading a transcript) and the # # probe, which takes ONE start for fifty-seven values. # # # # THE ORDINARY BUDGET, NOT AN ENLARGED ONE. Nothing here # # forks a child, sleeps, starts a thread or reaches a # # device, a peer or a terminal. The probe does open files # # -- the shipped headers, the shipped bytecode, and one # # throwaway copy of the module under $TMPDIR -- which for # # THIS package is the subject rather than a concession. # # # # 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-cppy 1.2.1 (meta-openembedded/meta-python # recipes-devtools/python/python3-cppy_1.2.1.bb, `inherit pypi # python_setuptools_build_meta', PyPI sdist) on the wasm32-hwjs glibc image. # # ============================================================================ # WHAT THE PACKAGE IS, AND WHY THIS SUITE ASSERTS PACKAGING RATHER THAN # BEHAVIOUR. # ============================================================================ # # cppy is FOUR C++ HEADERS -- 10652 bytes of them -- plus a 2451-byte Python # module whose entire public job is to say where those headers are. There is # no algorithm here to get wrong. `get_include()' is one `os.path.join', and # `CppyBuildExt' is a four-line setuptools command class that pushes that # directory onto a compile line and forces `-std=c++11'. # # So "ported" is a PACKAGING claim, and packaging is what is asserted: the # headers ship, they ship non-empty, they ship where the module points, the # module points under the image's own prefix, and the .deb's file list is the # one the recipe promises. A suite that instead exercised the module's five # lines of Python would be green on an image where the headers had not shipped # at all -- which is the exact failure this port can have. # # ---------------------------------------------------------------------------- # THE ONE THING A CONSUMER CANNOT DO ON THIS IMAGE, SAID PLAINLY. # ---------------------------------------------------------------------------- # # Nothing here compiles a C++ extension against these headers, and nothing # could: the image ships no C++ compiler and no CPython development headers, # and the arm's compiler is a cross toolchain that lives on the build host. So # the strongest claim this suite makes is that the headers are PRESENT, # COMPLETE and LOCATABLE -- not that they compile here. What does compile # against them is the sibling port that consumes them: python3-kiwisolver # (BRINTOS-2447) builds against `python3-cppy-native', through the same recipe # via `BBCLASSEXTEND = "native"', and that build is the compile-time evidence # this package's headers are usable. The two halves are deliberately in # different tickets and neither one alone is the whole claim. # # ---------------------------------------------------------------------------- # THE ASSERTION THAT CATCHES A STAGING MISTAKE. # ---------------------------------------------------------------------------- # # `get_include()' derives its answer from `__file__' at call time and has no # memory of a build directory, so it CANNOT return a build-host path -- which # means the usual "did a host path leak into the package" question has to be # asked somewhere it can actually be answered. That place is the compiled # bytecode: `co_filename' is baked in at compile time, and a recipe that # byte-compiled out of ${D} or ${WORKDIR} without rewriting it would ship a # .pyc naming a directory that does not exist on the machine, which nothing at # runtime reports because the source is there and the path is only ever # rendered into a traceback. `pyc-co-filename-init' and its sibling read that # field out of the shipped .pyc and assert the TARGET path. # # The prefix question is asked from the other side too: `include-abs' pins the # absolute path under /usr/lib/python3.12/site-packages, and `include-rel' # pins the same fact with the prefix divided out, so a red says which half # moved. # # ---------------------------------------------------------------------------- # THE HEADER'S OWN VERSION MACRO SAYS 1.1.0, AND THAT IS CORRECT. # ---------------------------------------------------------------------------- # # `defines.h' has carried `#define CPPY_VERSION "1.1.0"' since the 1.1.0 # release and upstream did not bump it for 1.2.0 or 1.2.1. So a C++ consumer # compiling against these headers reads 1.1.0 while `import cppy' reads 1.2.1. # That is UPSTREAM's inconsistency carried faithfully, not something this port # did, and it is asserted AS MEASURED (`header-macro-version'). Pinning the # wrong-looking value is the point: a future upstream fix then shows up as a # red that says exactly what changed, instead of passing unnoticed. # # ---------------------------------------------------------------------------- # THE ERROR PATH, AND WHAT "DOCUMENTED BEHAVIOUR" MEANS FOR IT. # ---------------------------------------------------------------------------- # # Asked what `get_include()' does when the headers are gone, the answer is that # it does not notice: it joins two strings and returns. The caller finds out at # open(2), as an ordinary stdlib FileNotFoundError. The `nodata-*' cases # reproduce that WITHOUT touching the shipped tree -- the probe copies # `__init__.py' and `version.py' into $TMPDIR with no `include/' beside them, # imports the copy under its own name, and puts the same three questions to it. # The two that matter are `nodata-returns' (no exception, no traceback) and # `nodata-no-shipped-path-leak' (the answer is derived from the copy, so it # does not silently fall back to the real install). # # ---------------------------------------------------------------------------- # WHERE THE EXPECTATIONS COME FROM. # ---------------------------------------------------------------------------- # # Every value asserted below was MEASURED against THIS BUILD's own # `python3-cppy_1.2.1-r0_wasm32.deb', extracted and read on an x86-64 host at # CPython 3.12.3 -- the same 3.12 line this image ships. The payload was also # compared byte-for-byte against the PyPI sdist whose sha256 the recipe pins # (83b43bf1...): `__init__.py' (2451), `version.py' (872) and all four headers # are `cmp'-identical to the sdist's. So a red here is this arm, not a version # skew and not a repackaging. # # ---------------------------------------------------------------------------- # WHAT IS PREDICTED RATHER THAN MEASURED, AND SO MIGHT BE RED. # ---------------------------------------------------------------------------- # # EXPECTED, NOT YET MEASURED -- this is a prediction and is written as one. The # recipe phase cannot build the image and so cannot run this suite; its first # on-target run is the batch verify. # # THE PREDICTION IS THAT NOTHING HERE IS ARM-SENSITIVE, and it is a weak claim # rather than a discovery: the runtime half is 2451 bytes of pure Python over # `os', `sys' and one setuptools import, with no ext_modules, no subprocess, no # thread, no signal and no syscall beyond `open', `stat' and `listdir' on a # filesystem the rest of the image already uses. The one non-obvious exposure # is `import cppy' pulling in `setuptools.command.build_ext' at module level: # if python3-setuptools is broken on this image the import is red and every # behavioural case is skipped as one fact rather than scattered. # # So the honest statement is: if anything here is red, the expected shape is # either that WHOLESALE import failure, or a packaging case -- a header # missing, a path under the wrong prefix -- which is a real finding about the # port. A scatter of unrelated reds would itself be the finding, and the design # doc records what the verify measured rather than this paragraph. # # ============================================================================ P=python3-cppy # ---------------------------------------------------------------------------- # The suite reached its first line. Distinguishes "ran and found nothing" from # "never started", which the runner cannot tell apart on its own. # ---------------------------------------------------------------------------- bst_ok $P/suite-canary require python3 $P/python3-present SITE=/usr/lib/python3.12/site-packages PKG=$SITE/cppy MOD=$PKG/__init__.py VER=$PKG/version.py INC=$PKG/include HDR=$INC/cppy DI=$SITE/cppy-1.2.1.dist-info PKGLIST=/var/lib/dpkg/info/python3-cppy.list PROBE=$BST_DATA/python3-cppy-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-cppy.$$ # The probe transcript is written HERE, under $TMPDIR, and never onto the # virtiofs root: a redirect whose source is the rootfs makes dup2 return 513 # and ends the suite in silence (brintos/linux#73). mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # ---------------------------------------------------------------------------- # THE TREE. What the .deb actually installed. # ---------------------------------------------------------------------------- check $P/site-packages-dir test -d "$SITE" check $P/package-dir test -d "$PKG" check $P/module-present test -s "$MOD" check $P/version-py-present test -s "$VER" # A PACKAGE, NOT A SINGLE MODULE -- `packages = ["cppy"]' in pyproject, and it # has to be a package because the headers ride inside it as package-data. A # `py_modules=' distribution would put the module at site-packages/cppy.py with # nowhere for `include/' to live. check $P/not-a-flat-module test ! -e "$SITE/cppy.py" # The exact byte counts of the two shipped Python files, verified with `cmp' # against the sdist's own copies at recipe time. check_eq $P/module-size 2451 \ "$(wc -c < "$MOD" 2>/dev/null | tr -d ' ')" # BST-REDIRECT-OK: forked -- inside $( ) check_eq $P/version-py-size 872 \ "$(wc -c < "$VER" 2>/dev/null | tr -d ' ')" # BST-REDIRECT-OK: forked -- inside $( ) check $P/pycache-init test -s "$PKG/__pycache__/__init__.cpython-312.pyc" check $P/pycache-version test -s "$PKG/__pycache__/version.cpython-312.pyc" # The package directory holds exactly these four entries and nothing else. One # case rather than four: an entry APPEARING is the signal a per-file case # cannot report -- and `include' being one of them is the whole port. check_eq $P/package-file-set "__init__.py,__pycache__,include,version.py," \ "$(LC_ALL=C ls -1 "$PKG" 2>/dev/null | LC_ALL=C sort | tr '\n' ',')" # NO PEP 561 MARKER. cppy declares no `py.typed', so its inline annotations are # not to be trusted by a type checker. Asserted as an absence because the # neighbouring pure-Python ports in this Epic DO ship one, and "it was there # last time" is how a missing file gets read as normal. check $P/no-py-typed test ! -e "$PKG/py.typed" # NO COMPILED EXTENSION and NO CONSOLE SCRIPT. This package is headers and a # path function; a .so here would mean the recipe had grown an ext_module and # would put this port on BRINTOS-1476's static-interpreter path, and a # /usr/bin entry would mean it had grown a console_scripts entry point. check_eq $P/no-so "" "$(find "$PKG" -name '*.so' 2>/dev/null)" check $P/no-bin-script test ! -e /usr/bin/cppy # ---------------------------------------------------------------------------- # THE HEADERS. The payload this port exists to deliver, asserted from the # filesystem before the module is asked where they are -- so "the headers did # not ship" and "the module points somewhere else" stay two different reds. # ---------------------------------------------------------------------------- check $P/include-dir test -d "$INC" check $P/header-dir test -d "$HDR" # The include tree holds exactly one entry at the top and exactly the four # documented headers below it. The single top entry matters: the directory # `get_include()' returns is meant to go on a compile line as `-I', so anything # else appearing there enters every consumer's header search path. check_eq $P/include-listing "cppy," \ "$(LC_ALL=C ls -1 "$INC" 2>/dev/null | LC_ALL=C sort | tr '\n' ',')" check_eq $P/header-file-set "cppy.h,defines.h,errors.h,ptr.h," \ "$(LC_ALL=C ls -1 "$HDR" 2>/dev/null | LC_ALL=C sort | tr '\n' ',')" # Each header non-empty and at its sdist byte count. A header shipped as a # zero-byte file would satisfy "the headers are present" and be useless, which # is the failure a packaging suite is bought for -- so presence and size are # separate cases rather than one. check $P/cppy-h-nonempty test -s "$HDR/cppy.h" check $P/defines-h-nonempty test -s "$HDR/defines.h" check $P/errors-h-nonempty test -s "$HDR/errors.h" check $P/ptr-h-nonempty test -s "$HDR/ptr.h" check_eq $P/cppy-h-size 728 "$(wc -c < "$HDR/cppy.h" 2>/dev/null | tr -d ' ')" # BST-REDIRECT-OK: forked -- inside $( ) check_eq $P/defines-h-size 485 "$(wc -c < "$HDR/defines.h" 2>/dev/null | tr -d ' ')" # BST-REDIRECT-OK: forked -- inside $( ) check_eq $P/errors-h-size 1421 "$(wc -c < "$HDR/errors.h" 2>/dev/null | tr -d ' ')" # BST-REDIRECT-OK: forked -- inside $( ) check_eq $P/ptr-h-size 8018 "$(wc -c < "$HDR/ptr.h" 2>/dev/null | tr -d ' ')" # BST-REDIRECT-OK: forked -- inside $( ) # They are readable as well as present. The .deb installs them 0644 under # root:root and every consumer reads them as an unprivileged compile step # would; a mode mistake in packaging is invisible to `test -s'. check $P/cppy-h-readable test -r "$HDR/cppy.h" check $P/defines-h-readable test -r "$HDR/defines.h" check $P/errors-h-readable test -r "$HDR/errors.h" check $P/ptr-h-readable test -r "$HDR/ptr.h" # ---------------------------------------------------------------------------- # THE dist-info. The install's own record of what it installed. # ---------------------------------------------------------------------------- check $P/dist-info-dir test -d "$DI" check $P/dist-info-record test -s "$DI/RECORD" check $P/dist-info-metadata test -s "$DI/METADATA" check $P/dist-info-wheel test -s "$DI/WHEEL" # The whole file set, as one case rather than five. Note what is NOT here: # `INSTALLER' and `REQUESTED', which a `pip install' of the same wheel does # leave behind. Their absence is what says this tree was staged by the recipe's # own PEP 517 install rather than by pip on the target. check_eq $P/dist-info-file-set \ "LICENSE,METADATA,RECORD,WHEEL,top_level.txt," \ "$(LC_ALL=C ls -1 "$DI" 2>/dev/null | LC_ALL=C sort | tr '\n' ',')" # NO entry_points.txt: the package declares no console script and no setup # keyword, so it has no entry points at all. check $P/no-entry-points test ! -e "$DI/entry_points.txt" check_eq $P/dist-info-top-level "cppy" \ "$(cat "$DI/top_level.txt" 2>/dev/null | tr -d ' \r\n')" # A PURE-PYTHON WHEEL. `Root-Is-Purelib: true' and a `py3-none-any' tag are the # metadata statement that there is no arch-specific payload -- which is true # even though the package's whole point is C++ headers, because headers are # data here and nothing is compiled at install time. That is also why this port # needed no toolchain work at all. check $P/wheel-purelib grep -q '^Root-Is-Purelib: true$' "$DI/WHEEL" check $P/wheel-tag-py3 grep -q '^Tag: py3-none-any$' "$DI/WHEEL" check $P/metadata-name grep -q '^Name: cppy$' "$DI/METADATA" check $P/metadata-version grep -q '^Version: 1\.2\.1$' "$DI/METADATA" check $P/metadata-license grep -q '^Classifier: License :: OSI Approved :: BSD License$' "$DI/METADATA" check $P/metadata-requires-python grep -q '^Requires-Python: >=3\.7$' "$DI/METADATA" # THE SUMMARY IS THE CARRIED PATCH'S ONLY EFFECT, AND THIS IS THE CASE THAT # PROVES IT TOOK. cppy 1.2.1's own pyproject declares no `description', which # the PEP 517 build refuses ("description must be specified under [project] or # listed as a dynamic field"); meta-oe carries # 0001-Fix-build-error-as-following.patch, whose single added line supplies it. # With the patch dropped there is no package at all, so a red here means the # patch applied and then did something else than what it says. check $P/metadata-summary grep -q '^Summary: A small C++ header library$' "$DI/METADATA" # THE LICENSE FILE IS SHIPPED -- `license = {file = "LICENSE"}' in pyproject, # which is also what the recipe's LIC_FILES_CHKSUM checksums (md5 0bfb3e39...). check $P/license-file-present test -s "$DI/LICENSE" check $P/license-file-text grep -q 'Copyright (c) 2014, Nucleic' "$DI/LICENSE" check_eq $P/license-file-size 1485 \ "$(wc -c < "$DI/LICENSE" 2>/dev/null | tr -d ' ')" # BST-REDIRECT-OK: forked -- inside $( ) # RECORD lists every file at its measured size -- the install's own statement # about the payload, cross-checked against the byte counts asserted from the # tree above. Two independent readings of one fact, so a repackaging cannot # move one silently. THE FOUR HEADERS ARE IN RECORD: that is the metadata-side # statement that they are part of the distribution rather than strays the # recipe happened to copy. check $P/record-lists-module \ grep -q '^cppy/__init__\.py,sha256=.*,2451$' "$DI/RECORD" check $P/record-lists-version \ grep -q '^cppy/version\.py,sha256=.*,872$' "$DI/RECORD" check $P/record-lists-cppy-h \ grep -q '^cppy/include/cppy/cppy\.h,sha256=.*,728$' "$DI/RECORD" check $P/record-lists-defines-h \ grep -q '^cppy/include/cppy/defines\.h,sha256=.*,485$' "$DI/RECORD" check $P/record-lists-errors-h \ grep -q '^cppy/include/cppy/errors\.h,sha256=.*,1421$' "$DI/RECORD" check $P/record-lists-ptr-h \ grep -q '^cppy/include/cppy/ptr\.h,sha256=.*,8018$' "$DI/RECORD" # RECORD's own line count, so a file added to the distribution cannot hide # between the six asserted above and the four dist-info entries. check_eq $P/record-line-count 11 \ "$(wc -l < "$DI/RECORD" 2>/dev/null | tr -d ' ')" # BST-REDIRECT-OK: forked -- inside $( ) # ---------------------------------------------------------------------------- # dpkg's own record. The package is registered, not merely present on disk -- # which is what "the .deb installs on target" means once the image has been # built: dpkg unpacked it, recorded it, and its file list is the recipe's # promise in dpkg's words. # ---------------------------------------------------------------------------- check $P/dpkg-list test -s "$PKGLIST" check $P/dpkg-lists-module grep -q "^$MOD\$" "$PKGLIST" check $P/dpkg-lists-version grep -q "^$VER\$" "$PKGLIST" check $P/dpkg-lists-cppy-h grep -q "^$HDR/cppy\.h\$" "$PKGLIST" check $P/dpkg-lists-defines-h grep -q "^$HDR/defines\.h\$" "$PKGLIST" check $P/dpkg-lists-errors-h grep -q "^$HDR/errors\.h\$" "$PKGLIST" check $P/dpkg-lists-ptr-h grep -q "^$HDR/ptr\.h\$" "$PKGLIST" # THE FILE LIST AGAINST WHAT THE RECIPE PROMISES, as one case. Thirteen regular # files: two .py, two .pyc, four headers, five dist-info entries. The count is # the assertion a per-file case cannot make -- it catches an ADDITION, which is # how a package quietly grows a stray. # # Matched by SUFFIX rather than counted whole, because a dpkg .list carries the # shared directories on the way down too -- and `/usr/lib/python3.12' has a dot # in it, so the obvious `grep -c "\."' counts four directories as files and # reads 22 where the package ships 13. check_eq $P/dpkg-file-count 13 \ "$(grep -cE '\.(py|pyc|h)$|dist-info/(LICENSE|METADATA|RECORD|WHEEL|top_level\.txt)$' \ "$PKGLIST" 2>/dev/null | tr -d ' ')" # BST-REDIRECT-OK: forked -- inside $( ) sed -n '/^Package: python3-cppy$/,/^$/p' /var/lib/dpkg/status > "$W/st" 2>/dev/null check $P/deb-known test -s "$W/st" check_out $P/deb-installed "Status: install ok installed" cat "$W/st" check_out $P/deb-version "Version: 1.2.1-r0" cat "$W/st" # THE Depends LINE, EXACTLY. `python3-setuptools' is a REAL runtime dependency # here rather than the usual recipe boilerplate: `__init__.py' imports # `setuptools.command.build_ext' at module level, so without it `import cppy' # raises. The recipe declares it and this case is what holds that declaration # to the truth of the module's own first ten lines. grep '^Depends:' "$W/st" > "$W/dep" 2>/dev/null || : > "$W/dep" check $P/deb-depends-line test -s "$W/dep" check_eq $P/deb-depends-exact "Depends: python3-core, python3-setuptools" \ "$(cat "$W/dep")" # The dependency is not merely declared -- it is satisfied on THIS image. A # declared-but-absent dependency reads as a green Depends line and a red # import, two cases apart, and this is the one that names the cause. check $P/setuptools-installed test -d "$SITE/setuptools" # ---------------------------------------------------------------------------- # THE IMPORT, as its own case, so an import failure is legible from the summary # without reading a transcript. # ---------------------------------------------------------------------------- check $P/import python3 -c 'import cppy' if [ ! -s "$PROBE" ]; then bst_notok $P/probe-present "probe missing at $PROBE" rm -rf "$W" exit 0 fi bst_ok $P/probe-present # ---------------------------------------------------------------------------- # THE MODULE, MEASURED. One python3 start for all of it: the probe emits # `key=value' lines and every case below reads one by key. # ---------------------------------------------------------------------------- bst_probe $P/probe-run "$W/probe" 900 python3 "$PROBE" OUT="$W/probe" # One reader for the whole transcript. `head -n 1' so a key the probe emitted # twice cannot make a case read two lines, and `cut -d= -f2-' so a VALUE # carrying `=' survives whole. pv() { grep "^$1=" "$OUT" 2>/dev/null | head -n 1 | cut -d= -f2-; } # The probe ran to its last line, and emitted exactly the number of values this # suite was written against. Together these two catch a probe that died halfway # -- every case after the death would otherwise read the empty string and # report a value mismatch, burying one fact under fifty. check_eq $P/probe-complete "yes" "$(pv probe_done)" check_eq $P/probe-count "57" "$(pv emitted_count)" _imp=$(pv import_ok) check_eq $P/import-ok "yes" "$_imp" if [ "$_imp" != "yes" ]; then # The module did not import. Skip -- not red -- every case that reads the # module, each with the reason attached: they are all one fact, and forty # reds would bury it rather than report it. `import' and `import-ok' above # are the two reds that carry the verdict, and the design doc attributes # them. The FILESYSTEM cases above are unaffected and have already run, # which is the point of asserting the headers before asking the module. _why="cppy did not import: $(pv import_error)" for _c in \ module-file module-name is-package package-dir version version-info \ version-info-type version-info-fields version-major version-minor \ version-micro version-status version-agrees has-all all-names \ public-names get-include-callable buildext-is-class \ buildext-subclasses-setuptools buildext-c-opts include-abs \ include-is-abs include-exists include-under-package \ include-idempotent include-rel include-listing-probe \ header-dir-listing-probe header-size-cppy header-size-defines \ header-size-errors header-size-ptr cppy-h-pragma-once \ cppy-h-includes-siblings defines-h-includes-python \ errors-h-namespace errors-h-type-error ptr-h-class-ptr \ ptr-h-incref header-macro-version header-macro-major \ header-macro-minor header-macro-patch pyc-co-filename-init \ pyc-co-filename-version nodata-import nodata-returns \ nodata-under-copy nodata-not-exists nodata-no-shipped-path-leak \ nodata-listdir nodata-open-header nodata-version; do bst_skip $P/$_c "$_why" done rm -rf "$W" exit 0 fi # The module the probe imported is the one the .deb installed -- not a copy on # the path from somewhere else, which would make every case below a statement # about a file this port did not ship. check_eq $P/module-file "$MOD" "$(pv module_file)" check_eq $P/module-name "cppy" "$(pv module_name)" check_eq $P/is-package "yes" "$(pv is_package)" check_eq $P/package-dir "$PKG" "$(pv package_dir)" # ---------------------------------------------------------------------------- # THE VERSION, from the module rather than from metadata. `version.py' is # generated by setuptools-scm at SDIST time and carried in the sdist -- which # is why `python3-setuptools-scm-native' is a DEPENDS of the recipe and yet # nothing regenerates this string during our build. A wheel built from a git # checkout with no tags would put `0.1.dev1+...' here instead. # ---------------------------------------------------------------------------- check_eq $P/version "1.2.1" "$(pv version)" check_eq $P/version-info "(1, 2, 1, '')" "$(pv version_info)" check_eq $P/version-info-type "_version_info" "$(pv version_info_type)" check_eq $P/version-info-fields "major,minor,micro,status" \ "$(pv version_info_fields)" check_eq $P/version-major "1" "$(pv version_major)" check_eq $P/version-minor "2" "$(pv version_minor)" check_eq $P/version-micro "1" "$(pv version_micro)" # The fourth field is the EMPTY STRING on a release version -- `1.2.1'.split # (".", 3) yields three parts, so `status' takes its default. A pre-release # would put `1rc1' or similar here, and asserting the empty string is what # distinguishes "the recipe pinned a release" from "the recipe pinned whatever # PyPI had". check_eq $P/version-status "''" "$(pv version_status)" check_eq $P/version-agrees "yes" "$(pv version_agrees)" # ---------------------------------------------------------------------------- # THE MODULE'S SURFACE. `__all__' IS declared -- four names -- so `from cppy # import *' takes exactly those. `dir()' shows three more, the module-level # `os', `sys' and `build_ext' imports, and the set is asserted WHOLE rather # than sampled so an addition or a removal is the signal. # ---------------------------------------------------------------------------- check_eq $P/has-all "yes" "$(pv has_all)" check_eq $P/all-names "__version__,__version_info__,get_include,CppyBuildExt" \ "$(pv all_names)" check_eq $P/public-names "CppyBuildExt,build_ext,get_include,os,sys,version" \ "$(pv public_names)" check_eq $P/get-include-callable "yes" "$(pv get_include_callable)" check_eq $P/buildext-is-class "yes" "$(pv CppyBuildExt_is_class)" # `CppyBuildExt' IS a setuptools command class, not a helper of cppy's own. # That inheritance is the whole of what makes `cmdclass={"build_ext": # CppyBuildExt}' work in a consumer's setup.py, and it is also the one thing # here that depends on a package outside this port. check_eq $P/buildext-subclasses-setuptools "yes" \ "$(pv buildext_subclasses_setuptools)" # The flags it forces. `-std=c++11' on every unix compiler is the package's # stated reason to exist beside the headers; `/EHsc' is the MSVC branch, dead # on this arm and asserted anyway because it is part of the shipped literal. check_eq $P/buildext-c-opts "{'msvc': ['/EHsc'], 'unix': ['-std=c++11']}" \ "$(pv buildext_c_opts)" # ---------------------------------------------------------------------------- # get_include(). THE PACKAGE'S ONE PUBLIC FUNCTION, and the case the port turns # on: the headers are only ported if the module can find them ON THE MACHINE. # ---------------------------------------------------------------------------- # The absolute path, under the image's own prefix. This is the assertion the # ticket asks for in so many words -- a build-host path here would mean the # module itself had been staged outside /usr, because the answer is derived # from `__file__' rather than recorded at build time. check_eq $P/include-abs "$INC" "$(pv include)" check_eq $P/include-is-abs "yes" "$(pv include_is_abs)" # ...and it EXISTS. Separate from the path being right: a correct path to a # directory the packaging dropped is precisely the failure mode of a # header-only port, and it is the one case here that no amount of Python # behaviour would reveal. check_eq $P/include-exists "yes" "$(pv include_exists)" check_eq $P/include-under-package "yes" "$(pv include_under_package)" check_eq $P/include-idempotent "yes" "$(pv include_idempotent)" # The same fact with the prefix divided out, so a red says which half moved -- # the prefix, or the layout under it. check_eq $P/include-rel "cppy/include" "$(pv include_rel)" # The tree as the MODULE sees it, beside the same two sets read from the # filesystem above. Two independent readings of one fact. check_eq $P/include-listing-probe "cppy" "$(pv include_listing)" check_eq $P/header-dir-listing-probe "cppy.h,defines.h,errors.h,ptr.h" \ "$(pv header_dir_listing)" check_eq $P/header-size-cppy "728" "$(pv header_size_cppy_h)" check_eq $P/header-size-defines "485" "$(pv header_size_defines_h)" check_eq $P/header-size-errors "1421" "$(pv header_size_errors_h)" check_eq $P/header-size-ptr "8018" "$(pv header_size_ptr_h)" # ---------------------------------------------------------------------------- # THE HEADERS ARE C++ TEXT, not placeholders of the right size. One identifier # read from each shipped file: the include guard, the CPython dependency the # whole library is built on, the namespace, and the class it is about. # ---------------------------------------------------------------------------- check_eq $P/cppy-h-pragma-once "yes" "$(pv cppy_h_pragma_once)" check_eq $P/cppy-h-includes-siblings "yes" "$(pv cppy_h_includes_siblings)" check_eq $P/defines-h-includes-python "yes" "$(pv defines_h_includes_python)" check_eq $P/errors-h-namespace "yes" "$(pv errors_h_namespace)" check_eq $P/errors-h-type-error "yes" "$(pv errors_h_type_error)" check_eq $P/ptr-h-class-ptr "yes" "$(pv ptr_h_class_ptr)" check_eq $P/ptr-h-incref "yes" "$(pv ptr_h_incref)" # THE HEADER'S OWN VERSION MACRO, ASSERTED AS MEASURED AT 1.1.0. See the # header comment: upstream stopped bumping it after the 1.1.0 release, so a # C++ consumer reads 1.1.0 where `import cppy' reads 1.2.1. Carried faithfully; # a red here means upstream fixed it, which is worth a look rather than a # silent pass. check_eq $P/header-macro-version '"1.1.0"' "$(pv header_macro_CPPY_VERSION)" check_eq $P/header-macro-major "1" "$(pv header_macro_major)" check_eq $P/header-macro-minor "1" "$(pv header_macro_minor)" check_eq $P/header-macro-patch "0" "$(pv header_macro_patch)" # ---------------------------------------------------------------------------- # NO BUILD-HOST PATH IN THE PACKAGE. `co_filename' is baked into the bytecode # at compile time and is the one field on this arm where ${D} or ${WORKDIR} # really can survive into a shipped file -- invisibly, because the source is # present and the path is only rendered into a traceback. # ---------------------------------------------------------------------------- check_eq $P/pyc-co-filename-init "$MOD" "$(pv pyc_co_filename_init)" check_eq $P/pyc-co-filename-version "$VER" "$(pv pyc_co_filename_version)" # ---------------------------------------------------------------------------- # THE ERROR PATH: the API called with the module's data directory removed. # Reproduced on a throwaway copy under $TMPDIR -- the shipped tree is never # touched -- and the documented answer is that the function does not notice. # ---------------------------------------------------------------------------- check_eq $P/nodata-import "yes" "$(pv nodata_import_ok)" # IT RETURNED. No exception, no traceback, no host path: the value is one # `os.path.join' over the copy's own `__file__'. check_eq $P/nodata-returns "yes" "$(pv nodata_returns)" check_eq $P/nodata-under-copy "yes" "$(pv nodata_is_under_copy)" check_eq $P/nodata-not-exists "no" "$(pv nodata_exists)" # ...and it did NOT silently answer with the real install's path, which is the # way this could have been implemented and the way that would make the whole # assertion vacuous. check_eq $P/nodata-no-shipped-path-leak "no" \ "$(pv nodata_leaks_shipped_path)" # The caller finds out at open(2), as an ordinary stdlib FileNotFoundError # rather than as anything cppy raises -- which is the honest statement of this # package's contract: it reports a path, it does not guarantee one. check_eq $P/nodata-listdir "FileNotFoundError" "$(pv nodata_listdir)" check_eq $P/nodata-open-header "FileNotFoundError" "$(pv nodata_open_header)" # The copy is otherwise a working cppy -- so the missing headers are the only # variable in the four cases above. check_eq $P/nodata-version "1.2.1" "$(pv nodata_version)" rm -rf "$W" exit 0