aboutsummaryrefslogtreecommitdiff
path: root/tests/defer.test
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