You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

9.6 KiB

src/tests/shell — Shell Behavior Tests

Purpose

Tests for shell-level behavior, command-line execution, and stdin/stdout interaction that are not owned by a specific source module namespace.

Ownership

  • Agents may update this subtree when changing shell behavior, command execution, or interactive stream handling.
  • Keep module API behavior tests in src/tests/modules/ unless shell invocation is the behavior under test.

Local Contracts

  • Tests here may exercise process execution, stdin, stdout, stderr, and shell filtering behavior.
  • Keep command inputs and expected outputs explicit so failures can be reproduced outside the aggregate runner.
  • Avoid relying on installed packages or root-level build outputs unless the test explicitly targets a built executable.
  • testsuites/punkexe/ targets a built punk executable: resolved from env(PUNK_SHELL_TEST_EXE), else <projectroot>/bin/punk902z.exe, else <projectroot>/bin/punkshell902; tests auto-skip via the punkexeavailable constraint when none is found.
  • Tests that spawn the built executable must be hang-proof: use the event-loop punk_run pattern (pipe stdin, half-close for EOF, timeout then force-kill) rather than plain exec, because known failure modes leave the child waiting on a reopened console.
  • testsuites/punkexe/shellexit.test guards piped-stdin termination behaviour of the built executable (no invalid thread handle on shutdown races, no console-reopen hang on eof/error paths, exitcode propagation); do not weaken these tests - a red result means a regression in punk::repl shutdown or app-punkshell eof handling.
  • testsuites/punkexe/shellnavns.test proves the namespace-navigation session contract end-to-end over a piped 'src'-mode session (dev modules - deliberate, so current punk::repl/nav source is exercised without a rebuild): n/ sets ns_current, the current namespace continues into subshell punk and back out (currently via the documented first-subshell shared-code-interp asymmetry - the SHARED marker pins it; the repl::start seeding template for genuinely separate interps is proven in modules/punk/repl nscurrent.test), the punk::repl 0.5.2 stray-namespace fix holds live, and the session exits cleanly. KNOWN GAP pinned there: piped submissions evaluate at :: despite ns_current (interactive and module-level inscope behaviour differ) - investigate alongside G-038.
  • testsuites/punkexe/scriptexec.test covers the script subcommand (app-punkscript, G-015): piped execution + honest exit codes, result echo, file-form argv, the lib:<name> scriptlib-resolution mechanism (incl. punk::path 0.3.0 extensionless-tcl matching: first-line identification, called-spelling precedence over a .tcl sibling, sh-trampoline shebang acceptance, and the non-tcl skip note on not-found), and the tclsh-matching Tk main-loop behaviour. The lib: and Tk cases resolve committed fixtures under scriptlib/_punktest/ (test-owned; a lib:-resolvable fixture cannot live in a tcltest temp dir since resolution only searches scriptlib locations relative to the exe). Tk cases are gated by the punktk constraint, auto-detected by probing whether the kit can load Tk.
  • punkexe tests run against the built binary: after changing punk::repl or app-punkshell source, rebuild via make.tcl packages, make.tcl vfscommonupdate -confirm 0 (unattended runs must pass the flag - the REPLACE confirmation aborts fast on non-interactive stdin instead of reading a piped y), then make.tcl bake before trusting results; make.tcl bake/bakehouse alone does not refresh _vfscommon.vfs (vfscommonupdate is the promotion gate).
  • testsuites/punkexe/tclshcmd.test covers the tclsh subcommand (punk_main.tcl dispatch): script-file sourcing (argv0/argv/info-script state, honest exit codes incl. explicit exit), piped no-arg stdin evaluation, stock argument forms (leading - arg = no script, all args stay in ::argv; -encoding name file sources with the named encoding, incomplete -encoding forms fall through to argv; verified byte-level via a utf-8/iso8859-1 fixture), the lib: refusal (plain-tclsh semantics - pointer to the script subcommand, exit 1), and - on kits built on a TCLSH_PIPEREPL-patched runtime (G-096/G-103, see src/buildsuites/suite_tcl90/patches/README.md) - the piperepl launch-state contract: ::tclsh(istty) published, piped script-arg launch state all-zero, unconsumed piped input NOT evaluated unless the script sets ::tclsh(evalinput) 1, and the script's right to consume stdin itself; plus the degraded-mode notice on unpatched runtimes. Patched-kit resolution: env(PUNK_PIPEREPL_TEST_EXE) if it probes as patched, else the punkexe if patched, else bin/punk9_beta.exe/bin/punk9bi_beta.exe. HANG RULE: no PIPED test or fixture may set ::tclsh(dorepl) - that path reopens stdin from the console (CONIN$) and blocks under the piped punk_run harness. Console-reopen coverage exists but is env-gated (G-118 item 11; constraint punkconsole = windows + env PUNK_TEST_CONSOLE=1, normal runs skip): two tests drive the dorepl path end-to-end in the child's OWN hidden console per the G-106 recipe - an outer Start-Process -WindowStyle Hidden powershell wrapper owns a fresh console and runs <kit> tclsh there with std handles redirected to files, then testsupport/consoleinject.ps1 attaches to that console and types the verification lines + exit 0 as key events. They pin: ::tclsh(reopened) flips 0 -> 1 at reopen while istty stays 0 (immutable launch fact), ::tclsh(inputbuffer) appears empty (the repl loop consumed all piped input), the reopen notice is silent by default and appears on STDERR only under TCLSH_PIPEREPL_DEBUG=1, and the injected exit completes the process (exit code 0). Deadlines plus wrapper-tree kill keep them hang-proof.
  • testsuites/binscripts/runtimebash_wsl.test (G-059) exercises the punk-runtime.cmd unix payload (src/scriptapps/bin/punk-runtime.bash) through WSL: active/use/run resolution, env override, stale-active guidance, single-candidate fallback, and list -remote local-vs-server comparison (offline via a crafted cached sha1sums.txt + PUNKBIN_URL pointed at an unreachable address). Gated by the wsllinux capability constraint from src/tests/testsupport/wslprobe.tcl; all execution happens in a WSL-native staging dir, and a final test asserts the Windows checkout's git status is unchanged by the run.
  • testsuites/binscripts/runtimecmd_freshness.test (G-119) exercises the punk-runtime freshness verdict (active-vs-server-default: current/ahead/behind/incomparable) in BOTH payloads against a fixture punkbin server: a child-process http file server (testsupport/httpfixture.tcl) serves a crafted layout (defaults.txt, sha1sums, artifacts + metadata tomls) via the PUNKBIN_URL override, with all execution in a tempdir staging area on a fixture platform folder (testplat-x86_64 - the real bin/runtime store is untouched). The powershell payload runs as the staged committed bin/punk-runtime.cmd via cmd.exe (the wrap-pinned path); the bash payload is driven DIRECTLY under a probed msys/git bash (uname MINGW*/MSYS*; WSL bash is rejected - under WSL2 it cannot reach the 127.0.0.1 fixture server, and on windows the polyglot routes unix shells to the powershell payload anyway). Asserts the G-119 acceptance scenarios (ahead/behind+guidance/current-annotation/pre-family no-basis and hash-identity/no-name-fetch note) with ps1-vs-bash verdict-line parity, plus the identity middle column row shapes (2026-07-25 follow-on): from=<artifact> working-copy rows with content-verified Remote statuses (Same version / CONTENT DIFFERS / artifact-not-listed), sha1=<name> byte-copy strays, and the identity-following (server default) row annotation. Gated by capability constraints (windows, committed cmd, bash source, certutil, msys bash, fixture server started); skips cleanly elsewhere.
  • testsuites/punkexe/staticruntime.test (G-058) targets a kit built on a runtime with statically-linked packages: resolved from env(PUNK_STATICKIT_TEST_EXE), else <projectroot>/bin/punk91.exe, gated by the capability-probed statickitavailable constraint (a probe run must show a captured ::punkboot::static_packages baseline including Thread - not mere file existence). It asserts repl boot without the codethread Thread failure, Thread resolution in the code interp / fabricated interps / snapshot-seeded threads, and twapi resolution per the static-vs-bundled policy. The verification runtime (tclsfe-x64.exe) is pinned in the punkbin artifact repo so the constraint is satisfiable off the original dev machine. Kit script-exec paths must be passed with forward slashes.
  • GOAL tests (constraint punkgoals, enabled by env PUNK_TEST_GOALS=1) cover the piped-stdin-then-interactive-shell drop-in (shellpipe_eof_then_interactive_shell, shellpipe_script_and_eof_then_interactive_shell); they are excluded from normal runs because they require an openable console in the test environment and briefly attach a live shell to it - they pass in console-attached environments against a current build.
  • The eof-policy discriminator is env(PUNK_PIPE_EOF) (exit|interactive|unset=console heuristic) implemented in app-punkshell; termination-guard tests spawn children with exit, goal tests spawn with it unset.

Work Guidance

  • Use focused harness runs with -report compact -show-passes 0 -include-paths shell/*** when editing this subtree.
  • Include enough failure output to identify the launched command and stream comparison.
  • Keep platform-sensitive assumptions visible in the test body.

Verification

  • <tcl_interpreter> src/tests/runtests.tcl -report compact -show-passes 0 -include-paths shell/*** passes for changes in this subtree.

Child DOX Index