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