/ Check-in [b627e153]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Change the OOM and IO error test cases in walfault.test so that each test case runs both types of error simulation.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: b627e1536822bb7e3ef91867661a53be0efc13ef
User & Date: dan 2010-06-01 17:46:38
Context
2010-06-01
19:15
Changes to the way faults are injected into xShmXXX VFS calls. check-in: 716d99f3 user: dan tags: trunk
17:46
Change the OOM and IO error test cases in walfault.test so that each test case runs both types of error simulation. check-in: b627e153 user: dan tags: trunk
15:44
Delay the decision to restart the log file until data is actually ready to be written to the log file (instead of at the start of a write transaction). check-in: b1abfaaf user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to test/malloc_common.tcl.

    18     18   #
    19     19   ifcapable builtin_test {
    20     20     set MEMDEBUG 1
    21     21   } else {
    22     22     set MEMDEBUG 0
    23     23     return 0
    24     24   }
           25  +
           26  +
           27  +#--------------------------------------------------------------------------
           28  +# Usage do_faultsim_test NAME ?OPTIONS...? 
           29  +#
           30  +#     -faults           List of fault types to simulate.
           31  +#
           32  +#     -prep             Script to execute before -body.
           33  +#
           34  +#     -body             Script to execute (with fault injection).
           35  +#
           36  +#     -test             Script to execute after -body.
           37  +#
           38  +proc do_faultsim_test {name args} {
           39  +  set DEFAULT(-faults)        [list       \
           40  +    oom-transient     oom-persistent      \
           41  +    ioerr-transient   ioerr-persistent    \
           42  +  ]
           43  +  set DEFAULT(-prep)          ""
           44  +  set DEFAULT(-body)          ""
           45  +  set DEFAULT(-test)          ""
           46  +
           47  +  array set O [array get DEFAULT]
           48  +  array set O $args
           49  +  foreach o [array names O] {
           50  +    if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" }
           51  +  }
           52  +
           53  +  set A(oom-transient) [list                 \
           54  +    -injectstart   {oom_injectstart 0}       \
           55  +    -injectstop    oom_injectstop            \
           56  +    -injecterrlist {{1 {out of memory}}}     \
           57  +  ]
           58  +  set A(oom-persistent) [list                \
           59  +    -injectstart {oom_injectstart 1000000}   \
           60  +    -injectstop oom_injectstop               \
           61  +    -injecterrlist {{1 {out of memory}}}     \
           62  +  ]
           63  +  
           64  +  set A(ioerr-transient) [list               \
           65  +    -injectstart   {ioerr_injectstart 0}     \
           66  +    -injectstop    ioerr_injectstop          \
           67  +    -injecterrlist {{1 {disk I/O error}}}    \
           68  +  ]
           69  +
           70  +  set A(ioerr-persistent) [list              \
           71  +    -injectstart   {ioerr_injectstart 1}     \
           72  +    -injectstop    ioerr_injectstop          \
           73  +    -injecterrlist {{1 {disk I/O error}}}    \
           74  +  ]
           75  +
           76  +  foreach f $O(-faults) {
           77  +    if {[info exists A($f)]==0} { error "unknown fault: $f" }
           78  +  }
           79  +  set testspec [list -prep $O(-prep) -body $O(-body) -test $O(-test)]
           80  +  foreach f $O(-faults) {
           81  +    eval do_one_faultsim_test "$name-$f" $A($f) $testspec
           82  +  }
           83  +}
           84  +
           85  +#-------------------------------------------------------------------------
           86  +# Procedures to save and restore the current file-system state:
           87  +#
           88  +#   faultsim_save_and_close
           89  +#   faultsim_restore_and_reopen
           90  +#
           91  +proc faultsim_save_and_close {} {
           92  +  foreach {a => b} {
           93  +      test.db          =>  testX.db 
           94  +      test.db-wal      =>  testX.db-wal 
           95  +      test.db-journal  =>  testX.db-journal
           96  +  } {
           97  +    if {[file exists $a]} {
           98  +      file copy -force $a $b
           99  +    } else {
          100  +      file delete -force $b
          101  +    }
          102  +  }
          103  +  catch { db close }
          104  +  return ""
          105  +}
          106  +proc faultsim_restore_and_reopen {} {
          107  +  catch { db close }
          108  +  foreach {a => b} {
          109  +      testX.db          =>  test.db 
          110  +      testX.db-wal      =>  test.db-wal 
          111  +      testX.db-journal  =>  test.db-journal
          112  +  } {
          113  +    if {[file exists $a]} {
          114  +      file copy -force $a $b
          115  +    } else {
          116  +      file delete -force $b
          117  +    }
          118  +  }
          119  +  sqlite3 db test.db
          120  +  sqlite3_extended_result_codes db 1
          121  +  sqlite3_db_config_lookaside db 0 0 0
          122  +}
          123  +
    25    124   
    26    125   # The following procs are used as [do_faultsim_test] when injecting OOM
    27    126   # faults into test cases.
    28    127   #
    29    128   proc oom_injectstart {nRepeat iFail} {
    30    129     sqlite3_memdebug_fail $iFail -repeat $nRepeat
    31    130   }
    32    131   proc oom_injectstop {} {
    33    132     sqlite3_memdebug_fail -1
    34    133   }
    35    134   
    36         -# This command is only useful when used by the -test script of a 
    37         -# [do_faultsim_test] test case.
          135  +proc ioerr_injectstart {persist iFail} {
          136  +  set ::sqlite_io_error_persist $persist
          137  +  set ::sqlite_io_error_pending $iFail
          138  +}
          139  +proc ioerr_injectstop {} {
          140  +  set sv $::sqlite_io_error_hit
          141  +  set ::sqlite_io_error_persist 0
          142  +  set ::sqlite_io_error_pending 0
          143  +  set ::sqlite_io_error_hardhit 0
          144  +  set ::sqlite_io_error_hit     0
          145  +  set ::sqlite_io_error_pending 0
          146  +  return $sv
          147  +}
          148  +
          149  +# This command is not called directly. It is used by the 
          150  +# [faultsim_test_result] command created by [do_faultsim_test] and used
          151  +# by -test scripts.
    38    152   #
    39         -proc faultsim_test_result {args} {
          153  +proc faultsim_test_result_int {args} {
    40    154     upvar testrc testrc testresult testresult testnfail testnfail
    41    155     set t [list $testrc $testresult]
    42         -  set r [concat $args [list {1 {out of memory}}]]
          156  +  set r $args
    43    157     if { ($testnfail==0 && $t != [lindex $r 0]) || [lsearch $r $t]<0 } {
    44    158       error "nfail=$testnfail rc=$testrc result=$testresult"
    45    159     }
    46    160   }
    47    161   
    48         -# Usage do_faultsim_test NAME ?OPTIONS...? 
          162  +#--------------------------------------------------------------------------
          163  +# Usage do_one_faultsim_test NAME ?OPTIONS...? 
    49    164   #
    50    165   # The first argument, <test number>, is used as a prefix of the test names
    51    166   # taken by tests executed by this command. Options are as follows. All
    52    167   # options take a single argument.
    53    168   #
    54    169   #     -injectstart      Script to enable fault-injection.
    55    170   #
    56    171   #     -injectstop       Script to disable fault-injection.
    57    172   #
          173  +#     -injecterrlist    List of generally acceptable test results (i.e. error
          174  +#                       messages). Example: [list {1 {out of memory}}]
          175  +#
    58    176   #     -prep             Script to execute before -body.
    59    177   #
    60    178   #     -body             Script to execute (with fault injection).
    61    179   #
    62    180   #     -test             Script to execute after -body.
    63    181   #
    64         -proc do_faultsim_test {testname args} {
          182  +proc do_one_faultsim_test {testname args} {
    65    183   
    66         -  set DEFAULT(-injectstart) {oom_injectstart 0}
    67         -  set DEFAULT(-injectstop)  {oom_injectstop}
    68         -  set DEFAULT(-prep)        ""
    69         -  set DEFAULT(-body)        ""
    70         -  set DEFAULT(-test)        ""
          184  +  set DEFAULT(-injectstart)   {oom_injectstart 0}
          185  +  set DEFAULT(-injectstop)    {oom_injectstop}
          186  +  set DEFAULT(-injecterrlist) [list {1 {out of memory}}]
          187  +  set DEFAULT(-prep)          ""
          188  +  set DEFAULT(-body)          ""
          189  +  set DEFAULT(-test)          ""
    71    190   
    72    191     array set O [array get DEFAULT]
    73    192     array set O $args
    74    193     foreach o [array names O] {
    75    194       if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" }
    76    195     }
    77    196   
    78    197     proc faultsim_test_proc {testrc testresult testnfail} $O(-test)
          198  +  proc faultsim_test_result {args} "
          199  +    uplevel faultsim_test_result_int \$args [list $O(-injecterrlist)]
          200  +  "
    79    201   
    80    202     set stop 0
    81    203     for {set iFail 1} {!$stop} {incr iFail} {
    82    204   
    83    205       # Evaluate the -prep script.
    84    206       #
    85    207       eval $O(-prep)

Changes to test/walfault.test.

    15     15   
    16     16   set testdir [file dirname $argv0]
    17     17   source $testdir/tester.tcl
    18     18   source $testdir/malloc_common.tcl
    19     19   
    20     20   ifcapable !wal {finish_test ; return }
    21     21   
    22         -do_faultsim_test walfault-oom-1 -prep {
    23         -  catch { db close }
           22  +#-------------------------------------------------------------------------
           23  +# This test case, walfault-1-*, simulates faults while executing a
           24  +#
           25  +#   PRAGMA journal_mode = WAL;
           26  +#
           27  +# statement immediately after creating a new database.
           28  +#
           29  +do_test walfault-1-pre-1 {
           30  +  db close
    24     31     file delete -force test.db test.db-wal test.db-journal
    25         -  sqlite3 db test.db
           32  +  faultsim_save_and_close
           33  +} {}
           34  +do_faultsim_test walfault-1 -prep {
           35  +  faultsim_restore_and_reopen
    26     36   } -body {
    27     37     db eval { PRAGMA main.journal_mode = WAL }
    28     38   } -test {
    29     39   
    30     40     faultsim_test_result {0 wal}
    31     41   
    32     42     # Test that the connection that encountered an error as part of 
................................................................................
    45     55     set jm2 [db2 one {SELECT * FROM sqlite_master ; PRAGMA main.journal_mode}]
    46     56     db2 close
    47     57   
    48     58     if { $jm!=$jm2 } { error "Journal modes do not match: $jm $jm2" }
    49     59     if { $testrc==0 && $jm!="wal" } { error "Journal mode is not WAL" }
    50     60   }
    51     61   
    52         -do_malloc_test walfault-oom-2 -tclprep {
           62  +#--------------------------------------------------------------------------
           63  +# Test case walfault-2-* tests fault injection during recovery of a 
           64  +# short WAL file (a dozen frames or thereabouts).
           65  +#
           66  +do_test walfault-2-pre-1 {
           67  +  sqlite3 db test.db
    53     68     execsql {
    54     69       PRAGMA journal_mode = WAL;
    55     70       BEGIN;
    56     71         CREATE TABLE x(y, z, UNIQUE(y, z));
    57     72         INSERT INTO x VALUES(randomblob(100), randomblob(100));
    58     73       COMMIT;
    59     74       PRAGMA wal_checkpoint;
    60     75   
    61     76       INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
    62     77       INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
    63     78       INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x;
    64     79     }
    65         -  file copy -force test.db testX.db
    66         -  file copy -force test.db-wal testX.db-wal
    67         -  db close
    68         -  file rename -force testX.db test.db
    69         -  file rename -force testX.db-wal test.db-wal
           80  +  execsql {
           81  +    SELECT count(*) FROM x
           82  +  }
           83  +} {8}
           84  +do_test walfault-2-pre-2 {
           85  +  faultsim_save_and_close
           86  +  faultsim_restore_and_reopen
           87  +  execsql { SELECT count(*) FROM x }
           88  +} {8}
           89  +do_faultsim_test walfault-2 -prep {
           90  +  faultsim_restore_and_reopen
           91  +} -body {
           92  +  execsql { SELECT count(*) FROM x }
           93  +} -test {
    70     94   
    71         -  sqlite3 db test.db
    72         -  sqlite3_extended_result_codes db 1
    73         -  sqlite3_db_config_lookaside db 0 0 0
    74         -} -sqlbody {
    75         -  SELECT count(*) FROM x;
           95  +  # Test that all the rows in the WAL were recovered.
           96  +  faultsim_test_result {0 8}
           97  +
           98  +  # Run the integrity_check to make sure nothing strange has occurred.
           99  +  set ic [db eval { PRAGMA integrity_check }]
          100  +  if {$ic != "ok"} { error "Integrity check: $ic" }
    76    101   }
    77    102   
    78         -do_ioerr_test walfault-ioerr-1 -sqlprep {
    79         -  PRAGMA auto_vacuum = 1;
    80         -  PRAGMA journal_mode = WAL;
    81         -  CREATE TABLE abc(a PRIMARY KEY);
    82         -  INSERT INTO abc VALUES(randomblob(1500));
    83         -} -sqlbody {
    84         -  DELETE FROM abc;
    85         -  PRAGMA wal_checkpoint;
          103  +#--------------------------------------------------------------------------
          104  +# Test fault injection while writing and checkpointing a small WAL file.
          105  +#
          106  +do_test walfault-3-pre-1 {
          107  +  sqlite3 db test.db
          108  +  execsql {
          109  +    PRAGMA auto_vacuum = 1;
          110  +    PRAGMA journal_mode = WAL;
          111  +    CREATE TABLE abc(a PRIMARY KEY);
          112  +    INSERT INTO abc VALUES(randomblob(1500));
          113  +  }
          114  +  db close
          115  +  faultsim_save_and_close
          116  +} {}
          117  +
          118  +do_faultsim_test walfault-3 -prep {
          119  +  faultsim_restore_and_reopen
          120  +} -body {
          121  +  db eval {
          122  +    DELETE FROM abc;
          123  +    PRAGMA wal_checkpoint;
          124  +  }
          125  +} -test {
          126  +  faultsim_test_result {0 {}}
    86    127   }
    87         -catch {db close}
    88    128   
    89    129   # A [testvfs] callback for the VFS created by [do_shmfault_test]. This
    90    130   # callback injects SQLITE_IOERR faults into methods for which an entry
    91    131   # in array ::shmfault_ioerr_methods is defined. For example, to enable
    92    132   # errors in xShmOpen:
    93    133   #
    94    134   #   set ::shmfault_ioerr_methods(xShmOpen) 1