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