summaryrefslogtreecommitdiffstats
path: root/tests/NRE.test
diff options
context:
space:
mode:
authorcvs2fossil <cvs2fossil>2011-01-25 19:02:56 (GMT)
committercvs2fossil <cvs2fossil>2011-01-25 19:02:56 (GMT)
commit352fce86be9d102b2284de839b7f7ff94ed971f2 (patch)
treee454e0d4460f15029e4ed5ae3f3131a992445426 /tests/NRE.test
parent75f084f6970d2344bb5a82fdff6a73825bc6e64e (diff)
downloadtcl-dgp_refactor_merge.zip
tcl-dgp_refactor_merge.tar.gz
tcl-dgp_refactor_merge.tar.bz2
Created branch dgp-refactor-merge-syntheticdgp_refactor_mergedgp_refactor_merge_synthetic
Diffstat (limited to 'tests/NRE.test')
-rw-r--r--tests/NRE.test476
1 files changed, 476 insertions, 0 deletions
diff --git a/tests/NRE.test b/tests/NRE.test
new file mode 100644
index 0000000..4a279bc
--- /dev/null
+++ b/tests/NRE.test
@@ -0,0 +1,476 @@
+# Commands covered: proc, apply, [interp alias], [namespce import], tailcall
+#
+# This file contains a collection of tests for the non-recursive executor that
+# avoids recursive calls to TEBC.
+#
+# 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.
+#
+# RCS: @(#) $Id: NRE.test,v 1.10 2008/08/01 00:44:05 msofer Exp $
+
+if {[lsearch [namespace children] ::tcltest] == -1} {
+ package require tcltest
+ namespace import -force ::tcltest::*
+}
+
+testConstraint tailcall [llength [info commands ::tcl::unsupported::tailcall]]
+testConstraint testnrelevels [llength [info commands testnrelevels]]
+
+#
+# The tests that risked blowing the C stack on failure have been removed: we
+# can now actually measure using testnrelevels.
+#
+
+if {[testConstraint testnrelevels]} {
+ namespace eval testnre {
+ #
+ # [testnrelevels] returns a 6-list with: C-stack depth, iPtr->numlevels,
+ # cmdFrame level, callFrame level, tosPtr and callback depth
+ #
+ variable last [testnrelevels]
+ proc depthDiff {} {
+ variable last
+ set depth [testnrelevels]
+ set res {}
+ foreach t $depth l $last {
+ lappend res [expr {$t-$l}]
+ }
+ set last $depth
+ return $res
+ }
+ proc setabs {} {
+ uplevel 1 variable abs -[lindex [testnrelevels] 0]
+ }
+
+ variable body0 {
+ set x [depthDiff]
+ if {[incr i] > 10} {
+ variable abs
+ incr abs [lindex [testnrelevels] 0]
+ return [list [lrange $x 0 3] $abs]
+ }
+ }
+ proc makebody txt {
+ variable body0
+ return "$body0; $txt"
+ }
+ namespace export *
+ }
+ namespace import testnre::*
+}
+
+test NRE-1.1 {self-recursive procs} -setup {
+ proc a i [makebody {a $i}]
+} -body {
+ setabs
+ a 0
+} -cleanup {
+ rename a {}
+ unset abs
+} -result {{0 1 1 1} 0}
+
+test NRE-1.2 {self-recursive lambdas} -setup {
+ set a [list i [makebody {apply $::a $i}]]
+} -body {
+ setabs
+ apply $a 0
+} -cleanup {
+ unset a abs
+} -result {{0 1 1 1} 0}
+
+test NRE-1.3 {mutually recursive procs and lambdas} -setup {
+ proc a i {
+ apply $::b [incr i]
+ }
+ set b [list i [makebody {a $i}]]
+} -body {
+ setabs
+ a 0
+} -cleanup {
+ rename a {}
+ unset b abs
+} -result {{0 2 2 2} 0}
+
+#
+# Test that aliases are non-recursive
+#
+
+test NRE-2.1 {alias is not recursive} -setup {
+ proc a i [makebody {b $i}]
+ interp alias {} b {} a
+} -body {
+ setabs
+ a 0
+} -cleanup {
+ rename a {}
+ rename b {}
+ unset abs
+} -result {{0 2 1 1} 0}
+
+#
+# Test that imports are non-recursive
+#
+
+test NRE-3.1 {imports are not recursive} -setup {
+ namespace eval foo {
+ setabs
+ namespace export a
+ }
+ proc foo::a i [makebody {::a $i}]
+ namespace import foo::a
+} -body {
+ a 0
+} -cleanup {
+ rename a {}
+ namespace delete ::foo
+} -result {{0 2 1 1} 0}
+
+test NRE-4.1 {ensembles are not recursive} -setup {
+ proc a i [makebody {b foo $i}]
+ namespace ensemble create \
+ -command b \
+ -map [list foo a]
+} -body {
+ setabs
+ a 0
+} -cleanup {
+ rename a {}
+ rename b {}
+ unset abs
+} -result {{0 2 1 1} 0}
+
+test NRE-5.1 {[namespace eval] is not recursive} -setup {
+ namespace eval ::foo {
+ setabs
+ }
+ proc foo::a i [makebody {namespace eval ::foo [list a $i]}]
+} -body {
+ ::foo::a 0
+} -cleanup {
+ namespace delete ::foo
+} -result {{0 2 2 2} 0}
+
+test NRE-5.2 {[namespace eval] is not recursive} -setup {
+ namespace eval ::foo {
+ setabs
+ }
+ proc foo::a i [makebody {namespace eval ::foo "set x $i; a $i"}]
+} -body {
+ foo::a 0
+} -cleanup {
+ namespace delete ::foo
+} -result {{0 2 2 2} 0}
+
+test NRE-6.1 {[uplevel] is not recursive} -setup {
+ proc a i [makebody {uplevel 1 [list a $i]}]
+} -body {
+ setabs
+ a 0
+} -cleanup {
+ rename a {}
+ unset abs
+} -result {{0 2 2 0} 0}
+
+test NRE-6.2 {[uplevel] is not recursive} -setup {
+ setabs
+ proc a i [makebody {uplevel 1 "set x $i; a $i"}]
+} -body {
+ a 0
+} -cleanup {
+ rename a {}
+ unset abs
+} -result {{0 2 2 0} 0}
+
+test NRE-7.1 {[catch] is not recursive} -setup {
+ setabs
+ proc a i [makebody {uplevel 1 "catch {a $i} msg; set msg"}]
+} -body {
+ a 0
+} -cleanup {
+ rename a {}
+ unset x abs
+} -result {{0 3 3 0} 0}
+
+#
+# Basic TclOO tests
+#
+
+test NRE-oo.1 {really deep calls in oo - direct} -setup {
+ oo::object create foo
+ oo::objdefine foo method bar i [makebody {foo bar $i}]
+} -body {
+ setabs
+ foo bar 0
+} -cleanup {
+ foo destroy
+ unset abs
+} -result {{0 1 1 1} 0}
+
+test NRE-oo.2 {really deep calls in oo - call via [self]} -setup {
+ oo::object create foo
+ oo::objdefine foo method bar i [makebody {[self] bar $i}]
+} -body {
+ setabs
+ foo bar 0
+} -cleanup {
+ foo destroy
+ unset abs
+} -result {{0 1 1 1} 0}
+
+test NRE-oo.3 {really deep calls in oo - private calls} -setup {
+ oo::object create foo
+ oo::objdefine foo method bar i [makebody {my bar $i}]
+} -body {
+ setabs
+ foo bar 0
+} -cleanup {
+ foo destroy
+ unset abs
+} -result {{0 1 1 1} 0}
+
+test NRE-oo.4 {really deep calls in oo - overriding} -setup {
+ oo::class create foo {
+ method bar i [makebody {my bar $i}]
+ }
+ oo::class create boo {
+ superclass foo
+ method bar i [makebody {next $i}]
+ }
+} -body {
+ setabs
+ [boo new] bar 0
+} -cleanup {
+ foo destroy
+ unset abs
+} -result {{0 1 1 1} 0}
+
+test NRE-oo.5 {really deep calls in oo - forwards} -setup {
+ oo::object create foo
+ set body [makebody {my boo $i}]
+ oo::objdefine foo "
+ method bar i {$body}
+ forward boo ::foo bar
+ "
+} -body {
+ setabs
+ foo bar 0
+} -cleanup {
+ foo destroy
+ unset abs
+} -result {{0 2 1 1} 0}
+
+
+#
+# NASTY BUG found by tcllib's interp package
+#
+
+test NRE-X.1 {eval in wrong interp} {
+ set i [interp create]
+ set res [$i eval {
+ set x {namespace children ::}
+ set y [list namespace children ::]
+ namespace delete {*}[{*}$y]
+ set j [interp create]
+ $j eval {namespace delete {*}[namespace children ::]}
+ namespace eval foo {}
+ set res [list [eval $x] [eval $y] [$j eval $x] [$j eval $y]]
+ interp delete $j
+ set res
+ }]
+ interp delete $i
+ set res
+} {::foo ::foo {} {}}
+
+#
+# Test tailcalls
+#
+
+if {[testConstraint tailcall]} {
+ namespace eval tcl::unsupported namespace export tailcall
+ namespace import tcl::unsupported::tailcall
+}
+
+test NRE-T.0 {tailcall is constant space} -constraints {tailcall} -setup {
+ proc a i {
+ if {[incr i] > 10} {
+ return [depthDiff]
+ }
+ depthDiff
+ tailcall a $i
+ }
+} -body {
+ a 0
+} -cleanup {
+ rename a {}
+} -result {0 0 0 0 0 0}
+
+test NRE-T.1 {tailcall} -constraints {tailcall} -body {
+ namespace eval a {
+ variable x *::a
+ proc xset {} {
+ set tmp {}
+ set ns {[namespace current]}
+ set level [info level]
+ for {set i 0} {$i <= [info level]} {incr i} {
+ uplevel #$i "set x $i$ns"
+ lappend tmp "$i [info level $i]"
+ }
+ lrange $tmp 1 end
+ }
+ proc foo {} {tailcall xset; set x noreach}
+ }
+ namespace eval b {
+ variable x *::b
+ proc xset args {error b::xset}
+ proc moo {} {set x 0; variable y [::a::foo]; set x}
+ }
+ variable x *::
+ proc xset args {error ::xset}
+ list [::b::moo] | $x $a::x $b::x | $::b::y
+} -cleanup {
+ unset x
+ rename xset {}
+ namespace delete a b
+} -result {1::b | 0:: *::a *::b | {{1 ::b::moo} {2 xset}}}
+
+
+test NRE-T.2 {tailcall in non-proc} -constraints {tailcall} -body {
+ list [catch {namespace eval a [list tailcall set x 1]} msg] $msg
+} -result {1 {tailcall can only be called from a proc or lambda}}
+
+test NRE-T.3 {tailcall falls off tebc} -constraints {tailcall} -body {
+ unset -nocomplain x
+ proc foo {} {tailcall set x 1}
+ list [catch foo msg] $msg [set x]
+} -cleanup {
+ rename foo {}
+ unset x
+} -result {0 1 1}
+
+test NRE-T.4 {tailcall falls off tebc} -constraints {tailcall} -body {
+ set x 2
+ proc foo {} {tailcall set x 1}
+ foo
+ set x
+} -cleanup {
+ rename foo {}
+ unset x
+} -result 1
+
+test NRE-T.5 {tailcall falls off tebc} -constraints {tailcall} -body {
+ set x 2
+ namespace eval bar {
+ variable x 3
+ proc foo {} {tailcall set x 1}
+ }
+ bar::foo
+ list $x $bar::x
+} -cleanup {
+ unset x
+ namespace delete bar
+} -result {1 3}
+
+test NRE-T.6 {tailcall does remove callframes} -constraints {tailcall} -body {
+ proc foo {} {info level}
+ proc moo {} {tailcall foo}
+ proc boo {} {expr {[moo] - [info level]}}
+ boo
+} -cleanup {
+ rename foo {}
+ rename moo {}
+ rename boo {}
+} -result 1
+
+test NRE-T.7 {tailcall does return} -constraints {tailcall} -setup {
+ namespace eval ::foo {
+ variable res {}
+ proc a {} {
+ variable res
+ append res a
+ tailcall set x 1
+ append res a
+ }
+ proc b {} {
+ variable res
+ append res b
+ a
+ append res b
+ }
+ proc c {} {
+ variable res
+ append res c
+ b
+ append res c
+ }
+ }
+} -body {
+ namespace eval ::foo c
+} -cleanup {
+ namespace delete ::foo
+} -result cbabc
+
+test NRE-T.8 {tailcall tailcall} -constraints {tailcall} -setup {
+ namespace eval ::foo {
+ variable res {}
+ proc a {} {
+ variable res
+ append res a
+ tailcall tailcall set x 1
+ append res a
+ }
+ proc b {} {
+ variable res
+ append res b
+ a
+ append res b
+ }
+ proc c {} {
+ variable res
+ append res c
+ b
+ append res c
+ }
+ }
+} -body {
+ namespace eval ::foo c
+} -cleanup {
+ namespace delete ::foo
+} -match glob -result *tailcall* -returnCodes error
+
+test NRE-T.9 {tailcall factorial} -constraints {tailcall} -setup {
+ proc fact {n {b 1}} {
+ if {$n == 1} {
+ return $b
+ }
+ tailcall fact [expr {$n-1}] [expr {$n*$b}]
+ }
+} -body {
+ list [fact 1] [fact 5] [fact 10] [fact 15]
+} -cleanup {
+ rename fact {}
+} -result {1 120 3628800 1307674368000}
+
+
+namespace forget tcl::unsupported::tailcall
+
+#
+# Test that ensembles are non-recursive
+#
+
+
+
+# cleanup
+::tcltest::cleanupTests
+
+if {[testConstraint testnrelevels]} {
+ namespace forget testnre::*
+ namespace delete testnre
+}
+
+if {[testConstraint tailcall]} {
+ namespace forget tcl::unsupported::tailcall
+}
+
+return