xref: /sqlite-3.40.0/test/testrunner.tcl (revision cf2ad7ae)
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