1# Commands covered:  error, catch, throw, try
2#
3# This file contains a collection of tests for one or more of the Tcl built-in
4# commands. Sourcing this file into Tcl runs the tests and generates output
5# for errors. No output means no errors were found.
6#
7# Copyright © 1991-1993 The Regents of the University of California.
8# Copyright © 1994-1996 Sun Microsystems, Inc.
9# Copyright © 1998-1999 Scriptics Corporation.
10#
11# See the file "license.terms" for information on usage and redistribution of
12# this file, and for a DISCLAIMER OF ALL WARRANTIES.
13
14if {"::tcltest" ni [namespace children]} {
15    package require tcltest 2.5
16    namespace import -force ::tcltest::*
17}
18
19testConstraint memory [llength [info commands memory]]
20customMatch pairwise {apply {{a b} {
21    string equal [lindex $b 0] [lindex $b 1]
22}}}
23namespace eval ::tcl::test::error {
24if {[testConstraint memory]} {
25    proc getbytes {} {
26	set lines [split [memory info] \n]
27	return [lindex $lines 3 3]
28    }
29    proc leaktest {script {iterations 3}} {
30	set end [getbytes]
31	for {set i 0} {$i < $iterations} {incr i} {
32	    uplevel 1 $script
33	    set tmp $end
34	    set end [getbytes]
35	}
36	return [expr {$end - $tmp}]
37    }
38}
39
40proc foo {} {
41    global errorInfo
42    set a [catch {format [error glorp2]} b]
43    error {Human-generated}
44}
45
46proc foo2 {} {
47    global errorInfo
48    set a [catch {format [error glorp2]} b]
49    error {Human-generated} $errorInfo
50}
51
52# Catch errors occurring in commands and errors from "error" command
53
54test error-1.1 {simple errors from commands} {
55    catch {format [string index]} b
56} 1
57test error-1.2 {simple errors from commands} {
58    catch {format [string index]} b
59    set b
60} {wrong # args: should be "string index string charIndex"}
61test error-1.3 {simple errors from commands} {
62    catch {format [string index]} b
63    set ::errorInfo
64    # This used to return '... while executing ...', but string index is fully
65    # compiled as of 8.4a3
66} {wrong # args: should be "string index string charIndex"
67    while executing
68"string index"}
69test error-1.4 {simple errors from commands} {
70    catch {error glorp} b
71} 1
72test error-1.5 {simple errors from commands} {
73    catch {error glorp} b
74    set b
75} glorp
76test error-1.6 {simple errors from commands} {
77    catch {catch a b c d} b
78} 1
79test error-1.7 {simple errors from commands} {
80    catch {catch a b c d} b
81    set b
82} {wrong # args: should be "catch script ?resultVarName? ?optionVarName?"}
83test error-1.8 {simple errors from commands} {
84    # This test is non-portable: it generates a memory fault on machines like
85    # DEC Alphas (infinite recursion overflows stack?)
86    #
87    # That claims sounds like a bug to be fixed rather than a portability
88    # problem. Anyhow, I believe it's out of date (bug's been fixed) so this
89    # test is re-enabled.
90    proc p {} {
91        uplevel 1 catch p error
92    }
93    p
94} 0
95
96# Check errors nested in procedures. Also check the optional argument to
97# "error" to generate a new error trace.
98
99test error-2.1 {errors in nested procedures} {
100    catch foo b
101} 1
102test error-2.2 {errors in nested procedures} {
103    catch foo b
104    set b
105} {Human-generated}
106test error-2.3 {errors in nested procedures} {
107    catch foo b
108    set ::errorInfo
109} {Human-generated
110    while executing
111"error {Human-generated}"
112    (procedure "foo" line 4)
113    invoked from within
114"foo"}
115test error-2.4 {errors in nested procedures} {
116    catch foo2 b
117} 1
118test error-2.5 {errors in nested procedures} {
119    catch foo2 b
120    set b
121} {Human-generated}
122test error-2.6 {errors in nested procedures} {
123    catch foo2 b
124    set ::errorInfo
125} {glorp2
126    while executing
127"error glorp2"
128    (procedure "foo2" line 3)
129    invoked from within
130"foo2"}
131
132# Error conditions related to "catch".
133
134test error-3.1 {errors in catch command} {
135    list [catch {catch} msg] $msg
136} {1 {wrong # args: should be "catch script ?resultVarName? ?optionVarName?"}}
137test error-3.2 {errors in catch command} {
138    list [catch {catch a b c} msg] $msg
139} {0 1}
140test error-3.3 {errors in catch command} {
141    catch {unset a}
142    set a(0) 22
143    list [catch {catch {format 44} a} msg] $msg
144} {1 {can't set "a": variable is array}}
145catch {unset a}
146
147# More tests related to errorInfo and errorCode
148
149test error-4.1 {errorInfo and errorCode variables} {
150    list [catch {error msg1 msg2 msg3} msg] $msg $::errorInfo $::errorCode
151} {1 msg1 msg2 msg3}
152test error-4.2 {errorInfo and errorCode variables} {
153    list [catch {error msg1 {} msg3} msg] $msg $::errorInfo $::errorCode
154} {1 msg1 {msg1
155    while executing
156"error msg1 {} msg3"} msg3}
157test error-4.3 {errorInfo and errorCode variables} {
158    list [catch {error msg1 {}} msg] $msg $::errorInfo $::errorCode
159} {1 msg1 {msg1
160    while executing
161"error msg1 {}"} NONE}
162test error-4.4 {errorInfo and errorCode variables} {
163    set ::errorCode bogus
164    list [catch {error msg1} msg] $msg $::errorInfo $::errorCode
165} {1 msg1 {msg1
166    while executing
167"error msg1"} NONE}
168test error-4.5 {errorInfo and errorCode variables} {
169    set ::errorCode bogus
170    list [catch {error msg1 msg2 {}} msg] $msg $::errorInfo $::errorCode
171} {1 msg1 msg2 {}}
172
173test error-4.6 {errorstack via info } -body {
174    proc f x {g $x$x}
175    proc g x {error G:$x}
176    catch {f 12}
177    info errorstack
178} -match glob -result {INNER * CALL {g 1212} CALL {f 12} UP 1}
179test error-4.7 {errorstack via options dict } -body {
180    proc f x {g $x$x}
181    proc g x {error G:$x}
182    catch {f 12} m d
183    dict get $d -errorstack
184} -match glob -result {INNER * CALL {g 1212} CALL {f 12} UP 1}
185test error-4.8 {errorstack from exec traces} -body {
186    proc foo args {}
187    proc goo {} foo
188    trace add execution foo enter {error bar;#}
189    catch goo m d
190    dict get $d -errorstack
191} -cleanup {
192    rename goo {}; rename foo {}
193    unset -nocomplain m d
194} -result {INNER {error bar} CALL goo UP 1}
195
196# Errors in error command itself
197
198test error-5.1 {errors in error command} {
199    list [catch {error} msg] $msg
200} {1 {wrong # args: should be "error message ?errorInfo? ?errorCode?"}}
201test error-5.2 {errors in error command} {
202    list [catch {error a b c d} msg] $msg
203} {1 {wrong # args: should be "error message ?errorInfo? ?errorCode?"}}
204
205# Make sure that catch resets error information
206
207test error-6.1 {catch must reset error state} {
208    catch {error outer [catch {error inner inner.errorInfo inner.errorCode}]}
209    list $::errorCode $::errorInfo
210} {NONE 1}
211test error-6.2 {catch must reset error state} {
212    catch {error outer [catch {return -level 0 -code error -errorcode BUG}]}
213    list $::errorCode $::errorInfo
214} {NONE 1}
215test error-6.3 {catch must reset error state} {
216    set ::errorCode BUG
217    catch {error outer [catch set]}
218    list $::errorCode $::errorInfo
219} {NONE 1}
220test error-6.4 {catch must reset error state} {
221    catch {error [catch {error foo bar baz}] 1}
222    list $::errorCode $::errorInfo
223} {NONE 1}
224test error-6.5 {catch must reset error state} {
225    catch {error [catch {return -level 0 -code error -errorcode BUG}] 1}
226    list $::errorCode $::errorInfo
227} {NONE 1}
228test error-6.6 {catch must reset error state} {
229    catch {return -level 0 -code error -errorinfo [catch {error foo bar baz}]}
230    list $::errorCode $::errorInfo
231} {NONE 1}
232test error-6.7 {catch must reset error state} {
233    proc foo {} {
234	return -code error -errorinfo [catch {error foo bar baz}]
235    }
236    catch foo
237    list $::errorCode
238} {NONE}
239test error-6.8 {catch must reset error state} {
240    catch {return -level 0 -code error [catch {error foo bar baz}]}
241    list $::errorCode
242} {NONE}
243test error-6.9 {catch must reset error state} {
244    proc foo {} {
245	return -code error [catch {error foo bar baz}]
246    }
247    catch foo
248    list $::errorCode
249} {NONE}
250test error-6.10 {catch must reset errorstack} -body {
251	proc f x {g $x$x}
252	proc g x {error G:$x}
253	catch {f 12}
254	set e1 [info errorstack]
255	catch {f 13}
256	set e2 [info errorstack]
257	list $e1 $e2
258} -match glob -result {{INNER * CALL {g 1212} CALL {f 12} UP 1} {INNER * CALL {g 1313} CALL {f 13} UP 1}}
259
260test error-7.1 {Bug 1397843} -body {
261    variable cmds
262    proc EIWrite args {
263	variable cmds
264	lappend cmds [lindex [info level -2] 0]
265    }
266    proc BadProc {} {
267	set i a
268	incr i
269    }
270    trace add variable ::errorInfo write [namespace code EIWrite]
271    catch BadProc
272    trace remove variable ::errorInfo write [namespace code EIWrite]
273    set cmds
274} -match glob -result {*BadProc*}
275
276# throw tests
277
278test error-8.1 {throw produces error 1 at level 0} {
279    catch { throw FOO bar }
280} {1}
281test error-8.2 {throw behaves as error does at level 0} {
282    catch { throw FOO bar } em1 opts1
283    catch { error bar {} FOO } em2 opts2
284    dict set opts1 -result $em1
285    dict set opts2 -result $em2
286    foreach key {-code -level -result -errorcode} {
287	if { [dict get $opts1 $key] ne [dict get $opts2 $key] } {
288	    error "error/throw outcome differs on '$key'"
289	}
290    }
291} {}
292test error-8.3 {throw produces error 1 at level > 0} {
293    proc throw_foo {} {
294	throw FOO bar
295    }
296    catch { throw_foo }
297} {1}
298test error-8.4 {throw behaves as error does at level > 0} {
299    proc throw_foo {} {
300	throw FOO bar
301    }
302    proc error_foo {} {
303	error bar {} FOO
304    }
305    catch { throw_foo } em1 opts1
306    catch { error_foo } em2 opts2
307    dict set opts1 -result $em1
308    dict set opts2 -result $em2
309    foreach key {-code -level -result -errorcode} {
310	if { [dict get $opts1 $key] ne [dict get $opts2 $key] } {
311	    error "error/throw outcome differs on '$key'"
312	}
313    }
314} {}
315test error-8.5 {throw syntax checks} -returnCodes error -body {
316    throw
317} -result {wrong # args: should be "throw type message"}
318test error-8.6 {throw syntax checks} -returnCodes error -body {
319    throw a
320} -result {wrong # args: should be "throw type message"}
321test error-8.7 {throw syntax checks} -returnCodes error -body {
322    throw a b c
323} -result {wrong # args: should be "throw type message"}
324test error-8.8 {throw syntax checks} -returnCodes error -body {
325    throw "not a \{ list" foo
326} -result {unmatched open brace in list}
327test error-8.9 {throw syntax checks} -returnCodes error -body {
328    throw {} foo
329} -result {type must be non-empty list}
330test error-8.10 {Bug 33b7abb8a2: throw stack usage} -returnCodes error -body {
331    apply {code {throw $code foo}} {}
332} -result {type must be non-empty list}
333test error-8.11 {Bug 7174354ecb: throw error message} -returnCodes error  -body {
334    throw {not {}a list} x[]y
335} -result {list element in braces followed by "a" instead of space}
336
337# simple try tests: body completes with code ok
338
339test error-9.1 {try (ok, empty result) with no handlers} {
340    try list
341} {}
342test error-9.2 {try (ok, non-empty result) with no handlers} {
343    try { list a b c }
344} {a b c}
345test error-9.3 {try (ok, non-empty result) with trap handler} {
346    try { list a b c } trap {} {} { list d e f }
347} {a b c}
348test error-9.4 {try (ok, non-empty result) with on handler} {
349    try { list a b c } on break {} { list d e f }
350} {a b c}
351test error-9.5 {try (ok, non-empty result) with on ok handler} {
352    try { list a b c } on ok {} { list d e f }
353} {d e f}
354
355# simple try tests - "on" handler matching
356
357test error-10.1 {try with on ok} {
358    try { list a b c } on ok {} { list d e f }
359} {d e f}
360test error-10.2 {try with on 0} {
361    try { list a b c } on 0 {} { list d e f }
362} {d e f}
363test error-10.3 {try with on error (using error)} {
364    try { error a b c } on error {} { list d e f }
365} {d e f}
366test error-10.4 {try with on error (using return -code)} {
367    try { return -level 0 -code 1 a } on error {} { list d e f }
368} {d e f}
369test error-10.5 {try with on error (using throw)} {
370    try { throw c a } on error {} { list d e f }
371} {d e f}
372test error-10.6 {try with on 1 (using error)} {
373    try { error a b c } on 1 {} { list d e f }
374} {d e f}
375test error-10.7 {try with on return} {
376    try { return [list a b c] } on return {} { list d e f }
377} {d e f}
378test error-10.8 {try with on break} {
379    try { break } on break {} { list d e f }
380} {d e f}
381test error-10.9 {try with on continue} {
382    try { continue } on continue {} { list d e f }
383} {d e f}
384test error-10.10 {try with on for arbitrary (decimal) return code} {
385    try { return -level 0 -code 123456 } on 123456 {} { list d e f }
386} {d e f}
387test error-10.11 {try with on for arbitrary (hex) return code} {
388    try { return -level 0 -code 0x123456 } on 0x123456 {} { list d e f }
389} {d e f}
390test error-10.12 {try with on for arbitrary return code (mixed number representations)} {
391    try { return -level 0 -code 0x10 } on 16 {} { list d e f }
392} {d e f}
393
394# simple try tests - "trap" handler matching
395
396test error-11.1 {try with trap all} {
397    try { throw FOO bar  } trap {} {} { list d e f }
398} {d e f}
399test error-11.2 {try with trap (exact)} {
400    try { throw FOO bar  } trap {FOO} {} { list d e f }
401} {d e f}
402test error-11.3 {try with trap (prefix 1)} {
403    try { throw [list FOO A B C D] bar  } trap {FOO} {} { list d e f }
404} {d e f}
405test error-11.4 {try with trap (prefix 2)} {
406    try { throw [list FOO A B C D] bar  } trap {FOO A} {} { list d e f }
407} {d e f}
408test error-11.5 {try with trap (prefix 3)} {
409    try { throw [list FOO A B C D] bar  } trap {FOO A B} {} { list d e f }
410} {d e f}
411test error-11.6 {try with trap (prefix 4)} {
412    try { throw [list FOO A B C D] bar  } trap {FOO A B C} {} { list d e f }
413} {d e f}
414test error-11.7 {try with trap (exact, 5 elements)} {
415    try { throw [list FOO A B C D] bar  } trap {FOO A B C D} {} { list d e f }
416} {d e f}
417
418# simple try tests - variable assignment and result handling
419
420test error-12.1 {try with no variable assignment in on handler} {
421    try { throw FOO bar } on error {} { list d e f }
422} {d e f}
423test error-12.2 {try with result variable assignment in on handler} {
424    try { throw FOO bar } on error {res} { set res }
425} {bar}
426test error-12.3 {try with result variable assignment in on handler, var remains in scope} {
427    try { throw FOO bar } on error {res} { list d e f }
428    set res
429} {bar}
430test error-12.4 {try with result/opts variable assignment in on handler} {
431    try {
432	throw FOO bar
433    } on error {res opts} {
434	set r "$res,[dict get $opts -errorcode]"
435    }
436} {bar,FOO}
437test error-12.5 {try with result/opts variable assignment in on handler, vars remain in scope} {
438    try { throw FOO bar } on error {res opts} { list d e f }
439    set r "$res,[dict get $opts -errorcode]"
440} {bar,FOO}
441test error-12.6 {try result is propagated if no matching handler} {
442    try { list a b c } on error {} { list d e f }
443} {a b c}
444test error-12.7 {handler result is propagated if handler executes} {
445    try { throw FOO bar } on error {} { list d e f }
446} {d e f}
447
448# negative case try tests - bad args to try
449
450test error-13.1 {try with no arguments} -body {
451    # warning: error message may change
452    try
453} -returnCodes error -match glob -result {wrong # args: *}
454test error-13.2 {try with body only (ok)} {
455    try list
456} {}
457test error-13.3 {try with missing finally body} -body {
458    # warning: error message may change
459    try list finally
460} -returnCodes error -match glob -result {wrong # args to finally clause: *}
461test error-13.4 {try with bad handler keyword} -body {
462    # warning: error message may change
463    try list then a b c
464} -returnCodes error -match glob -result {bad handler *}
465test error-13.5 {try with partial handler #1} -body {
466    # warning: error message may change
467    try list on
468} -returnCodes error -match glob -result {wrong # args to on clause: *}
469test error-13.6 {try with partial handler #2} -body {
470    # warning: error message may change
471    try list on error
472} -returnCodes error -match glob -result {wrong # args to on clause: *}
473test error-13.7 {try with partial handler #3} -body {
474    # warning: error message may change
475    try list on error {em opts}
476} -returnCodes error -match glob -result {wrong # args to on clause: *}
477test error-13.8 {try with multiple handlers and finally (ok)} {
478    try list on error {} {} trap {} {} {} finally {}
479} {}
480test error-13.9 {last handler body can't be a fallthrough #1} -body {
481    try list on error {} {} on break {} -
482} -returnCodes error -result {last non-finally clause must not have a body of "-"}
483test error-13.10 {last handler body can't be a fallthrough #2} -body {
484    try list on error {} {} on break {} - finally { list d e f }
485} -returnCodes error -result {last non-finally clause must not have a body of "-"}
486
487# try tests - multiple handlers (left-to-right matching, only one runs)
488
489test error-14.1 {try with multiple handlers (only one matches) #1} {
490    try { throw FOO bar } on ok {} { list a b c } trap FOO {} { list d e f }
491} {d e f}
492test error-14.2 {try with multiple handlers (only one matches) #2} {
493    try { throw FOO bar } trap FOO {} { list d e f } on ok {} { list a b c }
494} {d e f}
495test error-14.3 {try with multiple handlers (only one matches) #3} {
496    try {
497	throw FOO bar
498    } on break {} {
499	list x y z
500    } trap FOO {} {
501	list d e f
502    } on ok {} {
503	list a b c
504    }
505} {d e f}
506test error-14.4 {try with multiple matching handlers (only the first in left-to-right order runs) #1} {
507    try { throw FOO bar } on error {} { list a b c } trap FOO {} { list d e f }
508} {a b c}
509test error-14.5 {try with multiple matching handlers (only the first in left-to-right order runs) #2} {
510    try { throw FOO bar } trap FOO {} { list d e f } on error {} { list a b c }
511} {d e f}
512test error-14.6 {try with multiple matching handlers (only the first in left-to-right order runs) #3} {
513    try { throw FOO bar } trap {} {} { list d e f } on 1 {} { list a b c }
514} {d e f}
515test error-14.7 {try with multiple matching handlers (only the first in left-to-right order runs) #4} {
516    try { throw FOO bar } on 1 {} { list a b c } trap {} {} { list d e f }
517} {a b c}
518test error-14.8 {try with handler-of-last-resort "trap {}"} {
519    try { throw FOO bar } trap FOX {} { list a b c } trap {} {} { list d e f }
520} {d e f}
521test error-14.9 {try with handler-of-last-resort "on error"} {
522    try { foo } trap FOX {} { list a b c } on error {} { list d e f }
523} {d e f}
524
525# try tests - propagation (no matching handlers)
526
527test error-15.1 {try with no handler (ok result propagates)} {
528    try { list a b c }
529} {a b c}
530test error-15.2 {try with no matching handler (ok result propagates)} {
531    try { list a b c } on error {} { list d e f }
532} {a b c}
533test error-15.3 {try with no handler (error result propagates)} -body {
534    try { throw FOO bar }
535} -returnCodes error -result {bar}
536test error-15.4 {try with no matching handler (error result propagates)} -body {
537    try { throw FOO bar } trap FOX {} { list a b c }
538} -returnCodes error -result {bar}
539test error-15.5 {try with no handler (return result propagates)} -body {
540    try { return bar }
541} -returnCodes 2 -result {bar}
542test error-15.6 {try with no matching handler (break result propagates)} -body {
543    try { if {1} break } on error {} { list a b c }
544} -returnCodes 3 -result {}
545test error-15.7 {try with no matching handler (unknown integer result propagates)} -body {
546    try { return -level 0 -code 123456 } trap {} {} { list a b c }
547} -returnCodes 123456 -result {}
548
549foreach level {0 1 2 3} {
550    foreach code {0 1 2 3 4 5} {
551
552	# Following cases have different -errorinfo; avoid false alarms
553	# TODO: examine whether these difference are as they ought to be.
554	if {$level == 0 && $code == 1} continue
555
556	foreach extras {{} {-bar soom}} {
557
558test error-15.8.$level.$code.[llength $extras] {[try] coverage} {
559    set script {return -level $level -code $code {*}$extras foo}
560    catch $script m1 o1
561    catch {try $script} m2 o2
562    set o1 [lsort -stride 2 $o1]
563    set o2 [lsort -stride 2 $o2]
564    expr {$o1 eq $o2 ? "ok" : "$o1\n\tis not equal to\n$o2"}
565} ok
566
567test error-15.9.$level.$code.[llength $extras] {[try] coverage} {
568    set script {return -level $level -code $code {*}$extras foo}
569    catch $script m1 o1
570    catch {try $script finally {}} m2 o2
571    set o1 [lsort -stride 2 $o1]
572    set o2 [lsort -stride 2 $o2]
573    expr {$o1 eq $o2 ? "ok" : "$o1\n\tis not equal to\n$o2"}
574} ok
575
576test error-15.10.$level.$code.[llength $extras] {[try] coverage} {
577    set script {return -level $level -code $code {*}$extras foo}
578    catch $script m1 o1
579    catch {try $script on $code {x y} {return -options $y $x}} m2 o2
580    set o1 [lsort -stride 2 $o1]
581    set o2 [lsort -stride 2 $o2]
582    expr {$o1 eq $o2 ? "ok" : "$o1\n\tis not equal to\n$o2"}
583} ok
584
585	}
586    }
587}
588
589# try tests - propagation (exceptions in handlers, exception chaining)
590
591test error-16.1 {try with successfully executed handler} {
592    try { throw FOO bar } trap FOO {} { list a b c }
593} {a b c}
594test error-16.2 {try with exception (error) in handler} -body {
595    try { throw FOO bar } trap FOO {} { throw BAR foo }
596} -returnCodes error -result {foo}
597test error-16.3 {try with exception (return) in handler} -body {
598    try { throw FOO bar } trap FOO {} { return BAR }
599} -returnCodes 2 -result {BAR}
600test error-16.4 {try with exception (break) in handler #1} -body {
601    try { throw FOO bar } trap FOO {} { break }
602} -returnCodes 3 -result {}
603test error-16.5 {try with exception (break) in handler #2} {
604    for { set i 5 } { $i < 10 } { incr i } {
605	try { throw FOO bar } trap FOO {} { break }
606    }
607    set i
608} {5}
609test error-16.6 {try with variable assignment and propagation #1} {
610    # Ensure that the handler variables preserve the exception off the
611    # try-body, and are not modified by the exception off the handler
612    catch {
613	try { throw FOO bar } trap FOO {em} { throw BAR baz }
614    }
615    set em
616} {bar}
617test error-16.7 {try with variable assignment and propagation #2} {
618    catch {
619	try { throw FOO bar } trap FOO {em opts} { throw BAR baz }
620    }
621    list $em [dict get $opts -errorcode]
622} {bar FOO}
623test error-16.8 {exception chaining (try=ok, handler=error)} -body {
624    #FIXME is the intent of this test correct?
625    catch {
626	try { list a b c } on ok {em opts} { throw BAR baz }
627    } tryem tryopts
628    list $opts [dict get $tryopts -during]
629} -match pairwise -result equal
630test error-16.9 {exception chaining (try=error, handler=error)} -body {
631    # The exception off the handler should chain to the exception off the
632    # try-body (using the -during option)
633    catch {
634	try { throw FOO bar } trap {} {em opts} { throw BAR baz }
635    } tryem tryopts
636    list $opts [dict get $tryopts -during]
637} -match pairwise -result equal
638test error-16.10 {no exception chaining when handler is successful} {
639    catch {
640	try { throw FOO bar } trap {} {em opts} { list d e f }
641    } tryem tryopts
642    dict exists $tryopts -during
643} {0}
644test error-16.11 {no exception chaining when handler is a non-error exception} {
645    catch {
646	try { throw FOO bar } trap {} {em opts} { break }
647    } tryem tryopts
648    dict exists $tryopts -during
649} {0}
650test error-16.12 {compiled try with successfully executed handler} {
651    apply {{} {
652	try { throw FOO bar } trap FOO {} { list a b c }
653    }}
654} {a b c}
655test error-16.13 {compiled try with exception (error) in handler} -body {
656    apply {{} {
657	try { throw FOO bar } trap FOO {} { throw BAR foo }
658    }}
659} -returnCodes error -result {foo}
660test error-16.14 {compiled try with exception (return) in handler} -body {
661    apply {{} {
662	list [catch {
663	    try { throw FOO bar } trap FOO {} { return BAR }
664	} msg] $msg
665    }}
666} -result {2 BAR}
667test error-16.15 {compiled try with exception (break) in handler} {
668    apply {{} {
669	for { set i 5 } { $i < 10 } { incr i } {
670	    try { throw FOO bar } trap FOO {} { break }
671	}
672	return $i
673    }}
674} {5}
675test error-16.16 {compiled try with exception (continue) in handler} {
676    apply {{} {
677	for { set i 5 } { $i < 10 } { incr i } {
678	    try { throw FOO bar } trap FOO {} { continue }
679	    incr i 20
680	}
681	return $i
682    }}
683} {10}
684test error-16.17 {compiled try with variable assignment and propagation #1} {
685    # Ensure that the handler variables preserve the exception off the
686    # try-body, and are not modified by the exception off the handler
687    apply {{} {
688	catch {
689	    try { throw FOO bar } trap FOO {em} { throw BAR baz }
690	}
691	return $em
692    }}
693} {bar}
694test error-16.18 {compiled try with variable assignment and propagation #2} {
695    apply {{} {
696	catch {
697	    try { throw FOO bar } trap FOO {em opts} { throw BAR baz }
698	}
699	list $em [dict get $opts -errorcode]
700    }}
701} {bar FOO}
702test error-16.19 {compiled try exception chaining (try=ok, handler=error)} -body {
703    #FIXME is the intent of this test correct?
704    apply {{} {
705	catch {
706	    try { list a b c } on ok {em opts} { throw BAR baz }
707	} tryem tryopts
708	list $opts [dict get $tryopts -during]
709    }}
710} -match pairwise -result equal
711test error-16.20 {compiled try exception chaining (try=error, handler=error)} -body {
712    # The exception off the handler should chain to the exception off the
713    # try-body (using the -during option)
714    apply {{} {
715	catch {
716	    try { throw FOO bar } trap {} {em opts} { throw BAR baz }
717	} tryem tryopts
718	list $opts [dict get $tryopts -during]
719    }}
720} -match pairwise -result equal
721test error-16.21 {compiled try exception chaining (try=error, finally=error)} {
722    # The exception off the handler should chain to the exception off the
723    # try-body (using the -during option)
724    apply {{} {
725	catch {
726	    try { throw FOO bar } finally { throw BAR baz }
727	} tryem tryopts
728	dict get $tryopts -during -errorcode
729    }}
730} FOO
731test error-16.22 {compiled try: no exception chaining when handler is successful} {
732    apply {{} {
733	catch {
734	    try { throw FOO bar } trap {} {em opts} { list d e f }
735	} tryem tryopts
736	dict exists $tryopts -during
737    }}
738} {0}
739test error-16.23 {compiled try: no exception chaining when handler is a non-error exception} {
740    apply {{} {
741	catch {
742	    try { throw FOO bar } trap {} {em opts} { break }
743	} tryem tryopts
744	dict exists $tryopts -during
745    }}
746} {0}
747test error-16.24 {compiled try exception chaining (try=ok, handler=error, finally=error)} -body {
748    apply {{} {
749	catch {
750	    try {
751		list a b c
752	    } on ok {em opts} {
753		throw BAR baz
754	    } finally {
755		throw DING dong
756	    }
757	} tryem tryopts
758	list $opts [dict get $tryopts -during -during]
759    }}
760} -match pairwise -result equal
761test error-16.25 {compiled try exception chaining (all errors)} -body {
762    apply {{} {
763	catch {
764	    try {
765		throw FOO bar
766	    } on error {em opts} {
767		throw BAR baz
768	    } finally {
769		throw DING dong
770	    }
771	} tryem tryopts
772	list $opts [dict get $tryopts -during -during]
773    }}
774} -match pairwise -result equal
775
776# try tests - finally
777
778test error-17.1 {finally always runs (try with ok result)} {
779    set RES {}
780    try { list a b c } finally { set RES done }
781    set RES
782} {done}
783test error-17.2 {finally always runs (try with error result)} {
784    set RES {}
785    catch {
786	try { throw FOO bar } finally { set RES done }
787    }
788    set RES
789} {done}
790test error-17.3 {finally always runs (try with matching handler)} {
791    set RES {}
792    try { throw FOO bar } trap FOO {} { list a b c } finally { set RES done }
793    set RES
794} {done}
795test error-17.4 {finally always runs (try with exception in handler)} {
796    set RES {}
797    catch {
798	try {
799	    throw FOO bar
800	} trap FOO {} {
801	    throw BAR baz
802	} finally {
803	    set RES done
804	}
805    }
806    set RES
807} {done}
808test error-17.5 {successful finally doesn't modify try outcome (try=ok)} {
809    try { list a b c } finally { list d e f }
810} {a b c}
811test error-17.6 {successful finally doesn't modify try outcome (try=return)} -body {
812    try { return c } finally { list d e f }
813} -returnCodes 2 -result {c}
814test error-17.7 {successful finally doesn't modify try outcome (try=error)} -body {
815    try { error bar } finally { list d e f }
816} -returnCodes 1 -result {bar}
817test error-17.8 {successful finally doesn't modify handler outcome (handler=ok)} {
818    try { throw FOO bar } trap FOO {} { list a b c } finally { list d e f }
819} {a b c}
820test error-17.9 {successful finally doesn't modify handler outcome (handler=error)} -body {
821    try { throw FOO bar } trap FOO {} { throw BAR baz } finally { list d e f }
822} -returnCodes error -result {baz}
823test error-17.10 {successful finally doesn't affect variable assignment} {
824    catch {
825	try { throw FOO bar } trap FOO {em opts} { list d e f } finally { list d e f }
826    } result
827    list $em $result
828} {bar {d e f}}
829test error-17.11 {successful finally doesn't affect variable assignment or propagation} {
830    catch {
831	try { throw FOO bar } trap FOO {em opts} { throw BAR baz } finally { list d e f }
832    }
833    list $em [dict get $opts -errorcode]
834} {bar FOO}
835
836# try tests - propagation (exceptions in finally, exception chaining)
837
838test error-18.1 {try (ok) with exception in finally (error)} -body {
839    try { list a b c } finally { throw BAR foo }
840} -returnCodes error -result {foo}
841test error-18.2 {try (error) with exception in finally (break)} -body {
842    try { throw FOO bar } finally { break }
843} -returnCodes 3 -result {}
844test error-18.3 {try (ok) with handler (ok) and exception in finally (error)} -body {
845    try { list a b c } on ok {} { list d e f } finally { throw BAR foo }
846} -returnCodes error -result {foo}
847test error-18.4 {try (error) with exception in handler (error) and in finally (arb code)} -body {
848    try { throw FOO bar } on error {} { throw BAR baz } finally { return -level 0 -code 99 zing }
849} -returnCodes 99 -result {zing}
850test error-18.5 {exception in finally doesn't affect variable assignment} {
851    catch {
852	try { throw FOO bar } trap FOO {em opts} { throw BAR baz } finally { throw BAZ zing }
853    }
854    list $em [dict get $opts -errorcode]
855} {bar FOO}
856test error-18.6 {exception chaining in finally (try=ok)} -body {
857    catch {
858	list a b c
859    } em expopts
860    catch {
861	try { list a b c } finally { throw BAR foo }
862    } em opts
863    list $expopts [dict get $opts -during]
864} -match pairwise -result equal
865test error-18.7 {exception chaining in finally (try=error)} {
866    catch {
867	try { throw FOO bar } finally { throw BAR baz }
868    } em opts
869    dict get $opts -during -errorcode
870} {FOO}
871test error-18.8 {exception chaining in finally (try=ok, handler=ok)} {
872    catch {
873	try { list a b c } on ok {} { list d e f } finally { throw BAR baz }
874    } em opts
875    list [dict get $opts -during -code] [dict exists $opts -during -during]
876} {0 0}
877test error-18.9 {exception chaining in finally (try=error, handler=ok)} {
878    catch {
879	try {
880	    throw FOO bar
881	} on error {} {
882	    list d e f
883	} finally {
884	    throw BAR baz
885	}
886    } em opts
887    list [dict get $opts -during -code] [dict exists $opts -during -during]
888} {0 0}
889test error-18.10 {exception chaining in finally (try=error, handler=error)} {
890    catch {
891	try {
892	    throw FOO bar
893	} on error {} {
894	    throw BAR baz
895	} finally {
896	    throw BAR baz
897	}
898    } em opts
899    list [dict get $opts -during -errorcode] [dict get $opts -during -during -errorcode]
900} {BAR FOO}
901test error-18.11 {no exception chaining if finally produces a non-error exception} {
902    catch {
903	try { throw FOO bar } on error {} { throw BAR baz } finally { break }
904    } em opts
905    dict exists $opts -during
906} {0}
907test error-18.12 {variable assignment unaffected by exception in finally} {
908    catch {
909	try {
910	    throw FOO bar
911	} on error {em opts} {
912	    list a b c
913	} finally {
914	    throw BAR baz
915	}
916    }
917    list $em [dict get $opts -errorcode]
918} {bar FOO}
919
920# try tests - fallthough body cases
921
922test error-19.1 {try with fallthrough body #1} {
923    set RES {}
924    try { list a b c } on ok { set RES 0 } - on error {} { set RES 1 }
925    set RES
926} {1}
927test error-19.2 {try with fallthrough body #2} {
928    set RES {}
929    try {
930	throw FOO bar
931    } trap BAR {} {
932    } trap FOO {} - trap {} {} {
933	set RES foo
934    } on error {} {
935	set RES err
936    }
937    set RES
938} {foo}
939test error-19.3 {try with cascade fallthrough} {
940    set RES {}
941    try {
942	throw FOO bar
943    } trap FOO {} - trap BAR {} - trap {} {} {
944	set RES trap
945    } on error {} { set RES err }
946    set RES
947} {trap}
948test error-19.4 {multiple unrelated fallthroughs #1} {
949    set RES {}
950    try {
951	throw FOO bar
952    } trap FOO {} - trap BAR {} {
953	set RES foo
954    } trap {} {} - on error {} {
955	set RES err
956    }
957    set RES
958} {foo}
959test error-19.5 {multiple unrelated fallthroughs #2} {
960    set RES {}
961    try {
962	throw BAZ zing
963    } trap FOO {} - trap BAR {} {
964	set RES foo
965    } trap {} {} - on error {} {
966	set RES err
967    }
968    set RES
969} {err}
970proc addmsg msg {
971    variable RES
972    lappend RES $msg
973}
974test error-19.6 {compiled try executes all clauses} -setup {
975    set RES {}
976} -body {
977    apply {{} {
978	try {
979	    addmsg a
980	    throw bar hello
981	} trap bar {res opt} {
982	    addmsg b
983	} finally {
984	    addmsg c
985	}
986	addmsg d
987    } ::tcl::test::error}
988} -cleanup {
989    unset RES
990} -result {a b c d}
991test error-19.7 {compiled try executes all clauses} -setup {
992    set RES {}
993} -body {
994    apply {{} {
995	try {
996	    addmsg a
997	} on error {res opt} {
998	    addmsg b
999	} on ok {} {
1000	    addmsg c
1001	} finally {
1002	    addmsg d
1003	}
1004	addmsg e
1005    } ::tcl::test::error}
1006} -cleanup {
1007    unset RES
1008} -result {a c d e}
1009test error-19.8 {compiled try executes all clauses} -setup {
1010    set RES {}
1011} -body {
1012    apply {{} {
1013	try {
1014	    addmsg a
1015	    throw bar hello
1016	} trap bar {res opt} {
1017	    addmsg b
1018	}
1019	addmsg c
1020    } ::tcl::test::error}
1021} -cleanup {
1022    unset RES
1023} -result {a b c}
1024test error-19.9 {compiled try executes all clauses} -setup {
1025    set RES {}
1026} -body {
1027    apply {{} {
1028	try {
1029	    addmsg a
1030	} on error {res opt} {
1031	    addmsg b
1032	} on ok {} {
1033	    addmsg c
1034	}
1035	addmsg d
1036    } ::tcl::test::error}
1037} -cleanup {
1038    unset RES
1039} -result {a c d}
1040test error-19.10 {compiled try with chained clauses} -setup {
1041    set RES {}
1042} -body {
1043    list [apply {{} {
1044	try {
1045	    return good
1046	} on return {res} - on ok {res} {
1047	    addmsg ok
1048	    addmsg $res
1049	    return handler
1050	} finally {
1051	    addmsg finally
1052	}
1053    } ::tcl::test::error}] $RES
1054} -cleanup {
1055    unset RES
1056} -result {handler {ok good finally}}
1057test error-19.11 {compiled try and errors on variable write} -setup {
1058    set RES {}
1059} -body {
1060    apply {{} {
1061	array set foo {bar boo}
1062	set bar unset
1063	catch {
1064	    try {
1065		addmsg body
1066		return a
1067	    } on return {bar foo} {
1068		addmsg handler
1069		return b
1070	    } finally {
1071		addmsg finally,$bar
1072	    }
1073	} msg
1074	addmsg $msg
1075    } ::tcl::test::error}
1076} -cleanup {
1077    unset RES
1078} -result {body finally,a {can't set "foo": variable is array}}
1079test error-19.12 {interpreted try and errors on variable write} -setup {
1080    set RES {}
1081} -body {
1082    apply {try {
1083	array set foo {bar boo}
1084	set bar unset
1085	catch {
1086	    $try {
1087		addmsg body
1088		return a
1089	    } on return {bar foo} {
1090		addmsg handler
1091		return b
1092	    } finally {
1093		addmsg finally,$bar
1094	    }
1095	} msg
1096	addmsg $msg
1097    } ::tcl::test::error} try
1098} -cleanup {
1099    unset RES
1100} -result {body finally,a {can't set "foo": variable is array}}
1101test error-19.13 {compiled try and errors on variable write} -setup {
1102    set RES {}
1103} -body {
1104    apply {{} {
1105	array set foo {bar boo}
1106	set bar unset
1107	catch {
1108	    try {
1109		addmsg body
1110		return a
1111	    } on return {bar foo} - on error {bar foo} {
1112		addmsg handler
1113		return b
1114	    } finally {
1115		addmsg finally,$bar
1116	    }
1117	} msg
1118	addmsg $msg
1119    } ::tcl::test::error}
1120} -cleanup {
1121    unset RES
1122} -result {body finally,a {can't set "foo": variable is array}}
1123rename addmsg {}
1124
1125# FIXME test what vars get set on fallthough ... what is the correct behavior?
1126# It would seem appropriate to set at least those for the matching handler and
1127# the executed body; possibly for each handler we fall through as well?
1128
1129# negative case try tests - bad "on" handler
1130
1131test error-20.1 {bad code name in on handler} -body {
1132    try { list a b c } on err {} {}
1133} -returnCodes error -match glob -result {bad completion code "err": must be ok, error, return, break, continue*, or an integer}
1134test error-20.2 {bad code value in on handler} -body {
1135    try { list a b c } on 34985723094872345 {} {}
1136} -returnCodes error -match glob -result {bad completion code "34985723094872345": must be ok, error, return, break, continue*, or an integer}
1137
1138test error-21.1 {memory leaks in try: Bug 2910044} memory {
1139    leaktest {
1140	try {string repeat x 10} on ok {} {}
1141    }
1142} 0
1143test error-21.2 {memory leaks in try: Bug 2910044} memory {
1144    leaktest {
1145	try {error [string repeat x 10]} on error {} {}
1146    }
1147} 0
1148test error-21.3 {memory leaks in try: Bug 2910044} memory {
1149    leaktest {
1150	try {throw FOO [string repeat x 10]} trap FOO {} {}
1151    }
1152} 0
1153test error-21.4 {memory leaks in try: Bug 2910044} memory {
1154    leaktest {
1155	try {string repeat x 10}
1156    }
1157} 0
1158test error-21.5 {memory leaks in try: Bug 2910044} memory {
1159    leaktest {
1160	try {string repeat x 10} on ok {} {} finally {string repeat y 10}
1161    }
1162} 0
1163test error-21.6 {memory leaks in try: Bug 2910044} memory {
1164    leaktest {
1165	try {
1166	    error [string repeat x 10]
1167	} on error {} {} finally {
1168	    string repeat y 10
1169	}
1170    }
1171} 0
1172test error-21.7 {memory leaks in try: Bug 2910044} memory {
1173    leaktest {
1174	try {
1175	    throw FOO [string repeat x 10]
1176	} trap FOO {} {} finally {
1177	    string repeat y 10
1178	}
1179    }
1180} 0
1181test error-21.8 {memory leaks in try: Bug 2910044} memory {
1182    leaktest {
1183	try {string repeat x 10} finally {string repeat y 10}
1184    }
1185} 0
1186
1187test error-21.9 {Bug cee90e4e88} {
1188    # Just don't panic.
1189    apply {{} {try {} on ok {} - on return {} {}}}
1190} {}
1191
1192
1193# negative case try tests - bad "trap" handler
1194# what is the effect if we attempt to trap an errorcode that is not a list?
1195# nested try
1196# catch inside try
1197# no tests for bad varslist?
1198# -errorcode but code!=1 doesn't trap
1199# throw negative case tests (no args, too many args, etc)
1200
1201}
1202namespace delete ::tcl::test::error
1203
1204# cleanup
1205catch {rename p ""}
1206::tcltest::cleanupTests
1207return
1208
1209# Local Variables:
1210# mode: tcl
1211# End:
1212