xref: /illumos-gate/usr/src/lib/libsqlite/test/tester.tcl (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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