diff --git a/src/tests/modules/AGENTS.md b/src/tests/modules/AGENTS.md index a49206bc..572a4414 100644 --- a/src/tests/modules/AGENTS.md +++ b/src/tests/modules/AGENTS.md @@ -43,7 +43,7 @@ Unit tests for editable source modules under `src/modules/`, `src/modules_tcl8/` - `punk/args/` — punk::args tests (`testsuites/args/`): parsing, choices/choicegroups, forms, rendering/indentation characterization, synopsis display characterization (`synopsis.test`: basic italic argname/`` styling, longopt `--x=` alias forms, literal/literalprefix/stringstartswith/stringendswith type-alternates rendering unitalicised, option alternate parenthesization, multi-element clause display incl `?type?` members and argname tail-word hints, `-typesynopsis` value-element lists and option passthrough incl documenter ANSI, and the small-restricted-choice-set literal rule: 1-3 restricted choices render as unitalicised `|`-joined literals in leader/option/value positions with choicegroups counted, >3 or `-choicerestricted 0` falling back to italics, `-typesynopsis` taking precedence), usage-marking characterization (`usagemarking.test`: -parsedargs/-badarg/-parsestatus/-scheme marking primitives plus goodchoice highlighting of selected/default-in-effect choice words, asserted by SGR-parameter subset against the live colour arrays; the G-049 nocolour/colour-leak GAP pins flipped 2026-07-10 to scheme-statelessness assertions), the G-049 parse-status structure (`parsestatus.test`: punk::args::parse_status overall/per-argument statuses, badarg for type/allocation failures, -caller attribution, errorcode -argspecs stripping), -parsekey characterization (`parsekey.test`: result/received/solos/multis keying, shared-key required satisfaction and defaults, mash-path and prefix-abbreviation keying, plus GAP pins for last-defined-member default precedence, cross-member -multiple value loss, parsekey/optname collision conflation, and values/leaders parsekey breakage - desired-behaviour pins disabled behind punkargsKnownBug in `testsuites/dev/parsekey-knownbugs.test`), and tclcore doc/interpreter behavioural parity (`tclcoreparity.test`, G-054, gated on have_tclcoredocs: 'string is' class choices equal the live-harvested set, per-class docids exist, error-vs-ok agreement across the probe matrix, version-note labels conditional on class presence - expectations derived from the running interpreter, green on 8.6/8.7/9.0; under 8.6 run the file directly via a plain tclkit + tcltest driver since runtests' harness needs newer infrastructure) - `punk/nav/ns/` — punk::nav::ns tests (`testsuites/nav/navns.test`): the n/ n// n/// navigation state machine (ns/ transitions absolute/relative/glob-no-nav, failed-nav state preservation, quad-colon normalization, v-form content selection, ensemble annotation) and the ::punk::nav::ns::ns_current variable contract the repl/codethread/subshell seeding all consume; display content is covered in punk/ns nslist.test - `punk/repl/` — punk::repl tests (`testsuites/repl/`): opunk console backend integration (`consolebackends.test`) and repl current-namespace retention (`nscurrent.test`: real codethread via repl::init driven by synchronous runscript sends - inscope evaluation of ns_current, retention across submissions, n/-navigation retained, auto-create-with-notice for missing namespaces, the 2026-07-14 stray-namespace seeding fix pinned behaviourally plus a source-text guard on repl::start's inline template; the end-to-end piped subshell session is covered at shell level by shell/testsuites/punkexe/shellnavns.test - which found the first-subshell shared-code-interp asymmetry and the piped-inscope gap recorded there) -- `punk/ns/` — punk::ns tests (`testsuites/ns/`): cmdwhich/cmdinfo/cmd_traverse doc-lookup flow (`cmdflow.test`, G-040 parity), n/ display machinery characterization (`nslist.test`: tier A get_ns_dicts classification buckets as the machine contract for display reworks - incl package tail/prefix derivation, alias edge cases, usageinfo scan-dependence; tier B per-element layout-agnostic marking - underline/underdouble/underdotted namespace package styles, command type tag colours, exported/imported markers, the punkargs doc icon; tier C REWORK-flagged pins of the current hardcoded 2-col/4-col layout and nspath subtables, to flip deliberately with the planned punk-tables/width-responsive rework; plus the KNOWN QUIRK pin that bare nslist without -types errors with a malformed message), corp proc-retrieval and syntax/untabify interplay (`corp.test`: name edge cases, -ranges/-n line handling, basic-highlight ansistrip equivalence, -untabify spaces/unicode tab-free output, the KNOWN-DEFICIENCY pin for default -untabify none on tabbed bodies - grepstr warns per pass and brace overlays mangle tabbed lines, deterministic under mocked console tabstops - and a ::tcl::CopyDirectory -untabify spaces smoke test; precursor coverage for the planned punk::ns hygiene pass), cmdtrace characterization (`cmdtrace.test`: -pause 0 non-interactive runs, linedict line-mark keys for flat and 2-word-form nested switches as correct-mark guards, and GAP pins for the upstream nested-switch mismark - core.tcl-lang.org tktview 5d5b1052280c976ea3d4, arm bodies whose split-list index lands on a literal switch-command word report container-relative lines; mark tests gated on have_tclcoredocs because cmdtrace's arm-offset correction parses against the ::switch argdoc; plus the fixed-canary asserting punk::lib::check::has_tclbug_nestedswitch_tracelines still reports the bug - a live behavioural probe, so a fixed Tcl release fails the canary first and triggers the documented flip workflow), and cmdhelp usage-rendering integration (`cmdhelp.test`: scheme selection, goodarg/badarg marking incl type/allocation failures, goodchoice highlighting of supplied/default choice words, alias path, cmdinfo result shape, queried-command failure attribution, and `-return dict` parse-status returns (G-049 - its GAP pins flipped 2026-07-10); remaining GAP pins for pseudo-command cmdtype + space-form docid prefixes (G-051, real `string is` pins behind the have_tclcoredocs constraint), TclOO undocumented-method fallback (G-052), and synopsis marking absence (G-050)) +- `punk/ns/` — punk::ns tests (`testsuites/ns/`): cmdwhich/cmdinfo/cmd_traverse doc-lookup flow (`cmdflow.test`, G-040 parity), n/ display machinery characterization (`nslist.test`: tier A get_ns_dicts classification buckets as the machine contract for display reworks - incl package tail/prefix derivation, alias edge cases, usageinfo scan-dependence; tier B per-element layout-agnostic marking - underline/underdouble/underdotted namespace package styles, command type tag colours, exported/imported markers, the punkargs doc icon; tier C REWORK-flagged pins of the current hardcoded 2-col/4-col layout and nspath subtables, to flip deliberately with the planned punk-tables/width-responsive rework; plus the KNOWN QUIRK pin that bare nslist without -types errors with a malformed message), corp proc-retrieval and syntax/untabify interplay (`corp.test`: name edge cases, -ranges/-n line handling, basic-highlight ansistrip equivalence, -untabify spaces/unicode tab-free output, the KNOWN-DEFICIENCY pin for default -untabify none on tabbed bodies - grepstr warns per pass and brace overlays mangle tabbed lines, deterministic under mocked console tabstops - and a ::tcl::CopyDirectory -untabify spaces smoke test; precursor coverage for the planned punk::ns hygiene pass), cmdtrace characterization (`cmdtrace.test`: -pause 0 non-interactive runs, linedict line-mark keys for flat and 2-word-form nested switches as correct-mark guards, and GAP pins for the upstream nested-switch mismark - core.tcl-lang.org tktview 5d5b1052280c976ea3d4, arm bodies whose split-list index lands on a literal switch-command word report container-relative lines; mark tests gated on have_tclcoredocs because cmdtrace's arm-offset correction parses against the ::switch argdoc; plus the fixed-canary asserting punk::lib::check::has_tclbug_nestedswitch_tracelines still reports the bug - a live behavioural probe, so a fixed Tcl release fails the canary first and triggers the documented flip workflow), cmdhelp usage-rendering integration (`cmdhelp.test`: scheme selection, goodarg/badarg marking incl type/allocation failures, goodchoice highlighting of supplied/default choice words, alias path, cmdinfo result shape, queried-command failure attribution, and `-return dict` parse-status returns (G-049 - its GAP pins flipped 2026-07-10); remaining GAP pins for pseudo-command cmdtype + space-form docid prefixes (G-051, real `string is` pins behind the have_tclcoredocs constraint), TclOO undocumented-method fallback (G-052), and synopsis marking absence (G-050)), and name/path primitive characterization (`nsprimitives.test`: string pins for nsparts/nsprefix/nstail/nsjoin/nsjoinall incl weird colon-run (`:::`) splitting, the trailing-colon parse ambiguity (`::x:` + `y` joins to the same string as `::x` + `:y` and reparses leading-colon-greedy), prefix/tail/join round-trip and its absolutizing exceptions, and divergence pins showing the nsparts1/nsprefix1/nsprefix_orig/nstail1/nstail_orig twins are NOT drop-in equivalents (safe-deletion evidence for the planned hygiene pass); plus nseval fq-requirement/create-on-eval/evaluator-proc caching, the native-vs-punk `p:::x` resolution divergence (native namespace eval reaches child `x`, nseval creates/reaches literal `:x`), nseval_ifexists no-create + error propagation on plain and genuinely weird namespaces, nsexists/nschildren/nstree_raw weird-ns and relative-resolution pins, globmatchns `*`/`**`/`?` semantics (incl `*` matching a single inner colon - the 'should be fixed' comment above nsglob_as_re is stale), and nspath_to_absolute/nspath_here_absolute caller-resolution pins; known warts recorded in-test for the pass: nsjoinall error message prefixed 'nsjoin:') - `punk/mix/` — punk::mix::cli tests (prune helpers, punkcheck virtual sources), punk::mix::commandset::repo fossil move/rename characterization tests (`testsuites/repo/`, FOSSIL_HOME-isolated; GAP-marked tests pin behaviour G-022 will change), punk::mix::commandset::loadedlib tests (`testsuites/loadedlib/libsearch.test`: 'dev lib.search' match semantics via -return list — wrap-glob default, =exact prefix, case rules, explicit globs, version aggregation — plus the loadedlib 0.2.0 contract: deep discovery by default (deep .tm modules found without -refresh, registration persists), -refresh = genuine re-scan (epoch incr + rediscovery picks up .tm files added to already-scanned dirs), and highlight working without the shell-global a+ alias; shared provisioned child interp sourcing the source-tree libunknown directly — see the file's ORDERING NOTE), and the MULTISHELL polyglot build machinery (`testsuites/scriptwrap/multishell.test`: scriptset wrap via the punk.multishell.cmd template - structure/LF-only/determinism, checkfile 512-byte label validation of fresh wraps AND the committed bin/runtime.cmd, the runtime scriptset round-trip byte-identity pin, and platform-gated execution smoke: cmd.exe→powershell payload on windows, sh payload on unix or via the `wsllinux` capability constraint from `src/tests/testsupport/wslprobe.tcl` - staged to the WSL distro's native filesystem, G-059) - `punk/lib/` — punk::lib tests (`testsuites/lib/`): range/index/parse/compat/interp_sync utilities, G-058 static-baseline seeding (`staticseed.test`: interp_sync_package_paths/snapshot_package_paths propagate a simulated ::punkboot static baseline and seed `load {} ` ifneeded mappings; no-op without a baseline), and the repl command-completeness engine (`commandcomplete.test`: punk::lib::system::incomplete pending-opener stacks - the info-complete quoting quirk progression (`set x "{*}{"` standalone vs in-proc-body), single openers, tabs, escapes, incomplete<->info-complete parity property; pre-repl-refactor characterization, see goals/G-044 detail preserve-list) - `punk/packagepreference/` — punk::packagepreference tests (`testsuites/packagepreference/`): G-058 static-vs-bundled policy (`staticpolicy.test`: require of a baseline package triggers the index scan before resolution so a newer bundled copy wins, static beats older bundled, exact requires of bundled versions stay reachable, missing static mappings get seeded) diff --git a/src/tests/modules/punk/ns/testsuites/ns/nsprimitives.test b/src/tests/modules/punk/ns/testsuites/ns/nsprimitives.test new file mode 100644 index 00000000..6d0110dc --- /dev/null +++ b/src/tests/modules/punk/ns/testsuites/ns/nsprimitives.test @@ -0,0 +1,370 @@ +package require tcltest + +package require punk::ns + +namespace eval ::testspace { + namespace import ::tcltest::* + variable common { + set result "" + } + + #added 2026-07-14 (agent) - characterization pins for the punk::ns name/path string primitives and eval variants, coverage precondition for the planned punk::ns hygiene pass + #The primitive region of punk::ns contains variant/deprecated twins (nsparts1, nsprefix1, nsprefix_orig, nstail1, nstail_orig) that are + #consolidation candidates. The pins below record the exported primitives' behaviour AND where the twins diverge from them, so any + #dead-code removal during the hygiene pass is deliberate rather than assumed-equivalent. + #nsjoin/nsjoinall/nsprefix/nstail/nsparts are pure string functions - none of the namespaces used in these string pins need to exist. + + test nsparts_standard {nsparts splits well-formed paths - leading empty element marks fully qualified, :::: collapses to ::, spaces preserved}\ + -setup $common -body { + lappend result [punk::ns::nsparts ""] + lappend result [punk::ns::nsparts a] + lappend result [punk::ns::nsparts a::b] + lappend result [punk::ns::nsparts ::a::b] + lappend result [punk::ns::nsparts ::] + lappend result [punk::ns::nsparts ::a::::b] + lappend result [punk::ns::nsparts {::a b::c}] + }\ + -cleanup { + }\ + -result [list\ + {} a {a b} {{} a b} {{} {}} {{} a b} {{} {a b} c} + ] + + test nsparts_weird_colons {nsparts on odd colon runs - leading-colon-greedy splitting of :::, trailing colons, bare-colon segments}\ + -setup $common -body { + lappend result [punk::ns::nsparts :::] ;# {} : + lappend result [punk::ns::nsparts :::::] ;# {} : {} + lappend result [punk::ns::nsparts ::::::] ;# {} : : + lappend result [punk::ns::nsparts ::a:::b] ;# {} a :b - rightmost colon binds to the tail (leading-colon preference) + lappend result [punk::ns::nsparts ::a:::::b] ;# {} a : b - bare-colon middle segment + lappend result [punk::ns::nsparts ::a::::::b] ;# {} a : :b + lappend result [punk::ns::nsparts a:::::b] ;# a : b - relative path (no leading empty element) + lappend result [punk::ns::nsparts :x] ;# single segment with leading colon + lappend result [punk::ns::nsparts :::x] ;# {} :x + lappend result [punk::ns::nsparts ::x:] ;# {} x: - trailing colon stays in the segment + lappend result [punk::ns::nsparts ::x::] ;# {} x {} - trailing :: gives empty tail + lappend result [punk::ns::nsparts ::a::b:::] ;# {} a b : + }\ + -cleanup { + }\ + -result [list\ + {{} :} {{} : {}} {{} : :} {{} a :b} {{} a : b} {{} a : :b} {a : b} :x {{} :x} {{} x:} {{} x {}} {{} a b :} + ] + + test nsparts1_divergence {nsparts1 is NOT equivalent to nsparts - its blanket ::::->:: collapse merges colon runs before splitting}\ + -setup $common -body { + lappend result [punk::ns::nsparts1 ::a:::b] ;# {} a :b - same as nsparts here + lappend result [punk::ns::nsparts1 ::a:::::b] ;# {} a :b - DIFFERS (nsparts: {} a : b) + lappend result [punk::ns::nsparts1 ::a::::::b] ;# {} a {} b - DIFFERS (nsparts: {} a : :b) - produces an empty middle segment + lappend result [punk::ns::nsparts1 :::::] ;# {} : - DIFFERS (nsparts: {} : {}) + }\ + -cleanup { + }\ + -result [list\ + {{} a :b} {{} a :b} {{} a {} b} {{} :} + ] + + test nsparts_cached_coherence {nsparts_cached returns the same as nsparts for plain and weird paths}\ + -setup $common -body { + foreach p [list "" ::a::b ::a:::::b ::x: {::a b::c}] { + lappend result [expr {[punk::ns::nsparts_cached $p] eq [punk::ns::nsparts $p]}] + } + set result + }\ + -cleanup { + }\ + -result [list\ + 1 1 1 1 1 + ] + + test nstail_standard_and_weird {nstail returns the last nsparts segment - unlike 'namespace tail' it preserves leading/trailing colons in the tail}\ + -setup $common -body { + lappend result [punk::ns::nstail ""] + lappend result [punk::ns::nstail ::] + lappend result [punk::ns::nstail ::a] + lappend result [punk::ns::nstail ::a::b] + lappend result [punk::ns::nstail ::a:::b] ;# :b ('namespace tail' would give b) + lappend result [punk::ns::nstail :::a] ;# :a + lappend result [punk::ns::nstail ::::::a] ;# :a + lappend result [punk::ns::nstail ::a::b:] ;# b: + lappend result [punk::ns::nstail ::a::b::] ;# empty - trailing :: is an empty tail + }\ + -cleanup { + }\ + -result [list\ + {} {} a b :b :a :a b: {} + ] + + test nstail_variant_divergence {nstail1/nstail_orig diverge from nstail on long colon runs; nstail1 -strict errors on unpaired colons}\ + -setup $common -body { + lappend result [punk::ns::nstail1 ::::::a] ;# a - DIFFERS from nstail (:a) + lappend result [punk::ns::nstail_orig ::::::a] ;# a - DIFFERS from nstail (:a) + lappend result [punk::ns::nstail1 ::a:::b] ;# :b - same as nstail here + lappend result [catch {punk::ns::nstail1 ::a:::b -strict 1} emsg] $emsg + }\ + -cleanup { + }\ + -result [list\ + a a :b 1 {nstail unpaired colon ':' in ::a:::b} + ] + + test nsprefix_variant_divergence {nsprefix1/nsprefix_orig diverge from nsprefix on colon runs of 5+ (they collapse, nsprefix keeps bare-colon segments)}\ + -setup $common -body { + lappend result [punk::ns::nsprefix :::::a] ;# ::: (as pinned in basic.test) + lappend result [punk::ns::nsprefix1 :::::a] ;# :: - DIFFERS + lappend result [punk::ns::nsprefix_orig :::::a] ;# :: - DIFFERS + lappend result [punk::ns::nsprefix ::a::::::b] ;# ::a::: + lappend result [punk::ns::nsprefix1 ::a::::::b] ;# ::a - DIFFERS + lappend result [punk::ns::nsprefix_orig ::a::::::b] ;# ::a:: - DIFFERS (and differs from nsprefix1) + }\ + -cleanup { + }\ + -result [list\ + ::: :: :: ::a::: ::a ::a:: + ] + + test nsjoin_edges {nsjoin edge cases - empty prefix absolutizes, empty name gives trailing :: (fq form of empty command name), absolute name rejects non-empty prefix}\ + -setup $common -body { + lappend result [punk::ns::nsjoin {} x] ;# ::x - empty prefix produces an ABSOLUTE path + lappend result [punk::ns::nsjoin {} ::x] ;# ::x - absolute name passes through when prefix empty + lappend result [punk::ns::nsjoin :: x] ;# ::x + lappend result [punk::ns::nsjoin :: {}] ;# :: + lappend result [punk::ns::nsjoin ::x::y {}] ;# ::x::y:: - fully qualified form used to call a command named as the empty string + lappend result [punk::ns::nsjoin x y] ;# x::y - relative prefix stays relative + lappend result [punk::ns::nsjoin ::x :y] ;# ::x:::y + lappend result [catch {punk::ns::nsjoin ::x ::y} emsg] $emsg + }\ + -cleanup { + }\ + -result [list\ + ::x ::x ::x :: ::x::y:: x::y ::x:::y 1 {nsjoin: won't join non-empty prefix to absolute namespace path '::y'} + ] + + test nsjoin_parse_stability {a leading-colon tail rejoins/reparses losslessly, but a trailing-colon prefix produces the same joined string and reparses differently}\ + -setup $common -body { + lappend result [punk::ns::nsjoin ::x :y] ;# ::x:::y + lappend result [punk::ns::nsparts [punk::ns::nsjoin ::x :y]] ;# {} x :y - round trips + lappend result [punk::ns::nsjoin ::x: y] ;# ::x:::y - SAME string as above (the documented trailing-colon ambiguity) + lappend result [punk::ns::nsparts [punk::ns::nsjoin ::x: y]] ;# {} x :y - NOT {{} x: y} - leading-colon preference reassociates the colon + }\ + -cleanup { + }\ + -result [list\ + ::x:::y {{} x :y} ::x:::y {{} x :y} + ] + + test nsprefix_nstail_nsjoin_roundtrip {nsjoin [nsprefix p] [nstail p] reassembles most inputs - exceptions are empty/bare-relative names (absolutized) and :::: collapse}\ + -setup $common -body { + foreach p [list :: ::a ::a::b a::b ::a:::b :::a :::::a ::a::b:: ::a::b: ::a::b:::c] { + set rejoined [punk::ns::nsjoin [punk::ns::nsprefix $p] [punk::ns::nstail $p]] + lappend result [expr {$rejoined eq $p}] + } + #non-round-trip cases + lappend result [punk::ns::nsjoin [punk::ns::nsprefix ""] [punk::ns::nstail ""]] ;# "" -> :: + lappend result [punk::ns::nsjoin [punk::ns::nsprefix a] [punk::ns::nstail a]] ;# a -> ::a (absolutized) + lappend result [punk::ns::nsjoin [punk::ns::nsprefix ::::a] [punk::ns::nstail ::::a]] ;# ::::a -> ::a (collapsed) + }\ + -cleanup { + }\ + -result [list\ + 1 1 1 1 1 1 1 1 1 1 :: ::a ::a + ] + + test nsjoinall_edges {nsjoinall - multiple segments, empty segments dropped, absolute arg allowed only while all preceding segments empty; error message says 'nsjoin:'}\ + -setup $common -body { + lappend result [punk::ns::nsjoinall ::] + lappend result [punk::ns::nsjoinall :: a b] + lappend result [punk::ns::nsjoinall ::x y z] + lappend result [punk::ns::nsjoinall a b::c d] + lappend result [punk::ns::nsjoinall :: {} b] ;# empty segments dropped + lappend result [punk::ns::nsjoinall ::x {}] + lappend result [punk::ns::nsjoinall a] ;# single arg passes through + lappend result [punk::ns::nsjoinall {}] + lappend result [punk::ns::nsjoinall {} {} ::y] ;# absolute arg accepted - everything before it is empty + #note the error message prefix is 'nsjoin:' not 'nsjoinall:' (wart - candidate for the hygiene pass) + lappend result [catch {punk::ns::nsjoinall ::x ::y} emsg] $emsg + }\ + -cleanup { + }\ + -result [list\ + :: ::a::b ::x::y::z a::b::c::d ::b ::x a {} ::y 1 {nsjoin: won't join non-empty namespace prefix to absolute namespace path '::y'} + ] + + test nseval_creates_and_caches_evaluator {nseval requires a fully qualified ns, CREATES it if missing (documented WARNING), and caches a generated evaluator proc}\ + -setup $common -body { + lappend result [catch {punk::ns::nseval relns {string cat hi}} emsg] $emsg + lappend result [namespace exists ::punknstest_ev1] + lappend result [punk::ns::nseval ::punknstest_ev1 {::tcl::namespace::current}] + lappend result [namespace exists ::punknstest_ev1] ;# 1 - created as a side effect of evaluation + lappend result [llength [info commands ::punk::ns::evaluator::eval-_NS_punknstest_ev1]] + }\ + -cleanup { + catch {namespace delete ::punknstest_ev1} + catch {rename ::punk::ns::evaluator::eval-_NS_punknstest_ev1 ""} + }\ + -result [list\ + 1 {nseval only accepts a fully qualified namespace} 0 ::punknstest_ev1 1 1 + ] + + test nseval_weird_path_diverges_from_native {native 'namespace eval' resolves p:::x to child x - punk::ns::nseval parses it leading-colon-greedy and creates/reaches literal child :x}\ + -setup $common -body { + #native Tcl treats the ::: run as a separator to a child named x + lappend result [namespace eval ::punknstest_v:::x {namespace current}] ;# ::punknstest_v::x + lappend result [namespace children ::punknstest_v] ;# ::punknstest_v::x + #punk::ns::nseval on the same string form reaches (and creates) a child literally named :x + lappend result [punk::ns::nseval ::punknstest_v2:::x {::tcl::namespace::current}] + lappend result [namespace children ::punknstest_v2] ;# ::punknstest_v2:::x + }\ + -cleanup { + catch {namespace delete ::punknstest_v} + catch {namespace delete ::punknstest_v2} + catch {rename {::punk::ns::evaluator::eval-_NS_punknstest_v2_NS_:x} ""} + }\ + -result [list\ + ::punknstest_v::x ::punknstest_v::x ::punknstest_v2:::x ::punknstest_v2:::x + ] + + test nseval_ifexists_plain {nseval_ifexists on plain paths - evaluates in existing ns with script errors propagating, returns empty (no creation) for missing ns}\ + -setup { + set result "" + namespace eval ::punknstest_ie {variable marker 42} + }\ + -body { + lappend result [punk::ns::nseval_ifexists ::punknstest_ie {set marker}] + lappend result [catch {punk::ns::nseval_ifexists ::punknstest_ie {error boom}} emsg] $emsg + lappend result [punk::ns::nseval_ifexists ::punknstest_missing {set marker}] + lappend result [namespace exists ::punknstest_missing] ;# 0 - not created + }\ + -cleanup { + catch {namespace delete ::punknstest_ie} + }\ + -result [list\ + 42 1 boom {} 0 + ] + + test nseval_ifexists_weird {nseval_ifexists on a genuinely weird ns (child literally named :odd) - reaches it, script errors propagate, missing weird path returns empty without creating}\ + -setup { + set result "" + namespace eval ::punknstest_w {namespace eval {:odd} {variable marker 42}} + }\ + -body { + #native children display is the ambiguous string form - indistinguishable from a child odd of a ns named punknstest_w: + lappend result [namespace children ::punknstest_w] + lappend result [punk::ns::nschildren ::punknstest_w] + lappend result [punk::ns::nseval_ifexists ::punknstest_w:::odd {set marker}] + lappend result [punk::ns::nseval_ifexists ::punknstest_w:::odd {info vars}] + lappend result [catch {punk::ns::nseval_ifexists ::punknstest_w:::odd {error boom}} emsg] $emsg + lappend result [punk::ns::nseval_ifexists ::punknstest_w:::nope {set marker}] + lappend result [namespace eval ::punknstest_w {namespace exists {:nope}}] ;# 0 - not created + }\ + -cleanup { + catch {namespace delete ::punknstest_w} + }\ + -result [list\ + ::punknstest_w:::odd ::punknstest_w:::odd 42 marker 1 boom {} 0 + ] + + test nsexists_pins {nsexists - empty string false, global true, weird ns findable, relative paths resolve against the caller namespace}\ + -setup { + set result "" + namespace eval ::punknstest_e { + namespace eval {:odd} {} + namespace eval child {} + } + }\ + -body { + lappend result [punk::ns::nsexists ""] + lappend result [punk::ns::nsexists ::] + lappend result [punk::ns::nsexists ::punknstest_e] + lappend result [punk::ns::nsexists ::punknstest_e_missing] + lappend result [punk::ns::nsexists ::punknstest_e:::odd] ;# 1 - weird ns found via punk string parsing + lappend result [namespace eval ::punknstest_e {punk::ns::nsexists child}] + lappend result [namespace eval ::punknstest_e {punk::ns::nsexists nochild}] + }\ + -cleanup { + catch {namespace delete ::punknstest_e} + }\ + -result [list\ + 0 1 1 0 1 1 0 + ] + + test nschildren_sort {nschildren -sort choices - default natural, ascii lsort, none unpinned order; relative ns resolves against caller}\ + -setup { + set result "" + namespace eval ::punknstest_c { + namespace eval b2 {} + namespace eval b10 {} + namespace eval a {namespace eval g {}} + } + }\ + -body { + lappend result [punk::ns::nschildren ::punknstest_c] ;# natural: b2 before b10 + lappend result [punk::ns::nschildren -sort ascii ::punknstest_c] ;# ascii: b10 before b2 + lappend result [lsort [punk::ns::nschildren -sort none ::punknstest_c]] ;# -sort none order is tcl-internal - pin membership only + lappend result [namespace eval ::punknstest_c {punk::ns::nschildren a}] ;# relative resolves against caller + }\ + -cleanup { + catch {namespace delete ::punknstest_c} + }\ + -result [list\ + {::punknstest_c::a ::punknstest_c::b2 ::punknstest_c::b10}\ + {::punknstest_c::a ::punknstest_c::b10 ::punknstest_c::b2}\ + {::punknstest_c::a ::punknstest_c::b10 ::punknstest_c::b2}\ + ::punknstest_c::a::g + ] + + test nstree_raw_pins {nstree_raw requires a fully qualified ns and lists weird children literally}\ + -setup { + set result "" + namespace eval ::punknstest_t { + namespace eval sub {} + namespace eval {:odd} {} + } + }\ + -body { + lappend result [catch {punk::ns::nstree_raw punknstest_t} emsg] $emsg + lappend result [lsort [punk::ns::nstree_raw ::punknstest_t]] + }\ + -cleanup { + catch {namespace delete ::punknstest_t} + }\ + -result [list\ + 1 {nstree_raw requires a fully qualified namespace}\ + {::punknstest_t ::punknstest_t:::odd ::punknstest_t::sub} + ] + + test globmatchns_pins {globmatchns - * matches within a level (including a single inner colon) but not across ::, ** crosses levels, ? matches one non-colon char}\ + -setup $common -body { + lappend result [punk::ns::globmatchns ::a::* ::a::b] + lappend result [punk::ns::globmatchns ::a::* ::a::b::c] ;# 0 - * does not cross :: + lappend result [punk::ns::globmatchns ::a::** ::a::b::c] ;# 1 - ** crosses :: + lappend result [punk::ns::globmatchns ::g*t ::got:it] ;# 1 - * matches a single inner colon (the 'should be fixed' comment above nsglob_as_re is stale - behaviour is fixed) + lappend result [punk::ns::globmatchns ::a::? ::a::b] + lappend result [punk::ns::globmatchns ::a::? ::a::bc] ;# 0 - ? is a single char + lappend result [punk::ns::globmatchns ::w::* {::w:::odd}] ;# 1 - * at a level also matches a leading-colon (weird) child + }\ + -cleanup { + }\ + -result [list\ + 1 0 1 1 1 0 1 + ] + + test nspath_absolute_helpers {nspath_to_absolute joins relative paths to a base; nspath_here_absolute resolves against the caller ns with empty-name trailing :: form}\ + -setup $common -body { + #namespace current is ::testspace + lappend result [punk::ns::nspath_to_absolute x ::base] + lappend result [punk::ns::nspath_to_absolute ::x ::base] + lappend result [punk::ns::nspath_to_absolute {} ::base] + lappend result [punk::ns::nspath_here_absolute] + lappend result [punk::ns::nspath_here_absolute {}] ;# trailing :: - fq form of empty command name in caller ns + lappend result [punk::ns::nspath_here_absolute v] + lappend result [punk::ns::nspath_here_absolute ::v] + }\ + -cleanup { + }\ + -result [list\ + ::base::x ::x ::base ::testspace ::testspace:: ::testspace::v ::v + ] + +} +tcltest::cleanupTests ;#needed to produce test summary.