@ -37,6 +37,156 @@ package require punk::args
package require shellrun
package require punk::tcltestrun
proc runtests_json_string { text } {
set text [ string map [ list \ \ \ \ \ \ \ " \\ \" \n \\ n \r \\ r \t \\ t ] $ t e x t ]
return " \" $ t e x t \" "
}
proc runtests_json_string_array { values } {
set parts [ list ]
foreach value $values {
lappend parts [ runtests_json_string $value ]
}
return " \[ [ j o i n $ p a r t s , ] \] "
}
proc runtests_json_testdict_array { testdicts } {
set parts [ list ]
foreach testdict $testdicts {
set fields [ list ]
foreach { key value} $testdict {
if { [ string is entier - strict $value ] } {
lappend fields " [ r u n t e s t s _ j s o n _ s t r i n g $ k e y ] : $ v a l u e "
} else {
lappend fields " [ r u n t e s t s _ j s o n _ s t r i n g $ k e y ] : [ r u n t e s t s _ j s o n _ s t r i n g $ v a l u e ] "
}
}
lappend parts " { [ j o i n $ f i e l d s , ] } "
}
return " \[ [ j o i n $ p a r t s , ] \] "
}
proc runtests_json_report { status tallydict file_summaries slowest_tests} {
set files_failed [ dict get $tallydict files_with_fails]
set files_warned [ dict get $tallydict files_with_warnings]
set fields [ list ]
lappend fields " \" r u n n e r \" : \" s r c / t e s t s / r u n t e s t s . t c l \" "
lappend fields " \" s t a t u s \" : [ r u n t e s t s _ j s o n _ s t r i n g $ s t a t u s ] "
lappend fields " \" t o t a l \" : [ d i c t g e t $ t a l l y d i c t t o t a l ] "
lappend fields " \" p a s s e d \" : [ d i c t g e t $ t a l l y d i c t p a s s e d ] "
lappend fields " \" s k i p p e d \" : [ d i c t g e t $ t a l l y d i c t s k i p p e d ] "
lappend fields " \" f a i l e d \" : [ d i c t g e t $ t a l l y d i c t f a i l e d ] "
lappend fields " \" w a r n i n g s \" : [ l l e n g t h $ f i l e s _ w a r n e d ] "
lappend fields " \" f i l e s \" : { \" t o t a l \" : [ l l e n g t h $ f i l e _ s u m m a r i e s ] , \" f a i l e d \" : [ r u n t e s t s _ j s o n _ s t r i n g _ a r r a y $ f i l e s _ f a i l e d ] , \" w a r n i n g s \" : [ r u n t e s t s _ j s o n _ s t r i n g _ a r r a y $ f i l e s _ w a r n e d ] } "
set file_parts [ list ]
foreach file_summary $file_summaries {
set file_fields [ list ]
lappend file_fields " \" p a t h \" : [ r u n t e s t s _ j s o n _ s t r i n g [ d i c t g e t $ f i l e _ s u m m a r y p a t h ] ] "
lappend file_fields " \" s t a t u s \" : [ r u n t e s t s _ j s o n _ s t r i n g [ d i c t g e t $ f i l e _ s u m m a r y s t a t u s ] ] "
lappend file_fields " \" t o t a l \" : [ d i c t g e t $ f i l e _ s u m m a r y t o t a l ] "
lappend file_fields " \" p a s s e d \" : [ d i c t g e t $ f i l e _ s u m m a r y p a s s e d ] "
lappend file_fields " \" s k i p p e d \" : [ d i c t g e t $ f i l e _ s u m m a r y s k i p p e d ] "
lappend file_fields " \" f a i l e d \" : [ d i c t g e t $ f i l e _ s u m m a r y f a i l e d ] "
lappend file_fields " \" s u m m a r y l i n e _ d e t e c t e d \" : [ d i c t g e t $ f i l e _ s u m m a r y s u m m a r y l i n e _ d e t e c t e d ] "
lappend file_fields " \" f a i l u r e s \" : [ r u n t e s t s _ j s o n _ t e s t d i c t _ a r r a y [ d i c t g e t $ f i l e _ s u m m a r y f a i l u r e s ] ] "
lappend file_fields " \" s k i p s \" : [ r u n t e s t s _ j s o n _ t e s t d i c t _ a r r a y [ d i c t g e t $ f i l e _ s u m m a r y s k i p s ] ] "
lappend file_parts " { [ j o i n $ f i l e _ f i e l d s , ] } "
}
lappend fields " \" t e s t f i l e s \" : \[ [ j o i n $ f i l e _ p a r t s , ] \] "
lappend fields " \" s l o w e s t \" : [ r u n t e s t s _ j s o n _ t e s t d i c t _ a r r a y $ s l o w e s t _ t e s t s ] "
return " { [ j o i n $ f i e l d s , ] } "
}
proc runtests_status { tallydict } {
if { [ dict get $tallydict failed] > 0 || [ llength [ dict get $tallydict files_with_fails] ] > 0 } {
return fail
}
if { [ llength [ dict get $tallydict files_with_warnings] ] > 0 } {
return warn
}
return pass
}
proc runtests_emit_result_line { status tallydict file_summaries} {
puts stdout " R U N T E S T S _ R E S U L T s t a t u s = $ s t a t u s t o t a l = [ d i c t g e t $ t a l l y d i c t t o t a l ] p a s s e d = [ d i c t g e t $ t a l l y d i c t p a s s e d ] s k i p p e d = [ d i c t g e t $ t a l l y d i c t s k i p p e d ] f a i l e d = [ d i c t g e t $ t a l l y d i c t f a i l e d ] w a r n i n g s = [ l l e n g t h [ d i c t g e t $ t a l l y d i c t f i l e s _ w i t h _ w a r n i n g s ] ] f i l e s = [ l l e n g t h $ f i l e _ s u m m a r i e s ] f i l e s _ f a i l e d = [ l l e n g t h [ d i c t g e t $ t a l l y d i c t f i l e s _ w i t h _ f a i l s ] ] "
}
proc runtests_failure_summaries { resultdict } {
set failures [ list ]
dict for { testname testdict} [ dict get $resultdict testcase_fails] {
set failure [ dict create name $testname status [ dict get $testdict test_status] ]
if { [ dict exists $testdict errorcode] } {
dict set failure errorcode [ dict get $testdict errorcode]
}
lappend failures $failure
}
return $failures
}
proc runtests_skip_summaries { resultdict } {
set skips [ list ]
dict for { testname testdict} [ dict get $resultdict testcase_constraintskips] {
lappend skips [ dict create name $testname reason [ dict get $testdict reason] ]
}
return $skips
}
proc runtests_pass_summaries { testfile_relative resultdict} {
set passes [ list ]
dict for { testname testdict} [ dict get $resultdict testcase_passes] {
set pass [ dict create file $testfile_relative name $testname ]
if { [ dict exists $testdict microseconds] } {
dict set pass microseconds [ dict get $testdict microseconds]
}
lappend passes $pass
}
return $passes
}
proc runtests_print_failure_details { testfile_relative resultdict} {
dict for { testname testdict} [ dict get $resultdict testcase_fails] {
puts stdout " - t e s t n a m e : $ t e s t n a m e "
puts stdout " f i l e : $ t e s t f i l e _ r e l a t i v e "
puts stdout " t e s t _ d e s c r i p t i o n : [ d i c t g e t $ t e s t d i c t t e s t _ d e s c r i p t i o n ] "
puts stdout " t e s t _ b o d y : "
puts stdout " ` ` ` t c l "
puts stdout " [ d i c t g e t $ t e s t d i c t t e s t _ b o d y ] "
puts stdout " ` ` ` "
puts stdout " t e s t _ s t a t u s : [ d i c t g e t $ t e s t d i c t t e s t _ s t a t u s ] "
if { [ dict get $testdict test_status] eq " E R R O R " } {
puts stdout " r e t u r n c o d e : [ d i c t g e t $ t e s t d i c t r e t u r n c o d e ] "
puts stdout " e r r o r c o d e : [ d i c t g e t $ t e s t d i c t e r r o r c o d e ] "
}
}
}
proc runtests_compare_microseconds_desc { left right} {
set left_usec 0
set right_usec 0
if { [ dict exists $left microseconds] } {
set left_usec [ dict get $left microseconds]
}
if { [ dict exists $right microseconds] } {
set right_usec [ dict get $right microseconds]
}
if { $left_usec > $right_usec } {
return - 1
}
if { $left_usec < $right_usec } {
return 1
}
return 0
}
proc runtests_slowest_tests { passes count} {
if { $count <= 0 } {
return [ list ]
}
set sorted [ lsort - command runtests_compare_microseconds_desc $passes ]
return [ lrange $sorted 0 [ expr { $count - 1 } ] ]
}
@ -93,6 +243,19 @@ punk::args::define {
Only test files under paths matching these patterns will be included.
For example, to only include test files under < projectdir> / src/ modules/ punk
-include-paths { modules / punk/ ** } "
-report - type string - default markdown - choices { markdown compact json markdown+ json} - help\
" O u t p u t r e p o r t s t y l e . m a r k d o w n p r e s e r v e s t h e d e t a i l e d h u m a n - r e a d a b l e r e p o r t .
compact prints a short Markdown report with failures, warnings, and optional slow tests.
json prints a machine-readable JSON summary.
markdown + json prints the detailed Markdown report plus a fenced JSON summary."
-show-passes - type boolean - default 1 - help\
" I f t r u e , i n c l u d e p e r - t e s t p a s s l i s t s i n m a r k d o w n r e p o r t s . U s e f a l s e f o r s h o r t e r a g e n t - o r i e n t e d o u t p u t . "
-show-timings - type boolean - default 1 - help\
" I f t r u e , i n c l u d e m i c r o s e c o n d t i m i n g s w h e r e a v a i l a b l e . "
-slowest - type int - default 0 - help\
" N u m b e r o f s l o w e s t p a s s i n g t e s t s t o i n c l u d e i n c o m p a c t a n d J S O N - o r i e n t e d s u m m a r i e s . U s e 0 t o s u p p r e s s . "
-strict-exit - type boolean - default 0 - help\
" I f t r u e , e x i t 1 w h e n t e s t s f a i l a n d e x i t 2 w h e n f i l e s p r o d u c e p a r s e r w a r n i n g s . "
@ values - min 0 - max - 1
glob - type string - multiple 1 - optional 1 - help\
" N a m e s o r g l o b p a t t e r n s o f t e s t f i l e s t o r u n .
@ -105,6 +268,7 @@ if {"-h" in $::argv || "--help" in $::argv || "-help" in $::argv} {
exit 0
}
set ts_start [ clock seconds]
# p u t s " a r g v : $ : : a r g v "
set argd [ punk : : args::parse $::argv withid ( script ) : : runtests]
@ -113,6 +277,15 @@ set opt_tcltestoptions [dict get $opts -tcltestoptions]
set default_tcltestoptions { -singleproc 1 - verbose { body pass skip start error line usec} }
set opt_show_raw_output [ dict get $opts - show-raw-output]
set opt_report [ dict get $opts - report]
set opt_show_passes [ dict get $opts - show-passes]
set opt_show_timings [ dict get $opts - show-timings]
set opt_slowest [ dict get $opts - slowest]
set opt_strict_exit [ dict get $opts - strict-exit]
set report_detailed_markdown [ expr { $opt_report in { markdown markdown+ json} } ]
set report_compact [ expr { $opt_report eq " c o m p a c t " } ]
set report_json_only [ expr { $opt_report eq " j s o n " } ]
set report_emit_json [ expr { $opt_report in { json markdown+ json} } ]
set include_paths [ dict get $opts - include-paths]
@ -141,10 +314,16 @@ dict set tcltestoptions -file $file_globs
# p u t s " t c l t e s t o p t i o n s : $ t c l t e s t o p t i o n s "
# p u t s " f i l e _ g l o b s : $ f i l e _ g l o b s "
# p u t s " t e s t _ b a s e : $ t e s t _ b a s e "
set thisexecutable [ info nameofexecutable]
# p u t s " e x e c u t a b l e : $ t h i s e x e c u t a b l e "
if { ! $report_json_only } {
puts " "
puts " # r u n t e s t s . t c l s t a r t e d a t [ c l o c k f o r m a t $ t s _ s t a r t - f o r m a t " % Y - % m - % d % H : % M : % S " ] "
puts " "
puts " t e s t _ b a s e : $ t e s t _ b a s e "
puts " e x e c u t a b l e : $ t h i s e x e c u t a b l e "
puts " "
}
set exclude_files [ list AGENTS.md * .tcl]
@ -154,30 +333,39 @@ dict set tallydict total 0
dict set tallydict passed 0
dict set tallydict skipped 0
dict set tallydict failed 0
dict set tallydict pkgs_with_fails [ list ]
dict set tallydict pkgs_without_fails [ list ]
dict set tallydict pkgs_with_warnings [ list ]
dict set tallydict files_with_fails [ list ]
dict set tallydict files_without_fails [ list ]
dict set tallydict files_with_warnings [ list ]
set file_summaries [ list ]
set all_passes [ list ]
set testfiles [ punk : : path::treefilenames - dir $test_base - tailbase $test_base - exclude-files $exclude_files - include-paths $include_paths $file_globs ]
if { [ dict exists $tcltestoptions - singleproc] && [ dict get $tcltestoptions - singleproc] } {
if { ! $report_json_only } {
puts " R u n n i n g t e s t s i n s i n g l e p r o c e s s m o d e "
}
set singleproc 1
} else {
if { ! $report_json_only } {
puts " R u n n i n g t e s t s i n m u l t i p l e p r o c e s s e s "
}
set singleproc 0
}
puts " - - - - - - - - - - - - - - - "
puts " t e s t a u t o _ p a t h : $ t e s t _ a u t o _ p a t h "
puts " - - - - - - - - - - - - - - - "
puts " t c l t e s t o p t i o n s : $ t c l t e s t o p t i o n s "
if { ! $report_json_only } {
puts " t e s t a u t o _ p a t h : $ t e s t _ a u t o _ p a t h "
puts " t e s t t m l i s t : $ t e s t _ t m l i s t "
puts " t c l t e s t o p t i o n s : $ t c l t e s t o p t i o n s "
}
foreach testfile_relative $testfiles {
set testfile [ file normalize [ file join $test_base $testfile_relative ] ]
if { $report_detailed_markdown } {
puts stdout " "
# p u t s s t d o u t " r u n n i n g t e s t f i l e $ t e s t f i l e "
puts stdout " # # r u n t e s t s $ t e s t f i l e _ r e l a t i v e "
puts stdout " "
}
dict set tcltestoptions - testdir [ file dirname $testfile ]
@ -202,7 +390,9 @@ foreach testfile_relative $testfiles {
# } ] < < < < "
set result [ testinterp eval [ list shellrun::runx - tcl source $testfile ] ]
interp delete testinterp
if { $report_detailed_markdown } {
puts stdout " s o u r c e d $ t e s t f i l e "
}
flush stdout
} else {
# t o d o - w e n e e d t o s e t u p t h e e n v i r o n m e n t f o r t h e t e s t f i l e - s u c h a s t h e m o d u l e s e a r c h p a t h t o e n s u r e i t p i c k s u p t h e u n b u i l t m o d u l e s u n d e r d e v e l o p m e n t r a t h e r t h a n a n y i n s t a l l e d v e r s i o n s .
@ -216,8 +406,10 @@ foreach testfile_relative $testfiles {
puts stderr " e r r o r c a l l i n g ' r u n o u t $ t h i s e x e c u t a b l e $ t e s t f i l e ' $ e r r M " ; flush stderr
set result { none " " }
}
if { $report_detailed_markdown } {
puts stdout " e x e c u t e d $ t h i s e x e c u t a b l e $ t e s t f i l e "
puts stdout " r e s u l t k e y s : [ d i c t k e y s $ r e s u l t ] "
}
flush stdout
}
@ -240,15 +432,19 @@ foreach testfile_relative $testfiles {
if { [ dict exists $result error] } {
puts stderr " e r r o r r u n n i n g t e s t f i l e $ t e s t f i l e : [ d i c t g e t $ r e s u l t e r r o r ] "
flush stderr
dict lappend tallydict pkgs_with_fails $testfile
dict lappend tallydict files_with_fails $testfile_relative
lappend file_summaries [ dict create path $testfile_relative status error total 0 passed 0 skipped 0 failed 1 summaryline_detected 0 failures [ list [ dict create name $testfile_relative status ERROR errorcode [ dict get $result error] ] ] skips [ list ] ]
} else {
set resultdict [ punk : : tcltestrun::parse_testrun $result $testfile ]
set file_status pass
if { [ dict get $resultdict summaryline_detected] == 0 } {
puts stderr " W A R N I N G : N o r e s u l t s p a r s e d f r o m t e s t f i l e $ t e s t f i l e - t e s t f i l e m a y b e m i s s i n g t c l t e s t : : c l e a n u p T e s t s c a l l "
dict lappend tallydict pkgs_with_warnings $testfile
dict lappend tallydict files_with_warnings $testfile_relative
set file_status warning
} elseif { [ dict get $resultdict summaryline_detected] > 1 } {
puts stderr " W A R N I N G : M o r e t h a n o n e s u m m a r y l i n e d e t e c t e d i n t e s t f i l e $ t e s t f i l e - t e s t f i l e m a y b e c a l l i n g t c l t e s t : : c l e a n u p T e s t s m u l t i p l e t i m e s "
dict lappend tallydict pkgs_with_warnings $testfile
dict lappend tallydict files_with_warnings $testfile_relative
set file_status warning
} else {
dict incr tallydict total [ dict get $resultdict summaryline_total]
dict incr tallydict passed [ dict get $resultdict summaryline_passed]
@ -256,85 +452,176 @@ foreach testfile_relative $testfiles {
dict incr tallydict failed [ dict get $resultdict summaryline_failed]
# p u t s s t d o u t [ d i c t g e t $ r e s u l t d i c t o u t ]
if { [ dict get $resultdict summaryline_failed] > 0 } {
puts stdout " t e s t f i l e $ t e s t f i l e h a d f a i l u r e s "
dict lappend tallydict pkgs_with_fails $testfile
if { ! $report_json_only } {
puts stdout " t e s t f i l e $ t e s t f i l e _ r e l a t i v e h a d f a i l u r e s "
}
dict lappend tallydict files_with_fails $testfile_relative
set file_status fail
} else {
puts stdout " t e s t f i l e $ t e s t f i l e p a s s e d "
dict lappend tallydict pkgs_without_fails $testfile
if { $report_detailed_markdown } {
puts stdout " t e s t f i l e $ t e s t f i l e _ r e l a t i v e p a s s e d "
}
dict lappend tallydict files_without_fails $testfile_relative
}
}
set failures [ runtests_failure_summaries $resultdict ]
set skips [ runtests_skip_summaries $resultdict ]
set passes [ runtests_pass_summaries $testfile_relative $resultdict ]
set all_passes [ concat $all_passes $passes ]
lappend file_summaries [ dict create { * } {
} path $testfile_relative { * } {
} status $file_status { * } {
} total [ dict get $resultdict summaryline_total] { * } {
} passed [ dict get $resultdict summaryline_passed] { * } {
} skipped [ dict get $resultdict summaryline_skipped] { * } {
} failed [ dict get $resultdict summaryline_failed] { * } {
} summaryline_detected [ dict get $resultdict summaryline_detected] { * } {
} failures $failures { * } {
} skips $skips { * } {
}
]
# p u t s s t d o u t " r e s u l t d i c t : $ r e s u l t d i c t "
if { $report_detailed_markdown } {
if { $opt_show_passes } {
puts stdout " "
puts stdout " # # # t e s t c a s e p a s s e s f o r $ t e s t f i l e "
puts stdout " # # # t e s t c a s e p a s s e s f o r $ t e s t f i l e _ r e l a t i v e "
puts stdout " "
dict for { testname testdict} [ dict get $resultdict testcase_passes] {
puts stdout " - t e s t n a m e : $ t e s t n a m e "
if { [ dict exists $testdict microseconds] } {
if { $opt_show_timings && [ dict exists $testdict microseconds] } {
puts stdout " m i c r o s e c o n d s : [ d i c t g e t $ t e s t d i c t m i c r o s e c o n d s ] "
}
}
puts stdout " "
}
puts stdout " # # # t e s t c a s e f a i l u r e s f o r $ t e s t f i l e "
puts stdout " # # # t e s t c a s e f a i l u r e s f o r $ t e s t f i l e _ r e l a t i v e "
puts stdout " "
dict for { testname testdict} [ dict get $resultdict testcase_fails] {
puts stdout " - t e s t n a m e : $ t e s t n a m e "
puts stdout " t e s t _ d e s c r i p t i o n : [ d i c t g e t $ t e s t d i c t t e s t _ d e s c r i p t i o n ] "
puts stdout " t e s t _ b o d y : "
puts stdout " ` ` ` t c l "
puts stdout " [ d i c t g e t $ t e s t d i c t t e s t _ b o d y ] "
puts stdout " ` ` ` "
puts stdout " t e s t _ s t a t u s : [ d i c t g e t $ t e s t d i c t t e s t _ s t a t u s ] "
if { [ dict get $testdict test_status] eq " E R R O R " } {
puts stdout " r e t u r n c o d e : [ d i c t g e t $ t e s t d i c t r e t u r n c o d e ] "
puts stdout " e r r o r c o d e : [ d i c t g e t $ t e s t d i c t e r r o r c o d e ] "
}
}
runtests_print_failure_details $testfile_relative $resultdict
puts stdout " "
puts stdout " # # # t e s t c a s e _ c o n s t r a i n t s k i p s "
puts stdout " # # # t e s t c a s e _ c o n s t r a i n t s k i p s f o r $ t e s t f i l e _ r e l a t i v e "
puts stdout " "
dict for { testname testdict} [ dict get $resultdict testcase_constraintskips] {
puts stdout " - t e s t n a m e : $ t e s t n a m e "
puts stdout " s k i p r e a s o n : [ d i c t g e t $ t e s t d i c t r e a s o n ] "
}
puts - nonewline stdout " \n "
}
flush stdout
}
set i 0
}
puts stdout " # # r u n t e s t s s u m m a r y "
puts stdout " "
set status [ runtests_status $tallydict ]
set slowest_tests [ runtests_slowest_tests $all_passes $opt_slowest ]
puts stdout " # # # t e s t f i l e s w i t h o u t f a i l u r e s "
puts stdout " "
foreach pkg [ dict get $tallydict pkgs_without_fails] {
puts stdout " - $ p k g "
if { ! $report_json_only } {
puts stdout " # # r u n t e s t s s u m m a r y "
puts stdout " "
runtests_emit_result_line $status $tallydict $file_summaries
puts stdout " "
}
puts stdout " "
puts stdout " # # # t e s t f i l e s w i t h f a i l u r e s "
puts stdout " "
foreach pkg [ dict get $tallydict pkgs_with_fails] {
puts stdout " - $ p k g "
if { $report_compact } {
puts stdout " # # # t e s t f i l e s "
puts stdout " "
foreach file_summary $file_summaries {
set line " - [ d i c t g e t $ f i l e _ s u m m a r y s t a t u s ] ` [ d i c t g e t $ f i l e _ s u m m a r y p a t h ] ` t o t a l = [ d i c t g e t $ f i l e _ s u m m a r y t o t a l ] p a s s e d = [ d i c t g e t $ f i l e _ s u m m a r y p a s s e d ] s k i p p e d = [ d i c t g e t $ f i l e _ s u m m a r y s k i p p e d ] f a i l e d = [ d i c t g e t $ f i l e _ s u m m a r y f a i l e d ] "
puts stdout $line
}
puts stdout " "
puts stdout " # # # f a i l u r e s "
puts stdout " "
foreach file_summary $file_summaries {
foreach failure [ dict get $file_summary failures] {
set line " - ` [ d i c t g e t $ f i l e _ s u m m a r y p a t h ] ` [ d i c t g e t $ f a i l u r e n a m e ] s t a t u s = [ d i c t g e t $ f a i l u r e s t a t u s ] "
if { [ dict exists $failure errorcode] && [ dict get $failure errorcode] ne " " } {
append line " e r r o r c o d e = [ d i c t g e t $ f a i l u r e e r r o r c o d e ] "
}
puts stdout $line
}
}
puts stdout " "
puts stdout " # # # w a r n i n g s "
puts stdout " "
foreach f [ dict get $tallydict files_with_warnings] {
puts stdout " - $ f "
}
puts stdout " "
if { $opt_slowest > 0 } {
puts stdout " # # # s l o w e s t p a s s i n g t e s t s "
puts stdout " "
foreach testdict $slowest_tests {
set line " - ` [ d i c t g e t $ t e s t d i c t f i l e ] ` [ d i c t g e t $ t e s t d i c t n a m e ] "
if { $opt_show_timings && [ dict exists $testdict microseconds] } {
append line " m i c r o s e c o n d s = [ d i c t g e t $ t e s t d i c t m i c r o s e c o n d s ] "
}
puts stdout $line
}
puts stdout " "
}
}
puts stdout " "
puts stdout " # # # t e s t f i l e s w i t h w a r n i n g s "
puts stdout " "
foreach pkg [ dict get $tallydict pkgs_with_warnings] {
puts stdout " - $ p k g "
if { $report_detailed_markdown } {
puts stdout " # # # t e s t f i l e s w i t h o u t f a i l u r e s "
puts stdout " "
foreach f [ dict get $tallydict files_without_fails] {
puts stdout " - $ f "
}
puts stdout " "
puts stdout " # # # t e s t f i l e s w i t h f a i l u r e s "
puts stdout " "
foreach f [ dict get $tallydict files_with_fails] {
puts stdout " - $ f "
}
puts stdout " "
puts stdout " # # # t e s t f i l e s w i t h w a r n i n g s "
puts stdout " "
foreach f [ dict get $tallydict files_with_warnings] {
puts stdout " - $ f "
}
puts stdout " "
}
puts stdout " "
puts stdout " # # # r u n t e s t s t a l l y "
puts stdout " "
puts stdout " T o t a l [ d i c t g e t $ t a l l y d i c t t o t a l ] P a s s e d [ d i c t g e t $ t a l l y d i c t p a s s e d ] S k i p p e d [ d i c t g e t $ t a l l y d i c t s k i p p e d ] F a i l e d [ d i c t g e t $ t a l l y d i c t f a i l e d ] "
puts stdout " "
puts stdout " r u n t e s t s D O N E "
if { ! $report_json_only } {
puts stdout " # # # r u n t e s t s t a l l y "
puts stdout " "
puts stdout " T o t a l [ d i c t g e t $ t a l l y d i c t t o t a l ] P a s s e d [ d i c t g e t $ t a l l y d i c t p a s s e d ] S k i p p e d [ d i c t g e t $ t a l l y d i c t s k i p p e d ] F a i l e d [ d i c t g e t $ t a l l y d i c t f a i l e d ] "
puts stdout " "
}
if { $report_emit_json } {
if { ! $report_json_only } {
puts stdout " # # # r u n t e s t s j s o n "
puts stdout " "
puts stdout " ` ` ` j s o n "
}
puts stdout [ runtests_json_report $status $tallydict $file_summaries $slowest_tests ]
if { ! $report_json_only } {
puts stdout " ` ` ` "
puts stdout " "
}
}
if { ! $report_json_only } {
puts stdout " # # # r u n t e s t s D O N E a t [ c l o c k f o r m a t [ c l o c k s e c o n d s ] - f o r m a t " % Y - % m - % d % H : % M : % S " ] "
puts stdout " "
}
if { $opt_strict_exit } {
if { [ dict get $tallydict failed] > 0 || [ llength [ dict get $tallydict files_with_fails] ] > 0 } {
exit 1
}
if { [ llength [ dict get $tallydict files_with_warnings] ] > 0 } {
exit 2
}
}
exit 0