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