OLD | NEW |
| (Empty) |
1 # 2010 April 14 | |
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 code used by several different test scripts. The | |
12 # code in this file allows testfixture to control another process (or | |
13 # processes) to test locking. | |
14 # | |
15 | |
16 proc do_multiclient_test {varname script} { | |
17 | |
18 foreach code [list { | |
19 if {[info exists ::G(valgrind)]} { db close ; continue } | |
20 set ::code2_chan [launch_testfixture] | |
21 set ::code3_chan [launch_testfixture] | |
22 proc code2 {tcl} { testfixture $::code2_chan $tcl } | |
23 proc code3 {tcl} { testfixture $::code3_chan $tcl } | |
24 set tn 1 | |
25 } { | |
26 proc code2 {tcl} { uplevel #0 $tcl } | |
27 proc code3 {tcl} { uplevel #0 $tcl } | |
28 set tn 2 | |
29 }] { | |
30 faultsim_delete_and_reopen | |
31 | |
32 proc code1 {tcl} { uplevel #0 $tcl } | |
33 | |
34 # Open connections [db2] and [db3]. Depending on which iteration this | |
35 # is, the connections may be created in this interpreter, or in | |
36 # interpreters running in other OS processes. As such, the [db2] and [db3] | |
37 # commands should only be accessed within [code2] and [code3] blocks, | |
38 # respectively. | |
39 # | |
40 eval $code | |
41 code2 { sqlite3 db2 test.db } | |
42 code3 { sqlite3 db3 test.db } | |
43 | |
44 # Shorthand commands. Execute SQL using database connection [db2] or | |
45 # [db3]. Return the results. | |
46 # | |
47 proc sql1 {sql} { db eval $sql } | |
48 proc sql2 {sql} { code2 [list db2 eval $sql] } | |
49 proc sql3 {sql} { code3 [list db3 eval $sql] } | |
50 | |
51 proc csql1 {sql} { list [catch { sql1 $sql } msg] $msg } | |
52 proc csql2 {sql} { list [catch { sql2 $sql } msg] $msg } | |
53 proc csql3 {sql} { list [catch { sql3 $sql } msg] $msg } | |
54 | |
55 uplevel set $varname $tn | |
56 uplevel $script | |
57 | |
58 catch { code2 { db2 close } } | |
59 catch { code3 { db3 close } } | |
60 catch { close $::code2_chan } | |
61 catch { close $::code3_chan } | |
62 catch { db close } | |
63 } | |
64 } | |
65 | |
66 # Launch another testfixture process to be controlled by this one. A | |
67 # channel name is returned that may be passed as the first argument to proc | |
68 # 'testfixture' to execute a command. The child testfixture process is shut | |
69 # down by closing the channel. | |
70 proc launch_testfixture {{prg ""}} { | |
71 write_main_loop | |
72 if {$prg eq ""} { set prg [info nameofexec] } | |
73 if {$prg eq ""} { set prg testfixture } | |
74 if {[file tail $prg]==$prg} { set prg [file join . $prg] } | |
75 set chan [open "|$prg tf_main.tcl" r+] | |
76 fconfigure $chan -buffering line | |
77 set rc [catch { | |
78 testfixture $chan "sqlite3_test_control_pending_byte $::sqlite_pending_byte" | |
79 }] | |
80 if {$rc} { | |
81 testfixture $chan "set ::sqlite_pending_byte $::sqlite_pending_byte" | |
82 } | |
83 return $chan | |
84 } | |
85 | |
86 # Execute a command in a child testfixture process, connected by two-way | |
87 # channel $chan. Return the result of the command, or an error message. | |
88 # | |
89 proc testfixture {chan cmd args} { | |
90 | |
91 if {[llength $args] == 0} { | |
92 fconfigure $chan -blocking 1 | |
93 puts $chan $cmd | |
94 puts $chan OVER | |
95 | |
96 set r "" | |
97 while { 1 } { | |
98 set line [gets $chan] | |
99 if { $line == "OVER" } { | |
100 set res [lindex $r 1] | |
101 if { [lindex $r 0] } { error $res } | |
102 return $res | |
103 } | |
104 if {[eof $chan]} { | |
105 return "ERROR: Child process hung up" | |
106 } | |
107 append r $line | |
108 } | |
109 return $r | |
110 } else { | |
111 set ::tfnb($chan) "" | |
112 fconfigure $chan -blocking 0 -buffering none | |
113 puts $chan $cmd | |
114 puts $chan OVER | |
115 fileevent $chan readable [list testfixture_script_cb $chan [lindex $args 0]] | |
116 return "" | |
117 } | |
118 } | |
119 | |
120 proc testfixture_script_cb {chan script} { | |
121 if {[eof $chan]} { | |
122 append ::tfnb($chan) "ERROR: Child process hung up" | |
123 set line "OVER" | |
124 } else { | |
125 set line [gets $chan] | |
126 } | |
127 | |
128 if { $line == "OVER" } { | |
129 uplevel #0 $script [list [lindex $::tfnb($chan) 1]] | |
130 unset ::tfnb($chan) | |
131 fileevent $chan readable "" | |
132 } else { | |
133 append ::tfnb($chan) $line | |
134 } | |
135 } | |
136 | |
137 proc testfixture_nb_cb {varname chan} { | |
138 if {[eof $chan]} { | |
139 append ::tfnb($chan) "ERROR: Child process hung up" | |
140 set line "OVER" | |
141 } else { | |
142 set line [gets $chan] | |
143 } | |
144 | |
145 if { $line == "OVER" } { | |
146 set $varname [lindex $::tfnb($chan) 1] | |
147 unset ::tfnb($chan) | |
148 close $chan | |
149 } else { | |
150 append ::tfnb($chan) $line | |
151 } | |
152 } | |
153 | |
154 proc testfixture_nb {varname cmd} { | |
155 set chan [launch_testfixture] | |
156 set ::tfnb($chan) "" | |
157 fconfigure $chan -blocking 0 -buffering none | |
158 puts $chan $cmd | |
159 puts $chan OVER | |
160 fileevent $chan readable [list testfixture_nb_cb $varname $chan] | |
161 return "" | |
162 } | |
163 | |
164 # Write the main loop for the child testfixture processes into file | |
165 # tf_main.tcl. The parent (this script) interacts with the child processes | |
166 # via a two way pipe. The parent writes a script to the stdin of the child | |
167 # process, followed by the word "OVER" on a line of its own. The child | |
168 # process evaluates the script and writes the results to stdout, followed | |
169 # by an "OVER" of its own. | |
170 # | |
171 set main_loop_written 0 | |
172 proc write_main_loop {} { | |
173 if {$::main_loop_written} return | |
174 set wrapper "" | |
175 if {[sqlite3 -has-codec] && [info exists ::do_not_use_codec]==0} { | |
176 set wrapper " | |
177 rename sqlite3 sqlite_orig | |
178 proc sqlite3 {args} {[info body sqlite3]} | |
179 " | |
180 } | |
181 | |
182 set fd [open tf_main.tcl w] | |
183 puts $fd [string map [list %WRAPPER% $wrapper] { | |
184 %WRAPPER% | |
185 set script "" | |
186 while {![eof stdin]} { | |
187 flush stdout | |
188 set line [gets stdin] | |
189 if { $line == "OVER" } { | |
190 set rc [catch {eval $script} result] | |
191 puts [list $rc $result] | |
192 puts OVER | |
193 flush stdout | |
194 set script "" | |
195 } else { | |
196 append script $line | |
197 append script "\n" | |
198 } | |
199 } | |
200 }] | |
201 close $fd | |
202 set main_loop_written 1 | |
203 } | |
204 | |
OLD | NEW |