SQLite

Check-in [6e43eed931]
Login

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

Overview
Comment:Add test file pager1.test, containing tests of inter-process locking in non-wal mode.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 6e43eed9310bae9ca5e91f8fd9eafc45a16b7019
User & Date: dan 2010-06-15 17:44:48.000
Context
2010-06-15
18:00
Fix a problem introduced into lock2.test by the previous commit. (check-in: c1c9f6fa9d user: dan tags: trunk)
17:44
Add test file pager1.test, containing tests of inter-process locking in non-wal mode. (check-in: 6e43eed931 user: dan tags: trunk)
14:21
Run extra iterations in wal3.test to ensure test coverage. (check-in: ea80cbe51e user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to test/lock_common.tcl.
26
27
28
29
30
31
32

33
34
35
36
37
38
39


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  fconfigure $chan -buffering line
  testfixture $chan "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
  return $chan
}

# Execute a command in a child testfixture process, connected by two-way
# channel $chan. Return the result of the command, or an error message.

proc testfixture {chan cmd} {
  puts $chan $cmd
  puts $chan OVER
  set r ""
  while { 1 } {
    set line [gets $chan]
    if { $line == "OVER" } { 


      return $r
    }
    if {[eof $chan]} {
      return "ERROR: Child process hung up"
    }
    append r $line
  }
}

proc testfixture_nb_cb {varname chan} {
  if {[eof $chan]} {
    append ::tfnb($chan) "ERROR: Child process hung up"
    set line "OVER"
  } else {
    set line [gets $chan]
  }

  if { $line == "OVER" } {
    set $varname $::tfnb($chan)
    unset ::tfnb($chan)
    close $chan
  } else {
    append ::tfnb($chan) $line
  }
}








>







>
>
|

















|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  fconfigure $chan -buffering line
  testfixture $chan "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
  return $chan
}

# Execute a command in a child testfixture process, connected by two-way
# channel $chan. Return the result of the command, or an error message.
#
proc testfixture {chan cmd} {
  puts $chan $cmd
  puts $chan OVER
  set r ""
  while { 1 } {
    set line [gets $chan]
    if { $line == "OVER" } { 
      set res [lindex $r 1]
      if { [lindex $r 0] } { error $res }
      return $res
    }
    if {[eof $chan]} {
      return "ERROR: Child process hung up"
    }
    append r $line
  }
}

proc testfixture_nb_cb {varname chan} {
  if {[eof $chan]} {
    append ::tfnb($chan) "ERROR: Child process hung up"
    set line "OVER"
  } else {
    set line [gets $chan]
  }

  if { $line == "OVER" } {
    set $varname [lindex $::tfnb($chan) 1]
    unset ::tfnb($chan)
    close $chan
  } else {
    append ::tfnb($chan) $line
  }
}

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  set script ""
  while {![eof stdin]} {
    flush stdout
    set line [gets stdin]
    puts $l "READ $line"
    if { $line == "OVER" } {
      set rc [catch {eval $script} result]
      puts $result
      puts $l "WRITE $result"
      puts OVER
      puts $l "WRITE OVER"
      flush stdout
      set script ""
    } else {
      append script $line
      append script "\n"
    }
  }
  close $l
}
close $f







|
|












88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  set script ""
  while {![eof stdin]} {
    flush stdout
    set line [gets stdin]
    puts $l "READ $line"
    if { $line == "OVER" } {
      set rc [catch {eval $script} result]
      puts [list $rc $result]
      puts $l "WRITE [list $rc $result]"
      puts OVER
      puts $l "WRITE OVER"
      flush stdout
      set script ""
    } else {
      append script $line
      append script "\n"
    }
  }
  close $l
}
close $f
Added test/pager1.test.




































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# 2010 June 15
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl

foreach code [list {
  set ::code2_chan [launch_testfixture]
  set ::code3_chan [launch_testfixture]
  proc code2 {tcl} { testfixture $::code2_chan $tcl }
  proc code3 {tcl} { testfixture $::code3_chan $tcl }
  set tn 1
} {
  proc code2 {tcl} { uplevel #0 $tcl }
  proc code3 {tcl} { uplevel #0 $tcl }
  set tn 2
}] {

  faultsim_delete_and_reopen

  # Open connections [db2] and [db3]. Depending on which iteration this
  # is, the connections may be created in this interpreter, or in 
  # interpreters running in other OS processes. As such, the [db2] and [db3]
  # commands should only be accessed within [code2] and [code3] blocks,
  # respectively.
  #
  eval $code
  code2 { sqlite3 db2 test.db }
  code3 { sqlite3 db3 test.db }
  
  # Shorthand commands. Execute SQL using database connection [db2] or 
  # [db3]. Return the results.
  #
  proc sql1 {sql} { db eval $sql }
  proc sql2 {sql} { code2 [list db2 eval $sql] }
  proc sql3 {sql} { code3 [list db3 eval $sql] }

  proc csql1 {sql} { list [catch { sql1 $sql } msg] $msg }
  proc csql2 {sql} { list [catch { sql2 $sql } msg] $msg }
  proc csql3 {sql} { list [catch { sql3 $sql } msg] $msg }

  # Create and populate a database table using connection [db]. Check 
  # that connections [db2] and [db3] can see the schema and content.
  #
  do_test pager1-$tn.1 {
    sql1 {
      CREATE TABLE t1(a PRIMARY KEY, b);
      CREATE INDEX i1 ON t1(b);
      INSERT INTO t1 VALUES(1, 'one');
      INSERT INTO t1 VALUES(2, 'two');
    }
  } {}
  do_test pager1-$tn.2 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
  do_test pager1-$tn.3 { sql3 { SELECT * FROM t1 } } {1 one 2 two}

  # Open a transaction and add a row using [db]. This puts [db] in
  # RESERVED state. Check that connections [db2] and [db3] can still
  # read the database content as it was before the transaction was
  # opened. [db] should see the inserted row.
  #
  do_test pager1-$tn.4 {
    sql1 {
      BEGIN;
        INSERT INTO t1 VALUES(3, 'three');
    }
  } {}
  do_test pager1-$tn.5 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
  do_test pager1-$tn.6 { sql3 { SELECT * FROM t1 } } {1 one 2 two}
  do_test pager1-$tn.7 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}

  # [db] still has an open write transaction. Check that this prevents
  # other connections (specifically [db2]) from writing to the database.
  #
  # Even if [db2] opens a transaction first, it may not write to the
  # database. After the attempt to write the db within a transaction, 
  # [db2] is left with an open transaction, but not a read-lock on
  # the main database. So it does not prevent [db] from committing.
  #
  do_test pager1-$tn.8 { 
    csql2 { UPDATE t1 SET a = a + 10 }
  } {1 {database is locked}}
  do_test pager1-$tn.9 { 
    csql2 { 
      BEGIN;
      UPDATE t1 SET a = a + 10;
    }
  } {1 {database is locked}}

  # Have [db] commit its transactions. Check the other connections can
  # now see the new database content.
  #
  do_test pager1-$tn.10 { sql1 { COMMIT } } {}
  do_test pager1-$tn.11 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
  do_test pager1-$tn.12 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
  do_test pager1-$tn.13 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}

  # Check that, as noted above, [db2] really did keep an open transaction
  # after the attempt to write the database failed.
  #
  do_test pager1-$tn.14 { 
    csql2 { BEGIN } 
  } {1 {cannot start a transaction within a transaction}}
  do_test pager1-$tn.15 { sql2 { ROLLBACK } } {}

  # Have [db2] open a transaction and take a read-lock on the database.
  # Check that this prevents [db] from writing to the database (outside
  # of any transaction). After this fails, check that [db3] can read
  # the db (showing that [db] did not take a PENDING lock etc.)
  #
  do_test pager1-$tn.15 { 
    sql2 { BEGIN; SELECT * FROM t1; }
  } {1 one 2 two 3 three}
  do_test pager1-$tn.16 { 
    csql1 { UPDATE t1 SET a = a + 10 }
  } {1 {database is locked}}
  do_test pager1-$tn.17 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}

  # This time, have [db] open a transaction before writing the database.
  # This works - [db] gets a RESERVED lock which does not conflict with
  # the SHARED lock [db2] is holding.
  #
  do_test pager1-$tn.18 { 
    sql1 { 
      BEGIN;  
      UPDATE t1 SET a = a + 10; 
    }
  } {}
  do_test pager1-$tn-19 { 
    sql1 { PRAGMA lock_status } 
  } {main reserved temp closed}
  do_test pager1-$tn-20 { 
    sql2 { PRAGMA lock_status } 
  } {main shared temp closed}

  # Check that all connections can still read the database. Only [db] sees
  # the updated content (as the transaction has not been committed yet).
  #
  do_test pager1-$tn.21 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three}
  do_test pager1-$tn.22 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
  do_test pager1-$tn.23 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}

  # Because [db2] still has the SHARED lock, [db] is unable to commit the
  # transaction. If it tries, an error is returned and the connection 
  # upgrades to a PENDING lock.
  #
  # Once this happens, [db] can read the database and see the new content,
  # [db2] (still holding SHARED) can still read the old content, but [db3]
  # (not holding any lock) is prevented by [db]'s PENDING from reading
  # the database.
  #
  do_test pager1-$tn.24 { csql1 { COMMIT } } {1 {database is locked}}
  do_test pager1-$tn-25 { 
    sql1 { PRAGMA lock_status } 
  } {main pending temp closed}
  do_test pager1-$tn.26 { sql1 { SELECT * FROM t1  } } {11 one 12 two 13 three}
  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1  } } {1 one 2 two 3 three}
  do_test pager1-$tn.28 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}

  # Have [db2] commit its read transaction, releasing the SHARED lock it
  # is holding. Now, neither [db2] nor [db3] may read the database (as [db]
  # is still holding a PENDING).
  #
  do_test pager1-$tn.29 { sql2 { COMMIT } } {}
  do_test pager1-$tn.30 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
  do_test pager1-$tn.31 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}

  # [db] is now able to commit the transaction. Once the transaction is 
  # committed, all three connections can read the new content.
  #
  do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {}
  do_test pager1-$tn.26 { sql1 { COMMIT } } {}
  do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three}
  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three}
  do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three}

  code2 { db2 close }
  code3 { db3 close }
  catch { close $::code2_chan }
  catch { close $::code3_chan }
}

finish_test

Changes to test/permutations.test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2008 June 21
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# $Id: permutations.test,v 1.51 2009/07/01 18:09:02 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
db close

#-------------------------------------------------------------------------
# test_suite NAME OPTIONS











<







1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
# 2008 June 21
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#


set testdir [file dirname $argv0]
source $testdir/tester.tcl
db close

#-------------------------------------------------------------------------
# test_suite NAME OPTIONS
44
45
46
47
48
49
50

51
52
53
54
55
56
57
    if {[llength $o]>1}  { error "ambiguous option: $k" }
    if {[llength $o]==0} { error "unknown option: $k" }
    set options([lindex $o 0]) $v
  }

  set     ::testspec($name) [array get options]
  lappend ::testsuitelist $name

}

#-------------------------------------------------------------------------
# test_set ARGS...
#
proc test_set {args} {
  set isExclude 0







>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    if {[llength $o]>1}  { error "ambiguous option: $k" }
    if {[llength $o]==0} { error "unknown option: $k" }
    set options([lindex $o 0]) $v
  }

  set     ::testspec($name) [array get options]
  lappend ::testsuitelist $name

}

#-------------------------------------------------------------------------
# test_set ARGS...
#
proc test_set {args} {
  set isExclude 0
116
117
118
119
120
121
122

123
124
125
126
127
128
129
# Define the generic test suites:
#
#   veryquick
#   quick
#   full
#   veryquick_plus_notify2
#


test_suite "veryquick" -description {
  "Very" quick test suite. Runs in less than 5 minutes on a workstation. 
  This test suite is the same as the "quick" tests, except that some files
  that test malloc and IO errors are omitted.
} -files [
  test_set $allquicktests -exclude *malloc* *ioerr* *fault*







>







116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# Define the generic test suites:
#
#   veryquick
#   quick
#   full
#   veryquick_plus_notify2
#
lappend ::testsuitelist xxx

test_suite "veryquick" -description {
  "Very" quick test suite. Runs in less than 5 minutes on a workstation. 
  This test suite is the same as the "quick" tests, except that some files
  that test malloc and IO errors are omitted.
} -files [
  test_set $allquicktests -exclude *malloc* *ioerr* *fault*
146
147
148
149
150
151
152

153
154
155
156
157
158
159
160
161
162
163
164
165
166




167
168


169
170
171
172
173
174
175
  Full test suite. Takes a long time.
} -files [ 
  test_set $alltests 
] -initialize {
  unset -nocomplain ::G(isquick)
}


#-------------------------------------------------------------------------
# Define the coverage related test suites:
#
#   coverage-wal
#   coverage-fkey
#
test_suite "coverage-wal" -description {
  Coverage tests for file wal.c.
} -files {
  wal.test       wal2.test      wal3.test      walmode.test    
  walbak.test    walhook.test  walcrash2.test  walcksum.test
  walfault.test
} 









#-------------------------------------------------------------------------
# Define the permutation test suites:
#

# Run some tests using pre-allocated page and scratch blocks.
#
test_suite "memsubsys1" -description {







>




<









>
>
>
>
|

>
>







147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  Full test suite. Takes a long time.
} -files [ 
  test_set $alltests 
] -initialize {
  unset -nocomplain ::G(isquick)
}

lappend ::testsuitelist xxx
#-------------------------------------------------------------------------
# Define the coverage related test suites:
#
#   coverage-wal

#
test_suite "coverage-wal" -description {
  Coverage tests for file wal.c.
} -files {
  wal.test       wal2.test      wal3.test      walmode.test    
  walbak.test    walhook.test  walcrash2.test  walcksum.test
  walfault.test
} 

test_suite "coverage-pager" -description {
  Coverage tests for file pager.c.
} -files {
  pager1.test
} 


lappend ::testsuitelist xxx
#-------------------------------------------------------------------------
# Define the permutation test suites:
#

# Run some tests using pre-allocated page and scratch blocks.
#
test_suite "memsubsys1" -description {
799
800
801
802
803
804
805



806




807
808
809
810
811
812

813
814
815
816
817
818
819
  if {[info exists ::testspec($name)]==0} {
    error "No such test suite: $name"
  }
  uplevel run_tests $name $::testspec($name)
}

proc help {} {



  foreach k $::testsuitelist {




    array set o $::testspec($k)
    puts "Test suite: \"$k\""
    set d [string trim $o(-description)]
    set d [regsub {\n *} $d "\n  "]
    puts "  $d"
    puts ""

  }
  exit -1
}

if {[info script] == $argv0} {
  proc main {argv} {
    if {[llength $argv]==0} {







>
>
>

>
>
>
>
|
|
|
|
|
|
>







806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
  if {[info exists ::testspec($name)]==0} {
    error "No such test suite: $name"
  }
  uplevel run_tests $name $::testspec($name)
}

proc help {} {
  puts "Usage: $::argv0 TESTSUITE ?TESTFILE?"
  puts ""
  puts "Available test-suites are:"
  foreach k $::testsuitelist {
    if {[info exists ::testspec($k)]==0} {
      puts "         ----------------------------------------"
      puts ""
    } else {
      array set o $::testspec($k)
      puts "Test suite: \"$k\""
      set d [string trim $o(-description)]
      set d [regsub {\n *} $d "\n  "]
      puts "  $d"
      puts ""
    }
  }
  exit -1
}

if {[info script] == $argv0} {
  proc main {argv} {
    if {[llength $argv]==0} {
Changes to test/quick.test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file runs all tests.
#

set testdir [file dirname $argv0]
source $testdir/permutations.test

if {[info exists env(QUICKTEST_INCLUDE]} {
  # A hack so that releasetest.tcl keeps working.
  run_test_suite quick_plus_notify2
} else {
  run_test_suite quick
}

finish_test












<
<
<
<
|
<


1
2
3
4
5
6
7
8
9
10
11
12




13

14
15
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file runs all tests.
#

set testdir [file dirname $argv0]
source $testdir/permutations.test





run_test_suite quick


finish_test