cf2ad7ae36
FossilOrigin-Name: 22d280a5cd395abbedcfffbac3d3b3a614c327be25763ca380c1338a2a7bd33a
706 lines
18 KiB
Tcl
706 lines
18 KiB
Tcl
|
|
#-------------------------------------------------------------------------
|
|
# Usage:
|
|
#
|
|
proc usage {} {
|
|
set a0 testrunner.tcl
|
|
|
|
set ::argv [list]
|
|
uplevel [list source $::testdir/permutations.test]
|
|
|
|
puts stderr "Usage: $a0 ?SWITCHES? ?PERMUTATION? ?PATTERNS?"
|
|
puts stderr ""
|
|
puts stderr "where SWITCHES are:"
|
|
puts stderr " --jobs NUMBER-OF-JOBS"
|
|
puts stderr ""
|
|
puts stderr "available PERMUTATION values are:"
|
|
set ii 0
|
|
foreach name [lsort [array names ::testspec]] {
|
|
if {($ii % 3)==0} { puts -nonewline stderr " " }
|
|
puts -nonewline stderr [format "% -22s" $name]
|
|
if {($ii % 3)==2} { puts stderr "" }
|
|
incr ii
|
|
}
|
|
puts stderr ""
|
|
puts stderr ""
|
|
puts stderr "Examples:"
|
|
puts stderr " 1) Run the veryquick tests:"
|
|
puts stderr " $a0"
|
|
puts stderr " 2) Run all test scripts in the source tree:"
|
|
puts stderr " $a0 full"
|
|
puts stderr " 2) Run the 'memsubsys1' permutation:"
|
|
puts stderr " $a0 memsubsys1"
|
|
puts stderr " 3) Run all permutations usually run by \[make fulltest\]"
|
|
puts stderr " $a0 release"
|
|
puts stderr " 4) Run all scripts that match the pattern 'select%':"
|
|
puts stderr " $a0 select%"
|
|
puts stderr " $a0 all select%"
|
|
puts stderr " $a0 full select%"
|
|
puts stderr " 5) Run all scripts that are part of the veryquick permutation and match the pattern 'select%':"
|
|
puts stderr " $a0 veryquick select%"
|
|
puts stderr " 6) Run the 'memsubsys1' permutation, but just those scripts that match 'window%':"
|
|
puts stderr " $a0 memsubsys1 window%"
|
|
puts stderr " 7) Run all the permutations, but only the scripts that match either 'fts5%' or 'rtree%':"
|
|
puts stderr " $a0 release fts5% rtree%"
|
|
|
|
exit 1
|
|
}
|
|
#-------------------------------------------------------------------------
|
|
|
|
#-------------------------------------------------------------------------
|
|
# The database schema used by the testrunner.db database.
|
|
#
|
|
set R(schema) {
|
|
DROP TABLE IF EXISTS script;
|
|
DROP TABLE IF EXISTS msg;
|
|
DROP TABLE IF EXISTS malloc;
|
|
|
|
CREATE TABLE script(
|
|
config TEXT,
|
|
filename TEXT, -- full path to test script
|
|
slow BOOLEAN, -- true if script is "slow"
|
|
state TEXT CHECK( state IN ('ready', 'running', 'done') ),
|
|
testfixtureid, -- Id of process that ran script
|
|
time INTEGER, -- Time in ms
|
|
nerr INTEGER, -- if 'done', the number of errors
|
|
ntest INTEGER, -- if 'done', the number of tests
|
|
output TEXT, -- full output of test script
|
|
PRIMARY KEY(config, filename)
|
|
);
|
|
|
|
CREATE TABLE malloc(
|
|
id INTEGER PRIMARY KEY,
|
|
nmalloc INTEGER,
|
|
nbyte INTEGER,
|
|
leaker TEXT
|
|
);
|
|
|
|
CREATE TABLE msg(
|
|
id INTEGER PRIMARY KEY,
|
|
msg TEXT
|
|
);
|
|
}
|
|
#-------------------------------------------------------------------------
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Try to estimate a the number of processes to use.
|
|
#
|
|
# Command [guess_number_of_cores] attempts to glean the number of logical
|
|
# cores. Command [default_njob] returns the default value for the --jobs
|
|
# switch.
|
|
#
|
|
proc guess_number_of_cores {} {
|
|
set ret 4
|
|
|
|
if {$::tcl_platform(os)=="Darwin"} {
|
|
set cmd "sysctl -n hw.logicalcpu"
|
|
} else {
|
|
set cmd "nproc"
|
|
}
|
|
catch {
|
|
set fd [open "|$cmd" r]
|
|
set ret [gets $fd]
|
|
close $fd
|
|
set ret [expr $ret]
|
|
}
|
|
return $ret
|
|
}
|
|
|
|
proc default_njob {} {
|
|
set nCore [guess_number_of_cores]
|
|
set nHelper [expr int($nCore*0.75)]
|
|
expr $nHelper>0 ? $nHelper : 1
|
|
}
|
|
#-------------------------------------------------------------------------
|
|
|
|
|
|
set R(dbname) [file normalize testrunner.db]
|
|
set R(logname) [file normalize testrunner.log]
|
|
set R(info_script) [file normalize [info script]]
|
|
set R(timeout) 10000 ;# Default busy-timeout for testrunner.
|
|
set R(nJob) [default_njob] ;# Default number of helper processes
|
|
set R(leaker) "" ;# Name of first script to leak memory
|
|
|
|
set R(patternlist) [list]
|
|
|
|
set testdir [file dirname $argv0]
|
|
|
|
# Parse the command line options. There are two ways to invoke this
|
|
# script - to create a helper or coordinator process. If there are
|
|
# no helper processes, the coordinator runs test scripts.
|
|
#
|
|
# To create a helper process:
|
|
#
|
|
# testrunner.tcl helper ID
|
|
#
|
|
# where ID is an integer greater than 0. The process will create and
|
|
# run tests in the "testdir$ID" directory. Helper processes are only
|
|
# created by coordinators - there is no need for a user to create
|
|
# helper processes manually.
|
|
#
|
|
# If the first argument is anything other than "helper", then a coordinator
|
|
# process is started. See the implementation of the [usage] proc above for
|
|
# details.
|
|
#
|
|
switch -- [lindex $argv 0] {
|
|
helper {
|
|
set R(helper) 1
|
|
set R(helper_id) [lindex $argv 1]
|
|
set argv [list --testdir=testdir$R(helper_id)]
|
|
}
|
|
|
|
default {
|
|
set R(helper) 0
|
|
set R(helper_id) 0
|
|
|
|
}
|
|
}
|
|
if {$R(helper)==0} {
|
|
for {set ii 0} {$ii < [llength $argv]} {incr ii} {
|
|
set a [lindex $argv $ii]
|
|
set n [string length $a]
|
|
|
|
if {[string range $a 0 0]=="-"} {
|
|
if {($n>2 && [string match "$a*" --jobs]) || $a=="-j"} {
|
|
incr ii
|
|
set R(nJob) [lindex $argv $ii]
|
|
} else {
|
|
usage
|
|
}
|
|
} else {
|
|
lappend R(patternlist) [string map {% *} $a]
|
|
}
|
|
}
|
|
|
|
set argv [list]
|
|
}
|
|
source $testdir/permutations.test
|
|
|
|
#-------------------------------------------------------------------------
|
|
# Return a list of tests to run. Each element of the list is itself a
|
|
# list of two elements - the name of a permuations.test configuration
|
|
# followed by the full path to a test script. i.e.:
|
|
#
|
|
# {CONFIG FILENAME} {CONFIG FILENAME} ...
|
|
#
|
|
proc testset_patternlist {patternlist} {
|
|
|
|
set first [lindex $patternlist 0]
|
|
if {$first=="all"} { set first "full" }
|
|
|
|
if {$first=="release"} {
|
|
|
|
# The following mirrors the set of test suites invoked by "all.test".
|
|
#
|
|
set clist {
|
|
full
|
|
no_optimization memsubsys1 memsubsys2 singlethread
|
|
multithread onefile utf16 exclusive persistent_journal
|
|
persistent_journal_error no_journal no_journal_error
|
|
autovacuum_ioerr no_mutex_try fullmutex journaltest
|
|
inmemory_journal pcache0 pcache10 pcache50 pcache90
|
|
pcache100 prepare mmap
|
|
}
|
|
ifcapable rbu { lappend clist rbu }
|
|
if {$::tcl_platform(platform)=="unix"} {
|
|
ifcapable !default_autovacuum {
|
|
lappend clist autovacuum_crash
|
|
}
|
|
}
|
|
set patternlist [lrange $patternlist 1 end]
|
|
|
|
} elseif {[info exists ::testspec($first)]} {
|
|
set clist $first
|
|
set patternlist [lrange $patternlist 1 end]
|
|
} elseif { [llength $patternlist]==0 } {
|
|
set clist veryquick
|
|
} else {
|
|
set clist full
|
|
}
|
|
|
|
set testset [list]
|
|
|
|
foreach config $clist {
|
|
catch { array unset O }
|
|
array set O $::testspec($config)
|
|
foreach f $O(-files) {
|
|
if {[file pathtype $f]!="absolute"} {
|
|
set f [file join $::testdir $f]
|
|
}
|
|
lappend testset [list $config [file normalize $f]]
|
|
}
|
|
}
|
|
|
|
if {[llength $patternlist]>0} {
|
|
foreach t $testset {
|
|
set tail [file tail [lindex $t 1]]
|
|
foreach p $patternlist {
|
|
if {[string match $p $tail]} {
|
|
lappend ret $t
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
set ret $testset
|
|
}
|
|
|
|
set ret
|
|
}
|
|
#--------------------------------------------------------------------------
|
|
|
|
|
|
proc r_write_db {tcl} {
|
|
global R
|
|
|
|
sqlite3_test_control_pending_byte 0x010000
|
|
sqlite3 db $R(dbname)
|
|
db timeout $R(timeout)
|
|
db eval { BEGIN EXCLUSIVE }
|
|
|
|
uplevel $tcl
|
|
|
|
db eval { COMMIT }
|
|
db close
|
|
}
|
|
|
|
proc make_new_testset {} {
|
|
global R
|
|
|
|
set tests [testset_patternlist $R(patternlist)]
|
|
r_write_db {
|
|
db eval $R(schema)
|
|
foreach t $tests {
|
|
foreach {c s} $t {}
|
|
set slow 0
|
|
|
|
set fd [open $s]
|
|
for {set ii 0} {$ii<100 && ![eof $fd]} {incr ii} {
|
|
set line [gets $fd]
|
|
if {[string match -nocase *testrunner:* $line]} {
|
|
regexp -nocase {.*testrunner:(.*)} $line -> properties
|
|
foreach p $properties {
|
|
if {$p=="slow"} { set slow 1 }
|
|
}
|
|
}
|
|
}
|
|
close $fd
|
|
|
|
db eval {
|
|
INSERT INTO script(config, filename, slow, state)
|
|
VALUES ($c, $s, $slow, 'ready')
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Find the next job in the database and mark it as 'running'. Then return
|
|
# a list consisting of the
|
|
#
|
|
# CONFIG FILENAME
|
|
#
|
|
# pair for the test.
|
|
#
|
|
proc get_next_test {} {
|
|
global R
|
|
set myid $R(helper_id)
|
|
|
|
r_write_db {
|
|
set f ""
|
|
set c ""
|
|
db eval {
|
|
SELECT config, filename FROM script WHERE state='ready'
|
|
ORDER BY
|
|
(slow * (($myid+1) % 2)) DESC,
|
|
config!='full',
|
|
config,
|
|
filename
|
|
LIMIT 1
|
|
} {
|
|
set c $config
|
|
set f $filename
|
|
}
|
|
if {$f!=""} {
|
|
db eval {
|
|
UPDATE script SET state='running', testfixtureid=$myid
|
|
WHERE (config, filename) = ($c, $f)
|
|
}
|
|
}
|
|
}
|
|
|
|
if {$f==""} { return "" }
|
|
list $c $f
|
|
}
|
|
|
|
proc r_testname {config filename} {
|
|
set name [file tail $filename]
|
|
if {$config!="" && $config!="full" && $config!="veryquick"} {
|
|
set name "$config-$name"
|
|
}
|
|
return $name
|
|
}
|
|
|
|
proc r_set_test_result {config filename ms nerr ntest output} {
|
|
global R
|
|
|
|
set f [r_testname $config $filename]
|
|
if {$nerr==0} {
|
|
set msg "$f... Ok"
|
|
} else {
|
|
set msg "$f... FAILED - $nerr errors of $ntest tests"
|
|
}
|
|
append msg " (${ms}ms)"
|
|
if {$R(helper)} {
|
|
append msg " (helper $R(helper_id))"
|
|
}
|
|
|
|
sqlite3_shutdown
|
|
set nMalloc [lindex [sqlite3_status SQLITE_STATUS_MALLOC_COUNT 0] 1]
|
|
set nByte [sqlite3_memory_used]
|
|
if {($nByte>0 || $nMalloc>0) && $R(leaker)==""} {
|
|
set R(leaker) $f
|
|
}
|
|
|
|
r_write_db {
|
|
db eval {
|
|
UPDATE script
|
|
SET state='done', output=$output, nerr=$nerr, ntest=$ntest, time=$ms
|
|
WHERE (config, filename)=($config, $filename);
|
|
|
|
INSERT INTO msg(msg) VALUES ($msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
set R(iNextMsg) 1
|
|
proc r_get_messages {{db ""}} {
|
|
global R
|
|
|
|
sqlite3_test_control_pending_byte 0x010000
|
|
|
|
if {$db==""} {
|
|
sqlite3 rgmhandle $R(dbname)
|
|
set dbhandle rgmhandle
|
|
$dbhandle timeout $R(timeout)
|
|
} else {
|
|
set dbhandle $db
|
|
}
|
|
|
|
$dbhandle transaction {
|
|
set next $R(iNextMsg)
|
|
set ret [$dbhandle eval {SELECT msg FROM msg WHERE id>=$next}]
|
|
set R(iNextMsg) [$dbhandle one {SELECT COALESCE(max(id), 0)+1 FROM msg}]
|
|
}
|
|
|
|
if {$db==""} {
|
|
rgmhandle close
|
|
}
|
|
|
|
set ret
|
|
}
|
|
|
|
# This is called after all tests have been run to write the leaked memory
|
|
# report into the malloc table of testrunner.db.
|
|
#
|
|
proc r_memory_report {} {
|
|
global R
|
|
|
|
sqlite3_shutdown
|
|
|
|
set nMalloc [lindex [sqlite3_status SQLITE_STATUS_MALLOC_COUNT 0] 1]
|
|
set nByte [sqlite3_memory_used]
|
|
set id $R(helper_id)
|
|
set leaker $R(leaker)
|
|
|
|
r_write_db {
|
|
db eval {
|
|
INSERT INTO malloc(id, nMalloc, nByte, leaker)
|
|
VALUES($id, $nMalloc, $nByte, $leaker)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#--------------------------------------------------------------------------
|
|
#
|
|
set ::R_INSTALL_PUTS_WRAPPER {
|
|
proc puts_sts_wrapper {args} {
|
|
set n [llength $args]
|
|
if {$n==1 || ($n==2 && [string first [lindex $args 0] -nonewline]==0)} {
|
|
uplevel puts_into_caller $args
|
|
} else {
|
|
# A channel was explicitly specified.
|
|
uplevel puts_sts_original $args
|
|
}
|
|
}
|
|
rename puts puts_sts_original
|
|
proc puts {args} { uplevel puts_sts_wrapper $args }
|
|
}
|
|
|
|
proc r_install_puts_wrapper {} $::R_INSTALL_PUTS_WRAPPER
|
|
proc r_uninstall_puts_wrapper {} {
|
|
rename puts ""
|
|
rename puts_sts_original puts
|
|
}
|
|
|
|
proc slave_test_script {script} {
|
|
|
|
# Create the interpreter used to run the test script.
|
|
interp create tinterp
|
|
|
|
# Populate some global variables that tester.tcl expects to see.
|
|
foreach {var value} [list \
|
|
::argv0 $::argv0 \
|
|
::argv {} \
|
|
::SLAVE 1 \
|
|
] {
|
|
interp eval tinterp [list set $var $value]
|
|
}
|
|
|
|
# The alias used to access the global test counters.
|
|
tinterp alias set_test_counter set_test_counter
|
|
|
|
# Set up an empty ::cmdlinearg array in the slave.
|
|
interp eval tinterp [list array set ::cmdlinearg [array get ::cmdlinearg]]
|
|
|
|
# Set up the ::G array in the slave.
|
|
interp eval tinterp [list array set ::G [array get ::G]]
|
|
interp eval tinterp [list set ::G(runner.tcl) 1]
|
|
|
|
interp eval tinterp $::R_INSTALL_PUTS_WRAPPER
|
|
tinterp alias puts_into_caller puts_into_caller
|
|
|
|
# Load the various test interfaces implemented in C.
|
|
load_testfixture_extensions tinterp
|
|
|
|
# Run the test script.
|
|
set rc [catch { interp eval tinterp $script } msg opt]
|
|
if {$rc} {
|
|
puts_into_caller $msg
|
|
puts_into_caller [dict get $opt -errorinfo]
|
|
incr ::TC(errors)
|
|
}
|
|
|
|
# Check if the interpreter call [run_thread_tests]
|
|
if { [interp eval tinterp {info exists ::run_thread_tests_called}] } {
|
|
set ::run_thread_tests_called 1
|
|
}
|
|
|
|
# Delete the interpreter used to run the test script.
|
|
interp delete tinterp
|
|
}
|
|
|
|
proc slave_test_file {zFile} {
|
|
set tail [file tail $zFile]
|
|
|
|
# Remember the value of the shared-cache setting. So that it is possible
|
|
# to check afterwards that it was not modified by the test script.
|
|
#
|
|
ifcapable shared_cache { set scs [sqlite3_enable_shared_cache] }
|
|
|
|
# Run the test script in a slave interpreter.
|
|
#
|
|
unset -nocomplain ::run_thread_tests_called
|
|
reset_prng_state
|
|
set ::sqlite_open_file_count 0
|
|
set time [time { slave_test_script [list source $zFile] }]
|
|
set ms [expr [lindex $time 0] / 1000]
|
|
|
|
r_install_puts_wrapper
|
|
|
|
# Test that all files opened by the test script were closed. Omit this
|
|
# if the test script has "thread" in its name. The open file counter
|
|
# is not thread-safe.
|
|
#
|
|
if {[info exists ::run_thread_tests_called]==0} {
|
|
do_test ${tail}-closeallfiles { expr {$::sqlite_open_file_count>0} } {0}
|
|
}
|
|
set ::sqlite_open_file_count 0
|
|
|
|
# Test that the global "shared-cache" setting was not altered by
|
|
# the test script.
|
|
#
|
|
ifcapable shared_cache {
|
|
set res [expr {[sqlite3_enable_shared_cache] == $scs}]
|
|
do_test ${tail}-sharedcachesetting [list set {} $res] 1
|
|
}
|
|
|
|
# Add some info to the output.
|
|
#
|
|
output2 "Time: $tail $ms ms"
|
|
show_memstats
|
|
|
|
r_uninstall_puts_wrapper
|
|
return $ms
|
|
}
|
|
|
|
proc puts_into_caller {args} {
|
|
global R
|
|
if {[llength $args]==1} {
|
|
append R(output) [lindex $args 0]
|
|
append R(output) "\n"
|
|
} else {
|
|
append R(output) [lindex $args 1]
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
proc r_final_report {} {
|
|
global R
|
|
|
|
sqlite3_test_control_pending_byte 0x010000
|
|
sqlite3 db $R(dbname)
|
|
|
|
db timeout $R(timeout)
|
|
|
|
set errcode 0
|
|
|
|
# Create the text log file. This is just the concatenation of the
|
|
# 'output' column of the database for every script that was run.
|
|
set fd [open $R(logname) w]
|
|
db eval {SELECT output FROM script ORDER BY config!='full',config,filename} {
|
|
puts $fd $output
|
|
}
|
|
close $fd
|
|
|
|
# Check if any scripts reported errors. If so, print one line noting
|
|
# how many errors, and another identifying the scripts in which they
|
|
# occured. Or, if no errors occurred, print out "no errors at all!".
|
|
sqlite3 db $R(dbname)
|
|
db timeout $R(timeout)
|
|
db eval { SELECT sum(nerr) AS nerr, sum(ntest) AS ntest FROM script } { }
|
|
puts "$nerr errors from $ntest tests."
|
|
if {$nerr>0} {
|
|
db eval { SELECT config, filename FROM script WHERE nerr>0 } {
|
|
lappend errlist [r_testname $config $filename]
|
|
}
|
|
puts "Errors in: $errlist"
|
|
set errcode 1
|
|
}
|
|
|
|
# Check if any scripts were not run or did not finish. Print out a
|
|
# line identifying them if there are any.
|
|
set errlist [list]
|
|
db eval { SELECT config, filename FROM script WHERE state!='done' } {
|
|
lappend errlist [r_testname $config $filename]
|
|
}
|
|
if {$errlist!=[list]} {
|
|
puts "Tests DID NOT FINISH (crashed?): $errlist"
|
|
set errcode 1
|
|
}
|
|
|
|
set bLeak 0
|
|
db eval {
|
|
SELECT id, nmalloc, nbyte, leaker FROM malloc
|
|
WHERE nmalloc>0 OR nbyte>0
|
|
} {
|
|
if {$id==0} {
|
|
set line "This process "
|
|
} else {
|
|
set line "Helper $id "
|
|
}
|
|
append line "leaked $nbyte byte in $nmalloc allocations"
|
|
if {$leaker!=""} { append line " (perhaps in [file tail $leaker])" }
|
|
puts $line
|
|
set bLeak 1
|
|
}
|
|
if {$bLeak==0} {
|
|
puts "No leaks - all allocations freed."
|
|
}
|
|
|
|
db close
|
|
|
|
puts "Test database is $R(dbname)"
|
|
puts "Test log file is $R(logname)"
|
|
if {$errcode} {
|
|
puts "This test has FAILED."
|
|
}
|
|
return $errcode
|
|
}
|
|
|
|
|
|
if {$R(helper)==0} {
|
|
make_new_testset
|
|
}
|
|
|
|
set R(nHelperRunning) 0
|
|
if {$R(helper)==0 && $R(nJob)>1} {
|
|
cd $cmdlinearg(TESTFIXTURE_HOME)
|
|
for {set ii 1} {$ii <= $R(nJob)} {incr ii} {
|
|
set cmd "[info nameofexec] $R(info_script) helper $ii 2>@1"
|
|
puts "Launching helper $ii ($cmd)"
|
|
set chan [open "|$cmd" r]
|
|
fconfigure $chan -blocking false
|
|
fileevent $chan readable [list r_helper_readable $ii $chan]
|
|
incr R(nHelperRunning)
|
|
}
|
|
cd $cmdlinearg(testdir)
|
|
}
|
|
|
|
proc r_helper_readable {id chan} {
|
|
set data [gets $chan]
|
|
if {$data!=""} { puts "helper $id:$data" }
|
|
if {[eof $chan]} {
|
|
puts "helper $id is finished"
|
|
incr ::R(nHelperRunning) -1
|
|
close $chan
|
|
}
|
|
}
|
|
|
|
if {$R(nHelperRunning)==0} {
|
|
while { ""!=[set t [get_next_test]] } {
|
|
set R(output) ""
|
|
set TC(count) 0
|
|
set TC(errors) 0
|
|
|
|
foreach {config filename} $t {}
|
|
|
|
array set O $::testspec($config)
|
|
set ::G(perm:name) $config
|
|
set ::G(perm:prefix) $O(-prefix)
|
|
set ::G(isquick) 1
|
|
set ::G(perm:dbconfig) $O(-dbconfig)
|
|
set ::G(perm:presql) $O(-presql)
|
|
|
|
eval $O(-initialize)
|
|
set ms [slave_test_file $filename]
|
|
eval $O(-shutdown)
|
|
|
|
unset -nocomplain ::G(perm:sqlite3_args)
|
|
unset ::G(perm:name)
|
|
unset ::G(perm:prefix)
|
|
unset ::G(perm:dbconfig)
|
|
unset ::G(perm:presql)
|
|
|
|
r_set_test_result $config $filename $ms $TC(errors) $TC(count) $R(output)
|
|
|
|
if {$R(helper)==0} {
|
|
foreach msg [r_get_messages] { puts $msg }
|
|
}
|
|
}
|
|
|
|
# Tests are finished - write a record into testrunner.db describing
|
|
# any memory leaks.
|
|
r_memory_report
|
|
|
|
} else {
|
|
set TTT 0
|
|
sqlite3 db $R(dbname)
|
|
db timeout $R(timeout)
|
|
while {$R(nHelperRunning)>0} {
|
|
after 250 { incr TTT }
|
|
vwait TTT
|
|
foreach msg [r_get_messages db] { puts $msg }
|
|
}
|
|
db close
|
|
}
|
|
|
|
set errcode 0
|
|
if {$R(helper)==0} {
|
|
set errcode [r_final_report]
|
|
}
|
|
|
|
exit $errcode
|
|
|