880 lines
21 KiB
Plaintext
880 lines
21 KiB
Plaintext
# Commands covered: coroutine, yield, yieldto, [info coroutine]
|
||
#
|
||
# This file contains a collection of tests for experimental commands that are
|
||
# found in ::tcl::unsupported. The tests will migrate to normal test files
|
||
# if/when the commands find their way into the core.
|
||
#
|
||
# Copyright (c) 2008 by Miguel Sofer.
|
||
#
|
||
# See the file "license.terms" for information on usage and redistribution
|
||
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
|
||
if {"::tcltest" ni [namespace children]} {
|
||
package require tcltest 2.5
|
||
namespace import -force ::tcltest::*
|
||
}
|
||
|
||
::tcltest::loadTestedCommands
|
||
catch [list package require -exact Tcltest [info patchlevel]]
|
||
|
||
testConstraint testnrelevels [llength [info commands testnrelevels]]
|
||
testConstraint memory [llength [info commands memory]]
|
||
|
||
set lambda [list {{start 0} {stop 10}} {
|
||
# init
|
||
set i $start
|
||
set imax $stop
|
||
yield
|
||
while {$i < $imax} {
|
||
yield [expr {$i*$stop}]
|
||
incr i
|
||
}
|
||
}]
|
||
|
||
test coroutine-1.1 {coroutine basic} -setup {
|
||
coroutine foo ::apply $lambda
|
||
set res {}
|
||
} -body {
|
||
for {set k 1} {$k < 4} {incr k} {
|
||
lappend res [foo]
|
||
}
|
||
set res
|
||
} -cleanup {
|
||
rename foo {}
|
||
unset res
|
||
} -result {0 10 20}
|
||
test coroutine-1.2 {coroutine basic} -setup {
|
||
coroutine foo ::apply $lambda 2 8
|
||
set res {}
|
||
} -body {
|
||
for {set k 1} {$k < 4} {incr k} {
|
||
lappend res [foo]
|
||
}
|
||
set res
|
||
} -cleanup {
|
||
rename foo {}
|
||
unset res
|
||
} -result {16 24 32}
|
||
test coroutine-1.3 {yield returns new arg} -setup {
|
||
set body {
|
||
# init
|
||
set i $start
|
||
set imax $stop
|
||
yield
|
||
while {$i < $imax} {
|
||
set stop [yield [expr {$i*$stop}]]
|
||
incr i
|
||
}
|
||
}
|
||
coroutine foo ::apply [list {{start 2} {stop 10}} $body]
|
||
set res {}
|
||
} -body {
|
||
for {set k 1} {$k < 4} {incr k} {
|
||
lappend res [foo $k]
|
||
}
|
||
set res
|
||
} -cleanup {
|
||
rename foo {}
|
||
unset res
|
||
} -result {20 6 12}
|
||
test coroutine-1.4 {yield in nested proc} -setup {
|
||
proc moo {} {
|
||
upvar 1 i i stop stop
|
||
yield [expr {$i*$stop}]
|
||
}
|
||
set body {
|
||
# init
|
||
set i $start
|
||
set imax $stop
|
||
yield
|
||
while {$i < $imax} {
|
||
moo
|
||
incr i
|
||
}
|
||
}
|
||
coroutine foo ::apply [list {{start 0} {stop 10}} $body]
|
||
set res {}
|
||
} -body {
|
||
for {set k 1} {$k < 4} {incr k} {
|
||
lappend res [foo $k]
|
||
}
|
||
set res
|
||
} -cleanup {
|
||
rename foo {}
|
||
rename moo {}
|
||
unset body res
|
||
} -result {0 10 20}
|
||
test coroutine-1.5 {just yield} -body {
|
||
coroutine foo yield
|
||
list [foo] [catch foo msg] $msg
|
||
} -cleanup {
|
||
unset msg
|
||
} -result {{} 1 {invalid command name "foo"}}
|
||
test coroutine-1.6 {just yield} -body {
|
||
coroutine foo [list yield]
|
||
list [foo] [catch foo msg] $msg
|
||
} -cleanup {
|
||
unset msg
|
||
} -result {{} 1 {invalid command name "foo"}}
|
||
test coroutine-1.7 {yield in nested uplevel} -setup {
|
||
set body {
|
||
# init
|
||
set i $start
|
||
set imax $stop
|
||
yield
|
||
while {$i < $imax} {
|
||
uplevel 0 [list yield [expr {$i*$stop}]]
|
||
incr i
|
||
}
|
||
}
|
||
coroutine foo ::apply [list {{start 0} {stop 10}} $body]
|
||
set res {}
|
||
} -body {
|
||
for {set k 1} {$k < 4} {incr k} {
|
||
lappend res [eval foo $k]
|
||
}
|
||
set res
|
||
} -cleanup {
|
||
rename foo {}
|
||
unset body res
|
||
} -result {0 10 20}
|
||
test coroutine-1.8 {yield in nested uplevel} -setup {
|
||
set body {
|
||
# init
|
||
set i $start
|
||
set imax $stop
|
||
yield
|
||
while {$i < $imax} {
|
||
uplevel 0 yield [expr {$i*$stop}]
|
||
incr i
|
||
}
|
||
}
|
||
coroutine foo ::apply [list {{start 0} {stop 10}} $body]
|
||
set res {}
|
||
} -body {
|
||
for {set k 1} {$k < 4} {incr k} {
|
||
lappend res [eval foo $k]
|
||
}
|
||
set res
|
||
} -cleanup {
|
||
rename foo {}
|
||
unset body res
|
||
} -result {0 10 20}
|
||
test coroutine-1.9 {yield in nested eval} -setup {
|
||
proc moo {} {
|
||
upvar 1 i i stop stop
|
||
yield [expr {$i*$stop}]
|
||
}
|
||
set body {
|
||
# init
|
||
set i $start
|
||
set imax $stop
|
||
yield
|
||
while {$i < $imax} {
|
||
eval moo
|
||
incr i
|
||
}
|
||
}
|
||
coroutine foo ::apply [list {{start 0} {stop 10}} $body]
|
||
set res {}
|
||
} -body {
|
||
for {set k 1} {$k < 4} {incr k} {
|
||
lappend res [foo $k]
|
||
}
|
||
set res
|
||
} -cleanup {
|
||
rename moo {}
|
||
unset body res
|
||
} -result {0 10 20}
|
||
test coroutine-1.10 {yield in nested eval} -setup {
|
||
set body {
|
||
# init
|
||
set i $start
|
||
set imax $stop
|
||
yield
|
||
while {$i < $imax} {
|
||
eval yield [expr {$i*$stop}]
|
||
incr i
|
||
}
|
||
}
|
||
coroutine foo ::apply [list {{start 0} {stop 10}} $body]
|
||
set res {}
|
||
} -body {
|
||
for {set k 1} {$k < 4} {incr k} {
|
||
lappend res [eval foo $k]
|
||
}
|
||
set res
|
||
} -cleanup {
|
||
unset body res
|
||
} -result {0 10 20}
|
||
test coroutine-1.11 {yield outside coroutine} -setup {
|
||
proc moo {} {
|
||
upvar 1 i i stop stop
|
||
yield [expr {$i*$stop}]
|
||
}
|
||
} -body {
|
||
variable i 5 stop 6
|
||
moo
|
||
} -cleanup {
|
||
rename moo {}
|
||
unset i stop
|
||
} -returnCodes error -result {yield can only be called in a coroutine}
|
||
test coroutine-1.12 {proc as coroutine} -setup {
|
||
set body {
|
||
# init
|
||
set i $start
|
||
set imax $stop
|
||
yield
|
||
while {$i < $imax} {
|
||
uplevel 0 [list yield [expr {$i*$stop}]]
|
||
incr i
|
||
}
|
||
}
|
||
proc moo {{start 0} {stop 10}} $body
|
||
coroutine foo moo 2 8
|
||
} -body {
|
||
list [foo] [foo]
|
||
} -cleanup {
|
||
unset body
|
||
rename moo {}
|
||
rename foo {}
|
||
} -result {16 24}
|
||
test coroutine-1.13 {subst as coroutine: literal} {
|
||
list [coroutine foo eval {subst {>>[yield a],[yield b]<<}}] [foo x] [foo y]
|
||
} {a b >>x,y<<}
|
||
test coroutine-1.14 {subst as coroutine: in variable} {
|
||
set pattern {>>[yield c],[yield d]<<}
|
||
list [coroutine foo eval {subst $pattern}] [foo p] [foo q]
|
||
} {c d >>p,q<<}
|
||
|
||
test coroutine-2.1 {self deletion on return} -body {
|
||
coroutine foo set x 3
|
||
foo
|
||
} -returnCodes error -result {invalid command name "foo"}
|
||
test coroutine-2.2 {self deletion on return} -body {
|
||
coroutine foo ::apply [list {} {yield; yield 1; return 2}]
|
||
list [foo] [foo] [catch foo msg] $msg
|
||
} -result {1 2 1 {invalid command name "foo"}}
|
||
test coroutine-2.3 {self deletion on error return} -body {
|
||
coroutine foo ::apply [list {} {yield;yield 1; error ouch!}]
|
||
list [foo] [catch foo msg] $msg [catch foo msg] $msg
|
||
} -result {1 1 ouch! 1 {invalid command name "foo"}}
|
||
test coroutine-2.4 {self deletion on other return} -body {
|
||
coroutine foo ::apply [list {} {yield;yield 1; return -code 100 ouch!}]
|
||
list [foo] [catch foo msg] $msg [catch foo msg] $msg
|
||
} -result {1 100 ouch! 1 {invalid command name "foo"}}
|
||
test coroutine-2.5 {deletion of suspended coroutine} -body {
|
||
coroutine foo ::apply [list {} {yield; yield 1; return 2}]
|
||
list [foo] [rename foo {}] [catch foo msg] $msg
|
||
} -result {1 {} 1 {invalid command name "foo"}}
|
||
test coroutine-2.6 {deletion of running coroutine} -body {
|
||
coroutine foo ::apply [list {} {yield; rename foo {}; yield 1; return 2}]
|
||
list [foo] [catch foo msg] $msg
|
||
} -result {1 1 {invalid command name "foo"}}
|
||
|
||
test coroutine-3.1 {info level computation} -setup {
|
||
proc a {} {while 1 {yield [info level]}}
|
||
proc b {} foo
|
||
} -body {
|
||
# note that coroutines execute in uplevel #0
|
||
set l0 [coroutine foo a]
|
||
set l1 [foo]
|
||
set l2 [b]
|
||
list $l0 $l1 $l2
|
||
} -cleanup {
|
||
rename a {}
|
||
rename b {}
|
||
} -result {1 1 1}
|
||
test coroutine-3.2 {info frame computation} -setup {
|
||
proc a {} {while 1 {yield [info frame]}}
|
||
proc b {} foo
|
||
} -body {
|
||
set l0 [coroutine foo a]
|
||
set l1 [foo]
|
||
set l2 [b]
|
||
expr {$l2 - $l1}
|
||
} -cleanup {
|
||
rename a {}
|
||
rename b {}
|
||
} -result 1
|
||
test coroutine-3.3 {info coroutine} -setup {
|
||
proc a {} {info coroutine}
|
||
proc b {} a
|
||
} -body {
|
||
b
|
||
} -cleanup {
|
||
rename a {}
|
||
rename b {}
|
||
} -result {}
|
||
test coroutine-3.4 {info coroutine} -setup {
|
||
proc a {} {info coroutine}
|
||
proc b {} a
|
||
} -body {
|
||
coroutine foo b
|
||
} -cleanup {
|
||
rename a {}
|
||
rename b {}
|
||
} -result ::foo
|
||
test coroutine-3.5 {info coroutine} -setup {
|
||
proc a {} {info coroutine}
|
||
proc b {} {rename [info coroutine] {}; a}
|
||
} -body {
|
||
coroutine foo b
|
||
} -cleanup {
|
||
rename a {}
|
||
rename b {}
|
||
} -result {}
|
||
test coroutine-3.6 {info frame, bug #2910094} -setup {
|
||
proc stack {} {
|
||
set res [list "LEVEL:[set lev [info frame]]"]
|
||
for {set i 1} {$i < $lev} {incr i} {
|
||
lappend res [info frame $i]
|
||
}
|
||
set res
|
||
# the precise command depends on line numbers and such, is likely not
|
||
# to be stable: just check that the test completes!
|
||
return
|
||
}
|
||
proc a {} stack
|
||
} -body {
|
||
coroutine aa a
|
||
} -cleanup {
|
||
rename stack {}
|
||
rename a {}
|
||
} -result {}
|
||
test coroutine-3.7 {bug 0b874c344d} {
|
||
dict get [coroutine X coroutine Y info frame 0] cmd
|
||
} {coroutine X coroutine Y info frame 0}
|
||
|
||
test coroutine-4.1 {bug #2093188} -setup {
|
||
proc foo {} {
|
||
set v 1
|
||
trace add variable v {write unset} bar
|
||
yield
|
||
set v 2
|
||
yield
|
||
set v 3
|
||
}
|
||
proc bar args {lappend ::res $args}
|
||
coroutine a foo
|
||
} -body {
|
||
list [a] [a] $::res
|
||
} -cleanup {
|
||
rename foo {}
|
||
rename bar {}
|
||
unset ::res
|
||
} -result {{} 3 {{v {} write} {v {} write} {v {} unset}}}
|
||
test coroutine-4.2 {bug #2093188} -setup {
|
||
proc foo {} {
|
||
set v 1
|
||
trace add variable v {read unset} bar
|
||
yield
|
||
set v 2
|
||
set v
|
||
yield
|
||
set v 3
|
||
}
|
||
proc bar args {lappend ::res $args}
|
||
coroutine a foo
|
||
} -body {
|
||
list [a] [a] $::res
|
||
} -cleanup {
|
||
rename foo {}
|
||
rename bar {}
|
||
unset ::res
|
||
} -result {{} 3 {{v {} read} {v {} unset}}}
|
||
|
||
test coroutine-4.3 {bug #2093947} -setup {
|
||
proc foo {} {
|
||
set v 1
|
||
trace add variable v {write unset} bar
|
||
yield
|
||
set v 2
|
||
yield
|
||
set v 3
|
||
}
|
||
proc bar args {lappend ::res $args}
|
||
} -body {
|
||
coroutine a foo
|
||
a
|
||
a
|
||
coroutine a foo
|
||
a
|
||
rename a {}
|
||
set ::res
|
||
} -cleanup {
|
||
rename foo {}
|
||
rename bar {}
|
||
unset ::res
|
||
} -result {{v {} write} {v {} write} {v {} unset} {v {} write} {v {} unset}}
|
||
|
||
test coroutine-4.4 {bug #2917627: cmd resolution} -setup {
|
||
proc a {} {return global}
|
||
namespace eval b {proc a {} {return local}}
|
||
} -body {
|
||
namespace eval b {coroutine foo a}
|
||
} -cleanup {
|
||
rename a {}
|
||
namespace delete b
|
||
} -result local
|
||
|
||
test coroutine-4.5 {bug #2724403} -constraints {memory} \
|
||
-setup {
|
||
proc getbytes {} {
|
||
set lines [split [memory info] "\n"]
|
||
lindex $lines 3 3
|
||
}
|
||
} -body {
|
||
set end [getbytes]
|
||
for {set i 0} {$i < 5} {incr i} {
|
||
set ns ::y$i
|
||
namespace eval $ns {}
|
||
proc ${ns}::start {} {yield; puts hello}
|
||
coroutine ${ns}::run ${ns}::start
|
||
namespace delete $ns
|
||
set start $end
|
||
set end [getbytes]
|
||
}
|
||
set leakedBytes [expr {$end - $start}]
|
||
} -cleanup {
|
||
rename getbytes {}
|
||
unset i ns start end
|
||
} -result 0
|
||
|
||
test coroutine-4.6 {compile context, bug #3282869} -setup {
|
||
unset -nocomplain ::x
|
||
proc f x {
|
||
coroutine D eval {yield X$x;yield Y}
|
||
}
|
||
} -body {
|
||
f 12
|
||
} -cleanup {
|
||
rename f {}
|
||
} -returnCodes error -match glob -result {can't read *}
|
||
|
||
test coroutine-4.7 {compile context, bug #3282869} -setup {
|
||
proc f x {
|
||
coroutine D eval {yield X$x;yield Y$x}
|
||
}
|
||
} -body {
|
||
set ::x 15
|
||
set ::x [f 12]
|
||
D
|
||
} -cleanup {
|
||
D
|
||
unset ::x
|
||
rename f {}
|
||
} -result YX15
|
||
|
||
test coroutine-5.1 {right numLevels on coro return} -constraints {testnrelevels} \
|
||
-setup {
|
||
proc nestedYield {{val {}}} {
|
||
yield $val
|
||
}
|
||
proc getNumLevel {} {
|
||
# remove the level for this proc's call
|
||
expr {[lindex [testnrelevels] 1] - 1}
|
||
}
|
||
proc relativeLevel base {
|
||
# remove the level for this proc's call
|
||
expr {[getNumLevel] - $base - 1}
|
||
}
|
||
proc foo {} {
|
||
while 1 {
|
||
nestedYield
|
||
}
|
||
}
|
||
set res {}
|
||
} -body {
|
||
set base [getNumLevel]
|
||
lappend res [relativeLevel $base]
|
||
eval {coroutine a foo}
|
||
# back to base level
|
||
lappend res [relativeLevel $base]
|
||
a
|
||
lappend res [relativeLevel $base]
|
||
eval a
|
||
lappend res [relativeLevel $base]
|
||
eval {eval a}
|
||
lappend res [relativeLevel $base]
|
||
rename a {}
|
||
lappend res [relativeLevel $base]
|
||
set res
|
||
} -cleanup {
|
||
rename foo {}
|
||
rename nestedYield {}
|
||
rename getNumLevel {}
|
||
rename relativeLevel {}
|
||
unset res
|
||
} -result {0 0 0 0 0 0}
|
||
test coroutine-5.2 {right numLevels within coro} -constraints {testnrelevels} \
|
||
-setup {
|
||
proc nestedYield {{val {}}} {
|
||
yield $val
|
||
}
|
||
proc getNumLevel {} {
|
||
# remove the level for this proc's call
|
||
expr {[lindex [testnrelevels] 1] - 1}
|
||
}
|
||
proc relativeLevel base {
|
||
# remove the level for this proc's call
|
||
expr {[getNumLevel] - $base - 1}
|
||
}
|
||
proc foo base {
|
||
while 1 {
|
||
set base [nestedYield [relativeLevel $base]]
|
||
}
|
||
}
|
||
set res {}
|
||
} -body {
|
||
lappend res [eval {coroutine a foo [getNumLevel]}]
|
||
lappend res [a [getNumLevel]]
|
||
lappend res [eval {a [getNumLevel]}]
|
||
lappend res [eval {eval {a [getNumLevel]}}]
|
||
set base [lindex $res 0]
|
||
foreach x $res[set res {}] {
|
||
lappend res [expr {$x-$base}]
|
||
}
|
||
set res
|
||
} -cleanup {
|
||
rename a {}
|
||
rename foo {}
|
||
rename nestedYield {}
|
||
rename getNumLevel {}
|
||
rename relativeLevel {}
|
||
unset res
|
||
} -result {0 0 0 0}
|
||
|
||
test coroutine-6.1 {coroutine nargs} -body {
|
||
coroutine a ::apply $lambda
|
||
a
|
||
} -cleanup {
|
||
rename a {}
|
||
} -result 0
|
||
test coroutine-6.2 {coroutine nargs} -body {
|
||
coroutine a ::apply $lambda
|
||
a a
|
||
} -cleanup {
|
||
rename a {}
|
||
} -result 0
|
||
test coroutine-6.3 {coroutine nargs} -body {
|
||
coroutine a ::apply $lambda
|
||
a a a
|
||
} -cleanup {
|
||
rename a {}
|
||
} -returnCodes error -result {wrong # args: should be "a ?arg?"}
|
||
|
||
test coroutine-7.1 {yieldto} -body {
|
||
coroutine c apply {{} {
|
||
yield
|
||
yieldto return -level 0 -code 1 quux
|
||
return quuy
|
||
}}
|
||
set res [list [catch c msg] $msg]
|
||
lappend res [catch c msg] $msg
|
||
lappend res [catch c msg] $msg
|
||
} -cleanup {
|
||
unset res
|
||
} -result [list 1 quux 0 quuy 1 {invalid command name "c"}]
|
||
test coroutine-7.2 {multi-argument yielding with yieldto} -body {
|
||
proc corobody {} {
|
||
set a 1
|
||
while 1 {
|
||
set a [yield $a]
|
||
set a [yieldto return -level 0 $a]
|
||
lappend a [llength $a]
|
||
}
|
||
}
|
||
coroutine a corobody
|
||
coroutine b corobody
|
||
list [a x] [a y z] [a \{p] [a \{q r] [a] [a] [rename a {}] \
|
||
[b ok] [rename b {}]
|
||
} -cleanup {
|
||
rename corobody {}
|
||
} -result {x {y z 2} \{p {\{q r 2} {} 0 {} ok {}}
|
||
test coroutine-7.3 {yielding between coroutines} -body {
|
||
proc juggler {target {value ""}} {
|
||
if {$value eq ""} {
|
||
set value [yield [info coroutine]]
|
||
}
|
||
while {[llength $value]} {
|
||
lappend ::result $value [info coroutine]
|
||
set value [lrange $value 0 end-1]
|
||
lassign [yieldto $target $value] value
|
||
}
|
||
# Clear nested collection of coroutines
|
||
catch $target
|
||
}
|
||
set result ""
|
||
coroutine j1 juggler [coroutine j2 juggler [coroutine j3 juggler j1]]\
|
||
{a b c d e}
|
||
list $result [info command j1] [info command j2] [info command j3]
|
||
} -cleanup {
|
||
catch {rename juggler ""}
|
||
} -result {{{a b c d e} ::j1 {a b c d} ::j2 {a b c} ::j3 {a b} ::j1 a ::j2} {} {} {}}
|
||
test coroutine-7.4 {Bug 8ff0cb9fe1} -setup {
|
||
proc foo {a b} {catch yield; return 1}
|
||
} -cleanup {
|
||
rename foo {}
|
||
} -body {
|
||
coroutine demo lsort -command foo {a b}
|
||
} -result {b a}
|
||
test coroutine-7.5 {return codes} {
|
||
set result {}
|
||
foreach code {0 1 2 3 4 5} {
|
||
lappend result [catch {coroutine demo return -level 0 -code $code}]
|
||
}
|
||
set result
|
||
} {0 1 2 3 4 5}
|
||
test coroutine-7.6 {Early yield crashes} -setup {
|
||
set i [interp create]
|
||
} -body {
|
||
# Force into a child interpreter [bug 60559fd4a6]
|
||
$i eval {
|
||
proc foo args {}
|
||
trace add execution foo enter {catch yield}
|
||
coroutine demo foo
|
||
rename foo {}
|
||
return ok
|
||
}
|
||
} -cleanup {
|
||
interp delete $i
|
||
} -result ok
|
||
test coroutine-7.7 {Bug 2486550} -setup {
|
||
set i [interp create]
|
||
$i hide yield
|
||
} -body {
|
||
# Force into a child interpreter [bug 60559fd4a6]
|
||
$i eval {
|
||
coroutine demo interp invokehidden {} yield ok
|
||
}
|
||
} -cleanup {
|
||
$i eval demo
|
||
interp delete $i
|
||
} -result ok
|
||
test coroutine-7.8 {yieldto context nuke: Bug a90d9331bc} -setup {
|
||
namespace eval cotest {}
|
||
set ::result ""
|
||
} -body {
|
||
proc cotest::body {} {
|
||
lappend ::result a
|
||
yield OUT
|
||
lappend ::result b
|
||
yieldto ::return -level 0 123
|
||
lappend ::result c
|
||
return
|
||
}
|
||
lappend ::result [coroutine cotest cotest::body]
|
||
namespace delete cotest
|
||
namespace eval cotest {}
|
||
lappend ::result [cotest]
|
||
cotest
|
||
return $result
|
||
} -returnCodes error -cleanup {
|
||
catch {namespace delete ::cotest}
|
||
catch {rename cotest ""}
|
||
} -result {yieldto called in deleted namespace}
|
||
test coroutine-7.9 {yieldto context nuke: Bug a90d9331bc} -setup {
|
||
namespace eval cotest {}
|
||
set ::result ""
|
||
} -body {
|
||
proc cotest::body {} {
|
||
set y ::yieldto
|
||
lappend ::result a
|
||
yield OUT
|
||
lappend ::result b
|
||
$y ::return -level 0 123
|
||
lappend ::result c
|
||
return
|
||
}
|
||
lappend ::result [coroutine cotest cotest::body]
|
||
namespace delete cotest
|
||
namespace eval cotest {}
|
||
lappend ::result [cotest]
|
||
cotest
|
||
return $result
|
||
} -returnCodes error -cleanup {
|
||
catch {namespace delete ::cotest}
|
||
catch {rename cotest ""}
|
||
} -result {yieldto called in deleted namespace}
|
||
test coroutine-7.10 {yieldto context nuke: Bug a90d9331bc} -setup {
|
||
namespace eval cotest {}
|
||
set ::result ""
|
||
} -body {
|
||
proc cotest::body {} {
|
||
lappend ::result a
|
||
yield OUT
|
||
lappend ::result b
|
||
yieldto ::return -level 0 -cotest [namespace delete ::cotest] 123
|
||
lappend ::result c
|
||
return
|
||
}
|
||
lappend ::result [coroutine cotest cotest::body]
|
||
lappend ::result [cotest]
|
||
cotest
|
||
return $result
|
||
} -returnCodes error -cleanup {
|
||
catch {namespace delete ::cotest}
|
||
catch {rename cotest ""}
|
||
} -result {yieldto called in deleted namespace}
|
||
test coroutine-7.11 {yieldto context nuke: Bug a90d9331bc} -setup {
|
||
namespace eval cotest {}
|
||
set ::result ""
|
||
} -body {
|
||
proc cotest::body {} {
|
||
set y ::yieldto
|
||
lappend ::result a
|
||
yield OUT
|
||
lappend ::result b
|
||
$y ::return -level 0 -cotest [namespace delete ::cotest] 123
|
||
lappend ::result c
|
||
return
|
||
}
|
||
lappend ::result [coroutine cotest cotest::body]
|
||
lappend ::result [cotest]
|
||
cotest
|
||
return $result
|
||
} -returnCodes error -cleanup {
|
||
catch {namespace delete ::cotest}
|
||
catch {rename cotest ""}
|
||
} -result {yieldto called in deleted namespace}
|
||
test coroutine-7.12 {coro floor above street level #3008307} -body {
|
||
proc c {} {
|
||
yield
|
||
}
|
||
proc cc {} {
|
||
coroutine C c
|
||
}
|
||
proc boom {} {
|
||
cc ; # coro created at level 2
|
||
C ; # and called at level 1
|
||
}
|
||
boom ; # does not crash: the coro floor is a good insulator
|
||
list
|
||
} -cleanup {
|
||
rename boom {}; rename cc {}; rename c {}
|
||
} -result {}
|
||
|
||
test coroutine-8.0.0 {coro inject executed} -body {
|
||
coroutine demo apply {{} { foreach i {1 2} yield }}
|
||
demo
|
||
set ::result none
|
||
tcl::unsupported::inject demo set ::result inject-executed
|
||
demo
|
||
set ::result
|
||
} -result {inject-executed}
|
||
test coroutine-8.0.1 {coro inject after error} -body {
|
||
coroutine demo apply {{} { foreach i {1 2} yield; error test }}
|
||
demo
|
||
set ::result none
|
||
tcl::unsupported::inject demo set ::result inject-executed
|
||
lappend ::result [catch {demo} err] $err
|
||
} -result {inject-executed 1 test}
|
||
test coroutine-8.1.1 {coro inject, ticket 42202ba1e5ff566e} -body {
|
||
interp create child
|
||
child eval {
|
||
coroutine demo apply {{} { while {1} yield }}
|
||
demo
|
||
tcl::unsupported::inject demo set ::result inject-executed
|
||
}
|
||
interp delete child
|
||
} -result {}
|
||
test coroutine-8.1.2 {coro inject with result, ticket 42202ba1e5ff566e} -body {
|
||
interp create child
|
||
child eval {
|
||
coroutine demo apply {{} { while {1} yield }}
|
||
demo
|
||
tcl::unsupported::inject demo set ::result inject-executed
|
||
}
|
||
child eval demo
|
||
set result [child eval {set ::result}]
|
||
|
||
interp delete child
|
||
set result
|
||
} -result {inject-executed}
|
||
|
||
test coroutine-9.1 {coro type} {
|
||
coroutine demo eval {
|
||
yield
|
||
yield "PHASE 1"
|
||
yieldto string cat "PHASE 2"
|
||
::tcl::unsupported::corotype [info coroutine]
|
||
}
|
||
list [demo] [::tcl::unsupported::corotype demo] \
|
||
[demo] [::tcl::unsupported::corotype demo] [demo]
|
||
} {{PHASE 1} yield {PHASE 2} yieldto active}
|
||
test coroutine-9.2 {coro type} -setup {
|
||
catch {rename nosuchcommand ""}
|
||
} -returnCodes error -body {
|
||
::tcl::unsupported::corotype nosuchcommand
|
||
} -result {can only get coroutine type of a coroutine}
|
||
test coroutine-9.3 {coro type} -returnCodes error -body {
|
||
proc notacoroutine {} {}
|
||
::tcl::unsupported::corotype notacoroutine
|
||
} -returnCodes error -cleanup {
|
||
rename notacoroutine {}
|
||
} -result {can only get coroutine type of a coroutine}
|
||
|
||
test coroutine-10.1 {coroutine general introspection} -setup {
|
||
set i [interp create]
|
||
} -body {
|
||
$i eval {
|
||
# Make the introspection code
|
||
namespace path tcl::unsupported
|
||
proc probe {type var} {
|
||
upvar 1 $var v
|
||
set f [info frame]
|
||
incr f -1
|
||
set result [list $v [dict get [info frame $f] proc]]
|
||
if {$type eq "yield"} {
|
||
tailcall yield $result
|
||
} else {
|
||
tailcall yieldto string cat $result
|
||
}
|
||
}
|
||
proc pokecoro {c var} {
|
||
inject $c probe [corotype $c] $var
|
||
$c
|
||
}
|
||
|
||
# Coroutine implementations
|
||
proc cbody1 {} {
|
||
set val [info coroutine]
|
||
set accum {}
|
||
while {[set val [yield $val]] ne ""} {
|
||
lappend accum $val
|
||
set val ok
|
||
}
|
||
return $accum
|
||
}
|
||
proc cbody2 {} {
|
||
set val [info coroutine]
|
||
set accum {}
|
||
while {[llength [set val [yieldto string cat $val]]]} {
|
||
lappend accum {*}$val
|
||
set val ok
|
||
}
|
||
return $accum
|
||
}
|
||
|
||
# Make the coroutines
|
||
coroutine c1 cbody1
|
||
coroutine c2 cbody2
|
||
list [c1 abc] [c2 1 2 3] [pokecoro c1 accum] [pokecoro c2 accum] \
|
||
[c1 def] [c2 4 5 6] [pokecoro c1 accum] [pokecoro c2 accum] \
|
||
[c1] [c2]
|
||
}
|
||
} -cleanup {
|
||
interp delete $i
|
||
} -result {ok ok {abc ::cbody1} {{1 2 3} ::cbody2} ok ok {{abc def} ::cbody1} {{1 2 3 4 5 6} ::cbody2} {abc def} {1 2 3 4 5 6}}
|
||
|
||
# cleanup
|
||
unset lambda
|
||
::tcltest::cleanupTests
|
||
|
||
return
|
||
|
||
# Local Variables:
|
||
# mode: tcl
|
||
# End:
|