package require tcltest #Piped characterization of the make.tcl bakelist report and the bake-side selective #kit-name validation (goal G-121). Runs the WORKING TREE's src/make.tcl under the #built punk executable's 'script' subcommand with output captured through a pipe, #and pins: # - 'bakelist' exits 0, output is ESC-free (G-113 piped policy), the stable header # row is present and the punk91 row carries the expected type/runtime/vfs columns # with a deployed state from the documented vocabulary # - 'bakelist ' filters to the named entry and appends the per-kit detail # block; unfiltered rows do not appear # - 'bakelist ' exits 1 naming the unknown and listing the configured names # - 'bake ' exits 1 BEFORE any build ("nothing built"), listing the # configured names - the no-build guarantee of selective bake #The row pins are characterization of the current src/runtime/mapvfs.toml kit #matrix (punk91 = tclsfe-x64 + punk9wintk903.vfs, zip) - a deliberate config change #legitimately updates them. #G-024 additions: @group selection, toml parsing exercised through the #PUNK_MAPVFS_CONFIG env seam (fixture configs - the real mapping is never edited): #versioned-scheme expansion, strict entry-named config errors, and the legacy #mapvfs.config deprecation NOTE. # #Target executable resolved from env(PUNK_SHELL_TEST_EXE), else /bin/punk902z.exe #then /bin/punkshell902. Skipped (constraint punkexeavailable) if none found. namespace eval ::testspace { namespace import ::tcltest::* variable testdir [file dirname [file normalize [info script]]] #/src/tests/shell/testsuites/punkexe -> 5 levels up to variable projectroot [file normalize [file join $testdir .. .. .. .. ..]] variable maketcl [file join $projectroot src make.tcl] variable punkexe "" if {[info exists ::env(PUNK_SHELL_TEST_EXE)] && $::env(PUNK_SHELL_TEST_EXE) ne ""} { set punkexe [file normalize $::env(PUNK_SHELL_TEST_EXE)] } else { foreach candidate [list [file join $projectroot bin punk902z.exe] [file join $projectroot bin punkshell902]] { if {[file exists $candidate]} { set punkexe $candidate break } } } testConstraint punkexeavailable [expr {$punkexe ne "" && [file exists $punkexe]}] variable maketcl_run_timeout_ms 60000 variable runstate array set runstate {} proc maketcl_run_read {chan} { variable runstate append runstate(output) [read $chan] if {[chan eof $chan]} { chan event $chan readable {} set runstate(done) eof } } #Run script src/make.tcl with output captured through a #pipe (stdin half-closed for immediate EOF - make.tcl must never wait on stdin for #these subcommands). Returns dict: timedout 0|1, exitcode , output #. proc maketcl_run {cmdargs} { variable runstate variable maketcl_run_timeout_ms variable punkexe variable maketcl array unset runstate set runstate(output) "" set runstate(done) "" set chan [open |[list $punkexe script $maketcl {*}$cmdargs 2>@1] r+] chan configure $chan -blocking 0 -translation binary catch {chan close $chan write} ;#no stdin for the child - immediate EOF set timerid [after $maketcl_run_timeout_ms [list set [namespace current]::runstate(done) timeout]] chan event $chan readable [list [namespace current]::maketcl_run_read $chan] while {$runstate(done) eq ""} { vwait [namespace current]::runstate(done) } after cancel $timerid set timedout [expr {$runstate(done) eq "timeout"}] set exitcode "" if {$timedout} { catch {exec {*}[auto_execok taskkill] /F /T /PID [lindex [pid $chan] 0]} catch {chan close $chan} } else { chan configure $chan -blocking 1 if {[catch {chan close $chan} errdata errdict]} { set exitcode [lindex [dict get $errdict -errorcode] end] } else { set exitcode 0 } } return [dict create timedout $timedout exitcode $exitcode output $runstate(output)] } proc esc_count {text} { return [regexp -all {\x1b} $text] } #added 2026-07-26 (agent, G-121) test maketcl_bakelist_full_report {piped make.tcl bakelist: exit 0, ESC-free, header row + punk91 row with documented deployed vocabulary} -constraints {punkexeavailable} -body { set r [maketcl_run {bakelist}] set out [dict get $r output] set result [list] lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode] lappend result esc [esc_count $out] lappend result header [regexp {(?n)^kit\s+type\s+runtime\s+vfs\s+deployed\s*$} $out] lappend result punk91row [regexp {(?n)^punk91\s+zip\s+tclsfe-x64\s+punk9wintk903\.vfs\s+(current|stale|absent|nobuild)\y} $out] set result } -result {timedout 0 exitcode 0 esc 0 header 1 punk91row 1} #added 2026-07-26 (agent, G-121) test maketcl_bakelist_filtered_detail {bakelist punk91 filters to the named entry and appends the per-kit detail block} -constraints {punkexeavailable} -body { set r [maketcl_run {bakelist punk91}] set out [dict get $r output] set result [list] lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode] lappend result punk91row [regexp {(?n)^punk91\s+zip\s+} $out] lappend result otherrows [regexp {(?n)^(punk905|punkbi|punksys|punk86)\s} $out] lappend result detailblock [regexp {(?n)^detail: punk91\s*$} $out] lappend result detailpaths [regexp {(?n)^\s+build product: src/_build/punk91} $out] set result } -result {timedout 0 exitcode 0 punk91row 1 otherrows 0 detailblock 1 detailpaths 1} #added 2026-07-26 (agent, G-121) test maketcl_bakelist_unknown_name {bakelist with an unknown kit name exits 1 naming it and listing the configured names} -constraints {punkexeavailable} -body { set r [maketcl_run {bakelist nosuchkitxyz}] set out [dict get $r output] set result [list] lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode] lappend result named [regexp {bakelist: unknown kit name\(s\): nosuchkitxyz} $out] lappend result confignames [regexp {configured kit outputs: .*punk91} $out] set result } -result {timedout 0 exitcode 1 named 1 confignames 1} #added 2026-07-26 (agent, G-121) test maketcl_bake_unknown_name_no_build {bake with an unknown kit name exits 1 before any build, listing the configured names} -constraints {punkexeavailable} -body { set r [maketcl_run {bake nosuchkitxyz}] set out [dict get $r output] set result [list] lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode] lappend result nothingbuilt [regexp {make\.tcl bake: unknown kit name\(s\): nosuchkitxyz - nothing built} $out] lappend result confignames [regexp {configured kit outputs: .*punk91} $out] #the run must never reach the kit machinery (no vfs scan / kit processing output) lappend result nomachinery [expr {![regexp {processing targetkit:} $out]}] set result } -result {timedout 0 exitcode 1 nothingbuilt 1 confignames 1 nomachinery 1} #added 2026-07-31 (agent, G-024) test maketcl_bakelist_group_filter {bakelist @verify-ix86 selects the group's rows and no others} -constraints {punkexeavailable} -body { set r [maketcl_run {bakelist @verify-ix86}] set out [dict get $r output] set result [list] lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode] lappend result luckrow [regexp {(?n)^punkluck86\s+zip\s+} $out] lappend result sferow [regexp {(?n)^punk91ix86\s+zip\s+} $out] lappend result groupnote [regexp {group=verify-ix86} $out] lappend result otherrows [regexp {(?n)^(punk91|punk905|punkbi|punksys|punk86)\s} $out] set result } -result {timedout 0 exitcode 0 luckrow 1 sferow 1 groupnote 1 otherrows 0} #G-024 toml characterization runs through the PUNK_MAPVFS_CONFIG env seam so the #REAL kit mapping is never edited (fixture files in the tcltest temp dir). proc maketcl_run_mapseam {fixturename fixturelines cmdargs} { set fixture [tcltest::makeFile [join $fixturelines \n] $fixturename] set ::env(PUNK_MAPVFS_CONFIG) [file normalize $fixture] try { return [maketcl_run $cmdargs] } finally { unset -nocomplain ::env(PUNK_MAPVFS_CONFIG) tcltest::removeFile $fixturename } } #added 2026-07-31 (agent, G-024) test maketcl_bakelist_toml_scheme_expansion {fixture toml via PUNK_MAPVFS_CONFIG: one versioned scheme entry expands to - + -dev + release-gated rows} -constraints {punkexeavailable} -body { set r [maketcl_run_mapseam fixture_scheme.toml { {[kit.fixkit]} {runtime = "tclsfe-x64"} {vfs = "punk9wintk903.vfs"} {type = "zip"} {group = "fixg"} {} {[scheme.fixscheme]} {scheme = "versioned"} {prefix = "fixv"} {runtime = "tclsfe-x64"} {vfs = "punk9wintk903.vfs"} {type = "zip"} } {bakelist}] set out [dict get $r output] set result [list] lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode] lappend result fixkitrow [regexp {(?n)^fixkit\s+zip\s+tclsfe-x64\s+punk9wintk903\.vfs\s} $out] lappend result groupnote [regexp {(?n)^fixkit\s.*group=fixg} $out] #version derives from punkproject.toml at parse time - pin the shape, not the number lappend result versionedrow [regexp {(?n)^fixv-\d+[^ ]*\s+zip\s+.*scheme=versioned} $out] lappend result devrow [regexp {(?n)^fixv-dev\s+zip\s+.*scheme=dev} $out] lappend result releaserow [regexp {(?n)^fixv\s+zip\s+.*scheme=release} $out] #the real config's rows must not appear - the seam fully replaces the mapping lappend result realrows [regexp {(?n)^punk91\s} $out] set result } -result {timedout 0 exitcode 0 fixkitrow 1 groupnote 1 versionedrow 1 devrow 1 releaserow 1 realrows 0} #added 2026-07-31 (agent, G-024) test maketcl_bakelist_toml_configerror_names_entry {fixture toml with an unknown key: CONFIG FILE ERROR names the offending [kit.] entry and the key} -constraints {punkexeavailable} -body { set r [maketcl_run_mapseam fixture_badkey.toml { {[kit.badkit]} {runtime = "tclsfe-x64"} {vfs = "punk9wintk903.vfs"} {typo_key = "zip"} } {bakelist}] set out [dict get $r output] set result [list] lappend result timedout [dict get $r timedout] lappend result entrynamed [regexp {CONFIG FILE ERROR\. entry kit\.badkit .*unknown key 'typo_key'} $out] set result } -result {timedout 0 entrynamed 1} #added 2026-07-31 (agent, G-024) test maketcl_bake_toml_configerror_aborts {bake against a fixture toml with an unknown scheme kind exits 3 before any build, naming the entry} -constraints {punkexeavailable} -body { set r [maketcl_run_mapseam fixture_badscheme.toml { {[scheme.badscheme]} {scheme = "nosuchscheme"} {runtime = "tclsfe-x64"} {vfs = "punk9wintk903.vfs"} {type = "zip"} } {bake badname}] set out [dict get $r output] set result [list] lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode] lappend result entrynamed [regexp {CONFIG FILE ERROR\. entry scheme\.badscheme .*unknown scheme 'nosuchscheme'} $out] lappend result nomachinery [expr {![regexp {processing targetkit:} $out]}] set result } -result {timedout 0 exitcode 3 entrynamed 1 nomachinery 1} #added 2026-07-31 (agent, G-024) test maketcl_bakelist_legacy_deprecation_note {fixture legacy line-format config via the seam: parsed with the deprecation NOTE, rows still resolve} -constraints {punkexeavailable} -body { set r [maketcl_run_mapseam fixture_legacy.config { {tclsfe-x64.exe {punk9wintk903.vfs fixlegacy zip}} } {bakelist}] set out [dict get $r output] set result [list] lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode] lappend result depnote [regexp {NOTE: .*deprecated mapvfs\.config line format} $out] lappend result legacyrow [regexp {(?n)^fixlegacy\s+zip\s+tclsfe-x64\s+punk9wintk903\.vfs\s} $out] set result } -result {timedout 0 exitcode 0 depnote 1 legacyrow 1} cleanupTests } namespace delete ::testspace