JNI wrapper1: when checking for an out-of-bounds statement column index, perform...
[sqlite.git] / test / testrunner.tcl
blob025f2ecdb4b8100167896413e58b93c79f3f8023
2 set dir [pwd]
3 set testdir [file normalize [file dirname $argv0]]
4 set saved $argv
5 set argv [list]
6 source [file join $testdir testrunner_data.tcl]
7 source [file join $testdir permutations.test]
8 set argv $saved
9 cd $dir
11 # This script requires an interpreter that supports [package require sqlite3]
12 # to run. If this is not such an intepreter, see if there is a [testfixture]
13 # in the current directory. If so, run the command using it. If not,
14 # recommend that the user build one.
16 proc find_interpreter {} {
17 set interpreter [file tail [info nameofexec]]
18 set rc [catch { package require sqlite3 }]
19 if {$rc} {
20 if { [string match -nocase testfixture* $interpreter]==0
21 && [file executable ./testfixture]
22 } {
23 puts "Failed to find tcl package sqlite3. Restarting with ./testfixture.."
24 set status [catch {
25 exec ./testfixture [info script] {*}$::argv >@ stdout
26 } msg]
27 exit $status
30 if {$rc} {
31 puts stderr "Failed to find tcl package sqlite3"
32 puts stderr "Run \"make testfixture\" and then try again..."
33 exit 1
36 find_interpreter
38 # Usually this script is run by [testfixture]. But it can also be run
39 # by a regular [tclsh]. For these cases, emulate the [clock_milliseconds]
40 # command.
41 if {[info commands clock_milliseconds]==""} {
42 proc clock_milliseconds {} {
43 clock milliseconds
47 #-------------------------------------------------------------------------
48 # Usage:
50 proc usage {} {
51 set a0 [file tail $::argv0]
53 puts stderr [string trim [subst -nocommands {
54 Usage:
55 $a0 ?SWITCHES? ?PERMUTATION? ?PATTERNS?
56 $a0 PERMUTATION FILE
57 $a0 njob ?NJOB?
58 $a0 status
60 where SWITCHES are:
61 --jobs NUMBER-OF-JOBS
62 --zipvfs ZIPVFS-SOURCE-DIR
64 Interesting values for PERMUTATION are:
66 veryquick - a fast subset of the tcl test scripts. This is the default.
67 full - all tcl test scripts.
68 all - all tcl test scripts, plus a subset of test scripts rerun
69 with various permutations.
70 release - full release test with various builds.
72 If no PATTERN arguments are present, all tests specified by the PERMUTATION
73 are run. Otherwise, each pattern is interpreted as a glob pattern. Only
74 those tcl tests for which the final component of the filename matches at
75 least one specified pattern are run.
77 If no PATTERN arguments are present, then various fuzztest, threadtest
78 and other tests are run as part of the "release" permutation. These are
79 omitted if any PATTERN arguments are specified on the command line.
81 If a PERMUTATION is specified and is followed by the path to a Tcl script
82 instead of a list of patterns, then that single Tcl test script is run
83 with the specified permutation.
85 The "status" and "njob" commands are designed to be run from the same
86 directory as a running testrunner.tcl script that is running tests. The
87 "status" command prints a report describing the current state and progress
88 of the tests. The "njob" command may be used to query or modify the number
89 of sub-processes the test script uses to run tests.
90 }]]
92 exit 1
94 #-------------------------------------------------------------------------
96 #-------------------------------------------------------------------------
97 # Try to estimate a the number of processes to use.
99 # Command [guess_number_of_cores] attempts to glean the number of logical
100 # cores. Command [default_njob] returns the default value for the --jobs
101 # switch.
103 proc guess_number_of_cores {} {
104 if {[catch {number_of_cores} ret]} {
105 set ret 4
107 if {$::tcl_platform(platform)=="windows"} {
108 catch { set ret $::env(NUMBER_OF_PROCESSORS) }
109 } else {
110 if {$::tcl_platform(os)=="Darwin"} {
111 set cmd "sysctl -n hw.logicalcpu"
112 } else {
113 set cmd "nproc"
115 catch {
116 set fd [open "|$cmd" r]
117 set ret [gets $fd]
118 close $fd
119 set ret [expr $ret]
123 return $ret
126 proc default_njob {} {
127 set nCore [guess_number_of_cores]
128 if {$nCore<=2} {
129 set nHelper 1
130 } else {
131 set nHelper [expr int($nCore*0.5)]
133 return $nHelper
135 #-------------------------------------------------------------------------
137 #-------------------------------------------------------------------------
138 # Setup various default values in the global TRG() array.
140 set TRG(dbname) [file normalize testrunner.db]
141 set TRG(logname) [file normalize testrunner.log]
142 set TRG(build.logname) [file normalize testrunner_build.log]
143 set TRG(info_script) [file normalize [info script]]
144 set TRG(timeout) 10000 ;# Default busy-timeout for testrunner.db
145 set TRG(nJob) [default_njob] ;# Default number of helper processes
146 set TRG(patternlist) [list]
147 set TRG(cmdline) $argv
148 set TRG(reporttime) 2000
149 set TRG(fuzztest) 0 ;# is the fuzztest option present.
150 set TRG(zipvfs) "" ;# -zipvfs option, if any
152 switch -nocase -glob -- $tcl_platform(os) {
153 *darwin* {
154 set TRG(platform) osx
155 set TRG(make) make.sh
156 set TRG(makecmd) "bash make.sh"
157 set TRG(testfixture) testfixture
158 set TRG(run) run.sh
159 set TRG(runcmd) "bash run.sh"
161 *linux* {
162 set TRG(platform) linux
163 set TRG(make) make.sh
164 set TRG(makecmd) "bash make.sh"
165 set TRG(testfixture) testfixture
166 set TRG(run) run.sh
167 set TRG(runcmd) "bash run.sh"
169 *win* {
170 set TRG(platform) win
171 set TRG(make) make.bat
172 set TRG(makecmd) make.bat
173 set TRG(testfixture) testfixture.exe
174 set TRG(run) run.bat
175 set TRG(runcmd) "run.bat"
177 default {
178 error "cannot determine platform!"
181 #-------------------------------------------------------------------------
183 #-------------------------------------------------------------------------
184 # The database schema used by the testrunner.db database.
186 set TRG(schema) {
187 DROP TABLE IF EXISTS jobs;
188 DROP TABLE IF EXISTS config;
191 ** This table contains one row for each job that testrunner.tcl must run
192 ** before the entire test run is finished.
194 ** jobid:
195 ** Unique identifier for each job. Must be a +ve non-zero number.
197 ** displaytype:
198 ** 3 or 4 letter mnemonic for the class of tests this belongs to e.g.
199 ** "fuzz", "tcl", "make" etc.
201 ** displayname:
202 ** Name/description of job. For display purposes.
204 ** build:
205 ** If the job requires a make.bat/make.sh make wrapper (i.e. to build
206 ** something), the name of the build configuration it uses. See
207 ** testrunner_data.tcl for a list of build configs. e.g. "Win32-MemDebug".
209 ** dirname:
210 ** If the job should use a well-known directory name for its
211 ** sub-directory instead of an anonymous "testdir[1234...]" sub-dir
212 ** that is deleted after the job is finished.
214 ** cmd:
215 ** Bash or batch script to run the job.
217 ** depid:
218 ** The jobid value of a job that this job depends on. This job may not
219 ** be run before its depid job has finished successfully.
221 ** priority:
222 ** Higher values run first. Sometimes.
224 CREATE TABLE jobs(
225 /* Fields populated when db is initialized */
226 jobid INTEGER PRIMARY KEY, -- id to identify job
227 displaytype TEXT NOT NULL, -- Type of test (for one line report)
228 displayname TEXT NOT NULL, -- Human readable job name
229 build TEXT NOT NULL DEFAULT '', -- make.sh/make.bat file request, if any
230 dirname TEXT NOT NULL DEFAULT '', -- directory name, if required
231 cmd TEXT NOT NULL, -- shell command to run
232 depid INTEGER, -- identifier of dependency (or '')
233 priority INTEGER NOT NULL, -- higher priority jobs may run earlier
235 /* Fields updated as jobs run */
236 starttime INTEGER,
237 endtime INTEGER,
238 state TEXT CHECK( state IN ('', 'ready', 'running', 'done', 'failed') ),
239 output TEXT
242 CREATE TABLE config(
243 name TEXT COLLATE nocase PRIMARY KEY,
244 value
245 ) WITHOUT ROWID;
247 CREATE INDEX i1 ON jobs(state, priority);
248 CREATE INDEX i2 ON jobs(depid);
250 #-------------------------------------------------------------------------
252 #--------------------------------------------------------------------------
253 # Check if this script is being invoked to run a single file. If so,
254 # run it.
256 if {[llength $argv]==2
257 && ([lindex $argv 0]=="" || [info exists ::testspec([lindex $argv 0])])
258 && [file exists [lindex $argv 1]]
260 set permutation [lindex $argv 0]
261 set script [file normalize [lindex $argv 1]]
262 set ::argv [list]
264 set testdir [file dirname $argv0]
265 source $::testdir/tester.tcl
267 if {$permutation=="full"} {
269 unset -nocomplain ::G(isquick)
270 reset_db
272 } elseif {$permutation!="default" && $permutation!=""} {
274 if {[info exists ::testspec($permutation)]==0} {
275 error "no such permutation: $permutation"
278 array set O $::testspec($permutation)
279 set ::G(perm:name) $permutation
280 set ::G(perm:prefix) $O(-prefix)
281 set ::G(isquick) 1
282 set ::G(perm:dbconfig) $O(-dbconfig)
283 set ::G(perm:presql) $O(-presql)
285 rename finish_test helper_finish_test
286 proc finish_test {} "
287 uplevel {
288 $O(-shutdown)
290 helper_finish_test
293 eval $O(-initialize)
296 reset_db
297 source $script
298 exit
300 #--------------------------------------------------------------------------
302 #--------------------------------------------------------------------------
303 # Check if this is the "njob" command:
305 if {([llength $argv]==2 || [llength $argv]==1)
306 && [string compare -nocase njob [lindex $argv 0]]==0
308 sqlite3 mydb $TRG(dbname)
309 if {[llength $argv]==2} {
310 set param [lindex $argv 1]
311 if {[string is integer $param]==0 || $param<1 || $param>128} {
312 puts stderr "parameter must be an integer between 1 and 128"
313 exit 1
316 mydb eval { REPLACE INTO config VALUES('njob', $param); }
318 set res [mydb one { SELECT value FROM config WHERE name='njob' }]
319 mydb close
320 puts "$res"
321 exit
323 #--------------------------------------------------------------------------
325 #--------------------------------------------------------------------------
326 # Check if this is the "script" command:
328 if {[string compare -nocase script [lindex $argv 0]]==0} {
329 if {[llength $argv]!=2 && !([llength $argv]==3&&[lindex $argv 1]=="-msvc")} {
330 usage
333 set bMsvc [expr ([llength $argv]==3)]
334 set config [lindex $argv [expr [llength $argv]-1]]
336 puts [trd_buildscript $config [file dirname $testdir] $bMsvc]
337 exit
341 #--------------------------------------------------------------------------
342 # Check if this is the "status" command:
344 if {[llength $argv]==1
345 && [string compare -nocase status [lindex $argv 0]]==0
348 proc display_job {jobdict {tm ""}} {
349 array set job $jobdict
351 set dfname [format %-60s $job(displayname)]
353 set dtm ""
354 if {$tm!=""} { set dtm "\[[expr {$tm-$job(starttime)}]ms\]" }
355 puts " $dfname $dtm"
358 sqlite3 mydb $TRG(dbname)
359 mydb timeout 1000
360 mydb eval BEGIN
362 set cmdline [mydb one { SELECT value FROM config WHERE name='cmdline' }]
363 set nJob [mydb one { SELECT value FROM config WHERE name='njob' }]
365 set now [clock_milliseconds]
366 set tm [mydb one {
367 SELECT
368 COALESCE((SELECT value FROM config WHERE name='end'), $now) -
369 (SELECT value FROM config WHERE name='start')
372 set total 0
373 foreach s {"" ready running done failed} { set S($s) 0 }
374 mydb eval {
375 SELECT state, count(*) AS cnt FROM jobs GROUP BY 1
377 incr S($state) $cnt
378 incr total $cnt
380 set fin [expr $S(done)+$S(failed)]
381 if {$cmdline!=""} {set cmdline " $cmdline"}
383 set f ""
384 if {$S(failed)>0} {
385 set f "$S(failed) FAILED, "
387 puts "Command line: \[testrunner.tcl$cmdline\]"
388 puts "Jobs: $nJob"
389 puts "Summary: ${tm}ms, ($fin/$total) finished, ${f}$S(running) running"
391 set srcdir [file dirname [file dirname $TRG(info_script)]]
392 if {$S(running)>0} {
393 puts "Running: "
394 mydb eval {
395 SELECT * FROM jobs WHERE state='running' ORDER BY starttime
396 } job {
397 display_job [array get job] $now
400 if {$S(failed)>0} {
401 puts "Failures: "
402 mydb eval {
403 SELECT * FROM jobs WHERE state='failed' ORDER BY starttime
404 } job {
405 display_job [array get job]
409 mydb close
410 exit
413 #-------------------------------------------------------------------------
414 # Parse the command line.
416 for {set ii 0} {$ii < [llength $argv]} {incr ii} {
417 set isLast [expr $ii==([llength $argv]-1)]
418 set a [lindex $argv $ii]
419 set n [string length $a]
421 if {[string range $a 0 0]=="-"} {
422 if {($n>2 && [string match "$a*" --jobs]) || $a=="-j"} {
423 incr ii
424 set TRG(nJob) [lindex $argv $ii]
425 if {$isLast} { usage }
426 } elseif {($n>2 && [string match "$a*" --zipvfs]) || $a=="-z"} {
427 incr ii
428 set TRG(zipvfs) [file normalize [lindex $argv $ii]]
429 if {$isLast} { usage }
430 } else {
431 usage
433 } else {
434 lappend TRG(patternlist) [string map {% *} $a]
437 set argv [list]
441 # This script runs individual tests - tcl scripts or [make xyz] commands -
442 # in directories named "testdir$N", where $N is an integer. This variable
443 # contains a list of integers indicating the directories in use.
445 # This variable is accessed only via the following commands:
447 # dirs_nHelper
448 # Return the number of entries currently in the list.
450 # dirs_freeDir IDIR
451 # Remove value IDIR from the list. It is an error if it is not present.
453 # dirs_allocDir
454 # Select a value that is not already in the list. Add it to the list
455 # and return it.
457 set TRG(dirs_in_use) [list]
459 proc dirs_nHelper {} {
460 global TRG
461 llength $TRG(dirs_in_use)
463 proc dirs_freeDir {iDir} {
464 global TRG
465 set out [list]
466 foreach d $TRG(dirs_in_use) {
467 if {$iDir!=$d} { lappend out $d }
469 if {[llength $out]!=[llength $TRG(dirs_in_use)]-1} {
470 error "dirs_freeDir could not find $iDir"
472 set TRG(dirs_in_use) $out
474 proc dirs_allocDir {} {
475 global TRG
476 array set inuse [list]
477 foreach d $TRG(dirs_in_use) {
478 set inuse($d) 1
480 for {set iRet 0} {[info exists inuse($iRet)]} {incr iRet} { }
481 lappend TRG(dirs_in_use) $iRet
482 return $iRet
485 # Check that directory $dir exists. If it does not, create it. If
486 # it does, delete its contents.
488 proc create_or_clear_dir {dir} {
489 set dir [file normalize $dir]
490 catch { file mkdir $dir }
491 foreach f [glob -nocomplain [file join $dir *]] {
492 catch { file delete -force $f }
496 proc build_to_dirname {bname} {
497 set fold [string tolower [string map {- _} $bname]]
498 return "testrunner_build_$fold"
501 #-------------------------------------------------------------------------
503 proc r_write_db {tcl} {
504 trdb eval { BEGIN EXCLUSIVE }
505 uplevel $tcl
506 trdb eval { COMMIT }
509 # Obtain a new job to be run by worker $iJob (an integer). A job is
510 # returned as a three element list:
512 # {$build $config $file}
514 proc r_get_next_job {iJob} {
515 global T
517 if {($iJob%2)} {
518 set orderby "ORDER BY priority ASC"
519 } else {
520 set orderby "ORDER BY priority DESC"
523 set ret [list]
525 r_write_db {
526 set query "
527 SELECT * FROM jobs AS j WHERE state='ready' $orderby LIMIT 1
529 trdb eval $query job {
530 set tm [clock_milliseconds]
531 set T($iJob) $tm
532 set jobid $job(jobid)
534 trdb eval {
535 UPDATE jobs SET starttime=$tm, state='running' WHERE jobid=$jobid
538 set ret [array get job]
542 return $ret
545 #rename r_get_next_job r_get_next_job_r
546 #proc r_get_next_job {iJob} {
547 #puts [time { set res [r_get_next_job_r $iJob] }]
548 #set res
551 # Usage:
553 # add_job OPTION ARG OPTION ARG...
555 # where available OPTIONS are:
557 # -displaytype
558 # -displayname
559 # -build
560 # -dirname
561 # -cmd
562 # -depid
563 # -priority
565 # Returns the jobid value for the new job.
567 proc add_job {args} {
569 set options {
570 -displaytype -displayname -build -dirname
571 -cmd -depid -priority
574 # Set default values of options.
575 set A(-dirname) ""
576 set A(-depid) ""
577 set A(-priority) 0
578 set A(-build) ""
580 array set A $args
582 # Check all required options are present. And that no extras are present.
583 foreach o $options {
584 if {[info exists A($o)]==0} { error "missing required option $o" }
586 foreach o [array names A] {
587 if {[lsearch -exact $options $o]<0} { error "unrecognized option: $o" }
590 set state ""
591 if {$A(-depid)==""} { set state ready }
593 trdb eval {
594 INSERT INTO jobs(
595 displaytype, displayname, build, dirname, cmd, depid, priority,
596 state
597 ) VALUES (
598 $A(-displaytype),
599 $A(-displayname),
600 $A(-build),
601 $A(-dirname),
602 $A(-cmd),
603 $A(-depid),
604 $A(-priority),
605 $state
609 trdb last_insert_rowid
612 proc add_tcl_jobs {build config patternlist} {
613 global TRG
615 set topdir [file dirname $::testdir]
616 set testrunner_tcl [file normalize [info script]]
618 if {$build==""} {
619 set testfixture [info nameofexec]
620 } else {
621 set testfixture [file join [lindex $build 1] $TRG(testfixture)]
623 if {[lindex $build 2]=="Valgrind"} {
624 set setvar "export OMIT_MISUSE=1\n"
625 set testfixture "${setvar}valgrind -v --error-exitcode=1 $testfixture"
628 # The ::testspec array is populated by permutations.test
629 foreach f [dict get $::testspec($config) -files] {
631 if {[llength $patternlist]>0} {
632 set bMatch 0
633 foreach p $patternlist {
634 if {[string match $p [file tail $f]]} {
635 set bMatch 1
636 break
639 if {$bMatch==0} continue
642 if {[file pathtype $f]!="absolute"} { set f [file join $::testdir $f] }
643 set f [file normalize $f]
645 set displayname [string map [list $topdir/ {}] $f]
646 if {$config=="full" || $config=="veryquick"} {
647 set cmd "$testfixture $f"
648 } else {
649 set cmd "$testfixture $testrunner_tcl $config $f"
650 set displayname "config=$config $displayname"
652 if {$build!=""} {
653 set displayname "[lindex $build 2] $displayname"
656 set lProp [trd_test_script_properties $f]
657 set priority 0
658 if {[lsearch $lProp slow]>=0} { set priority 2 }
659 if {[lsearch $lProp superslow]>=0} { set priority 4 }
661 add_job \
662 -displaytype tcl \
663 -displayname $displayname \
664 -cmd $cmd \
665 -depid [lindex $build 0] \
666 -priority $priority
671 proc add_build_job {buildname target} {
672 global TRG
674 set dirname "[string tolower [string map {- _} $buildname]]_$target"
675 set dirname "testrunner_bld_$dirname"
677 set id [add_job \
678 -displaytype bld \
679 -displayname "Build $buildname ($target)" \
680 -dirname $dirname \
681 -build $buildname \
682 -cmd "$TRG(makecmd) $target" \
683 -priority 3
686 list $id [file normalize $dirname] $buildname
689 proc add_make_job {bld target} {
690 global TRG
692 if {$TRG(platform)=="win"} {
693 set path [string map {/ \\} [lindex $bld 1]]
694 set cmd "xcopy /S $path\\* ."
695 } else {
696 set cmd "cp -r [lindex $bld 1]/* ."
698 append cmd "\n$TRG(makecmd) $target"
700 add_job \
701 -displaytype make \
702 -displayname "[lindex $bld 2] make $target" \
703 -cmd $cmd \
704 -depid [lindex $bld 0] \
705 -priority 1
708 proc add_fuzztest_jobs {buildname} {
710 foreach {interpreter scripts} [trd_fuzztest_data] {
711 set subcmd [lrange $interpreter 1 end]
712 set interpreter [lindex $interpreter 0]
714 set bld [add_build_job $buildname $interpreter]
715 foreach {depid dirname displayname} $bld {}
717 foreach s $scripts {
719 # Fuzz data files fuzzdata1.db and fuzzdata2.db are larger than
720 # the others. So ensure that these are run as a higher priority.
721 set tail [file tail $s]
722 if {$tail=="fuzzdata1.db" || $tail=="fuzzdata2.db"} {
723 set priority 5
724 } else {
725 set priority 1
728 add_job \
729 -displaytype fuzz \
730 -displayname "$buildname $interpreter $tail" \
731 -depid $depid \
732 -cmd "[file join $dirname $interpreter] $subcmd $s" \
733 -priority $priority
738 proc add_zipvfs_jobs {} {
739 global TRG
740 source [file join $TRG(zipvfs) test zipvfs_testrunner.tcl]
742 set bld [add_build_job Zipvfs $TRG(testfixture)]
743 foreach s [zipvfs_testrunner_files] {
744 set cmd "[file join [lindex $bld 1] $TRG(testfixture)] $s"
745 add_job \
746 -displaytype tcl \
747 -displayname "Zipvfs [file tail $s]" \
748 -cmd $cmd \
749 -depid [lindex $bld 0]
752 set ::env(SQLITE_TEST_DIR) $::testdir
755 proc add_jobs_from_cmdline {patternlist} {
756 global TRG
758 if {$TRG(zipvfs)!=""} {
759 add_zipvfs_jobs
760 if {[llength $patternlist]==0} return
763 if {[llength $patternlist]==0} {
764 set patternlist [list veryquick]
767 set first [lindex $patternlist 0]
768 switch -- $first {
769 all {
770 set patternlist [lrange $patternlist 1 end]
771 set clist [trd_all_configs]
772 foreach c $clist {
773 add_tcl_jobs "" $c $patternlist
777 mdevtest {
778 foreach b [list All-O0 All-Debug] {
779 set bld [add_build_job $b $TRG(testfixture)]
780 add_tcl_jobs $bld veryquick ""
781 add_fuzztest_jobs $b
785 sdevtest {
786 foreach b [list All-Sanitize All-Debug] {
787 set bld [add_build_job $b $TRG(testfixture)]
788 add_tcl_jobs $bld veryquick ""
789 add_fuzztest_jobs $b
793 release {
794 foreach b [trd_builds $TRG(platform)] {
795 set bld [add_build_job $b $TRG(testfixture)]
796 foreach c [trd_configs $TRG(platform) $b] {
797 add_tcl_jobs $bld $c ""
800 foreach e [trd_extras $TRG(platform) $b] {
801 if {$e=="fuzztest"} {
802 add_fuzztest_jobs $b
803 } else {
804 add_make_job $bld $e
810 default {
811 if {[info exists ::testspec($first)]} {
812 add_tcl_jobs "" $first [lrange $patternlist 1 end]
813 } else {
814 add_tcl_jobs "" full $patternlist
820 proc make_new_testset {} {
821 global TRG
823 r_write_db {
824 trdb eval $TRG(schema)
825 set nJob $TRG(nJob)
826 set cmdline $TRG(cmdline)
827 set tm [clock_milliseconds]
828 trdb eval { REPLACE INTO config VALUES('njob', $nJob ); }
829 trdb eval { REPLACE INTO config VALUES('cmdline', $cmdline ); }
830 trdb eval { REPLACE INTO config VALUES('start', $tm ); }
832 add_jobs_from_cmdline $TRG(patternlist)
837 proc script_input_ready {fd iJob jobid} {
838 global TRG
839 global O
840 global T
842 if {[eof $fd]} {
843 trdb eval { SELECT * FROM jobs WHERE jobid=$jobid } job {}
845 # If this job specified a directory name, then delete the run.sh/run.bat
846 # file from it before continuing. This is because the contents of this
847 # directory might be copied by some other job, and we don't want to copy
848 # the run.sh file in this case.
849 if {$job(dirname)!=""} {
850 file delete -force [file join $job(dirname) $TRG(run)]
853 set ::done 1
854 fconfigure $fd -blocking 1
855 set state "done"
856 set rc [catch { close $fd } msg]
857 if {$rc} {
858 if {[info exists TRG(reportlength)]} {
859 puts -nonewline "[string repeat " " $TRG(reportlength)]\r"
861 puts "FAILED: $job(displayname) ($iJob)"
862 set state "failed"
865 set tm [clock_milliseconds]
866 set jobtm [expr {$tm - $job(starttime)}]
868 puts $TRG(log) "### $job(displayname) ${jobtm}ms ($state)"
869 puts $TRG(log) [string trim $O($iJob)]
871 r_write_db {
872 set output $O($iJob)
873 trdb eval {
874 UPDATE jobs
875 SET output=$output, state=$state, endtime=$tm
876 WHERE jobid=$jobid;
877 UPDATE jobs SET state='ready' WHERE depid=$jobid;
881 dirs_freeDir $iJob
882 launch_some_jobs
883 incr ::wakeup
884 } else {
885 set rc [catch { gets $fd line } res]
886 if {$rc} {
887 puts "ERROR $res"
889 if {$res>=0} {
890 append O($iJob) "$line\n"
896 proc dirname {ii} {
897 return "testdir$ii"
900 proc launch_another_job {iJob} {
901 global TRG
902 global O
903 global T
905 set testfixture [info nameofexec]
906 set script $TRG(info_script)
908 set O($iJob) ""
910 set jobdict [r_get_next_job $iJob]
911 if {$jobdict==""} { return 0 }
912 array set job $jobdict
914 set dir $job(dirname)
915 if {$dir==""} { set dir [dirname $iJob] }
916 create_or_clear_dir $dir
918 if {$job(build)!=""} {
919 set srcdir [file dirname $::testdir]
920 if {$job(build)=="Zipvfs"} {
921 set script [zipvfs_testrunner_script]
922 } else {
923 set bWin [expr {$TRG(platform)=="win"}]
924 set script [trd_buildscript $job(build) $srcdir $bWin]
926 set fd [open [file join $dir $TRG(make)] w]
927 puts $fd $script
928 close $fd
931 set pwd [pwd]
932 cd $dir
933 set fd [open $TRG(run) w]
934 puts $fd $job(cmd)
935 close $fd
936 set fd [open "|$TRG(runcmd) 2>@1" r]
937 cd $pwd
939 fconfigure $fd -blocking false
940 fileevent $fd readable [list script_input_ready $fd $iJob $job(jobid)]
942 return 1
945 proc one_line_report {} {
946 global TRG
948 set tm [expr [clock_milliseconds] - $TRG(starttime)]
949 set tm [format "%d" [expr int($tm/1000.0 + 0.5)]]
951 r_write_db {
952 trdb eval {
953 SELECT displaytype, state, count(*) AS cnt
954 FROM jobs
955 GROUP BY 1, 2
957 set v($state,$displaytype) $cnt
958 incr t($displaytype) $cnt
962 set text ""
963 foreach j [lsort [array names t]] {
964 foreach k {done failed running} { incr v($k,$j) 0 }
965 set fin [expr $v(done,$j) + $v(failed,$j)]
966 lappend text "${j}($fin/$t($j))"
967 if {$v(failed,$j)>0} {
968 lappend text "f$v(failed,$j)"
970 if {$v(running,$j)>0} {
971 lappend text "r$v(running,$j)"
975 if {[info exists TRG(reportlength)]} {
976 puts -nonewline "[string repeat " " $TRG(reportlength)]\r"
978 set report "${tm} [join $text { }]"
979 set TRG(reportlength) [string length $report]
980 if {[string length $report]<100} {
981 puts -nonewline "$report\r"
982 flush stdout
983 } else {
984 puts $report
987 after $TRG(reporttime) one_line_report
990 proc launch_some_jobs {} {
991 global TRG
992 set nJob [trdb one { SELECT value FROM config WHERE name='njob' }]
994 while {[dirs_nHelper]<$nJob} {
995 set iDir [dirs_allocDir]
996 if {0==[launch_another_job $iDir]} {
997 dirs_freeDir $iDir
998 break;
1003 proc run_testset {} {
1004 global TRG
1005 set ii 0
1007 set TRG(starttime) [clock_milliseconds]
1008 set TRG(log) [open $TRG(logname) w]
1010 launch_some_jobs
1012 one_line_report
1013 while {[dirs_nHelper]>0} {
1014 after 500 {incr ::wakeup}
1015 vwait ::wakeup
1017 close $TRG(log)
1018 one_line_report
1020 r_write_db {
1021 set tm [clock_milliseconds]
1022 trdb eval { REPLACE INTO config VALUES('end', $tm ); }
1023 set nErr [trdb one {SELECT count(*) FROM jobs WHERE state='failed'}]
1024 if {$nErr>0} {
1025 puts "$nErr failures:"
1026 trdb eval {
1027 SELECT displayname FROM jobs WHERE state='failed'
1029 puts "FAILED: $displayname"
1034 puts "\nTest database is $TRG(dbname)"
1035 puts "Test log is $TRG(logname)"
1039 sqlite3 trdb $TRG(dbname)
1040 trdb timeout $TRG(timeout)
1041 set tm [lindex [time { make_new_testset }] 0]
1042 if {$TRG(nJob)>1} {
1043 puts "splitting work across $TRG(nJob) jobs"
1045 puts "built testset in [expr $tm/1000]ms.."
1046 run_testset
1047 trdb close
1048 #puts [pwd]