xref: /sqlite-3.40.0/test/mallocA.test (revision a408adc5)
1# 2007 April 30
2#
3# The author disclaims copyright to this source code.  In place of
4# a legal notice, here is a blessing:
5#
6#    May you do good and not evil.
7#    May you find forgiveness for yourself and forgive others.
8#    May you share freely, never taking more than you give.
9#
10#***********************************************************************
11# This file contains additional out-of-memory checks (see malloc.tcl).
12#
13# $Id: mallocA.test,v 1.2 2007/05/12 15:00:15 drh Exp $
14
15set testdir [file dirname $argv0]
16source $testdir/tester.tcl
17
18# Only run these tests if memory debugging is turned on.
19#
20if {[info command sqlite_malloc_stat]==""} {
21   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
22   finish_test
23   return
24}
25
26# Usage: do_malloc_test <test number> <options...>
27#
28# The first argument, <test number>, is an integer used to name the
29# tests executed by this proc. Options are as follows:
30#
31#     -tclprep          TCL script to run to prepare test.
32#     -sqlprep          SQL script to run to prepare test.
33#     -tclbody          TCL script to run with malloc failure simulation.
34#     -sqlbody          TCL script to run with malloc failure simulation.
35#     -cleanup          TCL script to run after the test.
36#
37# This command runs a series of tests to verify SQLite's ability
38# to handle an out-of-memory condition gracefully. It is assumed
39# that if this condition occurs a malloc() call will return a
40# NULL pointer. Linux, for example, doesn't do that by default. See
41# the "BUGS" section of malloc(3).
42#
43# Each iteration of a loop, the TCL commands in any argument passed
44# to the -tclbody switch, followed by the SQL commands in any argument
45# passed to the -sqlbody switch are executed. Each iteration the
46# Nth call to sqliteMalloc() is made to fail, where N is increased
47# each time the loop runs starting from 1. When all commands execute
48# successfully, the loop ends.
49#
50proc do_malloc_test {tn args} {
51  array unset ::mallocopts
52  array set ::mallocopts $args
53
54  set ::go 1
55  for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
56    do_test mallocA-$tn.$::n {
57
58      sqlite_malloc_fail 0
59      catch {db close}
60      catch {file delete -force test.db test.db-journal}
61      catch {file copy test.db.bu test.db}
62      sqlite3 db test.db
63      set ::DB [sqlite3_connection_pointer db]
64
65      # Execute any -tclprep and -sqlprep scripts.
66      #
67      if {[info exists ::mallocopts(-tclprep)]} {
68        eval $::mallocopts(-tclprep)
69      }
70      if {[info exists ::mallocopts(-sqlprep)]} {
71        execsql $::mallocopts(-sqlprep)
72      }
73
74      # Now set the ${::n}th malloc() to fail and execute the -tclbody and
75      # -sqlbody scripts.
76      #
77      sqlite_malloc_fail $::n
78      set ::mallocbody {}
79      if {[info exists ::mallocopts(-tclbody)]} {
80        append ::mallocbody "$::mallocopts(-tclbody)\n"
81      }
82      if {[info exists ::mallocopts(-sqlbody)]} {
83        append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
84      }
85      set v [catch $::mallocbody msg]
86
87      # If the test fails (if $v!=0) and the database connection actually
88      # exists, make sure the failure code is SQLITE_NOMEM.
89      if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
90              && [db errorcode]!=7} {
91        set v 999
92      }
93
94      set leftover [lindex [sqlite_malloc_stat] 2]
95      if {$leftover>0} {
96        if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v  Message=$msg"}
97        set ::go 0
98        if {$v} {
99          puts "\nError message returned: $msg"
100        } else {
101          set v {1 1}
102        }
103      } else {
104        set v2 [expr {$msg=="" || [regexp {out of memory} $msg]}]
105        if {!$v2} {puts "\nError message returned: $msg"}
106        lappend v $v2
107      }
108    } {1 1}
109
110    if {[info exists ::mallocopts(-cleanup)]} {
111      catch [list uplevel #0 $::mallocopts(-cleanup)] msg
112    }
113  }
114  unset ::mallocopts
115}
116
117# Construct a test database
118#
119file delete -force test.db.bu
120db eval {
121  CREATE TABLE t1(a COLLATE NOCASE,b,c);
122  INSERT INTO t1 VALUES(1,2,3);
123  INSERT INTO t1 VALUES(1,2,4);
124  INSERT INTO t1 VALUES(2,3,4);
125  CREATE INDEX t1i1 ON t1(a);
126  CREATE INDEX t1i2 ON t1(b,c);
127  CREATE TABLE t2(x,y,z);
128}
129db close
130file copy test.db test.db.bu
131sqlite3 db test.db
132
133
134do_malloc_test 1 -sqlbody {
135  ANALYZE
136}
137do_malloc_test 2 -sqlbody {
138  REINDEX;
139}
140do_malloc_test 3 -sqlbody {
141  REINDEX t1;
142}
143do_malloc_test 4 -sqlbody {
144  REINDEX main.t1;
145}
146do_malloc_test 5 -sqlbody {
147  REINDEX nocase;
148}
149
150# Ensure that no file descriptors were leaked.
151do_test malloc-99.X {
152  catch {db close}
153  set sqlite_open_file_count
154} {0}
155
156file delete -force test.db.bu
157sqlite_malloc_fail 0
158finish_test
159