1*c5c4113dSnw141292 2*c5c4113dSnw141292#pragma ident "%Z%%M% %I% %E% SMI" 3*c5c4113dSnw141292 4*c5c4113dSnw141292# 2001 September 15 5*c5c4113dSnw141292# 6*c5c4113dSnw141292# The author disclaims copyright to this source code. In place of 7*c5c4113dSnw141292# a legal notice, here is a blessing: 8*c5c4113dSnw141292# 9*c5c4113dSnw141292# May you do good and not evil. 10*c5c4113dSnw141292# May you find forgiveness for yourself and forgive others. 11*c5c4113dSnw141292# May you share freely, never taking more than you give. 12*c5c4113dSnw141292# 13*c5c4113dSnw141292#*********************************************************************** 14*c5c4113dSnw141292# This file implements some common TCL routines used for regression 15*c5c4113dSnw141292# testing the SQLite library 16*c5c4113dSnw141292# 17*c5c4113dSnw141292# $Id: tester.tcl,v 1.28 2004/02/14 01:39:50 drh Exp $ 18*c5c4113dSnw141292 19*c5c4113dSnw141292# Make sure tclsqlite was compiled correctly. Abort now with an 20*c5c4113dSnw141292# error message if not. 21*c5c4113dSnw141292# 22*c5c4113dSnw141292if {[sqlite -tcl-uses-utf]} { 23*c5c4113dSnw141292 if {"\u1234"=="u1234"} { 24*c5c4113dSnw141292 puts stderr "***** BUILD PROBLEM *****" 25*c5c4113dSnw141292 puts stderr "$argv0 was linked against an older version" 26*c5c4113dSnw141292 puts stderr "of TCL that does not support Unicode, but uses a header" 27*c5c4113dSnw141292 puts stderr "file (\"tcl.h\") from a new TCL version that does support" 28*c5c4113dSnw141292 puts stderr "Unicode. This combination causes internal errors." 29*c5c4113dSnw141292 puts stderr "Recompile using a TCL library and header file that match" 30*c5c4113dSnw141292 puts stderr "and try again.\n**************************" 31*c5c4113dSnw141292 exit 1 32*c5c4113dSnw141292 } 33*c5c4113dSnw141292} else { 34*c5c4113dSnw141292 if {"\u1234"!="u1234"} { 35*c5c4113dSnw141292 puts stderr "***** BUILD PROBLEM *****" 36*c5c4113dSnw141292 puts stderr "$argv0 was linked against an newer version" 37*c5c4113dSnw141292 puts stderr "of TCL that supports Unicode, but uses a header file" 38*c5c4113dSnw141292 puts stderr "(\"tcl.h\") from a old TCL version that does not support" 39*c5c4113dSnw141292 puts stderr "Unicode. This combination causes internal errors." 40*c5c4113dSnw141292 puts stderr "Recompile using a TCL library and header file that match" 41*c5c4113dSnw141292 puts stderr "and try again.\n**************************" 42*c5c4113dSnw141292 exit 1 43*c5c4113dSnw141292 } 44*c5c4113dSnw141292} 45*c5c4113dSnw141292 46*c5c4113dSnw141292# Use the pager codec if it is available 47*c5c4113dSnw141292# 48*c5c4113dSnw141292if {[sqlite -has-codec] && [info command sqlite_orig]==""} { 49*c5c4113dSnw141292 rename sqlite sqlite_orig 50*c5c4113dSnw141292 proc sqlite {args} { 51*c5c4113dSnw141292 if {[llength $args]==2 && [string index [lindex $args 0] 0]!="-"} { 52*c5c4113dSnw141292 lappend args -key {xyzzy} 53*c5c4113dSnw141292 } 54*c5c4113dSnw141292 uplevel 1 sqlite_orig $args 55*c5c4113dSnw141292 } 56*c5c4113dSnw141292} 57*c5c4113dSnw141292 58*c5c4113dSnw141292 59*c5c4113dSnw141292# Create a test database 60*c5c4113dSnw141292# 61*c5c4113dSnw141292catch {db close} 62*c5c4113dSnw141292file delete -force test.db 63*c5c4113dSnw141292file delete -force test.db-journal 64*c5c4113dSnw141292sqlite db ./test.db 65*c5c4113dSnw141292if {[info exists ::SETUP_SQL]} { 66*c5c4113dSnw141292 db eval $::SETUP_SQL 67*c5c4113dSnw141292} 68*c5c4113dSnw141292 69*c5c4113dSnw141292# Abort early if this script has been run before. 70*c5c4113dSnw141292# 71*c5c4113dSnw141292if {[info exists nTest]} return 72*c5c4113dSnw141292 73*c5c4113dSnw141292# Set the test counters to zero 74*c5c4113dSnw141292# 75*c5c4113dSnw141292set nErr 0 76*c5c4113dSnw141292set nTest 0 77*c5c4113dSnw141292set nProb 0 78*c5c4113dSnw141292set skip_test 0 79*c5c4113dSnw141292set failList {} 80*c5c4113dSnw141292 81*c5c4113dSnw141292# Invoke the do_test procedure to run a single test 82*c5c4113dSnw141292# 83*c5c4113dSnw141292proc do_test {name cmd expected} { 84*c5c4113dSnw141292 global argv nErr nTest skip_test 85*c5c4113dSnw141292 if {$skip_test} { 86*c5c4113dSnw141292 set skip_test 0 87*c5c4113dSnw141292 return 88*c5c4113dSnw141292 } 89*c5c4113dSnw141292 if {[llength $argv]==0} { 90*c5c4113dSnw141292 set go 1 91*c5c4113dSnw141292 } else { 92*c5c4113dSnw141292 set go 0 93*c5c4113dSnw141292 foreach pattern $argv { 94*c5c4113dSnw141292 if {[string match $pattern $name]} { 95*c5c4113dSnw141292 set go 1 96*c5c4113dSnw141292 break 97*c5c4113dSnw141292 } 98*c5c4113dSnw141292 } 99*c5c4113dSnw141292 } 100*c5c4113dSnw141292 if {!$go} return 101*c5c4113dSnw141292 incr nTest 102*c5c4113dSnw141292 puts -nonewline $name... 103*c5c4113dSnw141292 flush stdout 104*c5c4113dSnw141292 if {[catch {uplevel #0 "$cmd;\n"} result]} { 105*c5c4113dSnw141292 puts "\nError: $result" 106*c5c4113dSnw141292 incr nErr 107*c5c4113dSnw141292 lappend ::failList $name 108*c5c4113dSnw141292 if {$nErr>100} {puts "*** Giving up..."; finalize_testing} 109*c5c4113dSnw141292 } elseif {[string compare $result $expected]} { 110*c5c4113dSnw141292 puts "\nExpected: \[$expected\]\n Got: \[$result\]" 111*c5c4113dSnw141292 incr nErr 112*c5c4113dSnw141292 lappend ::failList $name 113*c5c4113dSnw141292 if {$nErr>100} {puts "*** Giving up..."; finalize_testing} 114*c5c4113dSnw141292 } else { 115*c5c4113dSnw141292 puts " Ok" 116*c5c4113dSnw141292 } 117*c5c4113dSnw141292} 118*c5c4113dSnw141292 119*c5c4113dSnw141292# Invoke this procedure on a test that is probabilistic 120*c5c4113dSnw141292# and might fail sometimes. 121*c5c4113dSnw141292# 122*c5c4113dSnw141292proc do_probtest {name cmd expected} { 123*c5c4113dSnw141292 global argv nProb nTest skip_test 124*c5c4113dSnw141292 if {$skip_test} { 125*c5c4113dSnw141292 set skip_test 0 126*c5c4113dSnw141292 return 127*c5c4113dSnw141292 } 128*c5c4113dSnw141292 if {[llength $argv]==0} { 129*c5c4113dSnw141292 set go 1 130*c5c4113dSnw141292 } else { 131*c5c4113dSnw141292 set go 0 132*c5c4113dSnw141292 foreach pattern $argv { 133*c5c4113dSnw141292 if {[string match $pattern $name]} { 134*c5c4113dSnw141292 set go 1 135*c5c4113dSnw141292 break 136*c5c4113dSnw141292 } 137*c5c4113dSnw141292 } 138*c5c4113dSnw141292 } 139*c5c4113dSnw141292 if {!$go} return 140*c5c4113dSnw141292 incr nTest 141*c5c4113dSnw141292 puts -nonewline $name... 142*c5c4113dSnw141292 flush stdout 143*c5c4113dSnw141292 if {[catch {uplevel #0 "$cmd;\n"} result]} { 144*c5c4113dSnw141292 puts "\nError: $result" 145*c5c4113dSnw141292 incr nErr 146*c5c4113dSnw141292 } elseif {[string compare $result $expected]} { 147*c5c4113dSnw141292 puts "\nExpected: \[$expected\]\n Got: \[$result\]" 148*c5c4113dSnw141292 puts "NOTE: The results of the previous test depend on system load" 149*c5c4113dSnw141292 puts "and processor speed. The test may sometimes fail even if the" 150*c5c4113dSnw141292 puts "library is working correctly." 151*c5c4113dSnw141292 incr nProb 152*c5c4113dSnw141292 } else { 153*c5c4113dSnw141292 puts " Ok" 154*c5c4113dSnw141292 } 155*c5c4113dSnw141292} 156*c5c4113dSnw141292 157*c5c4113dSnw141292# The procedure uses the special "sqlite_malloc_stat" command 158*c5c4113dSnw141292# (which is only available if SQLite is compiled with -DMEMORY_DEBUG=1) 159*c5c4113dSnw141292# to see how many malloc()s have not been free()ed. The number 160*c5c4113dSnw141292# of surplus malloc()s is stored in the global variable $::Leak. 161*c5c4113dSnw141292# If the value in $::Leak grows, it may mean there is a memory leak 162*c5c4113dSnw141292# in the library. 163*c5c4113dSnw141292# 164*c5c4113dSnw141292proc memleak_check {} { 165*c5c4113dSnw141292 if {[info command sqlite_malloc_stat]!=""} { 166*c5c4113dSnw141292 set r [sqlite_malloc_stat] 167*c5c4113dSnw141292 set ::Leak [expr {[lindex $r 0]-[lindex $r 1]}] 168*c5c4113dSnw141292 } 169*c5c4113dSnw141292} 170*c5c4113dSnw141292 171*c5c4113dSnw141292# Run this routine last 172*c5c4113dSnw141292# 173*c5c4113dSnw141292proc finish_test {} { 174*c5c4113dSnw141292 finalize_testing 175*c5c4113dSnw141292} 176*c5c4113dSnw141292proc finalize_testing {} { 177*c5c4113dSnw141292 global nTest nErr nProb sqlite_open_file_count 178*c5c4113dSnw141292 if {$nErr==0} memleak_check 179*c5c4113dSnw141292 catch {db close} 180*c5c4113dSnw141292 puts "$nErr errors out of $nTest tests" 181*c5c4113dSnw141292 puts "Failures on these tests: $::failList" 182*c5c4113dSnw141292 if {$nProb>0} { 183*c5c4113dSnw141292 puts "$nProb probabilistic tests also failed, but this does" 184*c5c4113dSnw141292 puts "not necessarily indicate a malfunction." 185*c5c4113dSnw141292 } 186*c5c4113dSnw141292 if {$sqlite_open_file_count} { 187*c5c4113dSnw141292 puts "$sqlite_open_file_count files were left open" 188*c5c4113dSnw141292 incr nErr 189*c5c4113dSnw141292 } 190*c5c4113dSnw141292 exit [expr {$nErr>0}] 191*c5c4113dSnw141292} 192*c5c4113dSnw141292 193*c5c4113dSnw141292# A procedure to execute SQL 194*c5c4113dSnw141292# 195*c5c4113dSnw141292proc execsql {sql {db db}} { 196*c5c4113dSnw141292 # puts "SQL = $sql" 197*c5c4113dSnw141292 return [$db eval $sql] 198*c5c4113dSnw141292} 199*c5c4113dSnw141292 200*c5c4113dSnw141292# Execute SQL and catch exceptions. 201*c5c4113dSnw141292# 202*c5c4113dSnw141292proc catchsql {sql {db db}} { 203*c5c4113dSnw141292 # puts "SQL = $sql" 204*c5c4113dSnw141292 set r [catch {$db eval $sql} msg] 205*c5c4113dSnw141292 lappend r $msg 206*c5c4113dSnw141292 return $r 207*c5c4113dSnw141292} 208*c5c4113dSnw141292 209*c5c4113dSnw141292# Do an VDBE code dump on the SQL given 210*c5c4113dSnw141292# 211*c5c4113dSnw141292proc explain {sql {db db}} { 212*c5c4113dSnw141292 puts "" 213*c5c4113dSnw141292 puts "addr opcode p1 p2 p3 " 214*c5c4113dSnw141292 puts "---- ------------ ------ ------ ---------------" 215*c5c4113dSnw141292 $db eval "explain $sql" {} { 216*c5c4113dSnw141292 puts [format {%-4d %-12.12s %-6d %-6d %s} $addr $opcode $p1 $p2 $p3] 217*c5c4113dSnw141292 } 218*c5c4113dSnw141292} 219*c5c4113dSnw141292 220*c5c4113dSnw141292# Another procedure to execute SQL. This one includes the field 221*c5c4113dSnw141292# names in the returned list. 222*c5c4113dSnw141292# 223*c5c4113dSnw141292proc execsql2 {sql} { 224*c5c4113dSnw141292 set result {} 225*c5c4113dSnw141292 db eval $sql data { 226*c5c4113dSnw141292 foreach f $data(*) { 227*c5c4113dSnw141292 lappend result $f $data($f) 228*c5c4113dSnw141292 } 229*c5c4113dSnw141292 } 230*c5c4113dSnw141292 return $result 231*c5c4113dSnw141292} 232*c5c4113dSnw141292 233*c5c4113dSnw141292# Use the non-callback API to execute multiple SQL statements 234*c5c4113dSnw141292# 235*c5c4113dSnw141292proc stepsql {dbptr sql} { 236*c5c4113dSnw141292 set sql [string trim $sql] 237*c5c4113dSnw141292 set r 0 238*c5c4113dSnw141292 while {[string length $sql]>0} { 239*c5c4113dSnw141292 if {[catch {sqlite_compile $dbptr $sql sqltail} vm]} { 240*c5c4113dSnw141292 return [list 1 $vm] 241*c5c4113dSnw141292 } 242*c5c4113dSnw141292 set sql [string trim $sqltail] 243*c5c4113dSnw141292 while {[sqlite_step $vm N VAL COL]=="SQLITE_ROW"} { 244*c5c4113dSnw141292 foreach v $VAL {lappend r $v} 245*c5c4113dSnw141292 } 246*c5c4113dSnw141292 if {[catch {sqlite_finalize $vm} errmsg]} { 247*c5c4113dSnw141292 return [list 1 $errmsg] 248*c5c4113dSnw141292 } 249*c5c4113dSnw141292 } 250*c5c4113dSnw141292 return $r 251*c5c4113dSnw141292} 252*c5c4113dSnw141292 253*c5c4113dSnw141292# Delete a file or directory 254*c5c4113dSnw141292# 255*c5c4113dSnw141292proc forcedelete {filename} { 256*c5c4113dSnw141292 if {[catch {file delete -force $filename}]} { 257*c5c4113dSnw141292 exec rm -rf $filename 258*c5c4113dSnw141292 } 259*c5c4113dSnw141292} 260*c5c4113dSnw141292 261*c5c4113dSnw141292# Do an integrity check of the entire database 262*c5c4113dSnw141292# 263*c5c4113dSnw141292proc integrity_check {name} { 264*c5c4113dSnw141292 do_test $name { 265*c5c4113dSnw141292 execsql {PRAGMA integrity_check} 266*c5c4113dSnw141292 } {ok} 267*c5c4113dSnw141292} 268