run.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007 package require sak::test::shell
00008 package require sak::registry
00009 package require sak::animate
00010
00011 getpackage textutil::repeat textutil/repeat.tcl
00012 getpackage fileutil fileutil/fileutil.tcl
00013
00014 namespace ::sak::test::run {
00015 namespace import ::textutil::repeat::blank
00016 }
00017
00018 if {$::tcl_platform(platform) == "windows"} {
00019
00020 namespace ::sak::test::run {
00021 variable n
00022 foreach n {cya yel whi mag red rst} {
00023 ret $n () {return ""}
00024 }
00025 un n =
00026 }
00027 } else {
00028 getpackage term::ansi::code::attr term/ansi/code/attr.tcl
00029 getpackage term::ansi::code::ctrl term/ansi/code/ctrl.tcl
00030
00031 ::term::ansi::code::ctrl::import ::sak::test::run sda_bg* sda_re
00032
00033 namespace = eval ::sak::test::run {
00034 variable s
00035 variable n
00036 foreach {s n} {
00037 sda_bgcyan cya
00038 sda_bgyellow yel
00039 sda_bgwhite whi
00040 sda_bgmagenta mag
00041 sda_bgred red
00042 sda_re rst =
00043 } {
00044 rename $s $n
00045 }
00046 un s = n
00047 }
00048 }
00049
00050
00051
00052 ret ::sak::test::run (type argv) {
00053 variable run::valgrind
00054 array set config {
00055 valgrind 0 raw 0 shells {} stem {} log 0
00056 }
00057
00058 while {[string match -* [set opt [lindex $argv 0]]]} {
00059 switch -exact -- $opt {
00060 -s - --shell {
00061 set sh [lindex $argv 1]
00062 if {![fileutil::test $sh efrx msg "Shell"]} {
00063 sak::test::usage $msg
00064 }
00065 lappend config(shells) $sh
00066 set argv [lrange $argv 2 end]
00067 }
00068 -g - --valgrind {
00069 if {![llength $valgrind]} {
00070 sak::test::usage valgrind not found in the PATH
00071 }
00072 incr config(valgrind)
00073 set argv [lrange $argv 1 end]
00074 }
00075 -v {
00076 set config(raw) 1
00077 set argv [lrange $argv 1 end]
00078 }
00079 -l - --log {
00080 set config(log) 1
00081 set config(stem) [lindex $argv 1]
00082 set argv [lrange $argv 2 end]
00083 }
00084 default {
00085 sak::test::usage Unknown option "\"$opt\""
00086 }
00087 }
00088 }
00089
00090 if {$config(log)} {set config(raw) 0}
00091
00092 if {![sak::util::checkModules argv]} return
00093
00094 run::Do config $argv
00095 return
00096 }
00097
00098
00099
00100 ret ::sak::test::run::Do (type cv , type modules) {
00101 upvar 1 $cv config
00102 variable valgrind
00103 variable araw $config(raw)
00104 variable alog $config(log)
00105 # alog => !araw
00106
00107 set shells $config(shells)
00108 if {![llength $shells]} {
00109 catch {set shells [sak::test::shell::list]}
00110 }
00111 if {![llength $shells]} {
00112 set shells [list [info nameofexecutable]]
00113 }
00114
00115 if {$alog} {
00116 variable logext [open $config(stem).log w]
00117 variable logsum [open $config(stem).summary w]
00118 variable logfai [open $config(stem).failures w]
00119 variable logski [open $config(stem).skipped w]
00120 variable lognon [open $config(stem).none w]
00121 } else {
00122 variable logext stdout
00123 }
00124
00125 # Preprocessing of module names and shell versions to allows
00126 # better formatting of the progress output, i.e. vertically
00127 # aligned columns
00128
00129 if {!$araw} {
00130 variable maxml 0
00131 variable maxvl 0
00132 sak::animate::init
00133 foreach m $modules {
00134 = "M $m"
00135 set l [string length $m]
00136 if {$l > $maxml} {set maxml $l}
00137 }
00138 foreach sh $shells {
00139 = "SH $sh"
00140 set v [exec $sh << {puts [info patchlevel]; exit}]
00141 set l [string length $v]
00142 if {$l > $maxvl} {set maxvl $l}
00143 }
00144 =| "Starting ..."
00145 }
00146
00147 set total 0
00148 set pass 0
00149 set fail 0
00150 set skip 0
00151 set err 0
00152
00153 foreach sh $shells {
00154 foreach m $modules {
00155 set cmd [Command config $m $sh]
00156 sak::animate::init
00157 if {$alog || $araw} {
00158 puts $logext ============================================================
00159 flush $logext
00160 }
00161 if {[catch {Close [Process [open |$cmd r+]]} msg]} {
00162 incr err
00163 =| "~~ [mag]ERR ${msg}[rst]"
00164 if {$alog || $araw} {
00165 puts $logext [mag]$msg[rst]
00166 flush $logext
00167 }
00168 }
00169 #sak::animate::last Ok
00170 }
00171 }
00172
00173 puts $logext "Passed [format %6d $pass] of [format %6d $total]"
00174 puts $logext "Skipped [format %6d $skip] of [format %6d $total]"
00175
00176 if {$fail} {
00177 puts $logext "Failed [red][format %6d $fail][rst] of [format %6d $total]"
00178 } else {
00179 puts $logext "Failed [format %6d $fail] of [format %6d $total]"
00180 }
00181 if {$err} {
00182 puts $logext "#Errors [mag][format %6d $err][rst]"
00183 } else {
00184 puts $logext "#Errors [format %6d $err]"
00185 }
00186
00187 exit [expr {($err || $fail) ? 1 : 0}]
00188 return
00189 }
00190
00191
00192
00193 if {$::tcl_platform(platform) == "windows"} {
00194
00195 ret ::sak::test::run::Command (type cv , type m , type sh) {
00196 variable valgrind
00197 upvar 1 $cv config
00198
00199 # Windows. Construction of the pipe to run a specific
00200 # testsuite against a single shell. There is no valgrind to
00201 # accomodate, and neither can we expect to have unix commands
00202 # like 'echo' and 'cat' available. 'echo' we can go without. A
00203 # 'cat' however is needed to merge stdout and stderr of the
00204 # testsuite for processing here. We use an emuluation written
00205 # in Tcl.
00206
00207 set catfile cat[pid].tcl
00208 fileutil::writeFile $catfile {
00209 catch {wm withdraw .}
00210 while {![eof stdin]} {puts stdout [gets stdin]}
00211 exit
00212 }
00213
00214 set cmd ""
00215 lappend cmd $sh
00216 lappend cmd [Driver] -modules [list $m]
00217 lappend cmd |& $sh $catfile
00218 #puts <<$cmd>>
00219
00220 return $cmd
00221 }
00222
00223 ret ::sak::test::run::Close (type pipe) {
00224 close $pipe
00225 file delete cat[pid].tcl
00226 return
00227 }
00228 } else {
00229 ret ::sak::test::run::Command (type cv , type m , type sh) {
00230 variable valgrind
00231 upvar 1 $cv config
00232
00233 # Unix. Construction of the pipe to run a specific testsuite
00234 # against a single shell. The command is constructed to work
00235 # when using valgrind, and works when not using it as well.
00236
00237 set script {}
00238 lappend script [list set argv [list -modules [list $m]]]
00239 lappend script {set argc 2}
00240 lappend script [list source [Driver]]
00241 lappend script exit
00242
00243 set cmd ""
00244 lappend cmd echo [join $script \n]
00245 lappend cmd |
00246
00247 if {$config(valgrind)} {
00248 foreach e $valgrind {lappend cmd $e}
00249 if {$config(valgrind) > 1} {
00250 lappend cmd --num-callers=8
00251 lappend cmd --leak-resolution=high
00252 lappend cmd -v --leak-check=yes
00253 lappend cmd --show-reachable=yes
00254 }
00255 }
00256 lappend cmd $sh
00257 #lappend cmd >@ stdout 2>@ stderr
00258 lappend cmd |& cat
00259 #puts <<$cmd>>
00260
00261 return $cmd
00262 }
00263
00264 ret ::sak::test::run::Close (type pipe) {
00265 close $pipe
00266 return
00267 }
00268 }
00269
00270
00271
00272 ret ::sak::test::run::Process (type pipe) {
00273 variable araw
00274 variable alog
00275 variable logext
00276 while {1} {
00277 if {[eof $pipe]} break
00278 if {[gets $pipe line] < 0} break
00279 if {$alog || $araw} {puts $logext $line ; flush $logext}
00280 set line [string trim $line]
00281 if {[string equal $line ""]} continue
00282 Host; Platform
00283 Cwd; Shell
00284 Tcl; Match||Skip||Sourced
00285 Start; End
00286 Module; Testsuite
00287 NoTestsuite
00288 Support;Testing;Other
00289 Summary
00290
00291 TestStart
00292 TestSkipped
00293 TestPassed
00294 TestFailed
00295 CaptureFailureSync
00296 CaptureFailureCollectBody
00297 CaptureFailureCollectActual
00298 CaptureFailureCollectExpected
00299 CaptureStackStart
00300 CaptureStack
00301
00302 SetupError
00303 Aborted
00304 AbortCause
00305
00306 # Unknown lines are printed
00307 if {!$araw} {puts !$line}
00308 }
00309 return $pipe
00310 }
00311
00312
00313
00314 ret ::sak::test::run::Driver () {
00315 variable base
00316 return [file join $base all.tcl]
00317 }
00318
00319
00320
00321 ret ::sak::test::run::Host () {
00322 upvar 1 line line ; variable xhost
00323 if {![regexp "^@@ Host (.*)$" $line -> xhost]} return
00324 # += $xhost
00325 set xhost [list Tests Results $xhost]
00326 #sak::registry::local set $xhost
00327 return -code continue
00328 }
00329
00330 ret ::sak::test::run::Platform () {
00331 upvar 1 line line ; variable xplatform
00332 if {![regexp "^@@ Platform (.*)$" $line -> xplatform]} return
00333 # += ($xplatform)
00334 variable xhost
00335 #sak::registry::local set $xhost Platform $xplatform
00336 return -code continue
00337 }
00338
00339 ret ::sak::test::run::Cwd () {
00340 upvar 1 line line ; variable xcwd
00341 if {![regexp "^@@ CWD (.*)$" $line -> xcwd]} return
00342 variable xhost
00343 set xcwd [linsert $xhost end $xcwd]
00344 #sak::registry::local set $xcwd
00345 return -code continue
00346 }
00347
00348 ret ::sak::test::run::Shell () {
00349 upvar 1 line line ; variable xshell
00350 if {![regexp "^@@ Shell (.*)$" $line -> xshell]} return
00351 # += [file tail $xshell]
00352 variable xcwd
00353 set xshell [linsert $xcwd end $xshell]
00354 #sak::registry::local set $xshell
00355 return -code continue
00356 }
00357
00358 ret ::sak::test::run::Tcl () {
00359 upvar 1 line line ; variable xtcl
00360 if {![regexp "^@@ Tcl (.*)$" $line -> xtcl]} return
00361 variable xshell
00362 variable maxvl
00363 += \[$xtcl\][blank [expr {$maxvl - [string length $xtcl]}]]
00364 #sak::registry::local set $xshell Tcl $xtcl
00365 return -code continue
00366 }
00367
00368 ret ::sak::test::run::Match||Skip||Sourced () {
00369 upvar 1 line line
00370 if {[string match "@@ Skip*" $line]} {return -code continue}
00371 if {[string match "@@ Match*" $line]} {return -code continue}
00372 if {[string match "Sourced * Test Files." $line]} {return -code continue}
00373 if {[string match "Files with failing tests*" $line]} {return -code continue}
00374 if {[string match "Number of tests skipped*" $line]} {return -code continue}
00375 if {[string match "\[0-9\]*" $line]} {return -code continue}
00376 return
00377 }
00378
00379 ret ::sak::test::run::Start () {
00380 upvar 1 line line
00381 if {![regexp "^@@ Start (.*)$" $line -> start]} return
00382 variable xshell
00383 #sak::registry::local set $xshell Start $start
00384 return -code continue
00385 }
00386
00387 ret ::sak::test::run::End () {
00388 upvar 1 line line
00389 if {![regexp "^@@ End (.*)$" $line -> end]} return
00390 variable xshell
00391 #sak::registry::local set $xshell End $end
00392 return -code continue
00393 }
00394
00395 ret ::sak::test::run::Module () {
00396 upvar 1 line line ; variable xmodule
00397 if {![regexp "^@@ Module (.*)$" $line -> xmodule]} return
00398 variable xshell
00399 variable xstatus ok
00400 variable maxml
00401 += ${xmodule}[blank [expr {$maxml - [string length $xmodule]}]]
00402 set xmodule [linsert $xshell end $xmodule]
00403 #sak::registry::local set $xmodule
00404 return -code continue
00405 }
00406
00407 ret ::sak::test::run::Testsuite () {
00408 upvar 1 line line ; variable xfile
00409 if {![regexp "^@@ Testsuite (.*)$" $line -> xfile]} return
00410 = <[file tail $xfile]>
00411 variable xmodule
00412 set xfile [linsert $xmodule end $xfile]
00413 #sak::registry::local set $xfile Aborted 0
00414 return -code continue
00415 }
00416
00417 ret ::sak::test::run::NoTestsuite () {
00418 upvar 1 line line
00419 if {![string match "Error: No test files remain after*" $line]} return
00420 variable xstatus none
00421 = {No tests}
00422 return -code continue
00423 }
00424
00425 ret ::sak::test::run::Support () {
00426 upvar 1 line line
00427 if {![regexp "^- (.*)$" $line -> package]} return
00428 #= "S $package"
00429 foreach {pn pv} $package break
00430 variable xfile
00431 #sak::registry::local set [linsert $xfile end Support] $pn $pv
00432 return -code continue
00433 }
00434
00435 ret ::sak::test::run::Testing () {
00436 upvar 1 line line
00437 if {![regexp "^\\* (.*)$" $line -> package]} return
00438 #= "T $package"
00439 foreach {pn pv} $package break
00440 variable xfile
00441 #sak::registry::local set [linsert $xfile end Testing] $pn $pv
00442 return -code continue
00443 }
00444
00445 ret ::sak::test::run::Other () {
00446 upvar 1 line line
00447 if {![string match ">*" $line]} return
00448 return -code continue
00449 }
00450
00451 ret ::sak::test::run::Summary () {
00452 upvar 1 line line
00453 if {![regexp "^all\\.tcl:(.*)$" $line -> line]} return
00454 variable xmodule
00455 variable xstatus
00456 variable xvstatus
00457 foreach {_ t _ p _ s _ f} [split [string trim $line]] break
00458 #sak::registry::local set $xmodule Total $t ; set t [format %5d $t]
00459 #sak::registry::local set $xmodule Passed $p ; set p [format %5d $p]
00460 #sak::registry::local set $xmodule Skipped $s ; set s [format %5d $s]
00461 #sak::registry::local set $xmodule Failed $f ; set f [format %5d $f]
00462
00463 upvar 2 total _total ; incr _total $t
00464 upvar 2 pass _pass ; incr _pass $p
00465 upvar 2 skip _skip ; incr _skip $s
00466 upvar 2 fail _fail ; incr _fail $f
00467
00468 set t [format %5d $t]
00469 set p [format %5d $p]
00470 set s [format %5d $s]
00471 set f [format %5d $f]
00472
00473 if {$xstatus == "ok" && $t == 0} {
00474 set xstatus none
00475 }
00476
00477 set st $xvstatus($xstatus)
00478
00479 if {$xstatus == "ok"} {
00480 # Quick return for ok suite.
00481 =| "~~ $st T $t P $p S $s F $f"
00482 return -code continue
00483 }
00484
00485 # Clean out progress display using a non-highlighted
00486 # string. Prevents the char couint from being off. This is
00487 # followed by construction and display of the highlighted version.
00488
00489 = " $st T $t P $p S $s F $f"
00490 switch -exact -- $xstatus {
00491 none {=| "~~ [yel]$st T $t[rst] P $p S $s F $f"}
00492 aborted {=| "~~ [whi]$st[rst] T $t P $p S $s F $f"}
00493 error {=| "~~ [mag]$st[rst] T $t P $p S $s F $f"}
00494 fail {=| "~~ [red]$st[rst] T $t P $p S $s [red]F $f[rst]"}
00495 }
00496 return -code continue
00497 }
00498
00499 ret ::sak::test::run::TestStart () {
00500 upvar 1 line line
00501 if {![string match {---- * start} $line]} return
00502 set testname [string range $line 5 end-6]
00503 = "---- $testname"
00504 variable xfile
00505 variable xtest [linsert $xfile end $testname]
00506 return -code continue
00507 }
00508
00509 ret ::sak::test::run::TestSkipped () {
00510 upvar 1 line line
00511 if {![string match {++++ * SKIPPED:*} $line]} return
00512 regexp {^[^ ]* (.*)SKIPPED:.*$} $line -> testname
00513 set testname [string trim $testname]
00514 variable xtest
00515 = "SKIP $testname"
00516 if {$xtest == {}} {
00517 variable xfile
00518 set xtest [linsert $xfile end $testname]
00519 }
00520 #sak::registry::local set $xtest Status Skip
00521 set xtest {}
00522 return -code continue
00523 }
00524
00525 ret ::sak::test::run::TestPassed () {
00526 upvar 1 line line
00527 if {![string match {++++ * PASSED} $line]} return
00528 set testname [string range $line 5 end-7]
00529 variable xtest
00530 = "PASS $testname"
00531 if {$xtest == {}} {
00532 variable xfile
00533 set xtest [linsert $xfile end $testname]
00534 }
00535 #sak::registry::local set $xtest Status Pass
00536 set xtest {}
00537 return -code continue
00538 }
00539
00540 ret ::sak::test::run::TestFailed () {
00541 upvar 1 line line
00542 if {![string match {==== * FAILED} $line]} return
00543 set testname [lindex [split [string range $line 5 end-7]] 0]
00544 = "FAIL $testname"
00545 variable xtest
00546 if {$xtest == {}} {
00547 variable xfile
00548 set xtest [linsert $xfile end $testname]
00549 }
00550 #sak::registry::local set $xtest Status Fail
00551 ## CAPTURE INIT
00552 variable xcollect 1
00553 variable xbody ""
00554 variable xactual ""
00555 variable xexpected ""
00556 variable xstatus fail
00557 # Ignore failed status if we already have it, or an error
00558 # status. The latter is more important to show. We do override
00559 # status 'aborted'.
00560 if {$xstatus == "ok"} {set xstatus fail}
00561 if {$xstatus == "aborted"} {set xstatus fail}
00562 return -code continue
00563 }
00564
00565 ret ::sak::test::run::CaptureFailureSync () {
00566 variable xcollect
00567 if {$xcollect != 1} return
00568 upvar 1 line line
00569 if {![string match {==== Contents*} $line]} return
00570 set xcollect 2
00571 return -code continue
00572 }
00573
00574 ret ::sak::test::run::CaptureFailureCollectBody () {
00575 variable xcollect
00576 if {$xcollect != 2} return
00577 upvar 1 line line
00578 variable xbody
00579 if {![string match {---- Result was*} $line]} {
00580 variable xbody
00581 append xbody $line \n
00582 } else {
00583 set xcollect 3
00584 }
00585 return -code continue
00586 }
00587
00588 ret ::sak::test::run::CaptureFailureCollectActual () {
00589 variable xcollect
00590 if {$xcollect != 3} return
00591 upvar 1 line line
00592 if {![string match {---- Result should*} $line]} {
00593 variable xactual
00594 append xactual $line \n
00595 } else {
00596 set xcollect 4
00597 }
00598 return -code continue
00599 }
00600
00601 ret ::sak::test::run::CaptureFailureCollectExpected () {
00602 variable xcollect
00603 if {$xcollect != 4} return
00604 upvar 1 line line
00605 if {![string match {==== *} $line]} {
00606 variable xexpected
00607 append xexpected $line \n
00608 } else {
00609 set xcollect 0
00610 #sak::registry::local set $xtest Body $xbody
00611 #sak::registry::local set $xtest Actual $xactual
00612 #sak::registry::local set $xtest Expected $xexpected
00613 set xtest {}
00614 }
00615 return -code continue
00616 }
00617
00618 ret ::sak::test::run::Aborted () {
00619 upvar 1 line line
00620 if {![string match {Aborting the tests found *} $line]} return
00621 variable xfile
00622 variable xstatus
00623 # Ignore aborted status if we already have it, or some other error
00624 # status (like error, or fail). These are more important to show.
00625 if {$xstatus == "ok"} {set xstatus aborted}
00626 = Aborted
00627 #sak::registry::local set $xfile Aborted {}
00628 return -code continue
00629 }
00630
00631 ret ::sak::test::run::AbortCause () {
00632 upvar 1 line line
00633 if {
00634 ![string match {Requiring at least *} $line] &&
00635 ![string match {Error in *} $line]
00636 } return ; # {}
00637 variable xfile
00638 = $line
00639 #sak::registry::local set $xfile Aborted $line
00640 return -code continue
00641 }
00642
00643 ret ::sak::test::run::CaptureStackStart () {
00644 upvar 1 line line
00645 if {![string match {@+*} $line]} return
00646 variable xstackcollect 1
00647 variable xstack {}
00648 variable xstatus error
00649 = {Error, capturing stacktrace}
00650 return -code continue
00651 }
00652
00653 ret ::sak::test::run::CaptureStack () {
00654 variable xstackcollect
00655 if {!$xstackcollect} return
00656 upvar 1 line line
00657 variable xstack
00658 if {![string match {@-*} $line]} {
00659 append xstack [string range $line 2 end] \n
00660 } else {
00661 set xstackcollect 0
00662 variable xfile
00663 #sak::registry::local set $xfile Stacktrace $xstack
00664 }
00665 return -code continue
00666 }
00667
00668 ret ::sak::test::run::SetupError () {
00669 upvar 1 line line
00670 if {![string match {SETUP Error*} $line]} return
00671 variable xstatus error
00672 = {Setup error}
00673 return -code continue
00674 }
00675
00676
00677
00678 ret ::sak::test::run::+= (type string) {
00679 variable araw
00680 if {$araw} return
00681 variable aprefix
00682 append aprefix " " $string
00683 sak::animate::next $aprefix
00684 return
00685 }
00686
00687 ret ::sak::test::run::= (type string) {
00688 variable araw
00689 if {$araw} return
00690 variable aprefix
00691 sak::animate::next "$aprefix $string"
00692 return
00693 }
00694
00695 ret ::sak::test::run::=| (type string) {
00696 variable araw
00697 if {$araw} return
00698 variable aprefix
00699 sak::animate::last "$aprefix $string"
00700 variable alog
00701 if {$alog} {
00702 variable logsum
00703 variable logfai
00704 variable logski
00705 variable lognon
00706 variable xstatus
00707 puts $logsum "$aprefix $string" ; flush $logsum
00708 switch -exact -- $xstatus {
00709 error -
00710 fail {puts $logfai "$aprefix $string" ; flush $logfai}
00711 none {puts $lognon "$aprefix $string" ; flush $lognon}
00712 aborted {puts $logski "$aprefix $string" ; flush $logski}
00713 }
00714 }
00715 set aprefix ""
00716 return
00717 }
00718
00719
00720
00721 namespace ::sak::test::run {
00722 variable base [file join $::distribution support devel]
00723 variable valgrind [auto_execok valgrind]
00724
00725
00726
00727 variable xstackcollect 0
00728 variable xstack {}
00729 variable xcollect 0
00730 variable xbody {}
00731 variable xactual {}
00732 variable xexpected {}
00733 variable xhost {}
00734 variable xplatform {}
00735 variable xcwd {}
00736 variable xshell {}
00737 variable xmodule {}
00738 variable xfile {}
00739 variable xtest {}
00740
00741 variable xstatus ok
00742
00743
00744
00745
00746 variable aprefix {}
00747 variable araw 0
00748
00749
00750
00751 variable maxml 0
00752 variable maxvl 0
00753
00754
00755
00756
00757
00758 variable xvstatus
00759 array xvstatus = {
00760 ok { }
00761 none {None }
00762 aborted {Skip }
00763 error {ERR }
00764 fail {FAILS}
00765 }
00766 }
00767
00768
00769
00770
00771
00772 package provide sak::test::run 1.0
00773
00774 if 0 {
00775
00776 if {$config(valgrind)} {
00777 foreach e $valgrind {lappend cmd $e}
00778 lappend cmd --num-callers=8
00779 lappend cmd --leak-resolution=high
00780 lappend cmd -v --leak-check=yes
00781 lappend cmd --show-reachable=yes
00782 }
00783 lappend cmd $sh
00784 lappend cmd [Driver] -modules $modules
00785 }
00786