Browse Source

argparsingtest 1.0.0: per-style parser sets opts/tkstyle/tclstyle with N/A capability rows

Restructure of the parser-timing module around three calling styles:
- parser procs move into per-style child namespaces argparsingtest::opts /
  ::tkstyle (2 leading positionals) / ::tclstyle (2 trailing positionals),
  with identical short proc names across styles; the test1_ prefix is gone
- hand-rolled parsers share a manual_ prefix (manual_ni, manual_switch,
  manual_prefix, ...) so they group visually in the comparison tables;
  test1_prefix2 removed (not meaningfully distinct from test1_prefix)
- tkstyle/tclstyle carry a representative roster (manual_switch baseline +
  punkargs/punkargs_by_id/punkargs_parsecache + opt + cmdline + tepam);
  style parsers return a {p1 p2 opts} list
- new 'unsupported' discovery status for library/style combos the backing
  library cannot express: cmdline with leading positionals (getoptions stops
  at the first non-option word - probe verified), argp with any positionals
  (parseArgs walks the whole list as option pairs - source verified).
  compare renders these as N/A rows below the timed rows so every library
  still appears in every style's table
- harness procs (discover_parsers/timeit/first_call/compare) gain
  -style all|opts|tkstyle|tclstyle and return results keyed by style;
  -args requires a single -style (each style has its own default vector);
  external probe keys drop the test1_ prefix (getopt/parse_args/argparse/cmdr)
- punkargs_by_id variants keep the eager punk::args::define as sole doc
  source (the former duplicate argdoc lappend made the lazy namespace scan
  undefine/re-register the id on first introspection); unused struct::set
  require dropped; opt/cmdline/tepam requires hoisted to module top;
  first_call library warm-ups extended with positional coverage
- test suite rewritten: 17 tests including per-parser {p1 p2 opts} contract
  checks across both positional styles, N/A table rendering, roster counts
  and the -style/-args error paths

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
c05af65543
  1. 2283
      src/modules/argparsingtest-999999.0a1.0.tm
  2. 6
      src/modules/argparsingtest-buildversion.txt
  3. 204
      src/tests/modules/argparsingtest/testsuites/argparsingtest/argparsingtest.test

2283
src/modules/argparsingtest-999999.0a1.0.tm

File diff suppressed because it is too large Load Diff

6
src/modules/argparsingtest-buildversion.txt

@ -1,6 +1,8 @@
0.2.0
1.0.0
#First line must be a semantic version number
#all other lines are ignored.
#1.0.0 - BREAKING restructure: parser procs moved into per-style child namespaces (argparsingtest::opts, ::tkstyle, ::tclstyle) and renamed - test1_ prefix dropped, hand-rolled parsers now share a manual_ prefix (e.g. test1_switch -> opts::manual_switch, test1_punkargs -> opts::punkargs); test1_prefix2 removed (not meaningfully distinct from test1_prefix); unused struct::set require dropped; opt/cmdline/tepam requires hoisted to module top
#1.0.0 - new tkstyle (2 positionals first) and tclstyle (2 positionals last) parser sets returning {p1 p2 opts}; harness procs (discover_parsers/timeit/first_call/compare) gain -style and return results keyed by style; new 'unsupported' discovery status for library/style combos the backing library cannot express (cmdline in tkstyle, argp in both positional styles) - rendered as N/A rows in compare tables; external probe keys renamed (test1_getopt -> getopt etc)
#0.2.0 - added timeit/compare/discover_parsers procs for rough parser timing comparison; PUNKARGS argdoc blocks added to all procs
#0.2.0 - fixed timeit to call parsers with namespace-qualified name (workaround for upstream argp 0.2 caller-normalisation bug); noted the bug at the test1_argp registration site
#0.2.0 - first_call now times cold AND warm first calls per parser (warm = backing library pre-compiled via per-library sacrificial warm-up scripts in library_warmups); replaces the punk::args-only 'punk::args::parse {} withdef {}' warm hack which was unfair to other libraries; return shape now {parser {cold <us> warm <us>}}; compare table gains first_cold/first_warm columns
#0.2.0 - first_call now times cold AND warm first calls per parser (warm = backing library pre-compiled via per-library sacrificial warm-up scripts in library_warmups); replaces the punk::args-only 'punk::args::parse {} withdef {}' warm hack which was unfair to other libraries; return shape now {parser {cold <us> warm <us>}}; compare table gains first_cold/first_warm columns

204
src/tests/modules/argparsingtest/testsuites/argparsingtest/argparsingtest.test

@ -9,13 +9,16 @@ namespace eval ::testspace {
#added 2026-07-17 (agent) - argp is an optional package; its load-time require is guarded by catch in the module
testConstraint argp_loaded [expr {![catch {package present argp}]}]
#added 2026-07-17 (agent)
test timeit_basic {Test that timeit -iterations 50 returns a dict with expected keys} -body {
set r [argparsingtest::timeit -iterations 50 -parsers {test1_switch}]
#added 2026-07-18 (agent) - suite rewritten for the argparsingtest 1.0.0 restructure:
#parser procs live in per-style namespaces (opts/tkstyle/tclstyle), harness results are keyed by style
#added 2026-07-18 (agent)
test timeit_basic {Test that timeit -style opts returns style-keyed dict with numeric timing} -body {
set r [argparsingtest::timeit -iterations 50 -style opts -parsers {manual_switch}]
set ok 1
if {![dict exists $r test1_switch]} { set ok 0 }
if {![dict exists $r opts manual_switch]} { set ok 0 }
if {$ok} {
set sub [dict get $r test1_switch]
set sub [dict get $r opts manual_switch]
if {![dict exists $sub us_per_call]} { set ok 0 }
if {$ok && ![string is double -strict [dict get $sub us_per_call]]} { set ok 0 }
if {$ok && [dict get $sub iterations] != 50} { set ok 0 }
@ -23,31 +26,119 @@ namespace eval ::testspace {
set ok
} -result 1
#added 2026-07-17 (agent)
test compare_table {Test that compare -iterations 20 -format table returns string with Parser header and not loaded footer} -body {
set r [argparsingtest::compare -iterations 20 -format table]
#added 2026-07-18 (agent)
test timeit_all_styles {Test that timeit default -style all times a parser under each of the three styles} -body {
set r [argparsingtest::timeit -iterations 20 -parsers {manual_switch}]
set ok 1
if {[string first "Parser" $r] < 0} { set ok 0 }
if {$ok && [string first "not loaded:" $r] < 0} { set ok 0 }
if {[lsort [dict keys $r]] ne [lsort {opts tkstyle tclstyle}]} { set ok 0 }
foreach s {opts tkstyle tclstyle} {
if {!$ok} { break }
if {![dict exists $r $s manual_switch us_per_call]} { set ok "missing $s" ; break }
if {![string is double -strict [dict get $r $s manual_switch us_per_call]]} { set ok "non-numeric $s" ; break }
}
set ok
} -result 1
#added 2026-07-17 (agent)
test compare_dict {Test that compare -iterations 20 -format dict returns a dict with not_loaded and results keys} -body {
set r [argparsingtest::compare -iterations 20 -format dict]
#added 2026-07-18 (agent)
test timeit_nonexistent {Test that a nonexistent parser is recorded as error not re-raised} -body {
set r [argparsingtest::timeit -style opts -parsers {nonexistent_proc}]
set ok 1
if {![dict exists $r not_loaded]} { set ok 0 }
if {$ok && ![dict exists $r results]} { set ok 0 }
if {![dict exists $r opts nonexistent_proc]} { set ok 0 }
if {$ok} {
set sub [dict get $r opts nonexistent_proc]
if {![dict exists $sub error]} { set ok 0 }
}
set ok
} -result 1
#added 2026-07-17 (agent)
test compare_json {Test that compare -iterations 20 -format json returns valid JSON with results and not_loaded keys} -body {
set r [argparsingtest::compare -iterations 20 -format json]
#added 2026-07-18 (agent)
test timeit_args_requires_single_style {Test that timeit -args without a single -style raises} -body {
set caught [catch {argparsingtest::timeit -iterations 20 -args {-x hello}} err]
set ok 0
if {$caught && [string first "single -style" $err] >= 0} {
set ok 1
}
set ok
} -result 1
#added 2026-07-18 (agent) - roster sizes: opts 16 parsers + 4 probed externals, positional styles 9 + 4
test discover_parsers_shape {Test discover_parsers style keying, entry counts and statuses} -body {
set r [argparsingtest::discover_parsers]
set ok 1
if {[lsort [dict keys $r]] ne [lsort {opts tkstyle tclstyle}]} { set ok "style keys: [dict keys $r]" }
if {$ok eq 1 && [dict size [dict get $r opts]] != 20} { set ok "opts size [dict size [dict get $r opts]]" }
if {$ok eq 1 && [dict size [dict get $r tkstyle]] != 13} { set ok "tkstyle size [dict size [dict get $r tkstyle]]" }
if {$ok eq 1 && [dict size [dict get $r tclstyle]] != 13} { set ok "tclstyle size [dict size [dict get $r tclstyle]]" }
if {$ok eq 1 && [dict get $r opts manual_switch] ne "loaded"} { set ok "opts manual_switch status" }
if {$ok eq 1 && [dict get $r tkstyle cmdline_typed] ne "unsupported"} { set ok "tkstyle cmdline_typed status" }
if {$ok eq 1 && [dict get $r tkstyle argp] ne "unsupported"} { set ok "tkstyle argp status" }
if {$ok eq 1 && [dict get $r tclstyle cmdline_typed] ne "loaded"} { set ok "tclstyle cmdline_typed status" }
if {$ok eq 1 && [dict get $r tclstyle argp] ne "unsupported"} { set ok "tclstyle argp status" }
if {$ok eq 1 && [dict exists $r opts test1_switch]} { set ok "stale test1_switch key present" }
set ok
} -result 1
#added 2026-07-18 (agent) - every loaded tkstyle parser honours the {p1 p2 opts} return contract
test parser_contract_tkstyle {Test that each loaded tkstyle parser extracts leading positionals} -body {
set disco [dict get [argparsingtest::discover_parsers -style tkstyle] tkstyle]
set ok 1
foreach {pname status} $disco {
if {$status ne "loaded"} { continue }
set r [argparsingtest::tkstyle::$pname alpha beta -x hello -1 0]
if {[lindex $r 0] ne "alpha" || [lindex $r 1] ne "beta"} {
set ok "$pname returned: $r"
break
}
}
set ok
} -result 1
#added 2026-07-18 (agent) - every loaded tclstyle parser honours the {p1 p2 opts} return contract
test parser_contract_tclstyle {Test that each loaded tclstyle parser extracts trailing positionals} -body {
set disco [dict get [argparsingtest::discover_parsers -style tclstyle] tclstyle]
set ok 1
if {[catch {set d [json::json2dict $r]} _err]} { set ok 0 }
if {$ok && ![dict exists $d results]} { set ok 0 }
if {$ok && ![dict exists $d not_loaded]} { set ok 0 }
foreach {pname status} $disco {
if {$status ne "loaded"} { continue }
set r [argparsingtest::tclstyle::$pname -x hello -1 0 alpha beta]
if {[lindex $r 0] ne "alpha" || [lindex $r 1] ne "beta"} {
set ok "$pname returned: $r"
break
}
}
set ok
} -result 1
#added 2026-07-18 (agent) - N/A rows: unsupported library/style combos stay visible in the table
test compare_table_sections {Test that compare -format table emits all three style sections with N/A rows} -body {
set r [argparsingtest::compare -iterations 20 -format table -parsers {manual_switch punkargs cmdline_typed argp}]
set ok 1
foreach expected {"options only" "tk style (positionals first)" "tcl style (positionals last)" "N/A" "not loaded:" "Parser"} {
if {[string first $expected $r] < 0} { set ok "missing: $expected" ; break }
}
set ok
} -result 1
#added 2026-07-18 (agent)
test compare_dict {Test that compare -format dict returns style-keyed results/first_call/not_loaded/unsupported} -body {
set r [argparsingtest::compare -style opts -iterations 20 -parsers {manual_switch} -format dict]
set ok 1
foreach key {results first_call not_loaded unsupported} {
if {![dict exists $r $key]} { set ok "missing $key" ; break }
}
if {$ok eq 1 && ![dict exists $r results opts manual_switch us_per_call]} { set ok "missing results.opts.manual_switch" }
if {$ok eq 1 && [dict get $r unsupported opts] ne ""} { set ok "unexpected unsupported list" }
set ok
} -result 1
#added 2026-07-18 (agent)
test compare_json {Test that compare -format json round-trips with style-keyed unsupported and first_call structure} -body {
set r [argparsingtest::compare -style tkstyle -iterations 20 -parsers {manual_switch cmdline_typed} -format json]
set ok 1
if {[catch {set d [json::json2dict $r]} _err]} { set ok "json parse: $_err" }
if {$ok eq 1 && ![dict exists $d results tkstyle manual_switch us_per_call]} { set ok "missing results" }
if {$ok eq 1 && [dict get $d unsupported tkstyle] ne "cmdline_typed"} { set ok "unsupported: [dict get $d unsupported tkstyle]" }
if {$ok eq 1 && ![dict exists $d first_call tkstyle manual_switch cold]} { set ok "missing first_call cold" }
if {$ok eq 1 && ![dict exists $d first_call tkstyle manual_switch warm]} { set ok "missing first_call warm" }
set ok
} -result 1
@ -61,23 +152,23 @@ namespace eval ::testspace {
set ok
} -result 1
#added 2026-07-17 (agent)
test discover_parsers_basic {Test that discover_parsers returns a dict with test1_switch loaded and 21 entries} -body {
set r [argparsingtest::discover_parsers]
set ok 1
if {![dict exists $r test1_switch]} { set ok 0 }
if {$ok && [dict get $r test1_switch] ne "loaded"} { set ok 0 }
if {$ok && [dict size $r] != 21} { set ok 0 }
#added 2026-07-18 (agent)
test compare_badstyle {Test that compare -style bogus raises an error containing choiceviolation} -body {
set caught [catch {argparsingtest::compare -style bogus} err]
set ok 0
if {$caught && ([string first "choiceviolation" $err] >= 0 || [string first "must be one of" $err] >= 0)} {
set ok 1
}
set ok
} -result 1
#added 2026-07-17 (agent)
test first_call_handrolled {Test that first_call returns cold and warm float timings for a hand-rolled parser} -body {
set r [argparsingtest::first_call -parsers {test1_switch}]
set r [argparsingtest::first_call -style opts -parsers {manual_switch}]
set ok 1
if {![dict exists $r test1_switch]} { set ok 0 }
if {![dict exists $r opts manual_switch]} { set ok 0 }
if {$ok} {
set sub [dict get $r test1_switch]
set sub [dict get $r opts manual_switch]
foreach mode {cold warm} {
if {![dict exists $sub $mode]} { set ok 0 ; break }
if {![string is double -strict [dict get $sub $mode]]} { set ok 0 ; break }
@ -88,9 +179,9 @@ namespace eval ::testspace {
#added 2026-07-17 (agent)
test first_call_library_backed {Test that first_call returns cold and warm float timings for library-backed parsers (warm-up scripts must run cleanly)} -body {
set r [argparsingtest::first_call -parsers {test1_punkargs test1_punkargs_by_id test1_opt test1_cmdline_untyped test1_cmdline_typed test1_tepam}]
set r [argparsingtest::first_call -style opts -parsers {punkargs punkargs_by_id opt cmdline_untyped cmdline_typed tepam}]
set ok 1
foreach {pname sub} $r {
foreach {pname sub} [dict get $r opts] {
foreach mode {cold warm} {
if {![dict exists $sub $mode]} { set ok "missing $mode for $pname" ; break }
if {![string is double -strict [dict get $sub $mode]]} { set ok "$pname $mode: [dict get $sub $mode]" ; break }
@ -100,10 +191,27 @@ namespace eval ::testspace {
set ok
} -result 1
#added 2026-07-18 (agent) - positional-style parsers must also time cleanly in child interps
test first_call_positional_styles {Test first_call cold and warm for tkstyle and tclstyle parsers} -body {
set ok 1
foreach {s plist} {tkstyle {punkargs_by_id tepam} tclstyle {cmdline_untyped}} {
set r [argparsingtest::first_call -style $s -parsers $plist]
foreach {pname sub} [dict get $r $s] {
foreach mode {cold warm} {
if {![dict exists $sub $mode]} { set ok "missing $mode for $s $pname" ; break }
if {![string is double -strict [dict get $sub $mode]]} { set ok "$s $pname $mode: [dict get $sub $mode]" ; break }
}
if {$ok ne 1} { break }
}
if {$ok ne 1} { break }
}
set ok
} -result 1
#added 2026-07-17 (agent)
test first_call_argp {Test first_call cold and warm for test1_argp when argp is loaded} -constraints argp_loaded -body {
set r [argparsingtest::first_call -parsers {test1_argp}]
set sub [dict get $r test1_argp]
test first_call_argp {Test first_call cold and warm for argp when argp is loaded} -constraints argp_loaded -body {
set r [argparsingtest::first_call -style opts -parsers {argp}]
set sub [dict get $r opts argp]
set ok 1
foreach mode {cold warm} {
if {![string is double -strict [dict get $sub $mode]]} { set ok "$mode: [dict get $sub $mode]" ; break }
@ -113,33 +221,11 @@ namespace eval ::testspace {
#added 2026-07-17 (agent)
test compare_table_first_columns {Test that compare table output has first_cold and first_warm columns} -body {
set r [argparsingtest::compare -iterations 20 -format table -parsers {test1_switch}]
set r [argparsingtest::compare -style opts -iterations 20 -format table -parsers {manual_switch}]
set ok 1
if {[string first "first_cold" $r] < 0} { set ok 0 }
if {$ok && [string first "first_warm" $r] < 0} { set ok 0 }
set ok
} -result 1
#added 2026-07-17 (agent)
test compare_json_first_call_structure {Test that compare json first_call entries are objects with cold and warm keys} -body {
set r [argparsingtest::compare -iterations 20 -format json -parsers {test1_switch}]
set d [json::json2dict $r]
set ok 1
if {![dict exists $d first_call test1_switch cold]} { set ok 0 }
if {$ok && ![dict exists $d first_call test1_switch warm]} { set ok 0 }
set ok
} -result 1
#added 2026-07-17 (agent)
test timeit_nonexistent {Test that a nonexistent parser is recorded as error not re-raised} -body {
set r [argparsingtest::timeit -parsers {nonexistent_proc}]
set ok 1
if {![dict exists $r nonexistent_proc]} { set ok 0 }
if {$ok} {
set sub [dict get $r nonexistent_proc]
if {![dict exists $sub error]} { set ok 0 }
}
set ok
} -result 1
}
tcltest::cleanupTests

Loading…
Cancel
Save