aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2017-08-28 10:03:21 +1000
committerSteve Bennett <steveb@workware.net.au>2017-09-15 19:17:48 +1000
commiteb1918117c0ae5f2b67d441f2ed459718e79cad4 (patch)
tree593d49627e6c26a7158ac32c8a819415cd3ddf91 /tests
parenta5877cb1c624597f340fe5268c2ff8e61f6de4b0 (diff)
downloadjimtcl-eb1918117c0ae5f2b67d441f2ed459718e79cad4.zip
jimtcl-eb1918117c0ae5f2b67d441f2ed459718e79cad4.tar.gz
jimtcl-eb1918117c0ae5f2b67d441f2ed459718e79cad4.tar.bz2
Implement defer, $jim::defer
Allows commands to run when a proc or interpreter exits. If the $jim::defer variables exists at proc or interp exit, it is treated as a list of scripts to evaluate (in reverse order). The [defer] command is a helper to add scripts to $jim::defer See tests/defer.test Signed-off-by: Steve Bennett <steveb@workware.net.au>
Diffstat (limited to 'tests')
-rw-r--r--tests/defer.test237
1 files changed, 237 insertions, 0 deletions
diff --git a/tests/defer.test b/tests/defer.test
new file mode 100644
index 0000000..c714656
--- /dev/null
+++ b/tests/defer.test
@@ -0,0 +1,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