# This file contains a collection of tests for Tcl's built-in object system. # Sourcing this file into Tcl runs the tests and generates output for errors. # No output means no errors were found. # # Copyright (c) 2006 Donal K. Fellows # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: oo.test,v 1.1.2.40 2006/10/22 00:26:31 dkf Exp $ if {[lsearch [namespace children] ::tcltest] == -1} { package require tcltest 2 namespace import -force ::tcltest::* } testConstraint memory [llength [info commands memory]] test oo-0.1 {basic test of OO's ability to clean up its initial state} { interp create t interp delete t } {} test oo-0.2 {basic test of OO's ability to clean up its initial state} { interp eval [interp create] { namespace delete :: } } {} test oo-0.3 {basic test of OO's ability to clean up its initial state} -setup { proc getbytes {} { set lines [split [memory info] "\n"] lindex $lines 3 3 } } -constraints memory -body { set end [getbytes] for {set i 0} {$i < 5} {incr i} { [oo::object new] destroy set tmp $end set end [getbytes] } set leakedBytes [expr {$end - $tmp}] } -cleanup { rename getbytes {} } -result 0 test oo-0.4 {basic test of OO's ability to clean up its initial state} -setup { proc getbytes {} { set lines [split [memory info] "\n"] lindex $lines 3 3 } } -constraints memory -body { set end [getbytes] for {set i 0} {$i < 5} {incr i} { oo::class create foo foo new foo destroy set tmp $end set end [getbytes] } set leakedBytes [expr {$end - $tmp}] } -cleanup { rename getbytes {} } -result 0 test oo-1.1 {basic test of OO functionality: no classes} { set result {} lappend result [oo::object create foo] lappend result [oo::define foo { method bar args { global result lappend result {expand}$args return [llength $args] } }] lappend result [foo bar a b c] lappend result [foo destroy] [info commands foo] } {::foo {} a b c 3 {} {}} test oo-1.2 {basic test of OO functionality: no classes} -body { oo::define oo::object method missingArgs } -returnCodes 1 -result "wrong # args: should be \"oo::define oo::object method name args body\"" test oo-1.3 {basic test of OO functionality: no classes} { catch {oo::define oo::object method missingArgs} set errorInfo } "wrong # args: should be \"oo::define oo::object method name args body\" while executing \"oo::define oo::object method missingArgs\"" test oo-1.4 {basic test of OO functionality} -body { oo::object create {} } -returnCodes 1 -result {object name must not be empty} test oo-1.5 {basic test of OO functionality} -body { oo::object doesnotexist } -returnCodes 1 -result {unknown method "doesnotexist": must be create, destroy or new} test oo-1.6 {basic test of OO functionality} -setup { oo::object create aninstance } -body { oo::define aninstance unexport destroy aninstance doesnotexist } -cleanup { rename aninstance {} } -returnCodes 1 -result {object "::aninstance" has no visible methods} test oo-2.1 {basic test of OO functionality: constructor} -setup { # This is a bit complex because it needs to run in a sub-interp as # we're modifying the root object class's constructor interp create subinterp } -body { subinterp eval { oo::define oo::object constructor {} { lappend ::result [info level 0] } lappend result 1 lappend result 2 [oo::object create foo] } } -cleanup { interp delete subinterp } -result {1 {oo::object create foo} 2 ::foo} test oo-2.2 {basic test of OO functionality: constructor} { oo::class create testClass { constructor {} { global result lappend result "[self]->construct" } method bar {} { global result lappend result "[self]->bar" } } set result {} [testClass create foo] bar testClass destroy return $result } {::foo->construct ::foo->bar} test oo-3.1 {basic test of OO functionality: destructor} -setup { # This is a bit complex because it needs to run in a sub-interp as # we're modifying the root object class's constructor interp create subinterp } -body { subinterp eval { oo::define oo::object destructor { lappend ::result died } lappend result 1 [oo::object create foo] lappend result 2 [rename foo {}] oo::define oo::object destructor {} return $result } } -cleanup { interp delete subinterp } -result {1 ::foo died 2 {}} test oo-3.2 {basic test of OO functionality: destructor} -setup { # This is a bit complex because it needs to run in a sub-interp as # we're modifying the root object class's constructor interp create subinterp } -body { subinterp eval { oo::define oo::object destructor { lappend ::result died } lappend result 1 [oo::object create foo] lappend result 2 [rename foo {}] } } -cleanup { interp delete subinterp } -result {1 ::foo died 2 {}} test oo-4.1 {basic test of OO functionality: export} { set o [oo::object new] set result {} oo::define $o method Foo {} {lappend ::result Foo; return} lappend result [catch {$o Foo} msg] $msg oo::define $o export Foo lappend result [$o Foo] [$o destroy] } {1 {unknown method "Foo": must be destroy} Foo {} {}} test oo-4.2 {basic test of OO functionality: unexport} { set o [oo::object new] set result {} oo::define $o method foo {} {lappend ::result foo; return} lappend result [$o foo] oo::define $o unexport foo lappend result [catch {$o foo} msg] $msg [$o destroy] } {foo {} 1 {unknown method "foo": must be destroy} {}} test oo-5.1 {OO: manipulation of classes as objects} -setup { set obj [oo::object new] } -body { oo::define oo::object self.method foo {} { return "in object" } catch {$obj foo} result list [catch {$obj foo} result] $result [oo::object foo] } -cleanup { oo::define oo::object self.method foo {} {} $obj destroy } -result {1 {unknown method "foo": must be destroy} {in object}} test oo-6.1 {OO: forward} { oo::object create foo oo::define foo { forward a lappend forward b lappend result } set result {} foo a result 1 foo b 2 foo destroy return $result } {1 2} test oo-7.1 {OO: inheritance 101} -setup { oo::class create superClass oo::class create subClass subClass create instance } -body { oo::define superClass method doit x {lappend ::result $x} oo::define subClass superclass superClass set result [list [catch {subClass doit bad} msg] $msg] instance doit ok return $result } -cleanup { subClass destroy superClass destroy } -result {1 {unknown method "doit": must be create, destroy or new} ok} test oo-7.2 {OO: inheritance 101} -setup { oo::class create superClass oo::class create subClass subClass create instance } -body { oo::define superClass method doit x {lappend ::result |$x|} oo::define subClass superclass superClass oo::define instance method doit x {lappend ::result =$x=; next [incr x]} set result {} instance doit 1 return $result } -cleanup { subClass destroy superClass destroy } -result {=1= |2|} test oo-7.3 {OO: inheritance 101} -setup { oo::class create superClass oo::class create subClass subClass create instance } -body { oo::define superClass method doit x {lappend ::result |$x|} oo::define subClass { superclass superClass method doit x {lappend ::result -$x-; next [incr x]} } oo::define instance method doit x {lappend ::result =$x=; next [incr x]} set result {} instance doit 1 return $result } -cleanup { subClass destroy superClass destroy } -result {=1= -2- |3|} test oo-7.4 {OO: inheritance from oo::class} -body { oo::class create meta oo::define meta { superclass oo::class self.unexport create new self.method make {x {definitions {}}} { if {![string match ::* $x]} { set ns [uplevel 1 {::namespace current}] set x ${ns}::$x } set o [my create $x] lappend ::result "made $o" oo::define $o $definitions return $o } } set result [list [catch {meta create foo} msg] $msg] lappend result [meta make classinstance { lappend ::result "in definition script in [namespace current]" }] lappend result [classinstance create instance] } -cleanup { catch {classinstance destroy} catch {meta destroy} } -result {1 {unknown method "create": must be destroy or make} {made ::classinstance} {in definition script in ::oo::define} ::classinstance ::instance} test oo-7.5 {OO: inheritance from oo::class in the secondary chain} -body { oo::class create other oo::class create meta oo::define meta { superclass other oo::class self.unexport create new self.method make {x {definitions {}}} { if {![string match ::* $x]} { set ns [uplevel 1 {::namespace current}] set x ${ns}::$x } set o [my create $x] lappend ::result "made $o" oo::define $o $definitions return $o } } set result [list [catch {meta create foo} msg] $msg] lappend result [meta make classinstance { lappend ::result "in definition script in [namespace current]" }] lappend result [classinstance create instance] } -cleanup { catch {classinstance destroy} catch {meta destroy} catch {other destroy} } -result {1 {unknown method "create": must be destroy or make} {made ::classinstance} {in definition script in ::oo::define} ::classinstance ::instance} test oo-7.6 {OO: inheritance 101 - overridden methods should be oblivious} -setup { oo::class create Aclass oo::class create Bclass Bclass create Binstance } -body { oo::define Aclass { method incr {var step} { upvar 1 $var v ::incr v $step } } oo::define Bclass { superclass Aclass method incr {var {step 1}} { global result lappend result $var $step set r [next $var $step] lappend result returning:$r return $r } } set result {} set x 10 lappend result x=$x lappend result [Binstance incr x] lappend result x=$x } -result {x=10 x 1 returning:11 11 x=11} -cleanup { Aclass destroy } test oo-7.7 {OO: inheritance and errorInfo} -setup { oo::class create A oo::class create B B create c } -body { oo::define A method foo {} {error foo!} oo::define B { superclass A method foo {} { next } } oo::define c method foo {} { next } catch {c ?} msg set result [list $msg] catch {c foo} msg lappend result $msg $errorInfo } -cleanup { A destroy } -result {{unknown method "?": must be destroy or foo} foo! {foo! while executing "error foo!" (class "::A" method "foo" line 1) invoked from within "next " (class "::B" method "foo" line 1) invoked from within "next " (object "::c" method "foo" line 1) invoked from within "c foo"}} test oo-8.1 {OO: global must work in methods} { oo::object create foo oo::define foo method bar x {global result; lappend result $x} set result {} foo bar this foo bar is lappend result a foo bar test foo destroy return $result } {this is a test} test oo-9.1 {OO: multiple inheritance} -setup { oo::class create A oo::class create B oo::class create C oo::class create D D create foo } -body { oo::define A method test {} {lappend ::result A; return ok} oo::define B { superclass A method test {} {lappend ::result B; next} } oo::define C { superclass A method test {} {lappend ::result C; next} } oo::define D { superclass B C method test {} {lappend ::result D; next} } set result {} lappend result [foo test] } -cleanup { D destroy C destroy B destroy A destroy } -result {D B C A ok} test oo-9.2 {OO: multiple inheritance} -setup { oo::class create A oo::class create B oo::class create C oo::class create D D create foo } -body { oo::define A method test {} {lappend ::result A; return ok} oo::define B { superclass A method test {} {lappend ::result B; next} } oo::define C { superclass A method test {} {lappend ::result C; next} } oo::define D { superclass B C method test {} {lappend ::result D; next} } set result {} lappend result [foo test] } -cleanup { A destroy } -result {D B C A ok} test oo-10.1 {OO: recursive invoke and modify} -setup { [oo::class create C] create O } -cleanup { C destroy } -body { oo::define C method foo x { lappend ::result $x if {$x} { [self object] foo [incr x -1] } } oo::define O method foo x { lappend ::result -$x- if {$x == 1} { # delete the method oo::define O method foo {} {} } next $x } set result {} O foo 2 return $result } -result {-2- 2 -1- 1 0} test oo-11.1 {OO: cleanup} { oo::object create foo set result [list [catch {oo::object create foo} msg] $msg] lappend result [foo destroy] [oo::object create foo] [foo destroy] } {1 {can't create object "foo": command already exists with that name} {} ::foo {}} test oo-11.2 {OO: cleanup} { oo::class create bar bar create foo set result [list [catch {bar create foo} msg] $msg] lappend result [bar destroy] [oo::object create foo] [foo destroy] } {1 {can't create object "foo": command already exists with that name} {} ::foo {}} test oo-11.3 {OO: cleanup} { oo::class create bar0 oo::class create bar oo::define bar superclass bar0 bar create foo set result [list [catch {bar create foo} msg] $msg] lappend result [bar0 destroy] [oo::object create foo] [foo destroy] } {1 {can't create object "foo": command already exists with that name} {} ::foo {}} test oo-11.4 {OO: cleanup} { oo::class create bar0 oo::class create bar1 oo::define bar1 superclass bar0 oo::class create bar2 oo::define bar2 { superclass bar0 destructor {lappend ::result destroyed} } oo::class create bar oo::define bar superclass bar1 bar2 bar create foo set result [list [catch {bar create foo} msg] $msg] lappend result [bar0 destroy] [oo::object create foo] [foo destroy] \ [oo::object create bar2] [bar2 destroy] } {1 {can't create object "foo": command already exists with that name} destroyed {} ::foo {} ::bar2 {}} test oo-12.1 {OO: filters} { oo::class create Aclass Aclass create Aobject oo::define Aclass { method concatenate args { global result lappend result {expand}$args join $args {} } method logFilter args { global result lappend result "calling [self object]->[self method] $args" set r [next {expand}$args] lappend result "result=$r" return $r } } oo::define Aobject filter logFilter set result {} lappend result [Aobject concatenate 1 2 3 4 5] Aclass destroy return $result } {{calling ::Aobject->logFilter 1 2 3 4 5} 1 2 3 4 5 result=12345 12345} test oo-12.2 {OO: filters} -setup { oo::class create Aclass Aclass create Aobject } -body { oo::define Aclass { method concatenate args { global result lappend result {expand}$args join $args {} } method logFilter args { global result lappend result "calling [self object]->[self method] $args" set r [next {expand}$args] lappend result "result=$r" return $r } } oo::define Aobject filter logFilter set result {} lappend result [Aobject concatenate 1 2 3 4 5] [Aobject destroy] } -cleanup { Aclass destroy } -result {{calling ::Aobject->logFilter 1 2 3 4 5} 1 2 3 4 5 result=12345 {calling ::Aobject->logFilter } result= 12345 {}} test oo-12.3 {OO: filters} -setup { oo::class create Aclass Aclass create Aobject } -body { oo::define Aclass { method concatenate args { global result lappend result {expand}$args join $args {} } method logFilter args { global result lappend result "calling [self object]->[self method] $args" set r [next {expand}$args] lappend result "result=$r" return $r } filter logFilter } set result {} lappend result [Aobject concatenate 1 2 3 4 5] [Aobject destroy] } -cleanup { Aclass destroy } -result {{calling ::Aobject->logFilter 1 2 3 4 5} 1 2 3 4 5 result=12345 {calling ::Aobject->logFilter } result= 12345 {}} test oo-12.4 {OO: filters} -setup { oo::class create Aclass Aclass create Aobject } -body { oo::define Aclass { method foo {} { return foo } method Bar {} { return 1 } method boo {} { if {[my Bar]} { next } { error forbidden } } filter boo } Aobject foo } -cleanup { Aclass destroy } -result foo test oo-12.5 {OO: filters} -setup { oo::class create Aclass Aclass create Aobject } -body { oo::define Aclass { method foo {} { return foo } method Bar {} { return [my Bar2] } method Bar2 {} { return 1 } method boo {} { if {[my Bar]} { next } { error forbidden } } filter boo } Aobject foo } -cleanup { Aclass destroy } -result foo test oo-12.6 {OO: filters} -setup { oo::class create Aclass Aclass create Aobject } -body { oo::define Aclass { method foo {} { return foo } method Bar {} { return [my Bar2] } method Bar2 {} { return [my Bar3] } method Bar3 {} { return 1 } method boo {} { if {[my Bar]} { next } { error forbidden } } filter boo } Aobject foo } -cleanup { Aclass destroy } -result foo test oo-12.7 {OO: filters} -setup { oo::class create Aclass Aclass create Aobject } -body { oo::define Aclass { method outerfoo {} { return [my InnerFoo] } method InnerFoo {} { return foo } method Bar {} { return [my Bar2] } method Bar2 {} { return [my Bar3] } method Bar3 {} { return 1 } method boo {} { lappend ::log [self target] if {[my Bar]} { next } else { error forbidden } } filter boo } set log {} list [Aobject outerfoo] $log } -cleanup { Aclass destroy } -result {foo {{::Aclass outerfoo} {::Aclass InnerFoo}}} test oo-13.1 {OO: changing an object's class} { oo::class create Aclass oo::define Aclass {method bar {} {lappend ::result "in A [self object]"}} oo::class create Bclass oo::define Bclass {method bar {} {lappend ::result "in B [self object]"}} set result [Aclass create foo] foo bar oo::define foo self.class Bclass foo bar Aclass destroy lappend result [info command foo] Bclass destroy return $result } {::foo {in A ::foo} {in B ::foo} foo} test oo-13.2 {OO: changing an object's class} -body { oo::object create foo oo::define foo self.class oo::class } -cleanup { foo destroy } -returnCodes 1 -result {may not change a non-class object into a class object} test oo-13.3 {OO: changing an object's class} -body { oo::class create foo oo::define foo self.class oo::object } -cleanup { foo destroy } -returnCodes 1 -result {may not change a class object into a non-class object} # todo: changing a class subtype (metaclass) to another class subtype test oo-14.1 {OO: mixins} { oo::class create Aclass oo::define Aclass method bar {} {lappend ::result "[self object] in bar"} oo::class create Bclass oo::define Bclass method boo {} {lappend ::result "[self object] in boo"} oo::define [Aclass create fooTest] mixin Bclass oo::define [Aclass create fooTest2] mixin Bclass set result [list [catch {fooTest ?} msg] $msg] fooTest bar fooTest boo fooTest2 bar fooTest2 boo oo::define fooTest2 mixin lappend result [Bclass destroy] [info command fooTest*] [Aclass destroy] } {1 {unknown method "?": must be bar, boo or destroy} {::fooTest in bar} {::fooTest in boo} {::fooTest2 in bar} {::fooTest2 in boo} {} fooTest2 {}} test oo-14.2 {OO: mixins} { oo::class create Aclass { method bar {} {return "[self object] in bar"} } oo::class create Bclass { method boo {} {return "[self object] in boo"} } oo::define Aclass mixin Bclass Aclass create fooTest set result [list [catch {fooTest ?} msg] $msg] lappend result [catch {fooTest bar} msg] $msg lappend result [catch {fooTest boo} msg] $msg lappend result [Bclass destroy] [info commands Aclass] } {1 {unknown method "?": must be bar, boo or destroy} 0 {::fooTest in bar} 0 {::fooTest in boo} {} {}} test oo-15.1 {OO: object cloning} { oo::class create Aclass oo::define Aclass method test {} {lappend ::result [self object]->test} Aclass create Ainstance set result {} Ainstance test oo::copy Ainstance Binstance Binstance test Ainstance test Ainstance destroy namespace eval foo { oo::copy Binstance Cinstance Cinstance test } Aclass destroy namespace delete foo lappend result [info commands Binstance] } {::Ainstance->test ::Binstance->test ::Ainstance->test ::foo::Cinstance->test {}} test oo-15.2 {OO: object cloning} { oo::object create foo oo::define foo { method m x {lappend ::result [self object] >$x<} forward f ::lappend ::result fwd } set result {} foo m 1 foo f 2 lappend result [oo::copy foo bar] foo m 3 foo f 4 bar m 5 bar f 6 lappend result [foo destroy] bar m 7 bar f 8 lappend result [bar destroy] } {::foo >1< fwd 2 ::bar ::foo >3< fwd 4 ::bar >5< fwd 6 {} ::bar >7< fwd 8 {}} catch {foo destroy} catch {bar destroy} test oo-15.3 {OO: class cloning} { oo::class create foo { method testme {} {lappend ::result [self class]->[self object]} } set result {} foo create baseline baseline testme oo::copy foo bar baseline testme bar create tester tester testme foo destroy tester testme bar destroy return $result } {::foo->::baseline ::foo->::baseline ::bar->::tester ::bar->::tester} test oo-16.1 {OO: object introspection} -body { info object } -returnCodes 1 -result "wrong \# args: should be \"info object subcommand ?argument ...?\"" test oo-16.2 {OO: object introspection} -body { info object class NOTANOBJECT } -returnCodes 1 -result {NOTANOBJECT does not refer to an object} test oo-16.3 {OO: object introspection} -body { info object gorp oo::object } -returnCodes 1 -result {unknown or ambiguous subcommand "gorp": must be class, definition, filters, forward, isa, methods, mixins, or vars} test oo-16.4 {OO: object introspection} -setup { oo::class create meta { superclass oo::class } } -body { list [info object class oo::object] \ [info object class oo::class] \ [info object isa class oo::object] \ [info object isa metaclass oo::object] \ [info object isa metaclass meta] \ [info object isa object oo::object] \ [info object isa object oo::define] \ [info object isa object NOTANOBJECT] } -cleanup { meta destroy } -result {::oo::class ::oo::class 1 0 1 1 0 0} test oo-16.5 {OO: object introspection} {info object methods oo::object} {} test oo-16.6 {OO: object introspection} { oo::object create foo set result [list [info object methods foo]] oo::define foo method bar {} {...} lappend result [info object methods foo] [foo destroy] } {{} bar {}} test oo-16.7 {OO: object introspection} -setup { oo::object create foo } -body { oo::define foo method bar {a {b c} args} {the body} set result [info object methods foo] lappend result [info object definition foo bar] } -cleanup { foo destroy } -result {bar {{a {b c} args} {the body}}} test oo-16.8 {OO: object introspection} { oo::object create foo oo::class create bar oo::define foo mixin bar set result [list [info object mixins foo] \ [info object isa mixin foo bar] \ [info object isa mixin foo oo::class]] foo destroy bar destroy return $result } {::bar 1 0} test oo-16.9 {OO: object introspection} { oo::class create Ac oo::class create Bc; oo::define Bc superclass Ac oo::class create Cc; oo::define Cc superclass Bc Cc create D list [info object isa typeof D oo::class] \ [info object isa typeof D Ac] \ [Ac destroy] } {0 1 {}} test oo-16.10 {OO: object introspection} -setup { oo::object create foo } -body { oo::define foo export eval foo eval {variable c 3 a 1 b 2 ddd 4 e} lsort [info object vars foo ?] } -cleanup { foo destroy } -result {a b c} test oo-17.1 {OO: class introspection} -body { info class } -returnCodes 1 -result "wrong \# args: should be \"info class subcommand ?argument ...?\"" test oo-17.2 {OO: class introspection} -body { info class superclass NOTANOBJECT } -returnCodes 1 -result {NOTANOBJECT does not refer to an object} test oo-17.3 {OO: class introspection} -setup { oo::object create foo } -body { info class superclass foo } -returnCodes 1 -cleanup { foo destroy } -result {"foo" is not a class} test oo-17.4 {OO: class introspection} -body { info class gorp oo::object } -returnCodes 1 -result {unknown or ambiguous subcommand "gorp": must be constructor, definition, destructor, filters, forward, instances, methods, mixins, subclasses, or superclasses} test oo-17.5 {OO: class introspection} -setup { oo::class create testClass } -body { testClass create foo testClass create bar testClass create spong lsort [info class instances testClass] } -cleanup { testClass destroy } -result {::bar ::foo ::spong} test oo-17.6 {OO: object introspection} -setup { oo::class create foo } -body { oo::define foo method bar {a {b c} args} {the body} set result [info class methods foo] lappend result [info class definition foo bar] } -cleanup { foo destroy } -result {bar {{a {b c} args} {the body}}} test oo-17.7 {OO: object introspection} { info class superclasses oo::class } ::oo::object test oo-17.8 {OO: object introspection} -setup { oo::class create testClass oo::class create superClass1 oo::class create superClass2 } -body { oo::define testClass superclass superClass1 superClass2 list [info class superclasses testClass] \ [lsort [info class subclass oo::object ::superClass?]] } -cleanup { testClass destroy superClass1 destroy superClass2 destroy } -result {{::superClass1 ::superClass2} {::superClass1 ::superClass2}} test oo-18.1 {OO: define command support} { list [catch {oo::define oo::object {error foo}} msg] $msg $errorInfo } {1 foo {foo while executing "error foo" (in definition script for object "oo::object" line 1) invoked from within "oo::define oo::object {error foo}"}} test oo-18.2 {OO: define command support} { list [catch {oo::define oo::object error foo} msg] $msg $errorInfo } {1 foo {foo while executing "oo::define oo::object error foo"}} test oo-18.3 {OO: define command support} { list [catch {oo::class create foo {error bar}} msg] $msg $errorInfo } {1 bar {bar while executing "error bar" (in definition script for object "::foo" line 1) invoked from within "oo::class create foo {error bar}"}} test oo-18.4 {OO: more error traces from the guts} -setup { oo::object create obj } -body { oo::define obj method bar {} {my eval {error foo}} list [catch {obj bar} msg] $msg $errorInfo } -cleanup { obj destroy } -result {1 foo {foo while executing "error foo" (in "my eval" script line 1) invoked from within "my eval {error foo}" (object "::obj" method "bar" line 1) invoked from within "obj bar"}} test oo-18.5 {OO: more error traces from the guts} -setup { [oo::class create cls] create obj set errorInfo {} } -body { oo::define cls { method eval script {next $script} export eval } oo::define obj method bar {} {my eval {error foo}} set result {} lappend result [catch {obj bar} msg] $msg $errorInfo lappend result [catch {obj eval {error bar}} msg] $msg $errorInfo } -cleanup { cls destroy } -result {1 foo {foo while executing "error foo" (in "my eval" script line 1) invoked from within "next $script" (class "::cls" method "eval" line 1) invoked from within "my eval {error foo}" (object "::obj" method "bar" line 1) invoked from within "obj bar"} 1 bar {bar while executing "error bar" (in "::obj eval" script line 1) invoked from within "next $script" (class "::cls" method "eval" line 1) invoked from within "obj eval {error bar}"}} test oo-19.1 {OO: varname method} -setup { oo::object create inst oo::define inst export eval set result {} } -body { inst eval {trace add variable x write foo} set ns [inst eval namespace current] proc foo args { global ns result set context [uplevel 1 namespace current] lappend result $args [expr { $ns eq $context ? "ok" : [list $ns ne $context] }] [expr { "${ns}::x" eq [uplevel 1 my varname x] ? "ok" : [list ${ns}::x ne [uplevel 1 my varname x]] }] } lappend result [inst eval set x 0] } -cleanup { inst destroy rename foo {} } -result {{x {} write} ok ok 0} test oo-20.1 {OO: variable method} -body { oo::class create testClass { constructor {} { my variable ok set ok {} } } lsort [info object vars [testClass new]] } -cleanup { catch {testClass destroy} } -result ok test oo-20.2 {OO: variable method} -body { oo::class create testClass { constructor {} { my variable a b c set a [set b [set c {}]] } } lsort [info object vars [testClass new]] } -cleanup { catch {testClass destroy} } -result {a b c} test oo-20.3 {OO: variable method} -body { oo::class create testClass { export varname method bar {} { my variable a(b) } } testClass create foo array set [foo varname a] {b c} foo bar } -returnCodes 1 -cleanup { catch {testClass destroy} } -result {can't define "a(b)": name refers to an element in an array} test oo-20.4 {OO: variable method} -body { oo::class create testClass { export varname method bar {} { my variable a(b) } } testClass create foo set [foo varname a] b foo bar } -returnCodes 1 -cleanup { catch {testClass destroy} } -result {can't define "a(b)": name refers to an element in an array} test oo-20.5 {OO: variable method} -body { oo::class create testClass { method bar {} { my variable a::b } } testClass create foo foo bar } -returnCodes 1 -cleanup { catch {testClass destroy} } -result {variable name "a::b" illegal: must not contain namespace separator} test oo-20.6 {OO: variable method} -setup { oo::class create testClass { self.export eval export varname } } -body { testClass eval variable a 0 oo::define [testClass create foo] method bar {other} { $other variable a set a 3 } oo::define [testClass create boo] export variable set [foo varname a] 1 set [boo varname a] 2 foo bar boo list [testClass eval set a] [set [foo varname a]] [set [boo varname a]] } -cleanup { testClass destroy } -result {0 1 3} test oo-20.7 {OO: variable method} -setup { oo::class create cls } -body { oo::define cls { method a {} { my variable {b c} d lappend c $d } method e {} { my variable b d return [list $b $d] } method f {x y} { my variable b d set b $x set d $y } } cls create obj obj f p q obj a obj a obj e } -cleanup { cls destroy } -result {{p q q} q} test oo-20.8 {OO: variable method} -setup { oo::class create cls } -body { oo::define cls { constructor {} { namespace eval foo { variable bar 1 } } method ns {} {self namespace} method a {} { my variable {foo::bar c} d lappend c $d } method e {} { my variable {foo::bar b} d return [list $b $d] } method f {x} { my variable d set d $x } } cls create obj obj f p obj a obj a list [obj e] [set [obj ns]::foo::bar] } -cleanup { cls destroy } -result {{{1 p p} p} {1 p p}} test oo-20.9 {OO: variable method} -setup { oo::object create obj } -body { oo::define obj { method a {} { my variable {a ::b} } } obj a } -cleanup { obj destroy } -returnCodes 1 -result {variable name "::b" illegal: must not contain namespace separator} test oo-21.1 {OO: inheritance ordering} -setup { oo::class create A } -body { oo::define A method m {} {lappend ::result A} oo::class create B { superclass A method m {} {lappend ::result B;next} } oo::class create C { superclass A method m {} {lappend ::result C;next} } oo::class create D { superclass B C method m {} {lappend ::result D;next} } D create o oo::define o method m {} {lappend ::result o;next} set result {} o m return $result } -cleanup { A destroy } -result {o D B C A} test oo-21.2 {OO: inheritance ordering} -setup { oo::class create A } -body { oo::define A method m {} {lappend ::result A} oo::class create B { superclass A method m {} {lappend ::result B;next} } oo::class create C { superclass A method m {} {lappend ::result C;next} } oo::class create D { superclass B C method m {} {lappend ::result D;next} } oo::class create Emix { superclass C method m {} {lappend ::result Emix;next} } oo::class create Fmix { superclass Emix method m {} {lappend ::result Fmix;next} } D create o oo::define o method m {} {lappend ::result o;next} oo::define o mixin Fmix set result {} o m return $result } -cleanup { A destroy } -result {Fmix Emix o D B C A} test oo-21.3 {OO: inheritance ordering} -setup { oo::class create A } -body { oo::define A method m {} {lappend ::result A} oo::class create B { superclass A method m {} {lappend ::result B;next} method f {} {lappend ::result B-filt;next} } oo::class create C { superclass A method m {} {lappend ::result C;next} } oo::class create D { superclass B C method m {} {lappend ::result D;next} } oo::class create Emix { superclass C method m {} {lappend ::result Emix;next} method f {} {lappend ::result Emix-filt;next} } oo::class create Fmix { superclass Emix method m {} {lappend ::result Fmix;next} } D create o oo::define o { method m {} {lappend ::result o;next} mixin Fmix filter f } set result {} o m return $result } -cleanup { A destroy } -result {Emix-filt B-filt Fmix Emix o D B C A} test oo-21.4 {OO: inheritance ordering} -setup { oo::class create A } -body { oo::define A method m {} {lappend ::result A} oo::class create B { superclass A method m {} {lappend ::result B;next} method f {} {lappend ::result B-filt;next} method g {} {lappend ::result B-cfilt;next} } oo::class create C { superclass A method m {} {lappend ::result C;next} } oo::class create D { superclass B C method m {} {lappend ::result D;next} method g {} {lappend ::result D-cfilt;next} filter g } oo::class create Emix { superclass C method m {} {lappend ::result Emix;next} method f {} {lappend ::result Emix-filt;next} } oo::class create Fmix { superclass Emix method m {} {lappend ::result Fmix;next} } D create o oo::define o { method m {} {lappend ::result o;next} mixin Fmix filter f } set result {} o m return $result } -cleanup { A destroy } -result {Emix-filt B-filt D-cfilt B-cfilt Fmix Emix o D B C A} cleanupTests return # Local Variables: # mode: tcl # End: