Browse Source
- punkboot::utils 0.5.0: binary_arch_classify (PE Machine field, ELF e_machine,
Mach-O thin+universal - honest unknowns), platform_expected_binary,
platform_discriminated_segment (canonical <os>-<cpu> tokens + vendor spellings,
extensible namespace variables), vfs_binary_arch_report (per-directory exemption,
exempt subtrees counted unread)
- make.tcl: advisory binary-arch scan at the G-125 gate seam (recapped
BUILD-WARNINGs naming file/found-arch/kit-target, capped at 8 per kit with honest
total; cross-target kits included); post-build smoke-require probe running the
freshly built artifact via its tclsh subcommand with drained stdin (failures
recapped naming kit/package/actual error; cross-target skip with stated reason;
undeclared kits run nothing new); mapvfs.config entries accept a 5th
smoke-require element (mapvfs_parse/mapvfs_kit_outputs carry smokerequire);
'check' reports scan ACTIVE/UNAVAILABLE + declared smoke matrix; workflow K11
- mapvfs.config: smoke-requires declared - punkluck86 {Thread} (the 2026-07-27
incident construction), punk91ix86 {Thread iocp}, punkshell902 {Thread}
(cross-target skip demonstrator)
- tests: binaryarch.test (generated header fixtures only - no committed binaries;
punkluck86 case reproduced; iocp pair + win-x64 exemptions; real-tree sweep with
known-real findings filtered), maketclpayloadcheck.test (piped check
characterization, ESC-free per G-113)
- docs: src/AGENTS.md + bin/AGENTS.md state what the checks do and do NOT
guarantee; src/runtime/AGENTS.md entry grammar; ARCHITECTURE.md bake section
- REAL FINDING on the scan's first sweep: zint.dll 2.13.0 in punk8win.vfs
lib_tcl8/ is 32-bit (PE i386, confirmed with file(1)) and can never load in the
x64 tcl8 kits punk86/punkbi/punksys that carry it - those bakes warn until the
payload is fixed
- punkshell 0.28.2
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
12 changed files with 1171 additions and 24 deletions
@ -1,4 +1,4 @@
|
||||
[project] |
||||
name = "punkshell" |
||||
version = "0.28.1" |
||||
version = "0.28.2" |
||||
license = "BSD-2-Clause" |
||||
|
||||
@ -0,0 +1,353 @@
|
||||
# -*- tcl -*- |
||||
# Tests for the punkboot::utils G-133 payload/target binary-arch machinery: |
||||
# - binary_arch_classify: PE Machine-field classification (i386/x86_64/arm64), ELF and |
||||
# Mach-O (thin + fat/universal) classification, and honest 'unknown' for everything |
||||
# else (text named .dll, MZ without PE, java-class cafebabe decoy, short files) |
||||
# - platform_expected_binary / platform_discriminated_segment mapping + recognition |
||||
# - vfs_binary_arch_report: the 2026-07-27 punkluck86 case reproduced as a fixture (an |
||||
# x64 thread dll under plain lib_tcl8/ in a win32-ix86 kit), the platform-subdir |
||||
# exemptions (iocp-2.0.2's win32-ix86 + win32-x86_64 pair, blend2d-style win-x64), |
||||
# and the real assembled trees in src/_build when a build has been run |
||||
# All binary fixtures are GENERATED here with 'binary format' - header bytes only, no |
||||
# committed binaries (root AGENTS.md binary policy), nothing executable. The scan reads |
||||
# headers only and never executes anything - what makes it usable for cross-target kits. |
||||
# Run: tclsh src/tests/runtests.tcl -report compact -show-passes 0 -include-paths modules/punkboot/utils/*** binaryarch.test |
||||
|
||||
package require tcltest |
||||
package require punkboot::utils |
||||
|
||||
#added 2026-07-27 (agent, G-133) |
||||
|
||||
namespace eval ::testspace { |
||||
namespace import ::tcltest::* |
||||
|
||||
variable BASE [makeDirectory g133_binaryarch] |
||||
|
||||
#<projectroot>/src/tests/modules/punkboot/utils/testsuites/utils -> 7 levels up |
||||
variable projectroot [file normalize [file join [file dirname [info script]] .. .. .. .. .. .. ..]] |
||||
variable buildfolder [file join $projectroot src _build] |
||||
testConstraint builtvfsavailable [expr {[llength [glob -nocomplain -type d -directory $buildfolder *.exe.vfs]] > 0}] |
||||
|
||||
proc writebytes {path bytes} { |
||||
file mkdir [file dirname $path] |
||||
set fd [open $path w] |
||||
chan configure $fd -translation binary |
||||
puts -nonewline $fd $bytes |
||||
close $fd |
||||
} |
||||
#minimal PE: MZ header, e_lfanew(0x3c)=128, PE\0\0 + Machine at 128 |
||||
proc pebytes {machine} { |
||||
return [binary format a2x58ix64a4sx20 MZ 128 PE\x00\x00 $machine] |
||||
} |
||||
#minimal ELF: magic, EI_CLASS, EI_DATA, e_machine at 18 (endianness per EI_DATA) |
||||
proc elfbytes {class data machine} { |
||||
if {$data == 2} { |
||||
return [binary format a4ccx10x2Sx12 \x7fELF $class $data $machine] |
||||
} |
||||
return [binary format a4ccx10x2sx12 \x7fELF $class $data $machine] |
||||
} |
||||
proc mktree {name} { |
||||
variable BASE |
||||
set root [file join $BASE $name] |
||||
file delete -force $root |
||||
file mkdir $root |
||||
return $root |
||||
} |
||||
proc classify {path} { |
||||
return [punkboot::utils::binary_arch_classify $path] |
||||
} |
||||
#distinct name - a proc named 'scan' would shadow the Tcl builtin in this namespace |
||||
proc archscan {root target} { |
||||
return [punkboot::utils::vfs_binary_arch_report $root $target] |
||||
} |
||||
proc fmtarch {class} { |
||||
return [list [dict get $class format] [dict get $class arch]] |
||||
} |
||||
|
||||
variable common { |
||||
set result [list] |
||||
} |
||||
|
||||
# -- --- --- classifier: PE --- --- -- |
||||
|
||||
test binaryarch_classify_pe_fixtures {PE i386, x86_64 and arm64 fixtures classify by Machine field}\ |
||||
-setup $common -body { |
||||
variable BASE |
||||
foreach {name machine} {i386 0x014c x86_64 0x8664 arm64 -0x559c} { |
||||
#-0x559c is 0xaa64 as a signed 16-bit value - 'binary format s' takes either |
||||
set f [file join $BASE pe_$name.dll] |
||||
writebytes $f [pebytes [expr {$machine}]] |
||||
lappend result [fmtarch [classify $f]] |
||||
} |
||||
set result |
||||
}\ |
||||
-result {{pe i386} {pe x86_64} {pe arm64}} |
||||
|
||||
test binaryarch_classify_pe_unknown_machine {a real PE with an unmapped Machine value is definite-but-unmapped, not silently unknown}\ |
||||
-setup $common -body { |
||||
variable BASE |
||||
set f [file join $BASE pe_odd.dll] |
||||
writebytes $f [pebytes 0x1234] |
||||
set c [classify $f] |
||||
lappend result [dict get $c format] [dict get $c arch] |
||||
}\ |
||||
-result {pe unknown-0x1234} |
||||
|
||||
test binaryarch_classify_mz_without_pe {an MZ header without a PE signature (DOS executable) is honestly unknown}\ |
||||
-setup $common -body { |
||||
variable BASE |
||||
set f [file join $BASE dos.dll] |
||||
writebytes $f [binary format a2x58ix64a4x20 MZ 128 XX\x00\x00] |
||||
lappend result [fmtarch [classify $f]] |
||||
}\ |
||||
-result {{unknown unknown}} |
||||
|
||||
# -- --- --- classifier: ELF and Mach-O --- --- -- |
||||
|
||||
test binaryarch_classify_elf_fixtures {ELF i386, x86_64 and arm64 fixtures classify via e_machine}\ |
||||
-setup $common -body { |
||||
variable BASE |
||||
foreach {name class data machine} {i386 1 1 3 x86_64 2 1 62 arm64 2 1 183} { |
||||
set f [file join $BASE elf_$name.so] |
||||
writebytes $f [elfbytes $class $data $machine] |
||||
lappend result [fmtarch [classify $f]] |
||||
} |
||||
set result |
||||
}\ |
||||
-result {{elf i386} {elf x86_64} {elf arm64}} |
||||
|
||||
test binaryarch_classify_elf_bigendian {a big-endian ELF (EI_DATA=2) reads e_machine big-endian}\ |
||||
-setup $common -body { |
||||
variable BASE |
||||
set f [file join $BASE elf_be.so] |
||||
writebytes $f [elfbytes 2 2 21] |
||||
lappend result [fmtarch [classify $f]] |
||||
}\ |
||||
-result {{elf ppc64}} |
||||
|
||||
test binaryarch_classify_macho_fixtures {thin Mach-O x86_64 and arm64 (little-endian on-disk form) classify via cputype}\ |
||||
-setup $common -body { |
||||
variable BASE |
||||
foreach {name cputype} {x86_64 0x01000007 arm64 0x0100000C} { |
||||
set f [file join $BASE macho_$name.dylib] |
||||
writebytes $f [binary format iix56 [expr {0xfeedfacf}] [expr {$cputype}]] |
||||
lappend result [fmtarch [classify $f]] |
||||
} |
||||
set result |
||||
}\ |
||||
-result {{macho x86_64} {macho arm64}} |
||||
|
||||
test binaryarch_classify_macho_universal {a fat/universal Mach-O reports its member architectures}\ |
||||
-setup $common -body { |
||||
variable BASE |
||||
set f [file join $BASE macho_fat.dylib] |
||||
#fat header (big-endian): magic, nfat=2, then 20-byte entries (cputype first) |
||||
writebytes $f [binary format IIIIIIIIIIIIx8 [expr {0xcafebabe}] 2\ |
||||
[expr {0x01000007}] 3 4096 100 12\ |
||||
[expr {0x0100000C}] 0 8192 100 12] |
||||
set c [classify $f] |
||||
lappend result [dict get $c format] [dict get $c arch] [dict get $c archs] |
||||
}\ |
||||
-result {macho universal {x86_64 arm64}} |
||||
|
||||
test binaryarch_classify_java_class_decoy {a java class file (cafebabe with implausible member count) stays unknown}\ |
||||
-setup $common -body { |
||||
variable BASE |
||||
set f [file join $BASE decoy.dll] |
||||
writebytes $f [binary format IIx56 [expr {0xcafebabe}] 52] |
||||
lappend result [fmtarch [classify $f]] |
||||
}\ |
||||
-result {{unknown unknown}} |
||||
|
||||
test binaryarch_classify_nonbinary_content {a text file named .dll and a too-short file are honestly unknown}\ |
||||
-setup $common -body { |
||||
variable BASE |
||||
set f1 [file join $BASE textstub.dll] |
||||
writebytes $f1 "#this is a tcl stub pretending to be a dll\n" |
||||
set f2 [file join $BASE tiny.dll] |
||||
writebytes $f2 MZ |
||||
lappend result [fmtarch [classify $f1]] [fmtarch [classify $f2]] |
||||
}\ |
||||
-result {{unknown unknown} {unknown unknown}} |
||||
|
||||
# -- --- --- target mapping + segment recognition --- --- -- |
||||
|
||||
test binaryarch_expected_mapping {canonical target platforms map to expected format/arch; unknown os yields empty}\ |
||||
-setup $common -body { |
||||
foreach t {win32-x86_64 win32-ix86 win32-arm64 linux-x86_64 freebsd-x86_64 macosx-arm64 weirdos-x86 noarch} { |
||||
lappend result [punkboot::utils::platform_expected_binary $t] |
||||
} |
||||
set result |
||||
}\ |
||||
-result {{format pe arch x86_64} {format pe arch i386} {format pe arch arm64} {format elf arch x86_64} {format elf arch x86_64} {format macho arch arm64} {} {}} |
||||
|
||||
test binaryarch_segment_recognition {canonical platform-dir names, the universal macosx folder and recognised vendor spellings discriminate; package dirs do not}\ |
||||
-setup $common -body { |
||||
foreach seg {win32-x86_64 win32-ix86 linux-x86_64 macosx win-x64 win64 WIN32-X86_64 tcl8.6 BWidget1.10.1 thread2.8.5 x86_64-linux-gnu} { |
||||
lappend result [punkboot::utils::platform_discriminated_segment $seg] |
||||
} |
||||
set result |
||||
}\ |
||||
-result {1 1 1 1 1 1 1 0 0 0 0} |
||||
|
||||
# -- --- --- the scan: the motivating case and the exemptions --- --- -- |
||||
|
||||
test binaryarch_scan_punkluck86_case {the 2026-07-27 punkluck86 case: an x64 thread dll under plain lib_tcl8/ in a win32-ix86 kit is named as a mismatch}\ |
||||
-setup $common -body { |
||||
set root [mktree luckcase] |
||||
#the payload shape that broke the repl: wrong-arch Thread outside any platform |
||||
#subdir, beside the runtime's own working 32-bit copy |
||||
writebytes [file join $root thread2.8.5 thread285.dll] [pebytes 0x014c] |
||||
writebytes [file join $root lib_tcl8 thread2.8.12 thread28.dll] [pebytes [expr {0x8664}]] |
||||
set r [archscan $root win32-ix86] |
||||
lappend result ok [dict get $r ok] scanned [dict get $r scanned] matched [dict get $r matched] |
||||
set m [lindex [dict get $r mismatches] 0] |
||||
lappend result file [dict get $m file] arch [dict get $m arch] |
||||
}\ |
||||
-result {ok 0 scanned 2 matched 1 file lib_tcl8/thread2.8.12/thread28.dll arch x86_64} |
||||
|
||||
test binaryarch_scan_platform_subdir_exempt {the iocp-2.0.2 multi-arch pair layout produces no mismatch - platform subdirs are exempt without being read}\ |
||||
-setup $common -body { |
||||
set root [mktree iocppair] |
||||
writebytes [file join $root lib_tcl8 iocp-2.0.2 win32-ix86 iocp202.dll] [pebytes 0x014c] |
||||
writebytes [file join $root lib_tcl8 iocp-2.0.2 win32-x86_64 iocp202.dll] [pebytes [expr {0x8664}]] |
||||
set r [archscan $root win32-ix86] |
||||
lappend result ok [dict get $r ok] scanned [dict get $r scanned] exempt [dict get $r exempt] mismatches [llength [dict get $r mismatches]] |
||||
}\ |
||||
-result {ok 1 scanned 0 exempt 2 mismatches 0} |
||||
|
||||
test binaryarch_scan_vendor_spelling_exempt {a blend2d-style win-x64 vendor subdir is recognised - no warning for its wrong-arch dll in an ix86 kit}\ |
||||
-setup $common -body { |
||||
set root [mktree vendorspelling] |
||||
writebytes [file join $root lib_tcl9 blend2d1.5 win-x64 blend2d.dll] [pebytes [expr {0x8664}]] |
||||
set r [archscan $root win32-ix86] |
||||
lappend result ok [dict get $r ok] exempt [dict get $r exempt] mismatches [llength [dict get $r mismatches]] |
||||
}\ |
||||
-result {ok 1 exempt 1 mismatches 0} |
||||
|
||||
test binaryarch_scan_exemption_covers_subtree {everything below a platform-discriminated dir is exempt, not just its direct files}\ |
||||
-setup $common -body { |
||||
set root [mktree subtree] |
||||
writebytes [file join $root lib win32-x86_64 sub deeper thing.dll] [pebytes [expr {0x8664}]] |
||||
set r [archscan $root win32-ix86] |
||||
lappend result ok [dict get $r ok] exempt [dict get $r exempt] |
||||
}\ |
||||
-result {ok 1 exempt 1} |
||||
|
||||
test binaryarch_scan_format_mismatch {a wrong-FORMAT library (ELF .so in a windows-target kit) outside platform subdirs is a mismatch}\ |
||||
-setup $common -body { |
||||
set root [mktree formatmismatch] |
||||
writebytes [file join $root lib foreign libfoo.so] [elfbytes 2 1 62] |
||||
set r [archscan $root win32-x86_64] |
||||
lappend result ok [dict get $r ok] |
||||
set m [lindex [dict get $r mismatches] 0] |
||||
lappend result file [dict get $m file] format [dict get $m format] |
||||
}\ |
||||
-result {ok 0 file lib/foreign/libfoo.so format elf} |
||||
|
||||
test binaryarch_scan_unknown_not_warned {unclassifiable files are counted unknown and never reported as mismatches}\ |
||||
-setup $common -body { |
||||
set root [mktree unknowns] |
||||
writebytes [file join $root lib stub.dll] "#tcl stub\n" |
||||
set r [archscan $root win32-x86_64] |
||||
lappend result ok [dict get $r ok] scanned [dict get $r scanned] unknown [dict get $r unknown] mismatches [llength [dict get $r mismatches]] |
||||
}\ |
||||
-result {ok 1 scanned 1 unknown 1 mismatches 0} |
||||
|
||||
test binaryarch_scan_matching_tree {a tree whose libraries all match the target reports ok with counts}\ |
||||
-setup $common -body { |
||||
set root [mktree allmatch] |
||||
writebytes [file join $root bin tcl9tk90.dll] [pebytes [expr {0x8664}]] |
||||
writebytes [file join $root tcl_library dde tcl9dde14.dll] [pebytes [expr {0x8664}]] |
||||
set r [archscan $root win32-x86_64] |
||||
lappend result ok [dict get $r ok] scanned [dict get $r scanned] matched [dict get $r matched] |
||||
}\ |
||||
-result {ok 1 scanned 2 matched 2} |
||||
|
||||
test binaryarch_scan_macho_universal_matches {a universal Mach-O carrying the target's arch matches a macosx target}\ |
||||
-setup $common -body { |
||||
set root [mktree fattree] |
||||
writebytes [file join $root lib libuni.dylib] [binary format IIIIIIIIIIIIx8 [expr {0xcafebabe}] 2\ |
||||
[expr {0x01000007}] 3 4096 100 12\ |
||||
[expr {0x0100000C}] 0 8192 100 12] |
||||
set r1 [archscan $root macosx-arm64] |
||||
set r2 [archscan $root macosx-x86_64] |
||||
lappend result [dict get $r1 ok] [dict get $r1 matched] [dict get $r2 ok] [dict get $r2 matched] |
||||
}\ |
||||
-result {1 1 1 1} |
||||
|
||||
test binaryarch_scan_unmapped_target {a target with no recognised binary mapping reports not-applicable rather than guessing}\ |
||||
-setup $common -body { |
||||
set root [mktree unmappedtarget] |
||||
writebytes [file join $root lib foo.dll] [pebytes 0x014c] |
||||
set r [archscan $root weirdos-x86] |
||||
lappend result ok [dict get $r ok] scanned [dict get $r scanned] |
||||
lappend result reason_says [string match "*no recognised binary format/arch mapping*" [dict get $r reason]] |
||||
}\ |
||||
-result {ok 1 scanned 0 reason_says 1} |
||||
|
||||
test binaryarch_scan_missing_directory {a nonexistent folder is refused by name rather than erroring}\ |
||||
-setup $common -body { |
||||
variable BASE |
||||
set r [archscan [file join $BASE no_such_vfs] win32-x86_64] |
||||
lappend result [dict get $r ok] [string match "no such directory:*" [dict get $r reason]] |
||||
}\ |
||||
-result {0 1} |
||||
|
||||
test binaryarch_scan_does_not_write {the scan writes nothing into the tree it inspects}\ |
||||
-setup $common -body { |
||||
set root [mktree readonlycheck] |
||||
writebytes [file join $root lib thing.dll] [pebytes [expr {0x8664}]] |
||||
set before [lsort [glob -nocomplain -directory $root -tails *]] |
||||
archscan $root win32-x86_64 |
||||
set after [lsort [glob -nocomplain -directory $root -tails *]] |
||||
lappend result [expr {$before eq $after}] $before |
||||
}\ |
||||
-result {1 lib} |
||||
|
||||
# -- --- --- the trees a bake actually scans --- --- -- |
||||
|
||||
test binaryarch_scan_real_built_vfs_trees {assembled kit vfs trees in src/_build produce no findings beyond the KNOWN real ones - the advisory must not cry wolf on payloads that work today}\ |
||||
-constraints {builtvfsavailable} -setup $common -body { |
||||
variable buildfolder |
||||
#KNOWN REAL FINDINGS the scan is EXPECTED to report (true positives, not noise): |
||||
# zint.dll 2.13.0 in src/vfs/punk8win.vfs/lib_tcl8/ is a 32-bit (i386) PE |
||||
# ('file' confirms: PE32 Intel 80386) carried by the x64 tcl8 kits, where its |
||||
# unconditional pkgIndex load can never succeed - found by this scan's first |
||||
# real sweep 2026-07-27. Filtered here (not required to exist, so fixing the |
||||
# payload keeps this test green); anything OUTSIDE this list is a false |
||||
# positive or a new payload defect and fails the test. |
||||
set known_findings { |
||||
{*.exe.vfs:lib_tcl8/zint-2.13.0/zint.dll=i386} |
||||
} |
||||
set failures [list] |
||||
set checked 0 |
||||
foreach d [lsort [glob -nocomplain -type d -directory $buildfolder *.exe.vfs]] { |
||||
switch -glob -- [file tail $d] { |
||||
punkluck86.* - punk91ix86.* {set target win32-ix86} |
||||
default {set target win32-x86_64} |
||||
} |
||||
incr checked |
||||
set r [archscan $d $target] |
||||
if {![dict get $r ok]} { |
||||
foreach m [dict get $r mismatches] { |
||||
set finding "[file tail $d]:[dict get $m file]=[dict get $m arch]" |
||||
set is_known 0 |
||||
foreach pat $known_findings { |
||||
if {[string match $pat $finding]} { |
||||
set is_known 1 |
||||
break |
||||
} |
||||
} |
||||
if {!$is_known} { |
||||
lappend failures $finding |
||||
} |
||||
} |
||||
} |
||||
} |
||||
lappend result [expr {$checked > 0}] $failures |
||||
}\ |
||||
-result {1 {}} |
||||
|
||||
cleanupTests |
||||
} |
||||
@ -0,0 +1,146 @@
|
||||
package require tcltest |
||||
|
||||
#Piped characterization of the make.tcl side of the G-133 payload/target consistency |
||||
#checks: the advisory binary-arch scan at the G-125 gate seam and the post-build |
||||
#smoke-require probe. Runs the WORKING TREE's src/make.tcl under the built punk |
||||
#executable's 'script' subcommand with output captured through a pipe, and pins: |
||||
# - 'check' reports the arch scan with one of the two documented statuses (ACTIVE with |
||||
# a current bootsupport punkboot::utils carrying vfs_binary_arch_report; UNAVAILABLE |
||||
# is the documented guarded-require degradation), states its advisory nature, the |
||||
# platform-discriminated-subdir exemption and the recapped BUILD-WARNING reporting |
||||
# - 'check' describes the smoke-require probe contract (host-runnable kits only, |
||||
# cross-target kits skip with a stated reason, undeclared kits run nothing new) and |
||||
# lists the kits declaring smoke-require packages. The declared list is |
||||
# characterization of the current src/runtime/mapvfs.config - a deliberate config |
||||
# change legitimately updates it (same stance as maketclbakelist.test row pins) |
||||
# - the report stays ESC-free under the G-113 piped colour policy |
||||
#The scan's VERDICT - what classifies and what mismatches - is characterized against |
||||
#generated header fixtures in modules/punkboot/utils/testsuites/utils/binaryarch.test; |
||||
#the classifier lives in punkboot::utils precisely so it is testable without a bake. |
||||
# |
||||
#Target executable resolved from env(PUNK_SHELL_TEST_EXE), else <projectroot>/bin/punk902z.exe |
||||
#then <projectroot>/bin/punkshell902. Skipped (constraint punkexeavailable) if none found. |
||||
|
||||
namespace eval ::testspace { |
||||
namespace import ::tcltest::* |
||||
|
||||
variable testdir [file dirname [file normalize [info script]]] |
||||
#<projectroot>/src/tests/shell/testsuites/punkexe -> 5 levels up to <projectroot> |
||||
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 <punkexe> script src/make.tcl <subcommand...> 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 <int|"">, output |
||||
#<combined stdout+stderr>. |
||||
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)] |
||||
} |
||||
|
||||
#added 2026-07-27 (agent, G-133) |
||||
test maketcl_check_payloadchecks_status {make.tcl check reports the payload/target arch scan with one of the two documented statuses, ESC-free}\ |
||||
-constraints {punkexeavailable} -body { |
||||
set r [maketcl_run {check}] |
||||
set out [dict get $r output] |
||||
set result [list] |
||||
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode] |
||||
lappend result esc [regexp -all {\x1b} $out] |
||||
lappend result scanline [regexp -line {^payload/target arch scan \(G-133\): (ACTIVE \(advisory\)|UNAVAILABLE)} $out] |
||||
lappend result smokeline [regexp -line {^smoke-require probe \(G-133, advisory\):} $out] |
||||
set result |
||||
}\ |
||||
-result {timedout 0 exitcode 0 esc 0 scanline 1 smokeline 1} |
||||
|
||||
#added 2026-07-27 (agent, G-133) |
||||
test maketcl_check_payloadchecks_active_contract {with a current bootsupport snapshot the arch scan is ACTIVE and check states the advisory contract: subdir exemption, recapped warnings, cross-target coverage and skip, undeclared-kits-unchanged}\ |
||||
-constraints {punkexeavailable} -body { |
||||
set r [maketcl_run {check}] |
||||
set out [dict get $r output] |
||||
set result [list] |
||||
lappend result active [regexp -line {^payload/target arch scan \(G-133\): ACTIVE \(advisory\)\s*$} $out] |
||||
#structural coverage claim: nothing executed, cross-target kits included |
||||
lappend result crosstarget [regexp {nothing executed, cross-target kits included} $out] |
||||
#advisory reporting channel (appears for both the scan and the smoke probe) |
||||
lappend result recapped [expr {[regexp -all {recapped\s+BUILD-WARNINGs} $out] >= 1}] |
||||
#the exemption that keeps multi-arch payloads legitimate |
||||
lappend result exemption [regexp {platform-discriminated subdirs} $out] |
||||
#smoke probe contract lines |
||||
lappend result seesresolution [regexp {resolution-order defects} $out] |
||||
lappend result skipstated [regexp {cross-target kits skip with a stated reason} $out] |
||||
lappend result undeclared [regexp {undeclared kits run nothing new} $out] |
||||
set result |
||||
}\ |
||||
-result {active 1 crosstarget 1 recapped 1 exemption 1 seesresolution 1 skipstated 1 undeclared 1} |
||||
|
||||
#added 2026-07-27 (agent, G-133) - the declared list characterizes the current |
||||
#src/runtime/mapvfs.config smoke-require matrix (deliberate config changes update it) |
||||
test maketcl_check_payloadchecks_declared_kits {check lists the kits currently declaring smoke-require packages}\ |
||||
-constraints {punkexeavailable} -body { |
||||
set r [maketcl_run {check}] |
||||
set out [dict get $r output] |
||||
set result [list] |
||||
lappend result declared [regexp -line {^\s+declared: punkluck86 \(Thread\); punkshell902 \(Thread\); punk91ix86 \(Thread, iocp\)\s*$} $out] |
||||
set result |
||||
}\ |
||||
-result {declared 1} |
||||
|
||||
cleanupTests |
||||
} |
||||
namespace delete ::testspace |
||||
Loading…
Reference in new issue