blob: c71465617cd01003d9cce94e136ec0b5ea6e8f8c (
plain)
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
# vim:se syntax=tcl:
source [file dirname [info script]]/testing.tcl
needs cmd defer
needs cmd interp
test defer-1.1 {defer in proc} {
set x -
proc a {} {
set x +
# This does nothing since it increments a local variable
defer {append x L}
# This increments the global variable
defer {append ::x G}
# Will return "-", not "-L" since return happens before defer triggers
return $x
}
list [a] $x
} {+ -G}
test defer-1.2 {set $defer directly} {
set x -
proc a {} {
lappend jim::defer {append ::x a}
lappend jim::defer {append ::x b}
return $jim::defer
}
list [a] $x
} {{{append ::x a} {append ::x b}} -ba}
test defer-1.3 {unset $defer} {
set x -
proc a {} {
defer {append ::x a}
# unset, to remove all defer actions
unset jim::defer
}
a
set x
} {-}
test defer-1.4 {error in defer - error} {
set x -
proc a {} {
# First defer script will not happen because of error in next defer script
defer {append ::x a}
# Error ignored because of error from proc
defer {blah}
# Last defer script will happen
defer {append ::x b}
# This error will take precedence over the error from defer
error "from a"
}
set rc [catch {a} msg]
list [info ret $rc] $msg $x
} {error {from a} -b}
test defer-1.5 {error in defer - return} {
set x -
proc a {} {
# First defer script will not happen
defer {append ::x a}
defer {blah}
# Last defer script will happen
defer {append ::x b}
return 3
}
set rc [catch {a} msg]
list [info ret $rc] $msg $x
} {error {invalid command name "blah"} -b}
test defer-1.6 {error in defer - ok} {
set x -
proc a {} {
# First defer script will not happen
defer {append ::x a}
# Error ignored because of error from proc
defer {blah}
# Last defer script will happen
defer {append ::x b}
}
set rc [catch {a} msg]
list [info ret $rc] $msg $x
} {error {invalid command name "blah"} -b}
test defer-1.7 {error in defer - break} {
set x -
proc a {} {
# First defer script will not happen
defer {append ::x a}
# This non-zero return code will take precedence over the proc return
defer {return -code 30 ret30}
# Last defer script will happen
defer {append ::x b}
return -code 20 ret20
}
set rc [catch {a} msg]
list [info ret $rc] $msg $x
} {30 ret30 -b}
test defer-1.8 {error in defer - tailcall} {
set x -
proc a {} {
# This will prevent tailcall from happening
defer {blah}
# Tailcall will not happen because of error in defer
tailcall append ::x a
}
set rc [catch {a} msg]
list [info ret $rc] $msg $x
} {error {invalid command name "blah"} -}
test defer-1.9 {Add to defer in defer body} {
set x -
proc a {} {
defer {
# Add to defer in defer
defer {
# This will do nothing
error here
}
}
defer {append ::x a}
}
a
set x
} {-a}
test defer-1.10 {Unset defer in defer body} {
set x -
proc a {} {
defer {
# This will do nothing
unset -nocomplain jim::defer
}
defer {append ::x a}
}
a
set x
} {-a}
test defer-1.11 {defer through tailcall} {
set x {}
proc a {} {
defer {append ::x a}
b
}
proc b {} {
defer {append ::x b}
# c will be invoked as through called from a but this
# won't make any difference for defer
tailcall c
}
proc c {} {
defer {append ::x c}
}
a
set x
} {bca}
test defer-1.12 {defer in recursive call} {
set x {}
proc a {n} {
# defer happens just before the return, so after the recursive call to a
defer {lappend ::x $n}
if {$n > 0} {
a $($n - 1)
}
}
a 3
set x
} {0 1 2 3}
test defer-1.13 {defer in recursive tailcall} {
set x {}
proc a {n} {
# defer happens just before the return, so before the tailcall to a
defer {lappend ::x $n}
if {$n > 0} {
tailcall a $($n - 1)
}
}
a 3
set x
} {3 2 1 0}
test defer-1.14 {defer capture variables} {
set x {}
proc a {} {
set y 1
# A normal defer will evaluate at the end of the proc, so $y may change
defer {lappend ::x $y}
incr y
# What if we want to capture the value of y here? list will work
defer [list lappend ::x $y]
incr y
# But with multiple statements, list doesn't work, so use a lambda
# to capture the value instead
defer [lambda {} {y} {
# multi-line script
lappend ::x $y
}]
incr y
return $y
}
list [a] $x
} {4 {3 2 4}}
test defer-2.1 {defer from interp} -body {
set i [interp]
# defer needs to have some effect to detect on exit,
# so write to a file
file delete defer.tmp
$i eval {
defer {
[open defer.tmp w] puts "leaving child"
}
}
set a [file exists defer.tmp]
$i delete
# Now the file should exist
set f [open defer.tmp]
$f gets b
$f close
list $a $b
} -result {0 {leaving child}} -cleanup {
file delete defer.tmp
}
testreport
|