diff --git a/src/tests/modules/punk/path/testsuites/tests/path.test b/src/tests/modules/punk/path/testsuites/tests/path.test index f14fbf6f..faf4702a 100644 --- a/src/tests/modules/punk/path/testsuites/tests/path.test +++ b/src/tests/modules/punk/path/testsuites/tests/path.test @@ -1,410 +1,478 @@ -package require tcltest -tcltest::configure {*}$::argv - - -package require punk::lib -package require punk::path - -namespace eval ::testspace { - namespace import ::tcltest::* - - variable common { - set result "" - } - - variable subfolders_tree { - set sub_prevdir [pwd] - set sub_newbase [punk::lib::tempdir_newfolder -prefix punk_path_subfolders] - cd $sub_newbase - set sub_tree_tail __punk_path_subfolders_test__ - set sub_tree_root [file join $sub_newbase $sub_tree_tail] - file mkdir [file join $sub_tree_root keep] - file mkdir [file join $sub_tree_root src vfs deep] - file mkdir [file join $sub_tree_root aside child grandchild] - } - - variable subfolders_cleanup { - cd $sub_prevdir - file delete -force $sub_newbase - } - - variable repeated_subfolders_tree { - set rep_prevdir [pwd] - set rep_newbase [punk::lib::tempdir_newfolder -prefix punk_path_repeated_subfolders] - cd $rep_newbase - set rep_tree_tail __punk_path_repeated_subfolders_test__ - set rep_tree_root [file join $rep_newbase $rep_tree_tail] - file mkdir [file join $rep_tree_root alpha a a leaf deeper] - file mkdir [file join $rep_tree_root alpha a x a leaf deeper] - file mkdir [file join $rep_tree_root alpha a xx a leaf deeper] - file mkdir [file join $rep_tree_root alpha a x y a leaf deeper] - file mkdir [file join $rep_tree_root alpha a x y z a leaf deeper] - file mkdir [file join $rep_tree_root alpha a x y z keep] - file mkdir [file join $rep_tree_root alpha a q aa leaf deeper] - file mkdir [file join $rep_tree_root alpha aa x a leaf deeper] - } - - variable repeated_subfolders_cleanup { - cd $rep_prevdir - file delete -force $rep_newbase - } - - variable treefilenames_tree { - set tf_prevdir [pwd] - set tf_newbase [punk::lib::tempdir_newfolder -prefix punk_path_treefilenames] - cd $tf_newbase - set tf_tree_tail __punk_path_treefilenames_test__ - set tf_tree_root [file join $tf_newbase $tf_tree_tail] - file mkdir [file join $tf_tree_root keep] - file mkdir [file join $tf_tree_root src vfs deep] - file mkdir [file join $tf_tree_root aside child grandchild] - foreach relpath { - keep/keep.txt - src/srcroot.txt - src/vfs/vfs.txt - src/vfs/deep/deep.txt - aside/aside.txt - aside/child/child.txt - aside/child/grandchild/grandchild.txt - b/other/other.txt - } { - set filepath [file join $tf_tree_root $relpath] - file mkdir [file dirname $filepath] - set channel [open $filepath w] - puts $channel $relpath - close $channel - } - } - - variable treefilenames_cleanup { - cd $tf_prevdir - file delete -force $tf_newbase - } - - test globmatchpath_basic {Test single star between slashes pathglob argument will match exactly a single level} \ - -setup $common -body { - set result [list \ - [punk::path::globmatchpath /etc/*/*.doc /etc/A/test.doc] \ - [punk::path::globmatchpath /etc/*/*.doc /etc/A/B/test.doc] \ - [punk::path::globmatchpath /etc/*/*.doc /etc/test.doc] \ - ] - } \ - -cleanup { - } \ - -result {1 0 0} - - test subfolders_exclude_trailing_doublestar {Trailing /** prunes descendants but keeps the matching base directory} \ - -setup $subfolders_tree -body { - set result [lsort [punk::path::subfolders -recursive -exclude-paths {**/src/**} .]] - set expected [lsort [list \ - [file join . $sub_tree_tail] \ - [file join . $sub_tree_tail aside] \ - [file join . $sub_tree_tail aside child] \ - [file join . $sub_tree_tail aside child grandchild] \ - [file join . $sub_tree_tail keep] \ - [file join . $sub_tree_tail src] \ - ]] - expr {$result eq $expected} - } \ - -cleanup $subfolders_cleanup \ - -result 1 - - test subfolders_exclude_single_segment {Single-level excludes omit the node but still recurse into it} \ - -setup $subfolders_tree -body { - set result [lsort [punk::path::subfolders -recursive -exclude-paths {**/aside/*} .]] - set expected [lsort [list \ - [file join . $sub_tree_tail] \ - [file join . $sub_tree_tail aside] \ - [file join . $sub_tree_tail aside child grandchild] \ - [file join . $sub_tree_tail keep] \ - [file join . $sub_tree_tail src] \ - [file join . $sub_tree_tail src vfs] \ - [file join . $sub_tree_tail src vfs deep] \ - ]] - expr {$result eq $expected} - } \ - -cleanup $subfolders_cleanup \ - -result 1 - - test subfolders_exclude_exact_segment {Exact segment excludes omit the node but still traverse below it} \ - -setup $subfolders_tree -body { - set result [lsort [punk::path::subfolders -recursive -exclude-paths {**/aside} .]] - set expected [lsort [list \ - [file join . $sub_tree_tail] \ - [file join . $sub_tree_tail aside child] \ - [file join . $sub_tree_tail aside child grandchild] \ - [file join . $sub_tree_tail keep] \ - [file join . $sub_tree_tail src] \ - [file join . $sub_tree_tail src vfs] \ - [file join . $sub_tree_tail src vfs deep] \ - ]] - expr {$result eq $expected} - } \ - -cleanup $subfolders_cleanup \ - -result 1 - - test subfolders_exclude_combined_patterns {Subtree and exact excludes compose correctly in recursive traversal} \ - -setup $subfolders_tree -body { - set result [lsort [punk::path::subfolders -recursive -exclude-paths {**/src/** **/aside} .]] - set expected [lsort [list \ - [file join . $sub_tree_tail] \ - [file join . $sub_tree_tail aside child] \ - [file join . $sub_tree_tail aside child grandchild] \ - [file join . $sub_tree_tail keep] \ - [file join . $sub_tree_tail src] \ - ]] - expr {$result eq $expected} - } \ - -cleanup $subfolders_cleanup \ - -result 1 - - test subfolders_repeated_segments_single_star {Repeated segment exclude with one wildcard segment prunes only that shape} \ - -setup $repeated_subfolders_tree -body { - set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/a/**} .] - expr { \ - [file join . $rep_tree_tail alpha a x a] in $result \ - && [file join . $rep_tree_tail alpha a x a leaf] ni $result \ - && [file join . $rep_tree_tail alpha a x a leaf deeper] ni $result \ - && [file join . $rep_tree_tail alpha a x y a leaf] in $result \ - && [file join . $rep_tree_tail alpha a x y z keep] in $result \ - } - } \ - -cleanup $repeated_subfolders_cleanup \ - -result 1 - - test subfolders_repeated_segments_two_single_stars {Repeated segment exclude with two wildcard segments prunes only that shape} \ - -setup $repeated_subfolders_tree -body { - set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/*/a/**} .] - expr { \ - [file join . $rep_tree_tail alpha a x y a] in $result \ - && [file join . $rep_tree_tail alpha a x y a leaf] ni $result \ - && [file join . $rep_tree_tail alpha a x y a leaf deeper] ni $result \ - && [file join . $rep_tree_tail alpha a x a leaf] in $result \ - && [file join . $rep_tree_tail alpha a x y z a leaf] in $result \ - } - } \ - -cleanup $repeated_subfolders_cleanup \ - -result 1 - - test subfolders_repeated_segments_middle_doublestar {Repeated segment exclude with middle doublestar prunes repeated a descendants at multiple depths} \ - -setup $repeated_subfolders_tree -body { - set result [punk::path::subfolders -recursive -exclude-paths {**/a/**/a/**} .] - expr { \ - [file join . $rep_tree_tail alpha a x a] in $result \ - && [file join . $rep_tree_tail alpha a x y a] in $result \ - && [file join . $rep_tree_tail alpha a x y z a] in $result \ - && [file join . $rep_tree_tail alpha a x a leaf] ni $result \ - && [file join . $rep_tree_tail alpha a x y a leaf] ni $result \ - && [file join . $rep_tree_tail alpha a x y z a leaf] ni $result \ - } - } \ - -cleanup $repeated_subfolders_cleanup \ - -result 1 - - test subfolders_repeated_segments_nonmatching_pattern {Repeated path segments are retained when pattern literals do not match} \ - -setup $repeated_subfolders_tree -body { - set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/b/**} .] - expr { \ - [file join . $rep_tree_tail alpha a x a leaf] in $result \ - && [file join . $rep_tree_tail alpha a x y a leaf] in $result \ - && [file join . $rep_tree_tail alpha a x y z a leaf] in $result \ - } - } \ - -cleanup $repeated_subfolders_cleanup \ - -result 1 - - test subfolders_repeated_segments_exact_boundary {Exact repeated segment excludes boundary node but still traverses below it} \ - -setup $repeated_subfolders_tree -body { - set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/a} .] - expr { \ - [file join . $rep_tree_tail alpha a x a] ni $result \ - && [file join . $rep_tree_tail alpha a x a leaf] in $result \ - && [file join . $rep_tree_tail alpha a x y a] in $result \ - } - } \ - -cleanup $repeated_subfolders_cleanup \ - -result 1 - - test subfolders_repeated_segments_adjacent_literals {Adjacent repeated literals match only adjacent path segments} \ - -setup $repeated_subfolders_tree -body { - set result [punk::path::subfolders -recursive -exclude-paths {**/a/a/**} .] - expr { \ - [file join . $rep_tree_tail alpha a a] in $result \ - && [file join . $rep_tree_tail alpha a a leaf] ni $result \ - && [file join . $rep_tree_tail alpha a x a leaf] in $result \ - } - } \ - -cleanup $repeated_subfolders_cleanup \ - -result 1 - - test subfolders_repeated_segments_question_segment {Question mark wildcard matches exactly one character within one segment} \ - -setup $repeated_subfolders_tree -body { - set result [punk::path::subfolders -recursive -exclude-paths {**/a/?/a/**} .] - expr { \ - [file join . $rep_tree_tail alpha a x a] in $result \ - && [file join . $rep_tree_tail alpha a x a leaf] ni $result \ - && [file join . $rep_tree_tail alpha a xx a leaf] in $result \ - } - } \ - -cleanup $repeated_subfolders_cleanup \ - -result 1 - - test subfolders_repeated_segments_similar_names {Similar segment names do not match repeated literal a patterns accidentally} \ - -setup $repeated_subfolders_tree -body { - set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/a/**} .] - expr { \ - [file join . $rep_tree_tail alpha a q aa leaf] in $result \ - && [file join . $rep_tree_tail alpha aa x a leaf] in $result \ - } - } \ - -cleanup $repeated_subfolders_cleanup \ - -result 1 - - test subfolders_repeated_segments_similar_name_patterns {Similar literal aa patterns match only their own segment shapes} \ - -setup $repeated_subfolders_tree -body { - set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/aa/** **/aa/*/a/**} .] - expr { \ - [file join . $rep_tree_tail alpha a q aa] in $result \ - && [file join . $rep_tree_tail alpha a q aa leaf] ni $result \ - && [file join . $rep_tree_tail alpha aa x a] in $result \ - && [file join . $rep_tree_tail alpha aa x a leaf] ni $result \ - && [file join . $rep_tree_tail alpha a x a leaf] in $result \ - } - } \ - -cleanup $repeated_subfolders_cleanup \ - -result 1 - - test subfolders_repeated_segments_overlapping_patterns {Overlapping repeated segment excludes prune each matching shape independently} \ - -setup $repeated_subfolders_tree -body { - set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/a/** **/a/*/*/a/**} .] - expr { \ - [file join . $rep_tree_tail alpha a x a leaf] ni $result \ - && [file join . $rep_tree_tail alpha a x y a leaf] ni $result \ - && [file join . $rep_tree_tail alpha a x y z a leaf] in $result \ - && [file join . $rep_tree_tail alpha a x y z keep] in $result \ - } - } \ - -cleanup $repeated_subfolders_cleanup \ - -result 1 - - test treefilenames_recurse_nested_positive_glob {Positive glob traversal reaches nested matches below unmatched ancestors} \ - -setup $treefilenames_tree -body { - set result [lsort [punk::path::treefilenames -sort none -directory . -include-paths {**/src/**} *.txt]] - set expected [lsort [list \ - [file join . $tf_tree_tail src vfs deep deep.txt] \ - [file join . $tf_tree_tail src vfs vfs.txt] \ - ]] - expr {$result eq $expected} - } \ - -cleanup $treefilenames_cleanup \ - -result 1 - - test treefilenames_nonexistent_glob_path_returns_empty {Non-matching glob_paths subtree should return no files} \ - -setup $treefilenames_tree -body { - set result [punk::path::treefilenames -sort none -directory . -include-paths {**/nonexistantfolder/**} *] - expr {$result eq [list]} - } \ - -cleanup $treefilenames_cleanup \ - -result 1 - - test treefilenames_tailbase_newbase_returns_tree_tail {Tailbase can trim returned filenames to the tree folder} \ - -setup $treefilenames_tree -body { - set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_newbase *.txt]] - set expected [lsort [list \ - [file join $tf_tree_tail aside aside.txt] \ - [file join $tf_tree_tail aside child child.txt] \ - [file join $tf_tree_tail aside child grandchild grandchild.txt] \ - [file join $tf_tree_tail b other other.txt] \ - [file join $tf_tree_tail keep keep.txt] \ - [file join $tf_tree_tail src srcroot.txt] \ - [file join $tf_tree_tail src vfs deep deep.txt] \ - [file join $tf_tree_tail src vfs vfs.txt] \ - ]] - expr {$result eq $expected} - } \ - -cleanup $treefilenames_cleanup \ - -result 1 - - test treefilenames_tailbase_tree_root_returns_tree_relative {Tailbase can trim returned filenames to paths below the search root} \ - -setup $treefilenames_tree -body { - set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_tree_root *.txt]] - set expected [lsort [list \ - [file join aside aside.txt] \ - [file join aside child child.txt] \ - [file join aside child grandchild grandchild.txt] \ - [file join b other other.txt] \ - [file join keep keep.txt] \ - [file join src srcroot.txt] \ - [file join src vfs deep deep.txt] \ - [file join src vfs vfs.txt] \ - ]] - expr {$result eq $expected} - } \ - -cleanup $treefilenames_cleanup \ - -result 1 - - test treefilenames_tailbase_exclude_paths_match_returned_paths {Exclude paths match tailbase-relative returned paths} \ - -setup $treefilenames_tree -body { - set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_tree_root -exclude-paths {aside aside/** src/vfs/**} *.txt]] - set expected [lsort [list \ - [file join b other other.txt] \ - [file join keep keep.txt] \ - [file join src srcroot.txt] \ - [file join src vfs vfs.txt] \ - ]] - expr {$result eq $expected} - } \ - -cleanup $treefilenames_cleanup \ - -result 1 - - test treefilenames_tailbase_include_paths_match_tree_root_relative_paths {Include paths match tailbase-relative paths below the tree root} \ - -setup $treefilenames_tree -body { - set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_tree_root -include-paths {src/**} *.txt]] - set expected [lsort [list \ - [file join src vfs deep deep.txt] \ - [file join src vfs vfs.txt] \ - ]] - expr {$result eq $expected} - } \ - -cleanup $treefilenames_cleanup \ - -result 1 - - test treefilenames_tailbase_include_paths_match_newbase_relative_paths {Include paths include the tree folder when tailbase is above the search root} \ - -setup $treefilenames_tree -body { - set include_path [file join $tf_tree_tail src **] - set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_newbase -include-paths [list $include_path] *.txt]] - set expected [lsort [list \ - [file join $tf_tree_tail src vfs deep deep.txt] \ - [file join $tf_tree_tail src vfs vfs.txt] \ - ]] - expr {$result eq $expected} - } \ - -cleanup $treefilenames_cleanup \ - -result 1 - - test treefilenames_tailbase_include_and_exclude_paths_share_relative_base {Include and exclude paths use the same tailbase-relative base} \ - -setup $treefilenames_tree -body { - set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_tree_root -include-paths {src/**} -exclude-paths {src/vfs/**} *.txt]] - set expected [list [file join src vfs vfs.txt]] - expr {$result eq $expected} - } \ - -cleanup $treefilenames_cleanup \ - -result 1 - - test treefilenames_rejects_internal_call_flags {Internal recursion state flags are not public API} \ - -setup $treefilenames_tree -body { - set results [list] - foreach arglist { - {-call-depth-internal 1 -directory . *} - {-call-subvector {a b} -directory . *} - {-call-allbelow 1 -directory . *} - } { - lappend results [catch {punk::path::treefilenames {*}$arglist}] - } - expr {$results eq {1 1 1}} - } \ - -cleanup $treefilenames_cleanup \ - -result 1 -} - +package require tcltest +tcltest::configure {*}$::argv + + +package require punk::lib +package require punk::path + +namespace eval ::testspace { + namespace import ::tcltest::* + + variable common { + set result "" + } + + variable subfolders_tree { + set sub_prevdir [pwd] + set sub_newbase [punk::lib::tempdir_newfolder -prefix punk_path_subfolders] + cd $sub_newbase + set sub_tree_tail __punk_path_subfolders_test__ + set sub_tree_root [file join $sub_newbase $sub_tree_tail] + file mkdir [file join $sub_tree_root keep] + file mkdir [file join $sub_tree_root src vfs deep] + file mkdir [file join $sub_tree_root aside child grandchild] + } + + variable subfolders_cleanup { + cd $sub_prevdir + file delete -force $sub_newbase + } + + variable repeated_subfolders_tree { + set rep_prevdir [pwd] + set rep_newbase [punk::lib::tempdir_newfolder -prefix punk_path_repeated_subfolders] + cd $rep_newbase + set rep_tree_tail __punk_path_repeated_subfolders_test__ + set rep_tree_root [file join $rep_newbase $rep_tree_tail] + file mkdir [file join $rep_tree_root alpha a a leaf deeper] + file mkdir [file join $rep_tree_root alpha a x a leaf deeper] + file mkdir [file join $rep_tree_root alpha a xx a leaf deeper] + file mkdir [file join $rep_tree_root alpha a x y a leaf deeper] + file mkdir [file join $rep_tree_root alpha a x y z a leaf deeper] + file mkdir [file join $rep_tree_root alpha a x y z keep] + file mkdir [file join $rep_tree_root alpha a q aa leaf deeper] + file mkdir [file join $rep_tree_root alpha aa x a leaf deeper] + } + + variable repeated_subfolders_cleanup { + cd $rep_prevdir + file delete -force $rep_newbase + } + + variable treefilenames_tree { + set tf_prevdir [pwd] + set tf_newbase [punk::lib::tempdir_newfolder -prefix punk_path_treefilenames] + cd $tf_newbase + set tf_tree_tail __punk_path_treefilenames_test__ + set tf_tree_root [file join $tf_newbase $tf_tree_tail] + file mkdir [file join $tf_tree_root keep] + file mkdir [file join $tf_tree_root src vfs deep] + file mkdir [file join $tf_tree_root aside child grandchild] + foreach relpath { + keep/keep.txt + src/srcroot.txt + src/vfs/vfs.txt + src/vfs/deep/deep.txt + aside/aside.txt + aside/child/child.txt + aside/child/grandchild/grandchild.txt + b/other/other.txt + alpha/subdir/direct.txt + alpha/subdir/deep/deep.txt + } { + set filepath [file join $tf_tree_root $relpath] + file mkdir [file dirname $filepath] + set channel [open $filepath w] + puts $channel $relpath + close $channel + } + } + + variable treefilenames_cleanup { + cd $tf_prevdir + file delete -force $tf_newbase + } + + test globmatchpath_basic {Test single star between slashes pathglob argument will match exactly a single level} \ + -setup $common -body { + set result [list \ + [punk::path::globmatchpath /etc/*/*.doc /etc/A/test.doc] \ + [punk::path::globmatchpath /etc/*/*.doc /etc/A/B/test.doc] \ + [punk::path::globmatchpath /etc/*/*.doc /etc/test.doc] \ + ] + } \ + -cleanup { + } \ + -result {1 0 0} + + test subfolders_exclude_trailing_doublestar {Trailing /** prunes descendants but keeps the matching base directory} \ + -setup $subfolders_tree -body { + set result [lsort [punk::path::subfolders -recursive -exclude-paths {**/src/**} .]] + set expected [lsort [list \ + [file join . $sub_tree_tail] \ + [file join . $sub_tree_tail aside] \ + [file join . $sub_tree_tail aside child] \ + [file join . $sub_tree_tail aside child grandchild] \ + [file join . $sub_tree_tail keep] \ + [file join . $sub_tree_tail src] \ + ]] + expr {$result eq $expected} + } \ + -cleanup $subfolders_cleanup \ + -result 1 + + test subfolders_exclude_single_segment {Single-level excludes omit the node but still recurse into it} \ + -setup $subfolders_tree -body { + set result [lsort [punk::path::subfolders -recursive -exclude-paths {**/aside/*} .]] + set expected [lsort [list \ + [file join . $sub_tree_tail] \ + [file join . $sub_tree_tail aside] \ + [file join . $sub_tree_tail aside child grandchild] \ + [file join . $sub_tree_tail keep] \ + [file join . $sub_tree_tail src] \ + [file join . $sub_tree_tail src vfs] \ + [file join . $sub_tree_tail src vfs deep] \ + ]] + expr {$result eq $expected} + } \ + -cleanup $subfolders_cleanup \ + -result 1 + + test subfolders_exclude_exact_segment {Exact segment excludes omit the node but still traverse below it} \ + -setup $subfolders_tree -body { + set result [lsort [punk::path::subfolders -recursive -exclude-paths {**/aside} .]] + set expected [lsort [list \ + [file join . $sub_tree_tail] \ + [file join . $sub_tree_tail aside child] \ + [file join . $sub_tree_tail aside child grandchild] \ + [file join . $sub_tree_tail keep] \ + [file join . $sub_tree_tail src] \ + [file join . $sub_tree_tail src vfs] \ + [file join . $sub_tree_tail src vfs deep] \ + ]] + expr {$result eq $expected} + } \ + -cleanup $subfolders_cleanup \ + -result 1 + + test subfolders_exclude_combined_patterns {Subtree and exact excludes compose correctly in recursive traversal} \ + -setup $subfolders_tree -body { + set result [lsort [punk::path::subfolders -recursive -exclude-paths {**/src/** **/aside} .]] + set expected [lsort [list \ + [file join . $sub_tree_tail] \ + [file join . $sub_tree_tail aside child] \ + [file join . $sub_tree_tail aside child grandchild] \ + [file join . $sub_tree_tail keep] \ + [file join . $sub_tree_tail src] \ + ]] + expr {$result eq $expected} + } \ + -cleanup $subfolders_cleanup \ + -result 1 + + test subfolders_repeated_segments_single_star {Repeated segment exclude with one wildcard segment prunes only that shape} \ + -setup $repeated_subfolders_tree -body { + set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/a/**} .] + expr { \ + [file join . $rep_tree_tail alpha a x a] in $result \ + && [file join . $rep_tree_tail alpha a x a leaf] ni $result \ + && [file join . $rep_tree_tail alpha a x a leaf deeper] ni $result \ + && [file join . $rep_tree_tail alpha a x y a leaf] in $result \ + && [file join . $rep_tree_tail alpha a x y z keep] in $result \ + } + } \ + -cleanup $repeated_subfolders_cleanup \ + -result 1 + + test subfolders_repeated_segments_two_single_stars {Repeated segment exclude with two wildcard segments prunes only that shape} \ + -setup $repeated_subfolders_tree -body { + set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/*/a/**} .] + expr { \ + [file join . $rep_tree_tail alpha a x y a] in $result \ + && [file join . $rep_tree_tail alpha a x y a leaf] ni $result \ + && [file join . $rep_tree_tail alpha a x y a leaf deeper] ni $result \ + && [file join . $rep_tree_tail alpha a x a leaf] in $result \ + && [file join . $rep_tree_tail alpha a x y z a leaf] in $result \ + } + } \ + -cleanup $repeated_subfolders_cleanup \ + -result 1 + + test subfolders_repeated_segments_middle_doublestar {Repeated segment exclude with middle doublestar prunes repeated a descendants at multiple depths} \ + -setup $repeated_subfolders_tree -body { + set result [punk::path::subfolders -recursive -exclude-paths {**/a/**/a/**} .] + expr { \ + [file join . $rep_tree_tail alpha a x a] in $result \ + && [file join . $rep_tree_tail alpha a x y a] in $result \ + && [file join . $rep_tree_tail alpha a x y z a] in $result \ + && [file join . $rep_tree_tail alpha a x a leaf] ni $result \ + && [file join . $rep_tree_tail alpha a x y a leaf] ni $result \ + && [file join . $rep_tree_tail alpha a x y z a leaf] ni $result \ + } + } \ + -cleanup $repeated_subfolders_cleanup \ + -result 1 + + test subfolders_repeated_segments_nonmatching_pattern {Repeated path segments are retained when pattern literals do not match} \ + -setup $repeated_subfolders_tree -body { + set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/b/**} .] + expr { \ + [file join . $rep_tree_tail alpha a x a leaf] in $result \ + && [file join . $rep_tree_tail alpha a x y a leaf] in $result \ + && [file join . $rep_tree_tail alpha a x y z a leaf] in $result \ + } + } \ + -cleanup $repeated_subfolders_cleanup \ + -result 1 + + test subfolders_repeated_segments_exact_boundary {Exact repeated segment excludes boundary node but still traverses below it} \ + -setup $repeated_subfolders_tree -body { + set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/a} .] + expr { \ + [file join . $rep_tree_tail alpha a x a] ni $result \ + && [file join . $rep_tree_tail alpha a x a leaf] in $result \ + && [file join . $rep_tree_tail alpha a x y a] in $result \ + } + } \ + -cleanup $repeated_subfolders_cleanup \ + -result 1 + + test subfolders_repeated_segments_adjacent_literals {Adjacent repeated literals match only adjacent path segments} \ + -setup $repeated_subfolders_tree -body { + set result [punk::path::subfolders -recursive -exclude-paths {**/a/a/**} .] + expr { \ + [file join . $rep_tree_tail alpha a a] in $result \ + && [file join . $rep_tree_tail alpha a a leaf] ni $result \ + && [file join . $rep_tree_tail alpha a x a leaf] in $result \ + } + } \ + -cleanup $repeated_subfolders_cleanup \ + -result 1 + + test subfolders_repeated_segments_question_segment {Question mark wildcard matches exactly one character within one segment} \ + -setup $repeated_subfolders_tree -body { + set result [punk::path::subfolders -recursive -exclude-paths {**/a/?/a/**} .] + expr { \ + [file join . $rep_tree_tail alpha a x a] in $result \ + && [file join . $rep_tree_tail alpha a x a leaf] ni $result \ + && [file join . $rep_tree_tail alpha a xx a leaf] in $result \ + } + } \ + -cleanup $repeated_subfolders_cleanup \ + -result 1 + + test subfolders_repeated_segments_similar_names {Similar segment names do not match repeated literal a patterns accidentally} \ + -setup $repeated_subfolders_tree -body { + set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/a/**} .] + expr { \ + [file join . $rep_tree_tail alpha a q aa leaf] in $result \ + && [file join . $rep_tree_tail alpha aa x a leaf] in $result \ + } + } \ + -cleanup $repeated_subfolders_cleanup \ + -result 1 + + test subfolders_repeated_segments_similar_name_patterns {Similar literal aa patterns match only their own segment shapes} \ + -setup $repeated_subfolders_tree -body { + set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/aa/** **/aa/*/a/**} .] + expr { \ + [file join . $rep_tree_tail alpha a q aa] in $result \ + && [file join . $rep_tree_tail alpha a q aa leaf] ni $result \ + && [file join . $rep_tree_tail alpha aa x a] in $result \ + && [file join . $rep_tree_tail alpha aa x a leaf] ni $result \ + && [file join . $rep_tree_tail alpha a x a leaf] in $result \ + } + } \ + -cleanup $repeated_subfolders_cleanup \ + -result 1 + + test subfolders_repeated_segments_overlapping_patterns {Overlapping repeated segment excludes prune each matching shape independently} \ + -setup $repeated_subfolders_tree -body { + set result [punk::path::subfolders -recursive -exclude-paths {**/a/*/a/** **/a/*/*/a/**} .] + expr { \ + [file join . $rep_tree_tail alpha a x a leaf] ni $result \ + && [file join . $rep_tree_tail alpha a x y a leaf] ni $result \ + && [file join . $rep_tree_tail alpha a x y z a leaf] in $result \ + && [file join . $rep_tree_tail alpha a x y z keep] in $result \ + } + } \ + -cleanup $repeated_subfolders_cleanup \ + -result 1 + + test treefilenames_recurse_nested_positive_glob {Positive glob traversal reaches nested matches below unmatched ancestors} \ + -setup $treefilenames_tree -body { + set result [lsort [punk::path::treefilenames -sort none -directory . -include-paths {**/src/**} *.txt]] + set expected [lsort [list \ + [file join . $tf_tree_tail src vfs deep deep.txt] \ + [file join . $tf_tree_tail src vfs vfs.txt] \ + ]] + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_nonexistent_glob_path_returns_empty {Non-matching glob_paths subtree should return no files} \ + -setup $treefilenames_tree -body { + set result [punk::path::treefilenames -sort none -directory . -include-paths {**/nonexistantfolder/**} *] + expr {$result eq [list]} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_include_paths_subdir_boundary_before_exact {Include paths include internal and trailing subdir matches when subtree pattern appears first} \ + -setup $treefilenames_tree -body { + set result [lsort [punk::path::treefilenames -sort none -directory . -include-paths {**/subdir/** **/subdir} *.txt]] + set expected [lsort [list \ + [file join . $tf_tree_tail alpha subdir deep deep.txt] \ + [file join . $tf_tree_tail alpha subdir direct.txt] \ + ]] + if {$result ne $expected} { + error "result mismatch\nexpected: $expected\nactual: $result" + } + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_include_paths_subdir_exact_before_boundary {Include paths include internal and trailing subdir matches when exact pattern appears first} \ + -setup $treefilenames_tree -body { + set result [lsort [punk::path::treefilenames -sort none -directory . -include-paths {**/subdir **/subdir/**} *.txt]] + set expected [lsort [list \ + [file join . $tf_tree_tail alpha subdir deep deep.txt] \ + [file join . $tf_tree_tail alpha subdir direct.txt] \ + ]] + if {$result ne $expected} { + error "result mismatch\nexpected: $expected\nactual: $result" + } + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_tailbase_newbase_returns_tree_tail {Tailbase can trim returned filenames to the tree folder} \ + -setup $treefilenames_tree -body { + set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_newbase *.txt]] + set expected [lsort [list \ + [file join $tf_tree_tail alpha subdir deep deep.txt] \ + [file join $tf_tree_tail alpha subdir direct.txt] \ + [file join $tf_tree_tail aside aside.txt] \ + [file join $tf_tree_tail aside child child.txt] \ + [file join $tf_tree_tail aside child grandchild grandchild.txt] \ + [file join $tf_tree_tail b other other.txt] \ + [file join $tf_tree_tail keep keep.txt] \ + [file join $tf_tree_tail src srcroot.txt] \ + [file join $tf_tree_tail src vfs deep deep.txt] \ + [file join $tf_tree_tail src vfs vfs.txt] \ + ]] + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_tailbase_tree_root_returns_tree_relative {Tailbase can trim returned filenames to paths below the search root} \ + -setup $treefilenames_tree -body { + set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_tree_root *.txt]] + set expected [lsort [list \ + [file join alpha subdir deep deep.txt] \ + [file join alpha subdir direct.txt] \ + [file join aside aside.txt] \ + [file join aside child child.txt] \ + [file join aside child grandchild grandchild.txt] \ + [file join b other other.txt] \ + [file join keep keep.txt] \ + [file join src srcroot.txt] \ + [file join src vfs deep deep.txt] \ + [file join src vfs vfs.txt] \ + ]] + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_tailbase_exclude_paths_match_returned_paths {Exclude paths match tailbase-relative returned paths} \ + -setup $treefilenames_tree -body { + set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_tree_root -exclude-paths {aside aside/** src/vfs/**} *.txt]] + set expected [lsort [list \ + [file join alpha subdir deep deep.txt] \ + [file join alpha subdir direct.txt] \ + [file join b other other.txt] \ + [file join keep keep.txt] \ + [file join src srcroot.txt] \ + [file join src vfs vfs.txt] \ + ]] + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_tailbase_include_paths_match_tree_root_relative_paths {Include paths match tailbase-relative paths below the tree root} \ + -setup $treefilenames_tree -body { + set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_tree_root -include-paths {src/**} *.txt]] + set expected [lsort [list \ + [file join src vfs deep deep.txt] \ + [file join src vfs vfs.txt] \ + ]] + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_tailbase_include_paths_subdir_boundary_before_exact {Tailbase include paths include internal and trailing subdir matches when subtree pattern appears first} \ + -setup $treefilenames_tree -body { + set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_tree_root -include-paths {**/subdir/** **/subdir} *.txt]] + set expected [lsort [list \ + [file join alpha subdir deep deep.txt] \ + [file join alpha subdir direct.txt] \ + ]] + if {$result ne $expected} { + error "result mismatch\nexpected: $expected\nactual: $result" + } + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_tailbase_include_paths_subdir_exact_before_boundary {Tailbase include paths include internal and trailing subdir matches when exact pattern appears first} \ + -setup $treefilenames_tree -body { + set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_tree_root -include-paths {**/subdir **/subdir/**} *.txt]] + set expected [lsort [list \ + [file join alpha subdir deep deep.txt] \ + [file join alpha subdir direct.txt] \ + ]] + if {$result ne $expected} { + error "result mismatch\nexpected: $expected\nactual: $result" + } + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_tailbase_include_paths_match_newbase_relative_paths {Include paths include the tree folder when tailbase is above the search root} \ + -setup $treefilenames_tree -body { + set include_path [file join $tf_tree_tail src **] + set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_newbase -include-paths [list $include_path] *.txt]] + set expected [lsort [list \ + [file join $tf_tree_tail src vfs deep deep.txt] \ + [file join $tf_tree_tail src vfs vfs.txt] \ + ]] + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_tailbase_include_and_exclude_paths_share_relative_base {Include and exclude paths use the same tailbase-relative base} \ + -setup $treefilenames_tree -body { + set result [lsort [punk::path::treefilenames -sort none -directory $tf_tree_root -tailbase $tf_tree_root -include-paths {src/**} -exclude-paths {src/vfs/**} *.txt]] + set expected [list [file join src vfs vfs.txt]] + expr {$result eq $expected} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 + + test treefilenames_rejects_internal_call_flags {Internal recursion state flags are not public API} \ + -setup $treefilenames_tree -body { + set results [list] + foreach arglist { + {-call-depth-internal 1 -directory . *} + {-call-subvector {a b} -directory . *} + {-call-allbelow 1 -directory . *} + } { + lappend results [catch {punk::path::treefilenames {*}$arglist}] + } + expr {$results eq {1 1 1}} + } \ + -cleanup $treefilenames_cleanup \ + -result 1 +} + tcltest::cleanupTests ;#needed to produce test summary. \ No newline at end of file