blob: 1e84c53f108c908d22295aee70254f1965888377 [file] [log] [blame]
danielk1977c9cf9012007-05-30 10:36:47 +00001
2# Usage: do_malloc_test <test number> <options...>
3#
4# The first argument, <test number>, is an integer used to name the
5# tests executed by this proc. Options are as follows:
6#
7# -tclprep TCL script to run to prepare test.
8# -sqlprep SQL script to run to prepare test.
9# -tclbody TCL script to run with malloc failure simulation.
10# -sqlbody TCL script to run with malloc failure simulation.
11# -cleanup TCL script to run after the test.
12#
13# This command runs a series of tests to verify SQLite's ability
14# to handle an out-of-memory condition gracefully. It is assumed
15# that if this condition occurs a malloc() call will return a
16# NULL pointer. Linux, for example, doesn't do that by default. See
17# the "BUGS" section of malloc(3).
18#
19# Each iteration of a loop, the TCL commands in any argument passed
20# to the -tclbody switch, followed by the SQL commands in any argument
21# passed to the -sqlbody switch are executed. Each iteration the
22# Nth call to sqliteMalloc() is made to fail, where N is increased
23# each time the loop runs starting from 1. When all commands execute
24# successfully, the loop ends.
25#
26proc do_malloc_test {tn args} {
27 array unset ::mallocopts
28 array set ::mallocopts $args
29
30 if {[string is integer $tn]} {
31 set tn malloc-$tn
32 }
33
34 set ::go 1
35 for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
36 do_test $tn.$::n {
37
38 # Remove all traces of database files test.db and test2.db from the files
39 # system. Then open (empty database) "test.db" with the handle [db].
40 #
41 sqlite_malloc_fail 0
42 catch {db close}
43 catch {file delete -force test.db}
44 catch {file delete -force test.db-journal}
45 catch {file delete -force test2.db}
46 catch {file delete -force test2.db-journal}
47 catch {sqlite3 db test.db}
48 set ::DB [sqlite3_connection_pointer db]
49
50 # Execute any -tclprep and -sqlprep scripts.
51 #
52 if {[info exists ::mallocopts(-tclprep)]} {
53 eval $::mallocopts(-tclprep)
54 }
55 if {[info exists ::mallocopts(-sqlprep)]} {
56 execsql $::mallocopts(-sqlprep)
57 }
58
59 # Now set the ${::n}th malloc() to fail and execute the -tclbody and
60 # -sqlbody scripts.
61 #
62 sqlite_malloc_fail $::n
63 set ::mallocbody {}
64 if {[info exists ::mallocopts(-tclbody)]} {
65 append ::mallocbody "$::mallocopts(-tclbody)\n"
66 }
67 if {[info exists ::mallocopts(-sqlbody)]} {
68 append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
69 }
70 set v [catch $::mallocbody msg]
71
72 # If the test fails (if $v!=0) and the database connection actually
73 # exists, make sure the failure code is SQLITE_NOMEM.
74 if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
75 && [db errorcode]!=7} {
76 set v 999
77 }
78
79 set leftover [lindex [sqlite_malloc_stat] 2]
80 if {$leftover>0} {
81 if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v Message=$msg"}
82 set ::go 0
83 if {$v} {
84 puts "\nError message returned: $msg"
85 } else {
86 set v {1 1}
87 }
88 } else {
89 set v2 [expr {$msg=="" || $msg=="out of memory"}]
90 if {!$v2} {puts "\nError message returned: $msg"}
91 lappend v $v2
92 }
93 } {1 1}
94
95 if {[info exists ::mallocopts(-cleanup)]} {
96 catch [list uplevel #0 $::mallocopts(-cleanup)] msg
97 }
98 }
99 unset ::mallocopts
100}
101