. "$BST_LIB" # BST-TIMEOUT: 1200 # THREE python3 starts, and they are the whole cost: the # # probe, one bare import case, and one case asserting the # # module resolves out of site-packages rather than a stray. # # This package ships NO executable, so there is no script # # group and no start per script. # # # # 1200 rather than a data-heavy port's 1500 because nothing # # here sweeps the zone database: the probe opens the eight # # zones it stages plus the handful it asks offsets of, not # # all 1800. Every other assertion reads the filesystem or # # dpkg with shell builtins, or greps the single probe run. # # # # 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-tzlocal 5.2 (meta-openembedded meta-python # recipes-devtools/python/python3-tzlocal_5.2.bb, `inherit pypi # python_setuptools_build_meta', PyPI sdist) on the wasm32-hwjs glibc image -- # the library that answers "what timezone is this machine in?". # # ============================================================================ # THE SUBJECT IS DISCOVERY, AND DISCOVERY IS STAGEABLE. # ============================================================================ # # tzlocal reads TZ, /etc/timezone, /var/db/zoneinfo, /etc/sysconfig/clock, # /etc/conf.d/clock and /etc/localtime in a documented order. Every one of those # is a file or an environment variable, and upstream's own `_root' parameter -- # `_get_localzone_name(_root=D)' looks for all of them beneath D -- makes the # whole order drivable over trees the probe builds under $TMPDIR. So this suite # asserts the resolution ITSELF rather than asserting what this one image # happens to be configured as, and it never writes to the target's /etc. # # THE CLOCK IS AN INPUT TOO. Every offset is asked of a zone at an explicit UTC # instant, so no result here depends on when the suite runs. # # ============================================================================ # THE RECIPE'S RUNTIME DEPENDENCIES ARE CORRECT AND COMPLETE -- WHICH IS RARE. # ============================================================================ # # meta-python declares `python3-datetime python3-logging python3-zoneinfo', and # measured against poky's python3-manifest.json that is exactly right, with # nothing missing and nothing spare: # # unix.py imports logging, os, re, sys, warnings, datetime.timezone, zoneinfo # utils.py imports calendar, datetime, logging, os, time, warnings, zoneinfo # # calendar -> python3-datetime (the split owns calendar.py as well as # datetime.py -- the one a reader would expect # to be core's and miss) # logging -> python3-logging # zoneinfo -> python3-zoneinfo # re, warnings, os, sys, subprocess -> python3-core # time -> a BUILTIN on this arm; python3_%.bbappend links every stdlib # extension module statically because this image has no dynamic # loader, so it cannot be split out at all. # # So this port adds NO bbappend for dependencies. That is a finding, not an # omission, and the suite pins the whole Depends line by name so that a release # which drops one names itself in the red. # # ============================================================================ # THE PACKAGE IS INERT WITHOUT tzdata, AND tzdata IS WHAT MAKES IT ANSWER. # ============================================================================ # # python3-zoneinfo declares no dependency on the system zone database, so # `import tzlocal' would succeed on an image with no /usr/share/zoneinfo and then # fail to resolve a single name. This image installs `tzdata', whose # RDEPENDS:${PN} is the whole TZ_PACKAGES set, so the entire database reaches the # rootfs. The suite asserts the database directly -- by TZif magic read off the # file with shell tools, not through tzlocal -- so a tree of empty placeholders # cannot pass. # # ============================================================================ # FIVE MEASURED FACTS A READER WOULD GUESS WRONG. # ============================================================================ # # 1. THERE IS NO `tzlocal.__version__'. The version lives in distribution # metadata only. A caller reading the attribute gets an AttributeError. # # 2. THE UTC FALLBACK IS NOT A ZoneInfo. With no configuration at all, # `_get_localzone' warns and returns `datetime.timezone.utc' -- while # `get_localzone' is annotated `-> zoneinfo.ZoneInfo'. The annotation is # wrong on its own documented fallback path, so a caller doing # `get_localzone().key' crashes exactly when configuration is missing. Both # the type and the identity are pinned. # # 3. `America/New York' IN /etc/timezone RESOLVES TO `America/New'. The parser # strips a host definition by splitting at the FIRST space before it reaches # its own `replace(" ", "_")', so the substitution is unreachable on that # path. It IS reachable through /etc/sysconfig/clock, which does not split # on space, and both halves are asserted -- the second answers # `America/New_York'. # # 4. A ZONE= SETTING MUST BE DOUBLE-QUOTED. The regexes end at a literal quote, # so `ZONE=Asia/Tokyo' unquoted matches nothing and the file is ignored # entirely. Asserted as the None it produces. # # 5. get_localzone_name() AND get_localzone() DO NOT ALWAYS AGREE. For a # POSIX-style TZ such as `PST4DST' the name path rejects it and falls # through to the files, answering a name, while the zone path hands it to # ZoneInfo and raises ZoneInfoNotFoundError with tzlocal's own message. The # ticket's "the two agree" holds for a CONSISTENT configuration, which this # image has; the divergence is a property of the input and is pinned as one. # # ============================================================================ # WHY THE STAGED ROOTS HOLD COPIES AND NOT A SYMLINK. # ============================================================================ # # When more than one config is found, tzlocal reconciles them by resolving each # name under `/usr/share/zoneinfo' and stripping exactly that many leading # components off the realpath. Staging that directory as a SYMLINK to the real # tree makes realpath escape the root, the strip then runs off the end of a # shorter path, and every name collapses to the empty string -- which reads as # unanimous agreement and silently disarms the conflict detection. Measured: with # the directory symlinked, a Europe/Moscow-vs-Asia/Tokyo conflict did NOT raise. # The probe therefore copies real zone files in. This is upstream's arithmetic # and it is correct for `_root="/"', which is the only root a caller uses. # # ============================================================================ # UPSTREAM'S OWN TESTS ARE NOT ON THIS IMAGE. # ============================================================================ # # The sdist ships tests/test_tzlocal.py and a tests/test_data/ tree, but # pyproject.toml's setuptools configuration packages the `tzlocal' package alone, # so tests/ is not a package and never reaches site-packages. The recipe does not # inherit ptest and this distro's DISTRO_FEATURES carries no `ptest' either, so # there is no ${PN}-ptest by either route. Both are asserted as absences rather # than assumed, and the cases here stand in their place. P=python3-tzlocal SUITE_TIMEOUT_HINT=1200 bst_ok $P/suite-canary require python3 $P/python3-present SITE=/usr/lib/python3.12/site-packages PKGDIR=$SITE/tzlocal MOD=$PKGDIR/__init__.py DI=$SITE/tzlocal-5.2.dist-info ZI=/usr/share/zoneinfo PKGLIST=/var/lib/dpkg/info/python3-tzlocal.list PROBE=$BST_DATA/python3-tzlocal-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-tzlocal.$$ mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # --------------------------------------------------------------- the artifact check $P/site-packages-dir test -d "$SITE" check $P/package-dir test -d "$PKGDIR" check $P/module-file test -f "$MOD" # A PACKAGE directory, not a flat module. check_eq $P/no-flat-module "absent" \ "$(if [ -f "$SITE/tzlocal.py" ]; then echo present; else echo absent; fi)" for _f in unix utils win32 windows_tz; do check $P/module-$_f test -f "$PKGDIR/$_f.py" done # win32.py SHIPS and is never imported here -- `__init__' picks unix on any # non-Windows platform. Asserted present on disk and asserted unimported by the # probe, so its cost is disk and nothing else. check $P/py-typed test -f "$PKGDIR/py.typed" check $P/pycache-dir test -d "$PKGDIR/__pycache__" # ------------------------------------------------------- the zone database # tzlocal answers names; tzdata is what makes a name resolve. Read directly, not # through the package. check $P/zoneinfo-dir test -d "$ZI" check $P/zoneinfo-zone-tab test -f "$ZI/zone.tab" check $P/zoneinfo-tzdata-zi test -f "$ZI/tzdata.zi" check $P/zoneinfo-iso3166 test -f "$ZI/iso3166.tab" for _z in Europe/Moscow America/New_York Asia/Tokyo Asia/Kolkata \ Australia/Sydney Africa/Harare Europe/London Etc/UTC UTC Universal; do check $P/zoneinfo-file-$(echo "$_z" | tr / -) test -f "$ZI/$_z" done # The TZif magic, off the file with shell tools: a tree of empty placeholders # would satisfy every `test -f' above and fail only here. check_out $P/zoneinfo-tzif-magic "TZif" head -c 4 "$ZI/Europe/Moscow" check_out $P/zoneinfo-tzif-magic-universal "TZif" head -c 4 "$ZI/Universal" check_eq $P/zoneinfo-nonempty "yes" \ "$(if [ -s "$ZI/Europe/Moscow" ]; then echo yes; else echo no; fi)" # ------------------------------------------------- the image's own tz config # tzdata writes both of these, so the real root always presents TWO configs and # always goes through tzlocal's reconciliation. DEFAULT_TIMEZONE is `Universal'. check $P/etc-timezone-present test -f /etc/timezone check_eq $P/etc-timezone-value "Universal" "$(cat /etc/timezone 2>/dev/null)" check $P/etc-localtime-symlink test -L /etc/localtime check_eq $P/etc-localtime-target "$ZI/Universal" \ "$(readlink -f /etc/localtime 2>/dev/null)" # ---------------------------------------------------------------- the package sed -n '/^Package: python3-tzlocal$/,/^$/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: 5.2-r0" cat "$W/st" grep '^Depends:' "$W/st" > "$W/dep" 2>/dev/null || : > "$W/dep" check $P/pkg-depends-line test -s "$W/dep" # THE WHOLE DEPENDS LINE, PINNED, and read off the built # python3-tzlocal_5.2-r0_wasm32.deb rather than inferred from the recipe. # meta-python's three declared runtime dependencies are each genuinely reached at # import time and python3-core is setuptools3-base's default, so this line is the # measured closure -- nothing under-declared to add and nothing spare to remove. # Pinned whole AND asserted by name, so a release that drops one names itself in # the red rather than only moving a count. check_eq $P/pkg-depends-exact \ "Depends: python3-core, python3-datetime, python3-logging, python3-zoneinfo" \ "$(sed 's/[[:space:]]*$//' "$W/dep")" for _d in core datetime logging zoneinfo; do check_out $P/pkg-depends-$_d "python3-$_d" cat "$W/dep" done # The file list is COPIED before it is read, because the `while read' below takes # it on stdin: a top-level stdin redirect on the virtiofs root makes dup2(2) # return 513 and `ash' exits, ending the suite in silence. # # BRINTOS-WORKAROUND: brintos/linux#73 remove cat "$PKGLIST" > "$W/pkgf" 2>/dev/null || : > "$W/pkgf" check $P/pkg-list-present test -s "$W/pkgf" # NO COMPILED EXTENSION. This image's python3 is one static binary with an empty # lib-dynload and no dynamic loader, so a `.so' here could not be loaded at all. check_eq $P/pkg-no-so "" \ "$(grep -E '\.so($|\.)' "$W/pkgf" 2>/dev/null)" # Everything shipped is Python source, its bytecode, the py.typed marker or # dist-info metadata. Scoped to THIS package's own file list rather than to the # shape of site-packages, which siblings on this batch image also populate. _bad="" while IFS= read -r _f; do [ -f "$_f" ] || continue case "$_f" in *.py|*.pyc) continue ;; */__pycache__/*) continue ;; */py.typed) continue ;; */*.dist-info/*) continue ;; esac _bad="$_bad $_f" done < "$W/pkgf" check_eq $P/pkg-only-python "" "$_bad" # ------------------------------------------------------------- the metadata # A dist-info, built by `bdist_wheel' over setuptools -- `inherit # python_setuptools_build_meta' cuts a wheel and installs it, so there is no # egg-info here. Every value below was read off the built .deb. check $P/dist-info-dir test -d "$DI" check $P/dist-info-metadata test -f "$DI/METADATA" check $P/dist-info-record test -f "$DI/RECORD" check $P/dist-info-license test -f "$DI/LICENSE.txt" check_out $P/dist-info-name "Name: tzlocal" cat "$DI/METADATA" check_out $P/dist-info-version "Version: 5.2" cat "$DI/METADATA" check_out $P/dist-info-wheel-generator "bdist_wheel" cat "$DI/WHEEL" check_out $P/dist-info-wheel-purelib "Root-Is-Purelib: true" cat "$DI/WHEEL" # `py3-none-any': a pure-Python wheel with no ABI tag, which is what makes this # port arch-independent in everything but the package it lands in. check_out $P/dist-info-wheel-tag "Tag: py3-none-any" cat "$DI/WHEEL" check_eq $P/dist-info-top-level "tzlocal" "$(cat "$DI/top_level.txt" 2>/dev/null)" # NO -ptest PACKAGE, and no upstream tests by the other route either. check_eq $P/pkg-no-ptest-status "absent" \ "$(if grep -q '^Package: python3-tzlocal-ptest$' /var/lib/dpkg/status 2>/dev/null; \ then echo present; else echo absent; fi)" check_eq $P/pkg-no-tests-dir "absent" \ "$(if [ -d "$PKGDIR/tests" ]; then echo present; else echo absent; fi)" check_eq $P/pkg-no-test-module "absent" \ "$(if [ -f "$PKGDIR/test_tzlocal.py" ]; then echo present; else echo absent; fi)" # ----------------------------------------------------------------- the import check $P/import python3 -c 'import tzlocal; tzlocal.get_localzone_name' # The module resolves out of site-packages rather than a stray on the path. check_out $P/import-from-site "$PKGDIR" \ python3 -c 'import tzlocal, os; print(os.path.dirname(tzlocal.__file__))' # ------------------------------------------------------------------ the probe if [ ! -f "$PROBE" ]; then bst_notok $P/probe-present "$PROBE did not ship" rm -rf "$W" exit 0 fi bst_ok $P/probe-present bst_probe $P/probe-run "$W/probe" 900 python3 "$PROBE" OUT="$W/probe" pv() { grep "^$1=" "$OUT" 2>/dev/null | head -n 1 | cut -d= -f2-; } if [ -s "$OUT" ]; then bst_ok $P/probe-output else bst_notok $P/probe-output "the probe emitted nothing -- python3 did not start, or died before its first line; every CASE below is unscorable" fi check_eq $P/probe-complete "yes" "$(pv probe_complete)" # ------------------------------------------------- identity and provenance check_eq $P/python-version "3.12" "$(pv python_version)" check_eq $P/not-win32 "no" "$(pv platform_is_win32)" check_eq $P/import-ok "yes" "$(pv import_ok)" check_eq $P/module-answered "$MOD" "$(pv module_file)" check_eq $P/module-is-package "yes" "$(pv module_is_package)" # FACT 1: no __version__ attribute at all. check_eq $P/no-dunder-version "no" "$(pv has_dunder_version)" check_eq $P/meta-version "5.2" "$(pv meta_version)" check_eq $P/meta-name "tzlocal" "$(pv meta_name)" # The four names __init__ exports, and each of them present. check_eq $P/exports "assert_tz_offset,get_localzone,get_localzone_name,reload_localzone" \ "$(pv all_exports)" for _n in get_localzone get_localzone_name reload_localzone assert_tz_offset; do check_eq $P/has-$_n "yes" "$(pv has_$_n)" done # WHAT `import tzlocal' ACTUALLY LOADS. unix and utils are expected; windows_tz # is the one a reader would not expect -- utils.py imports it at module scope for # the TZ name table, so the Windows mapping is resident on Linux. win32 is NOT # imported, and that is the platform branch working. check_eq $P/imports-unix "yes" "$(pv mod_unix_imported)" check_eq $P/imports-utils "yes" "$(pv mod_utils_imported)" check_eq $P/imports-windows-tz "yes" "$(pv mod_windows_tz_imported)" check_eq $P/no-import-win32 "no" "$(pv mod_win32_imported)" # The two mapping tables, by size. tz_win is the one the TZ parser tests # membership against, so its size is the reach of "TZ names tzlocal recognises". check_eq $P/tz-win-len "587" "$(pv tz_win_len)" check_eq $P/win-tz-len "139" "$(pv win_tz_len)" # --------------------------------------------------- the database, via stdlib check_out $P/tzpath-has-usr-share "/usr/share/zoneinfo" echo "$(pv tzpath)" # This image runs at UTC: /etc/localtime is tzdata's Universal and no TZ is set. # It is what assert_tz_offset scores every zone against, so it is pinned. check_eq $P/system-offset "0" "$(pv system_offset)" # UTC offsets at explicit instants. Each date's rule has been settled law for # years, so the expected value does not move with a tzdata release: Europe/Moscow # has been permanent +03:00 since 2014-10-26; US DST bounds have been fixed since # 2007; Asia/Tokyo has had no DST since 1951; Asia/Kolkata is the half-hour case; # Australia/Sydney is southern-hemisphere, so its January is the DST one. check_eq $P/offset-moscow-jan "10800" "$(pv off_Europe_Moscow_jan)" check_eq $P/offset-moscow-jul "10800" "$(pv off_Europe_Moscow_jul)" check_eq $P/abbr-moscow "MSK" "$(pv abbr_Europe_Moscow_jan)" check_eq $P/offset-newyork-jan "-18000" "$(pv off_America_New_York_jan)" check_eq $P/offset-newyork-jul "-14400" "$(pv off_America_New_York_jul)" check_eq $P/abbr-newyork-jan "EST" "$(pv abbr_America_New_York_jan)" check_eq $P/abbr-newyork-jul "EDT" "$(pv abbr_America_New_York_jul)" check_eq $P/offset-tokyo-jan "32400" "$(pv off_Asia_Tokyo_jan)" check_eq $P/offset-tokyo-jul "32400" "$(pv off_Asia_Tokyo_jul)" check_eq $P/offset-kolkata-jan "19800" "$(pv off_Asia_Kolkata_jan)" check_eq $P/offset-sydney-jan "39600" "$(pv off_Australia_Sydney_jan)" check_eq $P/offset-sydney-jul "36000" "$(pv off_Australia_Sydney_jul)" check_eq $P/abbr-sydney-jan "AEDT" "$(pv abbr_Australia_Sydney_jan)" check_eq $P/offset-etc-utc-jan "0" "$(pv off_Etc_UTC_jan)" # `Universal' is the name THIS image is configured with. tzdata ships it in # tzdata-core; a Debian-derived build host keeps it in a separate tzdata-legacy # package and cannot resolve it at all, which is why it has its own case here # rather than standing in for UTC above. check_eq $P/universal-resolves "yes" "$(pv universal_resolves)" check_eq $P/universal-offset "0" "$(pv universal_offset)" # THE DST TRANSITION INSTANTS, straddled one second either side. This is the case # a zone that resolves by NAME but carries wrong DATA fails -- the offset table # above would still pass a zone frozen at a single offset. check_eq $P/dst-ny-spring-before "-18000" "$(pv trans_ny_spring_before)" check_eq $P/dst-ny-spring-after "-14400" "$(pv trans_ny_spring_after)" check_eq $P/dst-ny-fall-before "-14400" "$(pv trans_ny_fall_before)" check_eq $P/dst-ny-fall-after "-18000" "$(pv trans_ny_fall_after)" check_eq $P/dst-london-spring-before "0" "$(pv trans_london_spring_before)" check_eq $P/dst-london-spring-after "3600" "$(pv trans_london_spring_after)" check_eq $P/dst-london-fall-before "3600" "$(pv trans_london_fall_before)" check_eq $P/dst-london-fall-after "0" "$(pv trans_london_fall_after)" check_eq $P/dst-sydney-before "39600" "$(pv trans_sydney_apr_before)" check_eq $P/dst-sydney-after "36000" "$(pv trans_sydney_apr_after)" # ------------------------------------------------ the documented precedence # Each case is one staged root under $TMPDIR, driven through upstream's own # `_root' hook. The target's real /etc is never written to. check_eq $P/root-etctz-name "Europe/Moscow" "$(pv root_etctz_name)" check_eq $P/root-etctz-zone "Europe/Moscow" "$(pv root_etctz_zone)" check_eq $P/root-etctz-zone-type "ZoneInfo" "$(pv root_etctz_zone_type)" # /etc/localtime as a symlink, with NO /etc/timezone -- the systemd shape. The # name is recovered by stripping leading components off the link's realpath until # one parses as a zone. check_eq $P/root-symlink-name "Africa/Harare" "$(pv root_symlink_name)" check_eq $P/root-symlink-zone "Africa/Harare" "$(pv root_symlink_zone)" # Two configs that agree reconcile to the one name. check_eq $P/root-agree-name "Asia/Tokyo" "$(pv root_agree_name)" # The shape THIS image is in: /etc/timezone plus an /etc/localtime symlink, both # naming the same zone. Staged rather than read off the real root, so the # reconciliation is measured independently of what this guest is configured as. check_eq $P/root-tzdata-shape-name "Etc/UTC" "$(pv root_tzdata_shape_name)" check_eq $P/root-tzdata-shape-offset "0" "$(pv root_tzdata_shape_offset)" # Two configs that DISAGREE: the documented failure, and it is a # ZoneInfoNotFoundError -- not a ValueError, and not a silent pick of one. check_eq $P/root-conflict-raises "ZoneInfoNotFoundError" "$(pv root_conflict_raises)" check_out $P/root-conflict-message "Multiple conflicting time zone configurations found" \ echo "$(pv root_conflict_msg_head)" # FACT 2: nothing configured at all. The name is None; the zone is # `datetime.timezone.utc' and NOT a ZoneInfo, which the annotation on # get_localzone claims. Type, identity, offset and the warning are all pinned. check_eq $P/root-empty-name "" "$(pv root_empty_name)" check_eq $P/root-empty-zone "UTC" "$(pv root_empty_zone)" check_eq $P/root-empty-zone-type "timezone" "$(pv root_empty_zone_type)" check_eq $P/root-empty-is-dt-utc "yes" "$(pv root_empty_zone_is_dt_utc)" check_eq $P/root-empty-offset "0" "$(pv root_empty_offset)" check_eq $P/root-empty-warns "Can not find any timezone configuration, defaulting to UTC." \ "$(pv root_empty_warns)" # Parsing of /etc/timezone: a trailing comment and a host definition are stripped. check_eq $P/root-comment-name "Europe/Moscow" "$(pv root_comment_name)" check_eq $P/root-plain-name "America/New_York" "$(pv root_spaces_name)" # FACT 3: the space-to-underscore substitution is UNREACHABLE on the # /etc/timezone path -- the host-definition split runs first -- and reachable on # the sysconfig path, which does not split on space. check_eq $P/root-space-truncates "America/New" "$(pv root_underscore_name)" check_eq $P/root-space-in-clockfile "America/New_York" "$(pv root_underscore_clock_name)" # An EMPTY /etc/timezone is skipped rather than read as a config. check_eq $P/root-empty-file-name "" "$(pv root_emptyfile_name)" # /var/db/zoneinfo, the BSD location, read in the same loop as /etc/timezone. check_eq $P/root-vardb-name "Asia/Kolkata" "$(pv root_vardb_name)" # The CentOS/OpenSUSE ZONE= and Gentoo TIMEZONE= settings. check_eq $P/root-sysconfig-name "Asia/Tokyo" "$(pv root_sysconfig_name)" check_eq $P/root-confd-name "Europe/Moscow" "$(pv root_confd_name)" # FACT 4: the value must be DOUBLE-QUOTED. Unquoted, the regex matches nothing # and the whole file is ignored. check_eq $P/root-sysconfig-unquoted-ignored "" "$(pv root_sysconfig_unquoted_name)" # ------------------------------------------------------------- TZ precedence # TZ is consulted FIRST and is read from the real environment -- `_root' does not # cover it -- so it wins over a staged root that says something else. check_eq $P/tz-wins-over-etctz "Asia/Tokyo" "$(pv tz_wins_over_etctz)" check_eq $P/tz-unset-falls-back "Europe/Moscow" "$(pv tz_unset_falls_back)" check_eq $P/tz-colon-form "Asia/Tokyo" "$(pv tz_colon_form)" check_eq $P/tz-absolute-path "Asia/Tokyo" "$(pv tz_abs_path)" # A name tz_win does not carry is not recognised as a name and falls THROUGH to # the files rather than raising -- so an unknown TZ is silently ignored. check_eq $P/tz-unknown-falls-through "Europe/Moscow" "$(pv tz_unknown_name_falls_through)" # FACT 5: the two public entry points diverge on a POSIX-style TZ. check_eq $P/tz-posix-name-falls-through "Europe/Moscow" "$(pv tz_posix_name)" check_eq $P/tz-posix-zone-raises "ZoneInfoNotFoundError" "$(pv tz_posix_zone_raises)" check_out $P/tz-posix-zone-message "does not support non-zoneinfo timezones like PST4DST" \ echo "$(pv tz_posix_zone_msg)" # ------------------------------------------------ the real root, and caching # What this image itself resolves to, through the same code the staged cases # drive. tzdata's DEFAULT_TIMEZONE is Universal and both configs name it. check_eq $P/real-etc-timezone "Universal" "$(pv real_etc_timezone)" check_eq $P/real-localtime-link "yes" "$(pv real_etc_localtime_islink)" check_eq $P/real-localtime-target "$ZI/Universal" "$(pv real_etc_localtime_target)" check_eq $P/real-name "Universal" "$(pv real_name)" check_eq $P/real-zone "Universal" "$(pv real_zone)" check_eq $P/real-zone-type "ZoneInfo" "$(pv real_zone_type)" check_eq $P/real-offset "0" "$(pv real_offset)" # The public API, and the ticket's own criterion: the two agree HERE because this # image's configuration is consistent. check_eq $P/public-name "Universal" "$(pv public_name)" check_eq $P/public-zone "Universal" "$(pv public_zone)" check_eq $P/public-agree "yes" "$(pv public_agree)" # The module-level cache: two calls return the SAME object, which is what a cache # claims -- identity, not equality. check_eq $P/cache-same-object "yes" "$(pv cache_same_object)" check_eq $P/cache-name-populated "yes" "$(pv cache_name_populated)" check_eq $P/reload-returns-zone "Universal" "$(pv reload_returns_zone)" # --------------------------------------------------------- assert_tz_offset # The exported guard, both arms, driven from the MEASURED system offset so the # case says the same thing wherever it runs. check_eq $P/assert-offset-match-quiet "no-raise" "$(pv assert_offset_match_ok)" check_eq $P/assert-offset-utc-quiet "no-raise" "$(pv assert_offset_utc_ok)" check_eq $P/assert-offset-mismatch-raises "ValueError" "$(pv assert_offset_mismatch_raises)" # error=False downgrades the same mismatch to a warning. check_eq $P/assert-offset-warn-only "no-raise" "$(pv assert_offset_mismatch_warns_only)" check_eq $P/get-tz-offset-utc "0" "$(pv get_tz_offset_utc)" # ------------------------------------------------------------- the packaging check_eq $P/probe-py-typed "yes" "$(pv has_py_typed)" check_eq $P/probe-no-tests-pkg "absent" "$(pv no_tests_package)" rm -rf "$W"