105058514Sdan 205058514Sdan#------------------------------------------------------------------------- 305058514Sdan# Usage: 405058514Sdan# 505058514Sdanproc usage {} { 62bb2d53bSdan set a0 testrunner.tcl 72bb2d53bSdan 8ca0720a9Sdan set ::argv [list] 9ca0720a9Sdan uplevel [list source $::testdir/permutations.test] 10ca0720a9Sdan 11ca0720a9Sdan puts stderr "Usage: $a0 ?SWITCHES? ?PERMUTATION? ?PATTERNS?" 1205058514Sdan puts stderr "" 1305058514Sdan puts stderr "where SWITCHES are:" 1405058514Sdan puts stderr " --jobs NUMBER-OF-JOBS" 152bb2d53bSdan puts stderr "" 16ca0720a9Sdan puts stderr "available PERMUTATION values are:" 17ca0720a9Sdan set ii 0 18ca0720a9Sdan foreach name [lsort [array names ::testspec]] { 19ca0720a9Sdan if {($ii % 3)==0} { puts -nonewline stderr " " } 20ca0720a9Sdan puts -nonewline stderr [format "% -22s" $name] 21ca0720a9Sdan if {($ii % 3)==2} { puts stderr "" } 22ca0720a9Sdan incr ii 23ca0720a9Sdan } 24ca0720a9Sdan puts stderr "" 25ca0720a9Sdan puts stderr "" 262bb2d53bSdan puts stderr "Examples:" 27ca0720a9Sdan puts stderr " 1) Run the veryquick tests:" 28ca0720a9Sdan puts stderr " $a0" 29ca0720a9Sdan puts stderr " 2) Run all test scripts in the source tree:" 30ca0720a9Sdan puts stderr " $a0 full" 31ca0720a9Sdan puts stderr " 2) Run the 'memsubsys1' permutation:" 32ca0720a9Sdan puts stderr " $a0 memsubsys1" 33ca0720a9Sdan puts stderr " 3) Run all permutations usually run by \[make fulltest\]" 34ca0720a9Sdan puts stderr " $a0 release" 35ca0720a9Sdan puts stderr " 4) Run all scripts that match the pattern 'select%':" 36ca0720a9Sdan puts stderr " $a0 select%" 37ca0720a9Sdan puts stderr " $a0 all select%" 38ca0720a9Sdan puts stderr " $a0 full select%" 39ca0720a9Sdan puts stderr " 5) Run all scripts that are part of the veryquick permutation and match the pattern 'select%':" 40ca0720a9Sdan puts stderr " $a0 veryquick select%" 41ca0720a9Sdan puts stderr " 6) Run the 'memsubsys1' permutation, but just those scripts that match 'window%':" 42ca0720a9Sdan puts stderr " $a0 memsubsys1 window%" 43ca0720a9Sdan puts stderr " 7) Run all the permutations, but only the scripts that match either 'fts5%' or 'rtree%':" 44ca0720a9Sdan puts stderr " $a0 release fts5% rtree%" 452bb2d53bSdan 4605058514Sdan exit 1 4705058514Sdan} 4805058514Sdan#------------------------------------------------------------------------- 4905058514Sdan 50e9a7ebe1Sdan#------------------------------------------------------------------------- 51e9a7ebe1Sdan# The database schema used by the testrunner.db database. 52e9a7ebe1Sdan# 53e9a7ebe1Sdanset R(schema) { 54e9a7ebe1Sdan DROP TABLE IF EXISTS script; 55e9a7ebe1Sdan DROP TABLE IF EXISTS msg; 56e9a7ebe1Sdan DROP TABLE IF EXISTS malloc; 5705058514Sdan 58e9a7ebe1Sdan CREATE TABLE script( 59ca0720a9Sdan config TEXT, 60ca0720a9Sdan filename TEXT, -- full path to test script 61*cf2ad7aeSdan slow BOOLEAN, -- true if script is "slow" 62e9a7ebe1Sdan state TEXT CHECK( state IN ('ready', 'running', 'done') ), 63e9a7ebe1Sdan testfixtureid, -- Id of process that ran script 64e9a7ebe1Sdan time INTEGER, -- Time in ms 65e9a7ebe1Sdan nerr INTEGER, -- if 'done', the number of errors 66e9a7ebe1Sdan ntest INTEGER, -- if 'done', the number of tests 67ca0720a9Sdan output TEXT, -- full output of test script 68ca0720a9Sdan PRIMARY KEY(config, filename) 69e9a7ebe1Sdan ); 70e9a7ebe1Sdan 71e9a7ebe1Sdan CREATE TABLE malloc( 72e9a7ebe1Sdan id INTEGER PRIMARY KEY, 73e9a7ebe1Sdan nmalloc INTEGER, 74e9a7ebe1Sdan nbyte INTEGER, 75e9a7ebe1Sdan leaker TEXT 76e9a7ebe1Sdan ); 77e9a7ebe1Sdan 78e9a7ebe1Sdan CREATE TABLE msg( 79e9a7ebe1Sdan id INTEGER PRIMARY KEY, 80e9a7ebe1Sdan msg TEXT 81e9a7ebe1Sdan ); 82e9a7ebe1Sdan} 83e9a7ebe1Sdan#------------------------------------------------------------------------- 84e9a7ebe1Sdan 85e9a7ebe1Sdan#------------------------------------------------------------------------- 86e9a7ebe1Sdan# Try to estimate a the number of processes to use. 87e9a7ebe1Sdan# 88e9a7ebe1Sdan# Command [guess_number_of_cores] attempts to glean the number of logical 89e9a7ebe1Sdan# cores. Command [default_njob] returns the default value for the --jobs 90e9a7ebe1Sdan# switch. 91e9a7ebe1Sdan# 92e9a7ebe1Sdanproc guess_number_of_cores {} { 93e9a7ebe1Sdan set ret 4 94615aeceaSdan 95615aeceaSdan if {$::tcl_platform(os)=="Darwin"} { 96615aeceaSdan set cmd "sysctl -n hw.logicalcpu" 97615aeceaSdan } else { 98615aeceaSdan set cmd "nproc" 99615aeceaSdan } 100e9a7ebe1Sdan catch { 101615aeceaSdan set fd [open "|$cmd" r] 102e9a7ebe1Sdan set ret [gets $fd] 103e9a7ebe1Sdan close $fd 104e9a7ebe1Sdan set ret [expr $ret] 105e9a7ebe1Sdan } 106e9a7ebe1Sdan return $ret 107e9a7ebe1Sdan} 108e9a7ebe1Sdan 109e9a7ebe1Sdanproc default_njob {} { 110e9a7ebe1Sdan set nCore [guess_number_of_cores] 111e9a7ebe1Sdan set nHelper [expr int($nCore*0.75)] 112e9a7ebe1Sdan expr $nHelper>0 ? $nHelper : 1 113e9a7ebe1Sdan} 114e9a7ebe1Sdan#------------------------------------------------------------------------- 115e9a7ebe1Sdan 116e9a7ebe1Sdan 117e9a7ebe1Sdanset R(dbname) [file normalize testrunner.db] 118e9a7ebe1Sdanset R(logname) [file normalize testrunner.log] 119e9a7ebe1Sdanset R(info_script) [file normalize [info script]] 120e9a7ebe1Sdanset R(timeout) 10000 ;# Default busy-timeout for testrunner. 121e9a7ebe1Sdanset R(nJob) [default_njob] ;# Default number of helper processes 122e9a7ebe1Sdanset R(leaker) "" ;# Name of first script to leak memory 123e9a7ebe1Sdan 12495cc6a5eSdanset R(patternlist) [list] 12595cc6a5eSdan 12695cc6a5eSdanset testdir [file dirname $argv0] 127e9a7ebe1Sdan 128e9a7ebe1Sdan# Parse the command line options. There are two ways to invoke this 129e9a7ebe1Sdan# script - to create a helper or coordinator process. If there are 130e9a7ebe1Sdan# no helper processes, the coordinator runs test scripts. 131e9a7ebe1Sdan# 132e9a7ebe1Sdan# To create a helper process: 133e9a7ebe1Sdan# 134e9a7ebe1Sdan# testrunner.tcl helper ID 135e9a7ebe1Sdan# 136e9a7ebe1Sdan# where ID is an integer greater than 0. The process will create and 137e9a7ebe1Sdan# run tests in the "testdir$ID" directory. Helper processes are only 138e9a7ebe1Sdan# created by coordinators - there is no need for a user to create 139e9a7ebe1Sdan# helper processes manually. 140e9a7ebe1Sdan# 141e9a7ebe1Sdan# If the first argument is anything other than "helper", then a coordinator 142e9a7ebe1Sdan# process is started. See the implementation of the [usage] proc above for 143e9a7ebe1Sdan# details. 144e9a7ebe1Sdan# 14505058514Sdanswitch -- [lindex $argv 0] { 14605058514Sdan helper { 14705058514Sdan set R(helper) 1 14805058514Sdan set R(helper_id) [lindex $argv 1] 14905058514Sdan set argv [list --testdir=testdir$R(helper_id)] 15005058514Sdan } 15105058514Sdan 15205058514Sdan default { 15305058514Sdan set R(helper) 0 15405058514Sdan set R(helper_id) 0 155e9a7ebe1Sdan 15605058514Sdan } 15705058514Sdan} 15805058514Sdanif {$R(helper)==0} { 15905058514Sdan for {set ii 0} {$ii < [llength $argv]} {incr ii} { 16005058514Sdan set a [lindex $argv $ii] 16105058514Sdan set n [string length $a] 16205058514Sdan 16395cc6a5eSdan if {[string range $a 0 0]=="-"} { 16405058514Sdan if {($n>2 && [string match "$a*" --jobs]) || $a=="-j"} { 16505058514Sdan incr ii 166e9a7ebe1Sdan set R(nJob) [lindex $argv $ii] 16705058514Sdan } else { 16805058514Sdan usage 16905058514Sdan } 17095cc6a5eSdan } else { 171ca0720a9Sdan lappend R(patternlist) [string map {% *} $a] 17295cc6a5eSdan } 17305058514Sdan } 17405058514Sdan 17505058514Sdan set argv [list] 17605058514Sdan} 177ca0720a9Sdansource $testdir/permutations.test 17805058514Sdan 179ca0720a9Sdan#------------------------------------------------------------------------- 180ca0720a9Sdan# Return a list of tests to run. Each element of the list is itself a 181ca0720a9Sdan# list of two elements - the name of a permuations.test configuration 182ca0720a9Sdan# followed by the full path to a test script. i.e.: 183ca0720a9Sdan# 184ca0720a9Sdan# {CONFIG FILENAME} {CONFIG FILENAME} ... 185ca0720a9Sdan# 186ca0720a9Sdanproc testset_patternlist {patternlist} { 187ca0720a9Sdan 188ca0720a9Sdan set first [lindex $patternlist 0] 189ca0720a9Sdan if {$first=="all"} { set first "full" } 190ca0720a9Sdan 191ca0720a9Sdan if {$first=="release"} { 192ca0720a9Sdan 193ca0720a9Sdan # The following mirrors the set of test suites invoked by "all.test". 194ca0720a9Sdan # 195ca0720a9Sdan set clist { 196ca0720a9Sdan full 197ca0720a9Sdan no_optimization memsubsys1 memsubsys2 singlethread 198ca0720a9Sdan multithread onefile utf16 exclusive persistent_journal 199ca0720a9Sdan persistent_journal_error no_journal no_journal_error 200ca0720a9Sdan autovacuum_ioerr no_mutex_try fullmutex journaltest 201ca0720a9Sdan inmemory_journal pcache0 pcache10 pcache50 pcache90 202ca0720a9Sdan pcache100 prepare mmap 203ca0720a9Sdan } 204ca0720a9Sdan ifcapable rbu { lappend clist rbu } 205ca0720a9Sdan if {$::tcl_platform(platform)=="unix"} { 206ca0720a9Sdan ifcapable !default_autovacuum { 207ca0720a9Sdan lappend clist autovacuum_crash 208ca0720a9Sdan } 209ca0720a9Sdan } 210ca0720a9Sdan set patternlist [lrange $patternlist 1 end] 211ca0720a9Sdan 212ca0720a9Sdan } elseif {[info exists ::testspec($first)]} { 213ca0720a9Sdan set clist $first 214ca0720a9Sdan set patternlist [lrange $patternlist 1 end] 215ca0720a9Sdan } elseif { [llength $patternlist]==0 } { 216ca0720a9Sdan set clist veryquick 217ca0720a9Sdan } else { 218ca0720a9Sdan set clist full 219ca0720a9Sdan } 220ca0720a9Sdan 221ca0720a9Sdan set testset [list] 222ca0720a9Sdan 223ca0720a9Sdan foreach config $clist { 224ca0720a9Sdan catch { array unset O } 225ca0720a9Sdan array set O $::testspec($config) 226ca0720a9Sdan foreach f $O(-files) { 227ca0720a9Sdan if {[file pathtype $f]!="absolute"} { 228ca0720a9Sdan set f [file join $::testdir $f] 229ca0720a9Sdan } 230ca0720a9Sdan lappend testset [list $config [file normalize $f]] 231ca0720a9Sdan } 232ca0720a9Sdan } 233ca0720a9Sdan 234ca0720a9Sdan if {[llength $patternlist]>0} { 235ca0720a9Sdan foreach t $testset { 236ca0720a9Sdan set tail [file tail [lindex $t 1]] 237ca0720a9Sdan foreach p $patternlist { 238ca0720a9Sdan if {[string match $p $tail]} { 239ca0720a9Sdan lappend ret $t 240ca0720a9Sdan break; 241ca0720a9Sdan } 242ca0720a9Sdan } 243ca0720a9Sdan } 244ca0720a9Sdan } else { 245ca0720a9Sdan set ret $testset 246ca0720a9Sdan } 247ca0720a9Sdan 248ca0720a9Sdan set ret 249ca0720a9Sdan} 250ca0720a9Sdan#-------------------------------------------------------------------------- 25105058514Sdan 25205058514Sdan 253e9a7ebe1Sdanproc r_write_db {tcl} { 254e9a7ebe1Sdan global R 255de353fb5Sdan 256de353fb5Sdan sqlite3_test_control_pending_byte 0x010000 257e9a7ebe1Sdan sqlite3 db $R(dbname) 258e9a7ebe1Sdan db timeout $R(timeout) 259e9a7ebe1Sdan db eval { BEGIN EXCLUSIVE } 260e9a7ebe1Sdan 261e9a7ebe1Sdan uplevel $tcl 262e9a7ebe1Sdan 263e9a7ebe1Sdan db eval { COMMIT } 264e9a7ebe1Sdan db close 265e9a7ebe1Sdan} 266e9a7ebe1Sdan 26705058514Sdanproc make_new_testset {} { 26805058514Sdan global R 26905058514Sdan 270ca0720a9Sdan set tests [testset_patternlist $R(patternlist)] 271e9a7ebe1Sdan r_write_db { 27205058514Sdan db eval $R(schema) 273ca0720a9Sdan foreach t $tests { 274ca0720a9Sdan foreach {c s} $t {} 275*cf2ad7aeSdan set slow 0 276*cf2ad7aeSdan 277*cf2ad7aeSdan set fd [open $s] 278*cf2ad7aeSdan for {set ii 0} {$ii<100 && ![eof $fd]} {incr ii} { 279*cf2ad7aeSdan set line [gets $fd] 280*cf2ad7aeSdan if {[string match -nocase *testrunner:* $line]} { 281*cf2ad7aeSdan regexp -nocase {.*testrunner:(.*)} $line -> properties 282*cf2ad7aeSdan foreach p $properties { 283*cf2ad7aeSdan if {$p=="slow"} { set slow 1 } 284*cf2ad7aeSdan } 285*cf2ad7aeSdan } 286*cf2ad7aeSdan } 287*cf2ad7aeSdan close $fd 288*cf2ad7aeSdan 289ca0720a9Sdan db eval { 290*cf2ad7aeSdan INSERT INTO script(config, filename, slow, state) 291*cf2ad7aeSdan VALUES ($c, $s, $slow, 'ready') 292ca0720a9Sdan } 29305058514Sdan } 294e9a7ebe1Sdan } 29505058514Sdan} 29605058514Sdan 297ca0720a9Sdan# Find the next job in the database and mark it as 'running'. Then return 298ca0720a9Sdan# a list consisting of the 299ca0720a9Sdan# 300ca0720a9Sdan# CONFIG FILENAME 301ca0720a9Sdan# 302ca0720a9Sdan# pair for the test. 303ca0720a9Sdan# 30405058514Sdanproc get_next_test {} { 30505058514Sdan global R 30605058514Sdan set myid $R(helper_id) 30705058514Sdan 308e9a7ebe1Sdan r_write_db { 309ca0720a9Sdan set f "" 310ca0720a9Sdan set c "" 311ca0720a9Sdan db eval { 312ca0720a9Sdan SELECT config, filename FROM script WHERE state='ready' 313*cf2ad7aeSdan ORDER BY 314*cf2ad7aeSdan (slow * (($myid+1) % 2)) DESC, 315*cf2ad7aeSdan config!='full', 316*cf2ad7aeSdan config, 317*cf2ad7aeSdan filename 318*cf2ad7aeSdan LIMIT 1 319ca0720a9Sdan } { 320ca0720a9Sdan set c $config 321ca0720a9Sdan set f $filename 322ca0720a9Sdan } 32305058514Sdan if {$f!=""} { 32405058514Sdan db eval { 325ca0720a9Sdan UPDATE script SET state='running', testfixtureid=$myid 326ca0720a9Sdan WHERE (config, filename) = ($c, $f) 32705058514Sdan } 32805058514Sdan } 329e9a7ebe1Sdan } 33005058514Sdan 331ca0720a9Sdan if {$f==""} { return "" } 332ca0720a9Sdan list $c $f 33305058514Sdan} 33405058514Sdan 335ca0720a9Sdanproc r_testname {config filename} { 336ca0720a9Sdan set name [file tail $filename] 337ca0720a9Sdan if {$config!="" && $config!="full" && $config!="veryquick"} { 338ca0720a9Sdan set name "$config-$name" 339ca0720a9Sdan } 340ca0720a9Sdan return $name 341ca0720a9Sdan} 342ca0720a9Sdan 343ca0720a9Sdanproc r_set_test_result {config filename ms nerr ntest output} { 34405058514Sdan global R 34505058514Sdan 346ca0720a9Sdan set f [r_testname $config $filename] 34705058514Sdan if {$nerr==0} { 34805058514Sdan set msg "$f... Ok" 34905058514Sdan } else { 35005058514Sdan set msg "$f... FAILED - $nerr errors of $ntest tests" 35105058514Sdan } 35205058514Sdan append msg " (${ms}ms)" 35305058514Sdan if {$R(helper)} { 35405058514Sdan append msg " (helper $R(helper_id))" 35505058514Sdan } 35605058514Sdan 357e9a7ebe1Sdan sqlite3_shutdown 358e9a7ebe1Sdan set nMalloc [lindex [sqlite3_status SQLITE_STATUS_MALLOC_COUNT 0] 1] 359e9a7ebe1Sdan set nByte [sqlite3_memory_used] 360e9a7ebe1Sdan if {($nByte>0 || $nMalloc>0) && $R(leaker)==""} { 361ca0720a9Sdan set R(leaker) $f 362e9a7ebe1Sdan } 363e9a7ebe1Sdan 36405058514Sdan r_write_db { 36505058514Sdan db eval { 36605058514Sdan UPDATE script 367e9a7ebe1Sdan SET state='done', output=$output, nerr=$nerr, ntest=$ntest, time=$ms 368ca0720a9Sdan WHERE (config, filename)=($config, $filename); 36905058514Sdan 37005058514Sdan INSERT INTO msg(msg) VALUES ($msg); 37105058514Sdan } 37205058514Sdan } 37305058514Sdan} 37405058514Sdan 37505058514Sdanset R(iNextMsg) 1 37605058514Sdanproc r_get_messages {{db ""}} { 37705058514Sdan global R 37805058514Sdan 379de353fb5Sdan sqlite3_test_control_pending_byte 0x010000 380de353fb5Sdan 38105058514Sdan if {$db==""} { 38205058514Sdan sqlite3 rgmhandle $R(dbname) 38305058514Sdan set dbhandle rgmhandle 38405058514Sdan $dbhandle timeout $R(timeout) 38505058514Sdan } else { 38605058514Sdan set dbhandle $db 38705058514Sdan } 38805058514Sdan 38905058514Sdan $dbhandle transaction { 39005058514Sdan set next $R(iNextMsg) 39105058514Sdan set ret [$dbhandle eval {SELECT msg FROM msg WHERE id>=$next}] 39205058514Sdan set R(iNextMsg) [$dbhandle one {SELECT COALESCE(max(id), 0)+1 FROM msg}] 39305058514Sdan } 39405058514Sdan 39505058514Sdan if {$db==""} { 39605058514Sdan rgmhandle close 39705058514Sdan } 39805058514Sdan 39905058514Sdan set ret 40005058514Sdan} 40105058514Sdan 402e9a7ebe1Sdan# This is called after all tests have been run to write the leaked memory 403e9a7ebe1Sdan# report into the malloc table of testrunner.db. 404e9a7ebe1Sdan# 405e9a7ebe1Sdanproc r_memory_report {} { 406e9a7ebe1Sdan global R 407e9a7ebe1Sdan 408e9a7ebe1Sdan sqlite3_shutdown 409e9a7ebe1Sdan 410e9a7ebe1Sdan set nMalloc [lindex [sqlite3_status SQLITE_STATUS_MALLOC_COUNT 0] 1] 411e9a7ebe1Sdan set nByte [sqlite3_memory_used] 412e9a7ebe1Sdan set id $R(helper_id) 413e9a7ebe1Sdan set leaker $R(leaker) 414e9a7ebe1Sdan 415e9a7ebe1Sdan r_write_db { 416e9a7ebe1Sdan db eval { 417e9a7ebe1Sdan INSERT INTO malloc(id, nMalloc, nByte, leaker) 418e9a7ebe1Sdan VALUES($id, $nMalloc, $nByte, $leaker) 419e9a7ebe1Sdan } 420e9a7ebe1Sdan } 421e9a7ebe1Sdan} 422e9a7ebe1Sdan 423e9a7ebe1Sdan 42405058514Sdan#-------------------------------------------------------------------------- 42505058514Sdan# 42605058514Sdanset ::R_INSTALL_PUTS_WRAPPER { 42705058514Sdan proc puts_sts_wrapper {args} { 42805058514Sdan set n [llength $args] 42905058514Sdan if {$n==1 || ($n==2 && [string first [lindex $args 0] -nonewline]==0)} { 43005058514Sdan uplevel puts_into_caller $args 43105058514Sdan } else { 43205058514Sdan # A channel was explicitly specified. 43305058514Sdan uplevel puts_sts_original $args 43405058514Sdan } 43505058514Sdan } 43605058514Sdan rename puts puts_sts_original 43705058514Sdan proc puts {args} { uplevel puts_sts_wrapper $args } 43805058514Sdan} 43905058514Sdan 44005058514Sdanproc r_install_puts_wrapper {} $::R_INSTALL_PUTS_WRAPPER 44105058514Sdanproc r_uninstall_puts_wrapper {} { 44205058514Sdan rename puts "" 44305058514Sdan rename puts_sts_original puts 44405058514Sdan} 44505058514Sdan 44605058514Sdanproc slave_test_script {script} { 44705058514Sdan 44805058514Sdan # Create the interpreter used to run the test script. 44905058514Sdan interp create tinterp 45005058514Sdan 45105058514Sdan # Populate some global variables that tester.tcl expects to see. 45205058514Sdan foreach {var value} [list \ 45305058514Sdan ::argv0 $::argv0 \ 45405058514Sdan ::argv {} \ 45505058514Sdan ::SLAVE 1 \ 45605058514Sdan ] { 45705058514Sdan interp eval tinterp [list set $var $value] 45805058514Sdan } 45905058514Sdan 46005058514Sdan # The alias used to access the global test counters. 46105058514Sdan tinterp alias set_test_counter set_test_counter 46205058514Sdan 46305058514Sdan # Set up an empty ::cmdlinearg array in the slave. 46405058514Sdan interp eval tinterp [list array set ::cmdlinearg [array get ::cmdlinearg]] 46505058514Sdan 46605058514Sdan # Set up the ::G array in the slave. 46705058514Sdan interp eval tinterp [list array set ::G [array get ::G]] 46805058514Sdan interp eval tinterp [list set ::G(runner.tcl) 1] 46905058514Sdan 47005058514Sdan interp eval tinterp $::R_INSTALL_PUTS_WRAPPER 47105058514Sdan tinterp alias puts_into_caller puts_into_caller 47205058514Sdan 47305058514Sdan # Load the various test interfaces implemented in C. 47405058514Sdan load_testfixture_extensions tinterp 47505058514Sdan 47605058514Sdan # Run the test script. 47705058514Sdan set rc [catch { interp eval tinterp $script } msg opt] 47805058514Sdan if {$rc} { 47905058514Sdan puts_into_caller $msg 48005058514Sdan puts_into_caller [dict get $opt -errorinfo] 48105058514Sdan incr ::TC(errors) 48205058514Sdan } 48305058514Sdan 48405058514Sdan # Check if the interpreter call [run_thread_tests] 48505058514Sdan if { [interp eval tinterp {info exists ::run_thread_tests_called}] } { 48605058514Sdan set ::run_thread_tests_called 1 48705058514Sdan } 48805058514Sdan 48905058514Sdan # Delete the interpreter used to run the test script. 49005058514Sdan interp delete tinterp 49105058514Sdan} 49205058514Sdan 49305058514Sdanproc slave_test_file {zFile} { 49405058514Sdan set tail [file tail $zFile] 49505058514Sdan 49605058514Sdan # Remember the value of the shared-cache setting. So that it is possible 49705058514Sdan # to check afterwards that it was not modified by the test script. 49805058514Sdan # 49905058514Sdan ifcapable shared_cache { set scs [sqlite3_enable_shared_cache] } 50005058514Sdan 50105058514Sdan # Run the test script in a slave interpreter. 50205058514Sdan # 50305058514Sdan unset -nocomplain ::run_thread_tests_called 50405058514Sdan reset_prng_state 50505058514Sdan set ::sqlite_open_file_count 0 50605058514Sdan set time [time { slave_test_script [list source $zFile] }] 50705058514Sdan set ms [expr [lindex $time 0] / 1000] 50805058514Sdan 50905058514Sdan r_install_puts_wrapper 51005058514Sdan 51105058514Sdan # Test that all files opened by the test script were closed. Omit this 51205058514Sdan # if the test script has "thread" in its name. The open file counter 51305058514Sdan # is not thread-safe. 51405058514Sdan # 51505058514Sdan if {[info exists ::run_thread_tests_called]==0} { 51605058514Sdan do_test ${tail}-closeallfiles { expr {$::sqlite_open_file_count>0} } {0} 51705058514Sdan } 51805058514Sdan set ::sqlite_open_file_count 0 51905058514Sdan 52005058514Sdan # Test that the global "shared-cache" setting was not altered by 52105058514Sdan # the test script. 52205058514Sdan # 52305058514Sdan ifcapable shared_cache { 52405058514Sdan set res [expr {[sqlite3_enable_shared_cache] == $scs}] 52505058514Sdan do_test ${tail}-sharedcachesetting [list set {} $res] 1 52605058514Sdan } 52705058514Sdan 52805058514Sdan # Add some info to the output. 52905058514Sdan # 53005058514Sdan output2 "Time: $tail $ms ms" 53105058514Sdan show_memstats 53205058514Sdan 53305058514Sdan r_uninstall_puts_wrapper 53405058514Sdan return $ms 53505058514Sdan} 53605058514Sdan 53705058514Sdanproc puts_into_caller {args} { 53805058514Sdan global R 53905058514Sdan if {[llength $args]==1} { 54005058514Sdan append R(output) [lindex $args 0] 54105058514Sdan append R(output) "\n" 54205058514Sdan } else { 54305058514Sdan append R(output) [lindex $args 1] 54405058514Sdan } 54505058514Sdan} 54605058514Sdan 547e9a7ebe1Sdan#------------------------------------------------------------------------- 548e9a7ebe1Sdan# 549e9a7ebe1Sdanproc r_final_report {} { 550e9a7ebe1Sdan global R 551e9a7ebe1Sdan 552de353fb5Sdan sqlite3_test_control_pending_byte 0x010000 553e9a7ebe1Sdan sqlite3 db $R(dbname) 554de353fb5Sdan 555e9a7ebe1Sdan db timeout $R(timeout) 556e9a7ebe1Sdan 557e9a7ebe1Sdan set errcode 0 558e9a7ebe1Sdan 559e9a7ebe1Sdan # Create the text log file. This is just the concatenation of the 560e9a7ebe1Sdan # 'output' column of the database for every script that was run. 561e9a7ebe1Sdan set fd [open $R(logname) w] 562ca0720a9Sdan db eval {SELECT output FROM script ORDER BY config!='full',config,filename} { 563e9a7ebe1Sdan puts $fd $output 564e9a7ebe1Sdan } 565e9a7ebe1Sdan close $fd 566e9a7ebe1Sdan 567e9a7ebe1Sdan # Check if any scripts reported errors. If so, print one line noting 568e9a7ebe1Sdan # how many errors, and another identifying the scripts in which they 569e9a7ebe1Sdan # occured. Or, if no errors occurred, print out "no errors at all!". 570e9a7ebe1Sdan sqlite3 db $R(dbname) 571e9a7ebe1Sdan db timeout $R(timeout) 572e9a7ebe1Sdan db eval { SELECT sum(nerr) AS nerr, sum(ntest) AS ntest FROM script } { } 573e9a7ebe1Sdan puts "$nerr errors from $ntest tests." 574e9a7ebe1Sdan if {$nerr>0} { 575ca0720a9Sdan db eval { SELECT config, filename FROM script WHERE nerr>0 } { 576ca0720a9Sdan lappend errlist [r_testname $config $filename] 577e9a7ebe1Sdan } 578e9a7ebe1Sdan puts "Errors in: $errlist" 579e9a7ebe1Sdan set errcode 1 580e9a7ebe1Sdan } 581e9a7ebe1Sdan 582e9a7ebe1Sdan # Check if any scripts were not run or did not finish. Print out a 583e9a7ebe1Sdan # line identifying them if there are any. 584e9a7ebe1Sdan set errlist [list] 585ca0720a9Sdan db eval { SELECT config, filename FROM script WHERE state!='done' } { 586ca0720a9Sdan lappend errlist [r_testname $config $filename] 587e9a7ebe1Sdan } 588e9a7ebe1Sdan if {$errlist!=[list]} { 589e9a7ebe1Sdan puts "Tests DID NOT FINISH (crashed?): $errlist" 590e9a7ebe1Sdan set errcode 1 591e9a7ebe1Sdan } 592e9a7ebe1Sdan 593e9a7ebe1Sdan set bLeak 0 594e9a7ebe1Sdan db eval { 595e9a7ebe1Sdan SELECT id, nmalloc, nbyte, leaker FROM malloc 596e9a7ebe1Sdan WHERE nmalloc>0 OR nbyte>0 597e9a7ebe1Sdan } { 598e9a7ebe1Sdan if {$id==0} { 599e9a7ebe1Sdan set line "This process " 600e9a7ebe1Sdan } else { 601e9a7ebe1Sdan set line "Helper $id " 602e9a7ebe1Sdan } 603e9a7ebe1Sdan append line "leaked $nbyte byte in $nmalloc allocations" 604e9a7ebe1Sdan if {$leaker!=""} { append line " (perhaps in [file tail $leaker])" } 605e9a7ebe1Sdan puts $line 606e9a7ebe1Sdan set bLeak 1 607e9a7ebe1Sdan } 608e9a7ebe1Sdan if {$bLeak==0} { 609e9a7ebe1Sdan puts "No leaks - all allocations freed." 610e9a7ebe1Sdan } 611e9a7ebe1Sdan 612e9a7ebe1Sdan db close 613e9a7ebe1Sdan 614e9a7ebe1Sdan puts "Test database is $R(dbname)" 615e9a7ebe1Sdan puts "Test log file is $R(logname)" 616e9a7ebe1Sdan if {$errcode} { 617e9a7ebe1Sdan puts "This test has FAILED." 618e9a7ebe1Sdan } 619e9a7ebe1Sdan return $errcode 620e9a7ebe1Sdan} 621e9a7ebe1Sdan 622e9a7ebe1Sdan 62305058514Sdanif {$R(helper)==0} { 62405058514Sdan make_new_testset 625e9a7ebe1Sdan} 626e9a7ebe1Sdan 627e9a7ebe1Sdanset R(nHelperRunning) 0 628e9a7ebe1Sdanif {$R(helper)==0 && $R(nJob)>1} { 629e9a7ebe1Sdan cd $cmdlinearg(TESTFIXTURE_HOME) 630e9a7ebe1Sdan for {set ii 1} {$ii <= $R(nJob)} {incr ii} { 63105058514Sdan set cmd "[info nameofexec] $R(info_script) helper $ii 2>@1" 63205058514Sdan puts "Launching helper $ii ($cmd)" 63305058514Sdan set chan [open "|$cmd" r] 63405058514Sdan fconfigure $chan -blocking false 63505058514Sdan fileevent $chan readable [list r_helper_readable $ii $chan] 63605058514Sdan incr R(nHelperRunning) 63705058514Sdan } 63805058514Sdan cd $cmdlinearg(testdir) 63905058514Sdan} 64005058514Sdan 64105058514Sdanproc r_helper_readable {id chan} { 642e9a7ebe1Sdan set data [gets $chan] 643de353fb5Sdan if {$data!=""} { puts "helper $id:$data" } 64405058514Sdan if {[eof $chan]} { 645e9a7ebe1Sdan puts "helper $id is finished" 64605058514Sdan incr ::R(nHelperRunning) -1 64705058514Sdan close $chan 64805058514Sdan } 64905058514Sdan} 65005058514Sdan 651e9a7ebe1Sdanif {$R(nHelperRunning)==0} { 652ca0720a9Sdan while { ""!=[set t [get_next_test]] } { 65305058514Sdan set R(output) "" 65405058514Sdan set TC(count) 0 65505058514Sdan set TC(errors) 0 656e9a7ebe1Sdan 657ca0720a9Sdan foreach {config filename} $t {} 658ca0720a9Sdan 659ca0720a9Sdan array set O $::testspec($config) 660ca0720a9Sdan set ::G(perm:name) $config 661ca0720a9Sdan set ::G(perm:prefix) $O(-prefix) 662ca0720a9Sdan set ::G(isquick) 1 663ca0720a9Sdan set ::G(perm:dbconfig) $O(-dbconfig) 664ca0720a9Sdan set ::G(perm:presql) $O(-presql) 665ca0720a9Sdan 666ca0720a9Sdan eval $O(-initialize) 667ca0720a9Sdan set ms [slave_test_file $filename] 668ca0720a9Sdan eval $O(-shutdown) 669ca0720a9Sdan 670ca0720a9Sdan unset -nocomplain ::G(perm:sqlite3_args) 671ca0720a9Sdan unset ::G(perm:name) 672ca0720a9Sdan unset ::G(perm:prefix) 673ca0720a9Sdan unset ::G(perm:dbconfig) 674ca0720a9Sdan unset ::G(perm:presql) 675ca0720a9Sdan 676ca0720a9Sdan r_set_test_result $config $filename $ms $TC(errors) $TC(count) $R(output) 67705058514Sdan 67805058514Sdan if {$R(helper)==0} { 67905058514Sdan foreach msg [r_get_messages] { puts $msg } 68005058514Sdan } 68105058514Sdan } 68205058514Sdan 683e9a7ebe1Sdan # Tests are finished - write a record into testrunner.db describing 684e9a7ebe1Sdan # any memory leaks. 685e9a7ebe1Sdan r_memory_report 686e9a7ebe1Sdan 687e9a7ebe1Sdan} else { 68805058514Sdan set TTT 0 68905058514Sdan sqlite3 db $R(dbname) 69005058514Sdan db timeout $R(timeout) 69105058514Sdan while {$R(nHelperRunning)>0} { 69205058514Sdan after 250 { incr TTT } 69305058514Sdan vwait TTT 69405058514Sdan foreach msg [r_get_messages db] { puts $msg } 69505058514Sdan } 69605058514Sdan db close 697e9a7ebe1Sdan} 69805058514Sdan 69905058514Sdanset errcode 0 70005058514Sdanif {$R(helper)==0} { 701e9a7ebe1Sdan set errcode [r_final_report] 70205058514Sdan} 70305058514Sdan 70405058514Sdanexit $errcode 70505058514Sdan 706