. "$BST_LIB" # BST-TIMEOUT: 1200 # TWO python3 starts, 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, so # # the bound is two interpreter loads plus a constant-table # # dump and five short pipe round trips, none of which forks. # # It matches 99-python3-tomli-w's 1200 rather than doubling # # it: that suite takes ONE start and the same bound, and the # # start is what dominates -- a second load is not a second # # suite. 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-colorama 0.4.6 (meta-openembedded/meta-python, # python3-colorama_0.4.6.bb, `inherit pypi python_setuptools_build_meta') -- THE # ANSI CONSTANT TABLE AND THE STREAM WRAPPER, on the wasm32-hwjs glibc image. It # is the library every other coloured-output package on this image rests on. # # ============================================================================ # WHAT THE PACKAGE ACTUALLY IS, AND THEREFORE WHAT THIS SUITE ASSERTS. # ============================================================================ # # Two things, and they are asserted separately because they fail separately: # # 1. A TABLE OF LITERALS. `Fore', `Back' and `Style' are objects whose # attributes are the ANSI SGR sequences `ESC [ m' for the codes the # standard fixes -- 30..37/39/90..97 foreground, 40..47/49/100..107 # background, 0/1/2/22 style. Nothing about them is platform-dependent, so # every one is asserted by value. # # 2. A DECISION ABOUT A STREAM. `init()' wraps `sys.stdout' so that escape # sequences SURVIVE when the stream is a terminal and are STRIPPED when it # is not. That is the whole reason the package exists rather than being a # dict of strings, and it is what the pipe round trips below drive. # # A suite that asserted only (1) would pass on a copy of ansi.py with the rest # deleted; one that asserted only (2) would pass on a wrapper with the wrong # codes in it. # # ============================================================================ # WHY THIS PORT IS POSSIBLE AT ALL, AND WHAT THE .so CASE IS REALLY ASSERTING. # ============================================================================ # # 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 that # carried a compiled extension therefore could not be imported here -- not # "would be slow", could not be imported. colorama is pure Python, which is the # entire reason it ports. # # So `module-no-so' is not boilerplate. It is a REGRESSION TRIPWIRE on a version # bump, and it is anchored on the package's own dpkg file list as well as on a # find, because the two fail differently -- a find catches a file the package # does not declare, the list catches a declared file that is not a .py. # # THE WINDOWS HALF OF THE PACKAGE IS THE INTERESTING CASE HERE. colorama ships # `win32.py', which does `import ctypes' and then `LibraryLoader(ctypes.WinDLL)'. # It catches BOTH AttributeError (a POSIX ctypes has no WinDLL) AND ImportError # (no ctypes module at all -- which is a shape THIS image could genuinely take, # given the empty lib-dynload). Either arm leaves `windll' as None and turns # every win32 call into a no-op, which is why a Windows-facing package ports to # a platform with no Windows and no ctypes guarantee. `windll-none' asserts the # outcome; `ctypes-importable' RECORDS which arm this image took, and is # deliberately not asserted -- colorama is correct either way and the fact is # worth having for the next port that needs ctypes. # # ============================================================================ # THE STREAM DECISION IS DRIVEN OVER A REAL PIPE, AND ONE HALF IS SIMULATED. # ============================================================================ # # `init(strip=True)' writing to an `os.pipe()' is the ticket's own criterion and # it is exact: the text arrives with every escape removed. The OTHER direction -- # an escape that survives because the stream IS a terminal -- cannot be staged # here, because this suite has no pty. It is driven instead by asking for the # same code path explicitly, `AnsiToWin32(pipe, strip=False)', which is the flag # `AnsiToWin32.__init__' computes from `have_tty' when the stream is a terminal. # # THAT IS A SIMULATION OF THE TTY CASE AND IS RECORDED AS ONE. What it does # establish is that the stripping is a DECISION rather than an unconditional # deletion, which is the failure mode a strip-only suite could not see. What it # does not establish is that `isatty()' on a real terminal on this arm returns # true; `pipe-isatty' asserts the negative half of that, which is the half this # image can measure. # # ============================================================================ # ESC IS NEVER COMPARED RAW. # ============================================================================ # # The probe renders the 0x1b byte as the three letters `ESC', so every value # below is an ordinary printable string and `[ "$a" = "$b" ]' is safe. A raw ESC # in a result line would be re-interpreted by the console the host captures # through, and the assertion would then be about what the terminal did to the # byte. `esc-is-1b' is the single case that says the byte being spelled really is # 0x1b, so the rendering cannot hide a wrong escape character. # # EVERY ASSERTED VALUE WAS MEASURED, on x86-64 CPython 3.12.3, against THIS EXACT # PROBE and against the 0.4.6 wheel whose contents match what this recipe builds # from the sdist the SRC_URI sha256 pins # (08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44, verified by # digest before measuring). They are not predictions and they are not read off # the source. The control interpreter is 3.12 and the image ships 3.12, so there # is no version gap to declare. # # What makes the control transferable is that all of it is pure Python and # content-determined: an SGR sequence is a decimal number in a fixed frame, and # the strip/keep round trips are string operations over a pipe. Nothing asserted # depends on pointer width, byte order or the platform string. The one key that # legitimately could differ between platforms is `ctypes_importable', and it is # reported rather than asserted for exactly that reason. # # ============================================================================ # WHAT IS NOT COVERED, ON PURPOSE. # ============================================================================ # # - THE WIN32 CONVERSION PATH. `convert=True' turns SGR sequences into # SetConsoleTextAttribute calls through `windll', which is None here. There # is nothing to call and nothing to observe; asserting it would be asserting # a mock. `windll-none' and `atw-convert-default' bound it instead. # - A REAL TTY. See above: no pty on this image's suite runner. # - THE ptest PACKAGE. The recipe does not `inherit ptest', so none is built. # Upstream's test suite ships INSIDE the importable package # (`colorama/tests/'), needs pytest, and asserts Windows behaviour through # mocks; `module-tests-dir' records that the directory is present, and the # tests themselves are out of scope. # - `just_fix_windows_console()'. Its first statement is `if sys.platform != # "win32": return', so on this image it is a no-op by construction. # `has-just-fix-windows-console' asserts the name exists and stops there. # # THIS PORT CLOSES ON THIS SUITE: non-interactive, no manual browser pass, no # follow-on ticket under BRINTOS-780. P=python3-colorama # THE CANARY, which asserts nothing about python3-colorama. # # 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' guards 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 CO=$SITE/colorama DI=$SITE/colorama-0.4.6.dist-info PKGLIST=/var/lib/dpkg/info/python3-colorama.list D=$BST_DATA PROBE=$D/python3-colorama-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-colorama.$$ mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # ------------------------------------------------------------ the payload --- # Where the module has to land. `python3 -c 'import colorama'` 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 "$CO" check $P/module-init test -f "$CO/__init__.py" # The whole of the module, named individually rather than counted: a count moves # on any upstream refactor, while each of these is a distinct part of the # package. ansi.py is the constant table, initialise.py is init/deinit, # ansitowin32.py is the stream wrapper, and win32.py/winterm.py are the Windows # half that must be PRESENT and inert here rather than absent -- a package that # had dropped them would import differently and this suite would be describing a # different colorama. check $P/module-ansi test -f "$CO/ansi.py" check $P/module-initialise test -f "$CO/initialise.py" check $P/module-ansitowin32 test -f "$CO/ansitowin32.py" check $P/module-win32 test -f "$CO/win32.py" check $P/module-winterm test -f "$CO/winterm.py" # Upstream ships its own test suite INSIDE the importable package. It is not run # here (see the header) and it is not a defect; the case exists so that a release # which moved or dropped it is visible rather than surprising a later reader of # pkg-only-python. check $P/module-tests-dir test -d "$CO/tests" # 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' on a later ticket. check_eq $P/module-no-so "" \ "$(find "$CO" -name '*.so' -o -name '*.so.*' 2>/dev/null)" # ...and the same assertion from the PACKAGE's side rather than the filesystem's. # A find sees what is on disk; the dpkg list sees what the package CLAIMS. # # 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. # # A dpkg .list names DIRECTORIES too, and without a trailing slash, so a # name-only filter would report every directory in the package as an unexpected # shape; each entry is therefore tested for being a regular file first. The # allowed classes are .py, .pyc, __pycache__ and the dist-info tree -- colorama # ships no py.typed, no data file and no script, which is why no exemption for # one appears here. _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 dist-info ---- # All four files are asserted because they are what makes the installed module # discoverable by importlib.metadata; a package whose module landed but whose # metadata did not would import fine here and be invisible to any dependency # resolver on the image -- and `dist-version' below, which reads the metadata # through importlib, would be the case that goes red for it. # # The licence sits under a `licenses/' SUBDIRECTORY, which is where a # PEP 639-era wheel puts it and NOT where python3-tomli-w's is; that difference # is asserted rather than smoothed over, so a packaging change that flattened it # is visible here. 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-wheel test -f "$DI/WHEEL" check $P/dist-info-license test -f "$DI/licenses/LICENSE.txt" # THE VERSION THE RECIPE PINS, from the metadata rather than from the module. # Together with $P/version below -- which reads colorama.__version__ -- this is # two independent statements of the same fact, and the two disagreeing would mean # a mixed install: a dist-info from one version beside a module from another. The # directory name carries it a third time, in $DI itself. check_out $P/dist-info-version "Version: 0.4.6" cat "$DI/METADATA" # --------------------------------------------------------- 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-colorama 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 because `python3-colorama' is a prefix of nothing on this image # today and a package named `python3-colorama-' tomorrow would # otherwise be read as this one. sed -n '/^Package: python3-colorama$/,/^$/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" # THE RUNTIME DEPENDENCIES dpkg RECORDED. One case per dependency, against the # extracted line, rather than one case asserting the whole list as a string: the # recipe's RDEPENDS order is not dpkg's to preserve and the separator spacing is # dpkg's own, so an exact-string case would be asserting a FORMAT rather than a # fact. Split, each red also names which dependency went missing. grep '^Depends:' "$W/st" > "$W/dep" 2>/dev/null || : > "$W/dep" check $P/pkg-depends-line test -s "$W/dep" check_out $P/pkg-depends-core "python3-core" cat "$W/dep" # ---------------------------------------------------------- the interpreter -- # THE IMPORT, ON ITS OWN, BEFORE THE PROBE. If colorama 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, and it names the class the package is FOR, so a # module that imported without its constant table is caught here rather than # thirty cases later. check $P/import python3 -c 'from colorama import Fore, Back, Style' # ------------------------------------------------------------- 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" 900 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 `version' distinct from # `dist_version', and `fore_RED' from `fore_LIGHTRED_EX'. 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 constant table, 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 in 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 # THE VERSION THE RECIPE PINS, read from the module. 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 colorama. check_eq $P/version "0.4.6" "$(pv version)" check_eq $P/dist-version "0.4.6" "$(pv dist_version)" # 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 colorama. check_eq $P/module-file "$CO/__init__.py" "$(pv module_file)" # THE PUBLIC SURFACE. Each name the package promises, asserted on its own. check_eq $P/has-init "yes" "$(pv has_init)" check_eq $P/has-deinit "yes" "$(pv has_deinit)" check_eq $P/has-reinit "yes" "$(pv has_reinit)" check_eq $P/has-colorama-text "yes" "$(pv has_colorama_text)" check_eq $P/has-just-fix-windows-console "yes" "$(pv has_just_fix_windows_console)" check_eq $P/has-fore "yes" "$(pv has_Fore)" check_eq $P/has-back "yes" "$(pv has_Back)" check_eq $P/has-style "yes" "$(pv has_Style)" check_eq $P/has-cursor "yes" "$(pv has_Cursor)" check_eq $P/has-ansitowin32 "yes" "$(pv has_AnsiToWin32)" # ------------------------------------------------------- the constant table -- # THE NAME SETS, WHOLE. The per-name cases below catch a constant whose VALUE # moved; only these catch one that was ADDED or REMOVED, which is the shape a # version bump takes. Asserted as sorted comma-joined sets so a red prints the # difference rather than a boolean. check_eq $P/fore-names \ "BLACK,BLUE,CYAN,GREEN,LIGHTBLACK_EX,LIGHTBLUE_EX,LIGHTCYAN_EX,LIGHTGREEN_EX,LIGHTMAGENTA_EX,LIGHTRED_EX,LIGHTWHITE_EX,LIGHTYELLOW_EX,MAGENTA,RED,RESET,WHITE,YELLOW" \ "$(pv fore_names)" check_eq $P/back-names \ "BLACK,BLUE,CYAN,GREEN,LIGHTBLACK_EX,LIGHTBLUE_EX,LIGHTCYAN_EX,LIGHTGREEN_EX,LIGHTMAGENTA_EX,LIGHTRED_EX,LIGHTWHITE_EX,LIGHTYELLOW_EX,MAGENTA,RED,RESET,WHITE,YELLOW" \ "$(pv back_names)" check_eq $P/style-names "BRIGHT,DIM,NORMAL,RESET_ALL" "$(pv style_names)" check_eq $P/cursor-names "BACK,DOWN,FORWARD,POS,UP" "$(pv cursor_names)" # THE ESCAPE CHARACTER, asserted before anything that is rendered with it. Every # value below spells the 0x1b byte as `ESC'; this is the case that says the byte # being spelled really is 0x1b, so a wrong escape character cannot hide inside # the rendering. `csi' and `osc' are the two frames ansi.py builds from. check_eq $P/esc-is-1b "yes" "$(pv esc_is_1b)" check_eq $P/csi "ESC[" "$(pv csi)" check_eq $P/osc "ESC]" "$(pv osc)" # THE EIGHT STANDARD FOREGROUNDS plus RESET. `ESC [ 3n m', n = 0..7, and 39 for # the terminal's default. check_eq $P/fore-black "ESC[30m" "$(pv fore_BLACK)" check_eq $P/fore-red "ESC[31m" "$(pv fore_RED)" check_eq $P/fore-green "ESC[32m" "$(pv fore_GREEN)" check_eq $P/fore-yellow "ESC[33m" "$(pv fore_YELLOW)" check_eq $P/fore-blue "ESC[34m" "$(pv fore_BLUE)" check_eq $P/fore-magenta "ESC[35m" "$(pv fore_MAGENTA)" check_eq $P/fore-cyan "ESC[36m" "$(pv fore_CYAN)" check_eq $P/fore-white "ESC[37m" "$(pv fore_WHITE)" check_eq $P/fore-reset "ESC[39m" "$(pv fore_RESET)" # THE EIGHT BRIGHT FOREGROUNDS, 90..97. colorama's own source calls these "fairly # well supported, but not part of the standard" -- they are asserted all the same # because they are part of the package's published surface and a caller that used # one would break silently if it moved. check_eq $P/fore-lightblack "ESC[90m" "$(pv fore_LIGHTBLACK_EX)" check_eq $P/fore-lightred "ESC[91m" "$(pv fore_LIGHTRED_EX)" check_eq $P/fore-lightgreen "ESC[92m" "$(pv fore_LIGHTGREEN_EX)" check_eq $P/fore-lightyellow "ESC[93m" "$(pv fore_LIGHTYELLOW_EX)" check_eq $P/fore-lightblue "ESC[94m" "$(pv fore_LIGHTBLUE_EX)" check_eq $P/fore-lightmagenta "ESC[95m" "$(pv fore_LIGHTMAGENTA_EX)" check_eq $P/fore-lightcyan "ESC[96m" "$(pv fore_LIGHTCYAN_EX)" check_eq $P/fore-lightwhite "ESC[97m" "$(pv fore_LIGHTWHITE_EX)" # THE EIGHT STANDARD BACKGROUNDS plus RESET, 40..47 and 49. The foreground and # background tables differ ONLY by the tens digit, so a bug that built one from # the other with the wrong offset would show up here and nowhere else. check_eq $P/back-black "ESC[40m" "$(pv back_BLACK)" check_eq $P/back-red "ESC[41m" "$(pv back_RED)" check_eq $P/back-green "ESC[42m" "$(pv back_GREEN)" check_eq $P/back-yellow "ESC[43m" "$(pv back_YELLOW)" check_eq $P/back-blue "ESC[44m" "$(pv back_BLUE)" check_eq $P/back-magenta "ESC[45m" "$(pv back_MAGENTA)" check_eq $P/back-cyan "ESC[46m" "$(pv back_CYAN)" check_eq $P/back-white "ESC[47m" "$(pv back_WHITE)" check_eq $P/back-reset "ESC[49m" "$(pv back_RESET)" # THE EIGHT BRIGHT BACKGROUNDS, 100..107 -- THREE digits, where every other # sequence in this table has two. That is the one place in the table where the # frame's width changes, so it is the one a hand-rolled formatter gets wrong. check_eq $P/back-lightblack "ESC[100m" "$(pv back_LIGHTBLACK_EX)" check_eq $P/back-lightred "ESC[101m" "$(pv back_LIGHTRED_EX)" check_eq $P/back-lightgreen "ESC[102m" "$(pv back_LIGHTGREEN_EX)" check_eq $P/back-lightyellow "ESC[103m" "$(pv back_LIGHTYELLOW_EX)" check_eq $P/back-lightblue "ESC[104m" "$(pv back_LIGHTBLUE_EX)" check_eq $P/back-lightmagenta "ESC[105m" "$(pv back_LIGHTMAGENTA_EX)" check_eq $P/back-lightcyan "ESC[106m" "$(pv back_LIGHTCYAN_EX)" check_eq $P/back-lightwhite "ESC[107m" "$(pv back_LIGHTWHITE_EX)" # THE FOUR STYLES. RESET_ALL is `ESC[0m' and is the one every caller must emit to # stop a colour leaking into whatever prints next; it is asserted here as a value # and again below as the terminator of a composed string. check_eq $P/style-bright "ESC[1m" "$(pv style_BRIGHT)" check_eq $P/style-dim "ESC[2m" "$(pv style_DIM)" check_eq $P/style-normal "ESC[22m" "$(pv style_NORMAL)" check_eq $P/style-reset-all "ESC[0m" "$(pv style_RESET_ALL)" # THE CURSOR MOVERS, which are functions rather than constants -- so these also # say the defaults are what the source documents (n=1) and that POS takes its # arguments x,y and emits them y;x, which is the one place in this package where # an argument order can be silently transposed. check_eq $P/cursor-up "ESC[1A" "$(pv cursor_up)" check_eq $P/cursor-down-3 "ESC[3B" "$(pv cursor_down_3)" check_eq $P/cursor-forward "ESC[1C" "$(pv cursor_forward)" check_eq $P/cursor-back "ESC[1D" "$(pv cursor_back)" check_eq $P/cursor-pos-2-7 "ESC[7;2H" "$(pv cursor_pos_2_7)" # ------------------------------------------------------- the composed string -- # THE TICKET'S OWN CRITERION. The constants are strings and composition is `+'; # what has to be asserted is that the result carries the colour sequence AND the # RESET_ALL that terminates it. The whole string is asserted by value, and then # its three properties separately -- so a red says whether the colour, the text # or the reset is what went missing rather than printing twenty characters back. check_eq $P/composed "ESC[31mhello worldESC[0m" "$(pv composed)" check_eq $P/composed-len "20" "$(pv composed_len)" check_eq $P/composed-starts-fore "yes" "$(pv composed_starts_fore)" check_eq $P/composed-text "yes" "$(pv composed_text)" check_eq $P/composed-ends-reset "yes" "$(pv composed_ends_reset)" # ------------------------------------------------------------ the win32 half -- # The Windows path is inert here, and that is what makes this package portable. # See the header. `ctypes-importable' is REPORTED, not asserted: colorama is # correct whichever arm of its try/except this image takes, and pinning it would # turn a fact about the interpreter into a fact about colorama. check_eq $P/windll-none "yes" "$(pv windll_is_none)" check_eq $P/os-name "posix" "$(pv os_name)" # winapi_test is rebound to `lambda *_: None' on the non-Windows arm, so this # says the module took that arm -- from the OTHER side of the same try/except # that windll-none reads, which is why both are here. check_eq $P/winapi-test-none "None" "$(pv winapi_test)" check_eq $P/ctypes-importable-known "" \ "$(case "$(pv ctypes_importable)" in yes|no) ;; *) echo "unexpected:$(pv ctypes_importable)" ;; esac)" # -------------------------------------------------------- init() / deinit() -- # THE TICKET'S OTHER CRITERION: the wrap is reversible. `deinit-restored' is the # requirement and it is an IDENTITY, not an equality -- a stream that merely # compared equal would still be a different file for anything that cares about # buffering or fileno(). # # `init-wrapped' is what stops that being satisfiable by an `init()' that did # nothing at all: the wrap has to have happened for its reversal to mean # anything. `pipe-isatty' is the premise both rest on -- colorama wraps because # the stream is not a terminal -- and it is the half of the tty question this # image can actually measure. check_eq $P/pipe-isatty "no" "$(pv pipe_isatty)" check_eq $P/init-wrapped "yes" "$(pv init_wrapped)" check_eq $P/init-wrapper-class "StreamWrapper" "$(pv init_wrapper_class)" check_eq $P/deinit-restored "yes" "$(pv deinit_restored)" # ------------------------------------------- init(strip=True) over a pipe ---- # THE BEHAVIOUR THE LIBRARY EXISTS TO PROVIDE, driven end to end: a coloured, # composed string written through an `init(strip=True)'-wrapped stdout whose # underlying file is a real `os.pipe()', read back from the far end. # # Four cases rather than one, because they fail differently: the text arrived # whole, its LENGTH is what a stripped 20-character composed string plus the # newline `print' adds must be (12), no escape byte survived, and the payload # is exactly the text. A stripper that ate one character of the payload passes # `strip-has-esc' and fails the other three. check_eq $P/strip-wrapped "yes" "$(pv strip_wrapped)" check_eq $P/strip-out "hello world" "$(pv strip_out)" check_eq $P/strip-out-len "12" "$(pv strip_out_len)" check_eq $P/strip-has-esc "no" "$(pv strip_has_esc)" check_eq $P/strip-text-whole "yes" "$(pv strip_text_whole)" # ---------------------------------------------------- the other direction ---- # AN ESCAPE THAT SURVIVES. Stripping alone would also be satisfied by a library # that deleted escapes unconditionally, which would be useless -- this is the # pair that makes it a DECISION. It is `strip=False' rather than a real tty; the # header records that as a simulation of the tty case and says why. check_eq $P/keep-out "ESC[31mhello worldESC[0m" "$(pv keep_out)" check_eq $P/keep-out-len "20" "$(pv keep_out_len)" check_eq $P/keep-has-esc "yes" "$(pv keep_has_esc)" check_eq $P/keep-identical "yes" "$(pv keep_identical)" # ------------------------------------------------------------- the policy ---- # The two flags ARE colorama's policy, read off the wrapper rather than inferred # from what came out of the pipe: over a non-terminal it must decide to STRIP and # not to CONVERT. A red here explains a red above instead of leaving two # independent symptoms. check_eq $P/atw-strip-default "yes" "$(pv atw_strip_default)" check_eq $P/atw-convert-default "no" "$(pv atw_convert_default)" check_eq $P/atw-should-wrap "yes" "$(pv atw_should_wrap)" check_eq $P/atw-stream-class "StreamWrapper" "$(pv atw_stream_class)" # The wrapper is a TRANSPARENT PROXY for everything but write(). A caller that # asks the wrapped sys.stdout for fileno() or isatty() must still get the real # stream's answer -- every library downstream that introspects sys.stdout depends # on it, and a proxy that had lost __getattr__ would break them all while passing # every stripping case above. check_eq $P/atw-proxy-fileno "yes" "$(pv atw_proxy_fileno)" check_eq $P/atw-proxy-isatty "yes" "$(pv atw_proxy_isatty)" # autoreset: the third mode, and the one a caller reaches for instead of typing # RESET_ALL itself. Written with stripping OFF, so the reset the library appends # is observable. check_eq $P/autoreset-out "ESC[32mautoESC[0m" "$(pv autoreset_out)" check_eq $P/autoreset-ends-reset "yes" "$(pv autoreset_ends_reset)" # THE DOCUMENTED REFUSAL. `wrap=False' with any other flag is a contradiction the # library rejects rather than silently resolving. The exception TYPE is the # assertion -- a different exception would mean the guard fired for a different # reason, and `no-raise' would mean it did not fire at all. check_eq $P/wrap-false-conflict "ValueError" "$(pv wrap_false_conflict)" # 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"