1###
2# Test script build functions
3###
4
5set result {}
6putb result {# clay.test - Copyright (c) 2018 Sean Woods
7# -------------------------------------------------------------------------
8
9set MODDIR [file dirname [file dirname [file join [pwd] [info script]]]]
10if {[file exists [file join $MODDIR devtools testutilities.tcl]]} {
11  # Running inside tcllib
12  set TCLLIBMOD $MODDIR
13} else {
14  set TCLLIBMOD [file join $MODDIR .. .. tcllib modules]
15}
16source [file join $TCLLIBMOD devtools testutilities.tcl]
17
18testsNeedTcl     8.6
19testsNeedTcltest 2
20testsNeed        TclOO 1
21
22support {}
23testing {
24    useLocal clay.tcl clay
25}
26}
27
28putb result {
29set ::clay::trace 0
30}
31
32###
33# UUID test
34###
35putb result {
36
37# -------------------------------------------------------------------------
38# Handle multiple implementation testing
39#
40
41array set preserve [array get ::clay::uuid::accel]
42
43proc implementations {} {
44    variable ::clay::uuid::accel
45    foreach {a v} [array get accel] {if {$v} {lappend r $a}}
46    lappend r tcl; set r
47}
48
49proc select_implementation {impl} {
50    variable ::clay::uuid::accel
51    foreach e [array names accel] { set accel($e) 0 }
52    if {[string compare "tcl" $impl] != 0} {
53        set accel($impl) 1
54    }
55}
56
57proc reset_implementation {} {
58    variable ::clay::uuid::accel
59    array set accel [array get ::preserve]
60}
61
62# -------------------------------------------------------------------------
63# Setup any constraints
64#
65
66# -------------------------------------------------------------------------
67# Now the package specific tests....
68# -------------------------------------------------------------------------
69
70# -------------------------------------------------------------------------
71
72foreach impl [implementations] {
73    select_implementation $impl
74
75    test uuid-1.0-$impl "uuid requires args" {
76        list [catch {clay::uuid} msg]
77    } {1}
78
79    test uuid-1.1-$impl "uuid generate should create a 36 char string uuid" {
80        list [catch {string length [clay::uuid generate]} msg] $msg
81    } {0 36}
82
83    test uuid-1.2-$impl "uuid comparison of uuid with self should be true" {
84        list [catch {
85            set a [clay::uuid generate]
86            clay::uuid equal $a $a
87        } msg] $msg
88    } {0 1}
89
90    test uuid-1.3-$impl "uuid comparison of two different\
91        uuids should be false" {
92        list [catch {
93            set a [clay::uuid generate]
94            set b [clay::uuid generate]
95            clay::uuid equal $a $b
96        } msg] $msg
97    } {0 0}
98
99    reset_implementation
100}
101}
102
103
104putb result {
105# Modification History:
106###
107# Modification 2018-10-30
108# Fixed an error in our ancestry mapping and developed tests to
109# ensure we are actually following in the order TclOO follows methods
110###
111# Modification 2018-10-21
112# The clay metaclass no longer exports the clay method
113# to oo::class and oo::object, and clay::ancestors no
114# longer returns any class that lacks the clay method
115###
116# Modification 2018-10-10
117# clay::ancestors now rigged to descend into all classes depth-first
118# and then place metaclasses at the end of the search
119###
120# -------------------------------------------------------------------------
121
122# -------------------------------------------------------------------------
123# Test Helpers
124###
125proc dict_compare {a b} {
126  set result {}
127  set A {}
128  dict for {f v} $a {
129    set f [string trim $f :/]
130    if {$f eq {.}} continue
131    dict set A $f $v
132  }
133  set B {}
134  dict for {f v} $b {
135    set f [string trim $f :/]
136    if {$f eq {.}} continue
137    dict set B $f $v
138  }
139  dict for {f v} $A {
140    if {[dict exists $B $f]} {
141      if {[dict get $B $f] ne $v} {
142        lappend result [list B $f [dict get $B $f] [list != $v]]
143      }
144    } else {
145      lappend result [list B $f $v missing]
146    }
147  }
148  dict for {f v} $B {
149    if {![dict exists $A $f]} {
150      lappend result [list A $f $v missing]
151    }
152  }
153  return $result
154}
155
156test dict-compare-001 {Test our testing method} {
157  dict_compare {} {}
158} {}
159
160test dict-compare-002 {Test our testing method} {
161  dict_compare {a 1} {}
162} {{B a 1 missing}}
163
164test dict-compare-003 {Test our testing method} {
165  dict_compare {a 1 b 2} {a 1 b 2}
166} {}
167
168test dict-compare-003.a {Test our testing method} {
169  dict_compare {a 1 b 2} {b 2 a 1 }
170} {}
171
172test dict-compare-003.b {Test our testing method} {
173  dict_compare {b 2 a 1} {a 1 b 2}
174} {}
175
176
177test dict-compare-004 {Test our testing method} {
178  dict_compare {a: 1 b: 2} {a 1 b 2}
179} {}
180
181test dict-compare-005 {Test our testing method} {
182  dict_compare {a 1 b 3} {a 1 b 2}
183} {{B b 2 {!= 3}}}
184}
185
186
187###
188# Tests for clay::tree
189###
190
191putb result {
192###
193# Test canonical mapping
194###
195}
196set test 0
197  foreach {pattern canonical storage} {
198    {foo bar baz}       {foo/ bar/ baz}         {foo bar baz}
199    {foo bar baz/}      {foo/ bar/ baz/}        {foo bar baz}
200    {foo bar .}         {foo/ bar}              {foo bar .}
201    {foo/ bar/ .}       {foo/ bar}              {foo bar .}
202    {foo . bar . baz .} {foo/ bar/ baz}         {foo . bar . baz .}
203    {foo bar baz bat:}  {foo/ bar/ baz/ bat:}   {foo bar baz bat:}
204    {foo:}              {foo:}                  {foo:}
205    {foo/bar/baz/bat:}  {foo/ bar/ baz/ bat:}   {foo bar baz bat:}
206} {
207    dict set map %pattern% $pattern
208    dict set map %canonical% $canonical
209    dict set map %storage% $storage
210    incr test
211
212    dict set map %test% [format "test-storage-%04d" $test]
213    putb result $map {
214test {%test%} {Test ::clay::tree::storage with %pattern%} {
215  clay::tree::storage {%pattern%}
216} {%storage%}
217}
218}
219
220putb result {
221dict set r foo/ bar/ baz 1
222dict set s foo/ bar/ baz 0
223set t [clay::tree::merge $r $s]
224
225test rmerge-0001 {Test that the root is marked as a branch} {
226  dict get $t foo bar baz
227} 0
228
229set r [dict create]
230clay::tree::dictmerge r {
231  foo/ {
232    bar/ {
233      baz 1
234      bing: 2
235      bang { bim 3 boom 4 }
236      womp: {a 1 b 2}
237    }
238  }
239}
240
241test dictmerge-0001 {Test that the root is marked as a branch} {
242  dict exists $r .
243} 1
244test dictmerge-0002 {Test that branch foo is marked correctly} {
245  dict exists $r foo .
246} 1
247test dictmerge-0003 {Test that branch bar is marked correctly} {
248  dict exists $r foo bar .
249} 1
250test dictmerge-0004 {Test that leaf foo/bar/bang is not marked as branch despite being a dict} {
251  dict exists $r foo bar bang .
252} 0
253test dictmerge-0004 {Test that leaf foo/bar/bang/bim exists} {
254  dict exists $r foo bar bang bim
255} 1
256test dictmerge-0005 {Test that leaf foo/bar/bang/boom exists} {
257  dict exists $r foo bar bang boom
258} 1
259
260###
261# Replace bang with bang/
262###
263clay::tree::dictmerge r {
264  foo/ {
265    bar/ {
266      bang/ {
267        whoop 1
268      }
269    }
270  }
271}
272
273test dictmerge-0006 {Test that leaf foo/bar/bang/bim ceases to exist} {
274  dict exists $r foo bar bang bim
275} 0
276test dictmerge-0007 {Test that leaf foo/bar/bang/boom exists} {
277  dict exists $r foo bar bang boom
278} 0
279
280test dictmerge-0008 {Test that leaf foo/bar/bang is now a branch} {
281  dict exists $r foo bar bang .
282} 1
283
284test branch-0001 {Test that foo/ is a branch} {
285  clay::tree::is_branch $r foo/
286} 1
287test branch-0002 {Test that foo is a branch} {
288  clay::tree::is_branch $r foo
289} 1
290test branch-0003 {Test that foo/bar/ is a branch} {
291  clay::tree::is_branch $r {foo/ bar/}
292} 1
293test branch-0004 {Test that foo bar is not branch} {
294  clay::tree::is_branch $r {foo bar}
295} 1
296test branch-0004 {Test that foo/ bar is not branch} {
297  clay::tree::is_branch $r {foo/ bar}
298} 0
299}
300
301set test 0
302foreach {path isbranch} {
303  foo 1
304  {foo bar} 1
305  {foo bar baz} 0
306  {foo bar bing} 0
307  {foo bar bang} 1
308  {foo bar bang whoop} 0
309} {
310  set mpath [lrange $path 0 end-1]
311  set item  [lindex $path end]
312  set tests [list {} {} $isbranch {} : 0 {} / 1 . {} 0]
313  dict set map %mpath% $mpath
314  dict set map %item% $item
315  foreach {head tail isbranch} $tests {
316    dict set map %head% $head
317    dict set map %tail% $tail
318    dict set map %isbranch% $isbranch
319    dict set map %test% [format "test-branch-%04d" [incr test]]
320    putb result $map {
321test {%test%} {Test that %mpath% %head%%item%%tail% is_branch = %isbranch%} {
322  clay::tree::is_branch $r {%mpath% %head%%item%%tail%}
323} %isbranch%
324}
325  }
326}
327
328putb result {
329# -------------------------------------------------------------------------
330# dictmerge Testing - oometa
331unset -nocomplain foo
332clay::tree::dictmerge foo {
333  option/ {
334    color/ {
335      label Color
336      default green
337    }
338  }
339}
340clay::tree::dictmerge foo {
341  option/ {
342    color/ {
343      default purple
344    }
345  }
346}
347
348test oometa-0001 {Invoking dictmerge with empty args on a non existent variable create an empty variable} {
349  dict get $foo option color default
350} purple
351test oometa-0002 {Invoking dictmerge with empty args on a non existent variable create an empty variable} {
352  dict get $foo option color label
353} Color
354
355unset -nocomplain foo
356set foo {. {}}
357::clay::tree::dictmerge foo {. {} color {. {} default green label Color}}
358::clay::tree::dictmerge foo {. {} color {. {} default purple}}
359test oometa-0003 {Recursive merge problem from oometa/clay find} {
360  dict get $foo color default
361} purple
362test oometa-0004 {Recursive merge problem from oometa/clay find} {
363  dict get $foo color label
364} Color
365
366unset -nocomplain foo
367set foo {. {}}
368::clay::tree::dictmerge foo {. {} color {. {} default purple}}
369::clay::tree::dictmerge foo {. {} color {. {} default green label Color}}
370test oometa-0005 {Recursive merge problem from oometa/clay find} {
371  dict get $foo color default
372} green
373test oometa-0006 {Recursive merge problem from oometa/clay find} {
374  dict get $foo color label
375} Color
376
377test oometa-0008 {Un-Sanitized output} {
378  set foo
379} {. {} color {. {} default green label Color}}
380
381test oometa-0009 {Sanitize} {
382  clay::tree::sanitize $foo
383} {color {default green label Color}}
384}
385
386
387putb result {
388# -------------------------------------------------------------------------
389# dictmerge Testing - clay
390unset -nocomplain foo
391test clay-0001 {Invoking dictmerge with empty args on a non existent variable create an empty variable} {
392  ::clay::tree::dictmerge foo
393  set foo
394} {. {}}
395
396unset -nocomplain foo
397::clay::tree::dictset foo bar/ baz/ bell bang
398
399test clay-0002 {For new entries dictmerge is essentially a set} {
400  dict get $foo bar baz bell
401} {bang}
402::clay::tree::dictset foo bar/ baz/ boom/ bang
403test clay-0003 {For entries that do exist a zipper merge is performed} {
404  dict get $foo bar baz bell
405} {bang}
406test clay-0004 {For entries that do exist a zipper merge is performed} {
407  dict get $foo bar baz boom
408} {bang}
409
410::clay::tree::dictset foo bar/ baz/ bop {color green flavor strawberry}
411
412test clay-0005 {Leaves are replaced even if they look like a dict} {
413  dict get $foo bar baz bop
414} {color green flavor strawberry}
415
416::clay::tree::dictset foo bar/ baz/ bop {color yellow}
417test clay-0006 {Leaves are replaced even if they look like a dict} {
418  dict get $foo bar baz bop
419} {color yellow}
420
421::clay::tree::dictset foo bar/ baz/ bang/ {color green flavor strawberry}
422test clay-0007a {Branches are merged} {
423  dict get $foo bar baz bang
424} {. {} color green flavor strawberry}
425
426::clay::tree::dictset foo bar/ baz/ bang/ color yellow
427test clay-0007b {Branches are merged}  {
428  dict get $foo bar baz bang
429} {. {} color yellow flavor strawberry}
430
431::clay::tree::dictset foo bar/ baz/ bang/ {color blue}
432test clay-0007c {Branches are merged}  {
433  dict get $foo bar baz bang
434} {. {} color blue flavor strawberry}
435
436::clay::tree::dictset foo bar/ baz/ bang/ shape: {Sort of round}
437test clay-0007d {Branches are merged} {
438  dict get $foo bar baz bang
439} {. {} color blue flavor strawberry shape: {Sort of round}}
440
441::clay::tree::dictset foo bar/ baz/ bang/ color yellow
442test clay-0007e {Branches are merged}  {
443  dict get $foo bar baz bang
444} {. {} color yellow flavor strawberry shape: {Sort of round}}
445
446::clay::tree::dictset foo bar/ baz/ bang/ {color blue}
447test clay-0007f {Branches are merged}  {
448  dict get $foo bar baz bang
449} {. {} color blue flavor strawberry shape: {Sort of round}}
450
451::clay::tree::dictset foo dict my_var 10
452::clay::tree::dictset foo dict my_other_var 9
453
454test clay-0007g {Branches are merged}  {
455  dict get $foo dict
456} {. {} my_var 10 my_other_var 9}
457
458::clay::tree::dictset foo dict/ my_other_other_var 8
459test clay-0007h {Branches are merged}  {
460  dict get $foo dict
461} {. {} my_var 10 my_other_var 9 my_other_other_var 8}
462
463
464::clay::tree::dictmerge foo {option/ {color {type color} flavor {sense taste}}}
465::clay::tree::dictmerge foo {option/ {format {default ascii}}}
466
467test clay-0008 {Whole dicts are merged}  {
468  dict get $foo option color
469} {type color}
470test clay-0009 {Whole dicts are merged}  {
471  dict get $foo option flavor
472} {sense taste}
473test clay-0010 {Whole dicts are merged}  {
474  dict get $foo option format
475} {default ascii}
476
477###
478# Tests for the httpd module
479###
480test clay-0010 {Test that leaves are merged properly}
481set bar {}
482::clay::tree::dictmerge bar {
483   proxy/ {port 10101 host myhost.localhost}
484}
485::clay::tree::dictmerge bar {
486   mimetxt {Host: localhost
487Content_Type: text/plain
488Content-Length: 15
489}
490   http {HTTP_HOST {} CONTENT_LENGTH 15 HOST localhost CONTENT_TYPE text/plain UUID 3a7b4cdc-28d7-49b7-b18d-9d7d18382b9e REMOTE_ADDR 127.0.0.1 REMOTE_HOST 127.0.0.1 REQUEST_METHOD POST REQUEST_URI /echo REQUEST_PATH echo REQUEST_VERSION 1.0 DOCUMENT_ROOT {} QUERY_STRING {} REQUEST_RAW {POST /echo HTTP/1.0} SERVER_PORT 10001 SERVER_NAME 127.0.0.1 SERVER_PROTOCOL HTTP/1.1 SERVER_SOFTWARE {TclHttpd 4.2.0} LOCALHOST 0} UUID 3a7b4cdc-28d7-49b7-b18d-9d7d18382b9e uriinfo {fragment {} port {} path echo scheme http host {} query {} pbare 0 pwd {} user {}}
491   mixin {reply ::test::content.echo}
492   prefix /echo
493   proxy_port 10010
494   proxy/ {host localhost}
495}
496
497test clay-0011 {Whole dicts are merged}  {
498  dict get $bar proxy_port
499} {10010}
500
501test clay-0012 {Whole dicts are merged}  {
502  dict get $bar http CONTENT_LENGTH
503} 15
504test clay-0013 {Whole dicts are merged}  {
505  dict get $bar proxy host
506} localhost
507test clay-0014 {Whole dicts are merged}  {
508  dict get $bar proxy port
509} 10101
510}
511
512putb result {
513###
514# Dialect Testing
515###
516::clay::dialect::create ::alpha
517
518proc ::alpha::define::is_alpha {} {
519  dict set ::testinfo([current_class]) is_alpha 1
520}
521
522::alpha::define ::alpha::object {
523  is_alpha
524}
525
526::clay::dialect::create ::bravo ::alpha
527
528proc ::bravo::define::is_bravo {} {
529  dict set ::testinfo([current_class]) is_bravo 1
530}
531
532::bravo::define ::bravo::object {
533  is_bravo
534}
535
536::clay::dialect::create ::charlie ::bravo
537
538proc ::charlie::define::is_charlie {} {
539  dict set ::testinfo([current_class]) is_charlie 1
540}
541
542::charlie::define ::charlie::object {
543  is_charlie
544}
545
546::clay::dialect::create ::delta ::charlie
547
548proc ::delta::define::is_delta {} {
549  dict set ::testinfo([current_class]) is_delta 1
550}
551
552::delta::define ::delta::object {
553  is_delta
554}
555
556::delta::class create adam {
557  is_alpha
558  is_bravo
559  is_charlie
560  is_delta
561}
562
563test oodialect-keyword-001 {Testing keyword application} {
564  set ::testinfo(::adam)
565} {is_alpha 1 is_bravo 1 is_charlie 1 is_delta 1}
566
567test oodialect-keyword-002 {Testing keyword application} {
568  set ::testinfo(::alpha::object)
569} {is_alpha 1}
570
571test oodialect-keyword-003 {Testing keyword application} {
572  set ::testinfo(::bravo::object)
573} {is_bravo 1}
574
575test oodialect-keyword-004 {Testing keyword application} {
576  set ::testinfo(::charlie::object)
577} {is_charlie 1}
578
579test oodialect-keyword-005 {Testing keyword application} {
580  set ::testinfo(::delta::object)
581} {is_delta 1}
582
583###
584# Declare an object from a namespace
585###
586namespace eval ::test1 {
587  ::alpha::class create a {
588    aliases A
589    is_alpha
590  }
591  ::alpha::define b {
592    aliases B BEE
593    is_alpha
594  }
595  ::alpha::class create ::c {
596    aliases C
597    is_alpha
598  }
599  ::alpha::define ::d {
600    aliases D
601    is_alpha
602  }
603}
604
605test oodialect-naming-001 {Testing keyword application} {
606  set ::testinfo(::test1::a)
607} {is_alpha 1}
608
609test oodialect-naming-002 {Testing keyword application} {
610  set ::testinfo(::test1::b)
611} {is_alpha 1}
612
613test oodialect-naming-003 {Testing keyword application} {
614  set ::testinfo(::c)
615} {is_alpha 1}
616
617test oodialect-naming-004 {Testing keyword application} {
618  set ::testinfo(::d)
619} {is_alpha 1}
620
621test oodialect-aliasing-001 {Testing keyword application} {
622namespace eval ::test1 {
623    ::alpha::define e {
624       superclass A
625    }
626}
627} ::test1::e
628
629test oodialect-aliasing-002 {Testing keyword application} {
630namespace eval ::test1 {
631    ::bravo::define f {
632       superclass A
633    }
634}
635} ::test1::f
636
637
638test oodialect-aliasing-003 {Testing aliase method on class} {
639  ::test1::a aliases
640} {::test1::A}
641
642###
643# Test modified 2018-10-21
644###
645test oodialect-ancestry-003 {Testing heritage} {
646  ::clay::ancestors ::test1::f
647} {}
648
649###
650# Test modified 2018-10-21
651###
652test oodialect-ancestry-004 {Testing heritage} {
653  ::clay::ancestors ::alpha::object
654} {}
655
656###
657# Test modified 2018-10-21
658###
659test oodialect-ancestry-005 {Testing heritage} {
660  ::clay::ancestors ::delta::object
661} {}
662
663}
664
665putb result {
666# -------------------------------------------------------------------------
667# clay submodule testing
668# -------------------------------------------------------------------------
669
670}
671putb result {
672# Test canonical path building
673set path {const/ foo/ bar/ baz/}
674}
675set testnum 0
676foreach {pattern} {
677  {const foo bar baz}
678  {const/ foo/ bar/ baz}
679  {const/foo/bar/baz}
680  {const/foo bar/baz}
681  {const/foo/bar baz}
682  {const foo/bar/baz}
683  {const foo bar/baz}
684  {const/foo bar baz}
685} {
686  putb result [list %pattern% $pattern %testnum% [format %04d [incr testnum]]] {
687test oo-clay-path-%testnum% "Test path: %pattern%" {
688  ::clay::path %pattern%
689} $path
690}
691}
692putb result {set path {const/ foo/ bar/ baz/ bing}}
693set testnum 0
694foreach {pattern} {
695  {const foo bar baz bing}
696  {const/ foo/ bar/ baz/ bing}
697  {const/foo/bar/baz/bing}
698  {const/foo bar/baz/bing:}
699  {const/foo/bar baz bing}
700  {const/foo/bar baz bing:}
701  {const foo/bar/baz/bing}
702  {const foo bar/baz/bing}
703  {const/foo bar baz bing}
704} {
705  putb result [list %pattern% $pattern %testnum% [format %04d [incr testnum]]] {
706test oo-clay-leaf-%testnum% "Test leaf: %pattern%" {
707  ::clay::leaf %pattern%
708} $path
709}
710}
711
712putb result {namespace eval ::foo {}}
713
714set class-a ::foo::classa
715set commands-a {
716  clay set const color  blue
717  clay set const/flavor strawberry
718  clay set {const/ sound} zoink
719  clay set info/ {
720    animal no
721    building no
722    subelement {pedantic yes}
723  }
724
725  # Provide a method that returns a constant so we can compare clay's inheritance to
726  # TclOO
727  method color {} {
728    return blue
729  }
730  method flavor {} {
731    return strawberry
732  }
733  method sound {} {
734    return zoink
735  }
736}
737set claydict-a {
738  const/ {color blue flavor strawberry sound zoink}
739  info/  {
740    animal no
741    building no
742    subelement {pedantic yes}
743  }
744}
745
746putb result [list %class% ${class-a} %commands% ${commands-a}] {
747clay::define %class% {
748%commands%
749}
750}
751
752set testnum 0
753foreach {top children} ${claydict-a} {
754  foreach {child value} $children {
755    set map {}
756    dict set map %class% ${class-a}
757    dict set map %top% $top
758    dict set map %child% $child
759    dict set map %value% $value
760    dict set map %testnum% [format %04d [incr testnum]]
761    putb result $map {
762test oo-class-clay-method-%testnum% "Test %class% %top% %child% exists" {
763  %class% clay exists %top% %child%
764} 1
765}
766    dict set map %test% [format %04d [incr testnum]]
767    putb result $map {
768test oo-class-clay-method-%testnum% "Test %class% %top% %child% value" {
769  %class% clay get %top% %child%
770} {%value%}
771}
772  }
773}
774
775
776set class-b ::foo::classb
777set claydict-b {
778  const/ {color black flavor vanilla feeling dread}
779  info/  {subelement {spoon yes}}
780}
781set commands-b {}
782foreach {top children} ${claydict-b} {
783  foreach {child value} $children {
784    putb commands-b "  [list clay set $top $child $value]"
785    putb commands-b "  [list method $child {} [list return $value]]"
786  }
787}
788putb result [list %class% ${class-b} %commands% ${commands-b}] {
789clay::define %class% {
790%commands%
791}
792}
793
794foreach {top children} ${claydict-b} {
795  foreach {child value} $children {
796    set map {}
797    dict set map %class% ${class-b}
798    dict set map %top% $top
799    dict set map %child% $child
800    dict set map %value% $value
801    dict set map %testnum% [format %04d [incr testnum]]
802    putb result $map {
803test oo-class-clay-method-%testnum% "Test %class% %top% %child% exists" {
804  %class% clay exists %top% %child%
805} 1
806}
807    dict set map %test% [format %04d [incr testnum]]
808    putb result $map {
809test oo-class-clay-method-%testnum% "Test %class% %top% %child% value" {
810  %class% clay get %top% %child%
811} {%value%}
812}
813  }
814}
815
816set commands-c {superclass ::foo::classb ::foo::classa}
817set class-c ::foo::class.ab
818putb result [list %class% ${class-c} %commands% ${commands-c}] {
819clay::define %class% {
820%commands%
821}
822}
823set commands-d {superclass ::foo::classa ::foo::classb}
824set class-d ::foo::class.ba
825putb result [list %class% ${class-d} %commands% ${commands-d}] {
826clay::define %class% {
827%commands%
828}
829}
830
831###
832# Tests for objects
833###
834
835putb result {# -------------------------------------------------------------------------
836# Singleton
837::clay::define ::test::singletonbehavior {
838  method bar {} {
839    return CLASS
840  }
841  method booze {} {
842    return CLASS
843  }
844  Ensemble foo::bang {} {
845    return CLASS
846  }
847  Ensemble foo::both {} {
848    return CLASS
849  }
850  Ensemble foo::mixin {} {
851    return CLASS
852  }
853  Ensemble foo::sloppy {} {
854    return CLASS
855  }
856}
857::clay::define ::test::flavor.strawberry {
858  clay define property flavor strawbery
859  method bar {} {
860    return STRAWBERRY
861  }
862  Ensemble foo::bing {} {
863    return STRAWBERRY
864  }
865  Ensemble foo::both {} {
866    return STRAWBERRY
867  }
868  Ensemble foo::mixin {} {
869    return STRAWBERRY
870  }
871  Ensemble foo::sloppy {} {
872    return STRAWBERRY
873  }
874}
875::clay::singleton ::TEST {
876  class ::test::singletonbehavior
877  clay mixinmap flavor ::test::flavor.strawberry
878  clay set property color green
879  method bar {} {
880    return OBJECT
881  }
882  method booze {} {
883    return OBJECT
884  }
885  method baz {} {
886    return OBJECT
887  }
888  Ensemble foo::bar {} {
889    return OBJECT
890  }
891  Ensemble foo::both {} {
892    return OBJECT
893  }
894}
895
896test oo-object-singleton-001 {Test singleton superclass keyword} {
897  ::TEST clay delegate class
898} {::test::singletonbehavior}
899
900test oo-object-singleton-002 {Test singleton ensemble 1} {
901  ::TEST foo <list>
902} {bang bar bing both mixin sloppy}
903
904test oo-object-singleton-003 {Test singleton ensemble from script} {
905  ::TEST foo bar
906} {OBJECT}
907test oo-object-singleton-004 {Test singleton ensemble from mixin} {
908  ::TEST foo bing
909} {STRAWBERRY}
910test oo-object-singleton-005 {Test singleton ensemble from class} {
911  ::TEST foo bang
912} {CLASS}
913# Test note: the behavior from TclOO is unexpected
914# Intuitively, a local method should override a mixin
915# but this is not the case
916test oo-object-singleton-006 {Test singleton ensemble from conflict, should resolve to object} {
917  ::TEST foo both
918} {STRAWBERRY}
919test oo-object-singleton-007 {Test singleton ensemble from conflict, should resolve to mixin} {
920  ::TEST foo sloppy
921} {STRAWBERRY}
922###
923# Test note:
924# This should work but does not
925###
926#test oo-object-singleton-009 {Test property from mixin/class} {
927#  ::TEST clay get property flavor
928#} {strawberry}
929test oo-object-singleton-008 {Test property from script} {
930  ::TEST clay get property color
931} {green}
932
933
934# Test note: the behavior from TclOO is unexpected
935# Intuitively, a local method should override a mixin
936# but this is not the case
937test oo-object-singleton-010 {Test method declared in script} {
938  ::TEST bar
939} {STRAWBERRY}
940
941test oo-object-singleton-011 {Test method declared in script} {
942  ::TEST booze
943} {OBJECT}
944TEST destroy
945
946# OBJECT of ::foo::classa
947set OBJECTA [::foo::classa new]
948
949###
950# Test object degation
951###
952proc ::foo::fakeobject {a b} {
953  return [expr {$a + $b}]
954}
955
956::clay::object create TEST
957TEST clay delegate funct ::foo::fakeobject
958test oo-object-delegate-001 {Test object delegation} {
959  ::TEST clay delegate
960} {<class> ::clay::object <funct> ::foo::fakeobject}
961
962test oo-object-delegate-002 {Test object delegation} {
963  ::TEST clay delegate funct
964} {::foo::fakeobject}
965
966test oo-object-delegate-002a {Test object delegation} {
967  ::TEST clay delegate <funct>
968} {::foo::fakeobject}
969
970test oo-object-delegate-003 {Test object delegation} {
971  ::TEST <funct> 1 1
972} {2}
973test oo-object-delegate-004 {Test object delegation} {
974  ::TEST <funct> 10 -7
975} {3}
976
977# Replace the function out from under
978proc ::foo::fakeobject {a b} {
979  return [expr {$a * $b}]
980}
981test oo-object-delegate-005 {Test object delegation} {
982  ::TEST <funct> 10 -7
983} {-70}
984
985# Object with ::foo::classa mixed in
986set MIXINA  [::oo::object new]
987oo::objdefine $MIXINA mixin ::foo::classa
988}
989set matrix ${claydict-a}
990set testnum 0
991foreach {top children} $matrix {
992  foreach {child value} $children {
993    set map {}
994    dict set map %object1% OBJECTA
995    dict set map %object2% MIXINA
996
997    dict set map %top% $top
998    dict set map %child% $child
999    dict set map %value% $value
1000    dict set map %testnum% [format %04d [incr testnum]]
1001    putb result $map {
1002test oo-object-clay-method-native-%testnum% {Test native object gets the property %top%/%child%} {
1003  $%object1% clay get %top% %child%
1004} {%value%}
1005test oo-object-clay-method-mixin-%testnum% {Test mixin object gets the property %top%/%child%} {
1006  $%object2% clay get %top% %child%
1007} {%value%}
1008}
1009    if {$top eq "const/"} {
1010      putb result $map {
1011test oo-object-clay-method-native-methodcheck-%testnum% {Test that %top%/%child% would mimic method interheritance for a native class} {
1012  $%object1% %child%
1013} {%value%}
1014test oo-object-clay-method-mixin-%testnum% {Test that %top%/%child% would mimic method interheritance for a mixed in class} {
1015  $%object2% %child%
1016} {%value%}
1017    }
1018    }
1019  }
1020}
1021
1022putb result {# -------------------------------------------------------------------------
1023# OBJECT of ::foo::classb
1024set OBJECTB [::foo::classb new]
1025# Object with ::foo::classb mixed in
1026set MIXINB  [::oo::object new]
1027oo::objdefine $MIXINB mixin ::foo::classb
1028}
1029set matrix ${claydict-b}
1030#set testnum 0
1031foreach {top children} $matrix {
1032  foreach {child value} $children {
1033    set map {}
1034    dict set map %object1% OBJECTB
1035    dict set map %object2% MIXINB
1036
1037    dict set map %top% $top
1038    dict set map %child% $child
1039    dict set map %value% $value
1040    dict set map %testnum% [format %04d [incr testnum]]
1041    putb result $map {
1042test oo-object-clay-method-native-%testnum% {Test native object gets the property %top%/%child%} {
1043  $%object1% clay get %top% %child%
1044} {%value%}
1045test oo-object-clay-method-mixin-%testnum% {Test mixin object gets the property %top%/%child%} {
1046  $%object2% clay get %top% %child%
1047} {%value%}
1048}
1049    if {$top eq "const/"} {
1050      putb result $map {
1051test oo-object-clay-method-native-methodcheck-%testnum% {Test that %top%/%child% would mimic method interheritance for a native class} {
1052  $%object1% %child%
1053} {%value%}
1054test oo-object-clay-method-mixin-%testnum% {Test that %top%/%child% would mimic method interheritance for a mixed in class} {
1055  $%object2% %child%
1056} {%value%}
1057    }
1058    }
1059  }
1060}
1061
1062putb result {# -------------------------------------------------------------------------
1063# OBJECT descended from ::foo::classa ::foo::classb
1064set OBJECTAB [::foo::class.ab new]
1065# Object where classes were mixed in ::foo::classa ::foo::classb
1066set MIXINAB  [::oo::object new]
1067# Test modified 2018-10-30, mixin order was wrong before
1068oo::objdefine $MIXINAB mixin ::foo::classb ::foo::classa
1069}
1070set matrix ${claydict-b}
1071foreach {top children} ${claydict-a} {
1072  foreach {child value} $children {
1073    if {![dict exists $matrix $top $child]} {
1074      dict set matrix $top $child $value
1075    }
1076  }
1077}
1078#set testnum 0
1079foreach {top children} $matrix {
1080  foreach {child value} $children {
1081    set map {}
1082    dict set map %object1% OBJECTAB
1083    dict set map %object2% MIXINAB
1084
1085    dict set map %top% $top
1086    dict set map %child% $child
1087    dict set map %value% $value
1088    dict set map %testnum% [format %04d [incr testnum]]
1089    putb result $map {
1090test oo-object-clay-method-native-%testnum% {Test native object gets the property %top%/%child%} {
1091  $%object1% clay get %top% %child%
1092} {%value%}
1093test oo-object-clay-method-mixin-%testnum% {Test mixin object gets the property %top%/%child%} {
1094  $%object2% clay get %top% %child%
1095} {%value%}
1096}
1097    if {$top eq "const/"} {
1098      putb result $map {
1099test oo-object-clay-method-native-methodcheck-%testnum% {Test that %top%/%child% would mimic method interheritance for a native class} {
1100  $%object1% %child%
1101} {%value%}
1102test oo-object-clay-method-mixin-%testnum% {Test that %top%/%child% would mimic method interheritance for a mixed in class} {
1103  $%object2% %child%
1104} {%value%}
1105    }
1106    }
1107  }
1108}
1109
1110putb result {# -------------------------------------------------------------------------
1111# OBJECT descended from ::foo::classb ::foo::classa
1112set OBJECTBA [::foo::class.ba new]
1113# Object where classes were mixed in ::foo::classb ::foo::classa
1114set MIXINBA  [::oo::object new]
1115# Test modified 2018-10-30, mixin order was wrong before
1116oo::objdefine $MIXINBA mixin ::foo::classa ::foo::classb
1117}
1118set matrix ${claydict-a}
1119foreach {top children} ${claydict-b} {
1120  foreach {child value} $children {
1121    if {![dict exists $matrix $top $child]} {
1122      dict set matrix $top $child $value
1123    }
1124  }
1125}
1126#set testnum 0
1127foreach {top children} $matrix {
1128  foreach {child value} $children {
1129    set map {}
1130    dict set map %object1% OBJECTBA
1131    dict set map %object2% MIXINBA
1132
1133    dict set map %top% $top
1134    dict set map %child% $child
1135    dict set map %value% $value
1136    dict set map %testnum% [format %04d [incr testnum]]
1137    putb result $map {
1138test oo-object-clay-method-native-%testnum% {Test native object gets the property} {
1139  $%object1% clay get %top% %child%
1140} {%value%}
1141test oo-object-clay-method-mixin-%testnum% {Test mixin object gets the property} {
1142  $%object2% clay get %top% %child%
1143} {%value%}
1144}
1145
1146    if {$top eq "const/"} {
1147      putb result $map {
1148test oo-object-clay-method-native-methodcheck-%testnum% {Test that %top%/%child% would mimic method interheritance for a native class} {
1149  $%object1% %child%
1150} {%value%}
1151test oo-object-clay-method-mixin-%testnum% {Test that %top%/%child% would mimic method interheritance for a mixed in class} {
1152  $%object2% %child%
1153} {%value%}
1154    }
1155    }
1156  }
1157}
1158
1159putb resut {
1160###
1161# Test local setting if clay data in an object
1162###
1163set OBJECT [::foo::classa new]
1164test oo-object-clay-method-local-0001 {Test native object gets the property} {
1165  $OBJECT clay get const/ color
1166} {blue}
1167test oo-object-clay-method-local-0002 {Test that local settings override the inherited properties} {
1168  $OBJECT clay set const/ color black
1169  $OBJECT clay set const/
1170} {black}
1171
1172test oo-object-clay-method-local-0003 {Test native object gets an empty property} {
1173  $OBJECT clay get color
1174} {}
1175test oo-object-clay-method-local-0004 {Test that local settings override the empty property} {
1176  $OBJECT clay set color orange
1177  $OBJECT clay get color
1178} {orange}
1179
1180}
1181
1182putb result {
1183###
1184# put a do-nothing constructor on the books
1185###
1186::clay::define ::clay::object {
1187  constructor args {}
1188}
1189
1190oo::objdefine ::clay::object method foo args { return bar }
1191
1192test clay-core-method-0001 {Test that adding methods to the core ::clay::object class works} {
1193  ::clay::object foo
1194} {bar}
1195
1196namespace eval ::TEST {}
1197::clay::define ::TEST::myclass {
1198  clay color red
1199  clay flavor strawberry
1200
1201}
1202
1203###
1204# Test adding a clay property
1205###
1206test clay-class-clay-0001 {Test that a clay statement is recorded in the object of the class} {
1207  ::TEST::myclass clay get color
1208} red
1209test clay-class-clay-0002 {Test that a clay statement is recorded in the object of the class} {
1210  ::TEST::myclass clay get flavor
1211} strawberry
1212
1213###
1214# Test that objects of the class get the same properties
1215###
1216set OBJ [::clay::object new {}]
1217set OBJ2 [::TEST::myclass new {}]
1218
1219test clay-object-clay-a-0001 {Test that objects not thee class do not get properties} {
1220  $OBJ clay get color
1221} {}
1222test clay-object-clay-a-0002 {Test that objects not thee class do not get properties} {
1223  $OBJ clay get flavor
1224} {}
1225test clay-object-clay-a-0003 {Test that objects of the class get properties} {
1226  $OBJ2 clay get color
1227} red
1228test clay-object-clay-a-0004 {Test that objects of the class get properties} {
1229  $OBJ2 clay get flavor
1230} strawberry
1231
1232###
1233# Test modified 2018-10-21
1234###
1235test clay-object-clay-a-0005 {Test the clay ancestors function} {
1236  $OBJ clay ancestors
1237} {::clay::object}
1238
1239###
1240# Test modified 2018-10-21
1241###
1242test clay-object-clay-a-0006 {Test the clay ancestors function} {
1243  $OBJ2 clay ancestors
1244} {::TEST::myclass ::clay::object}
1245
1246test clay-object-clay-a-0007 {Test the clay provenance  function} {
1247  $OBJ2 clay provenance  flavor
1248} ::TEST::myclass
1249
1250###
1251# Test that object local setting override the class
1252###
1253test clay-object-clay-a-0008 {Test that object local setting override the class} {
1254  $OBJ2 clay set color purple
1255  $OBJ2 clay get color
1256} purple
1257test clay-object-clay-a-0009 {Test that object local setting override the class} {
1258  $OBJ2 clay provenance  color
1259} self
1260
1261::clay::define ::TEST::myclasse {
1262  superclass ::TEST::myclass
1263
1264  clay color blue
1265  method do args {
1266    return "I did $args"
1267  }
1268
1269  Ensemble which::color {} {
1270    return [my clay get color]
1271  }
1272  clay set method_ensemble which farbe: {tailcall my Which_color {*}$args}
1273}
1274
1275###
1276# Test clay information is passed town to subclasses
1277###
1278test clay-class-clay-0003 {Test that a clay statement is recorded in the object of the class} {
1279  ::TEST::myclasse clay get color
1280} blue
1281test clay-class-clay-0004 {Test that clay statements from the ancestors of this class are not present (we handle them seperately in objects)} {
1282  ::TEST::myclasse clay get flavor
1283} {}
1284test clay-class-clay-0005 {Test that clay statements from the ancestors of this class are found with the FIND method} {
1285  ::TEST::myclasse clay find flavor
1286} {strawberry}
1287
1288###
1289# Test that properties reach objects
1290###
1291set OBJ3 [::TEST::myclasse new {}]
1292test clay-object-clay-b-0001 {Test that objects of the class get properties} {
1293  $OBJ3 clay get color
1294} blue
1295test clay-object-clay-b-0002 {Test the clay provenance  function} {
1296  $OBJ3 clay provenance  color
1297} ::TEST::myclasse
1298test clay-object-clay-b-0003 {Test that objects of the class get properties} {
1299  $OBJ3 clay get flavor
1300} strawberry
1301test clay-object-clay-b-0004 {Test the clay provenance  function} {
1302  $OBJ3 clay provenance  flavor
1303} ::TEST::myclass
1304
1305###
1306# Test modified 2018-10-21
1307###
1308test clay-object-clay-b-0005 {Test the clay provenance  function} {
1309  $OBJ3 clay ancestors
1310} {::TEST::myclasse ::TEST::myclass ::clay::object}
1311
1312###
1313# Test defining a standard method
1314###
1315test clay-object-method-0001 {Test and standard method} {
1316  $OBJ3 do this really cool thing
1317} {I did this really cool thing}
1318
1319test clay-object-method-0003 {Test an ensemble} {
1320  $OBJ3 which color
1321} blue
1322# Test setting properties
1323test clay-object-method-0004 {Test an ensemble} {
1324  $OBJ3 clay set color black
1325  $OBJ3 which color
1326} black
1327
1328# Test setting properties
1329test clay-object-method-0004 {Test an ensemble alias} {
1330  $OBJ3 which farbe
1331} black
1332
1333
1334###
1335# Added 2019-06-24
1336# Test that grabbing a leaf does not pollute the cache
1337###
1338::clay::define ::TEST::class_with_deep_tree {
1339  clay set tree deep has depth 1
1340  clay set tree shallow has depth 0
1341}
1342
1343$OBJ3 clay mixinmap deep ::TEST::class_with_deep_tree
1344
1345test clay-deep-nested-0001 {Test that a leaf query does not pollute the cache} {
1346  $OBJ3 clay get tree shallow has depth
1347} 0
1348test clay-deep-nested-0001 {Test that a leaf query does not pollute the cache} {
1349  $OBJ3 clay get tree
1350} {deep {has {depth 1}} shallow {has {depth 0}}}
1351
1352
1353
1354###
1355# Test that if you try to replace a global command you get an error
1356###
1357test clay-nspace-0001 {Test that if you try to replace a global command you get an error} -body {
1358::clay::define open {
1359  method bar {} { return foo }
1360
1361}
1362}  -returnCodes {error} -result "::open does not refer to an object"
1363
1364::clay::define fubar {
1365  method bar {} { return foo }
1366}
1367test clay-nspace-0002 {Test a non qualified class ends up in the current namespace} {
1368  info commands ::fubar
1369} {::fubar}
1370
1371namespace eval ::cluster {
1372::clay::define fubar {
1373  method bar {} { return foo }
1374}
1375
1376::clay::define ::clay::pot {
1377  method bar {} { return foo }
1378}
1379
1380}
1381test clay-nspace-0003 {Test a non qualified class ends up in the current namespace} {
1382  info commands ::cluster::fubar
1383} {::cluster::fubar}
1384test clay-nspace-0003 {Test a fully qualified class ends up in the proper namespace} {
1385  info commands ::clay::pot
1386} {::clay::pot}
1387
1388#set ::clay::trace 3
1389
1390###
1391# New test - Added 2019-09-15
1392# Test that the "method" variable is exposed to a default method
1393###
1394
1395::clay::define ::ensembleWithDefault {
1396  Ensemble foo::bar {} { return A }
1397  Ensemble foo::baz {} { return B }
1398  Ensemble foo::bang {} { return C }
1399
1400  Ensemble foo::default {} { return $method }
1401}
1402
1403
1404set OBJ [::ensembleWithDefault new]
1405test clay-ensemble-default-0001 {Test a normal ensemble method} {
1406  $OBJ foo bar
1407} {A}
1408test clay-ensemble-default-0002 {Test a normal ensemble method} {
1409  $OBJ foo baz
1410} {B}
1411test clay-ensemble-default-0003 {Test a normal ensemble method} {
1412  $OBJ foo <list>
1413} [lsort -dictionary {bar baz bang}]
1414
1415test clay-ensemble-default-0004 {Test a normal ensemble method} {
1416  $OBJ foo bing
1417} {bing}
1418test clay-ensemble-default-0005 {Test a normal ensemble method} {
1419  $OBJ foo bong
1420} {bong}
1421###
1422# Mixin tests
1423###
1424
1425###
1426# Define a core class
1427###
1428::clay::define ::TEST::thing {
1429
1430  method do args {
1431    return "I did $args"
1432  }
1433}
1434
1435
1436::clay::define ::TEST::vegetable {
1437
1438  clay color unknown
1439  clay flavor unknown
1440
1441  Ensemble which::flavor {} {
1442    return [my clay get flavor]
1443  }
1444  Ensemble which::color {} {
1445    return [my clay get color]
1446  }
1447
1448}
1449
1450::clay::define ::TEST::animal {
1451
1452  clay color unknown
1453  clay sound unknown
1454
1455  Ensemble which::sound {} {
1456    return [my clay get sound]
1457  }
1458  Ensemble which::color {} {
1459    return [my clay get color]
1460  }
1461  method sound {} {
1462    return unknown
1463  }
1464}
1465
1466::clay::define ::TEST::species.cat {
1467  superclass ::TEST::animal
1468  clay sound meow
1469  method sound {} {
1470    return meow
1471  }
1472}
1473
1474::clay::define ::TEST::coloring.calico {
1475  clay color calico
1476
1477}
1478
1479::clay::define ::TEST::condition.dark {
1480  Ensemble which::color {} {
1481    return grey
1482  }
1483}
1484
1485::clay::define ::TEST::mood.happy {
1486  Ensemble which::sound {} {
1487    return purr
1488  }
1489  method sound {} {
1490    return purr
1491  }
1492}
1493test clay-object-0001 {Test than an object is created when clay::define is invoked} {
1494  info commands ::TEST::mood.happy
1495} ::TEST::mood.happy
1496
1497set OBJ [::TEST::thing new]
1498test clay-mixin-a-0001 {Test that prior to a mixin an ensemble doesn't exist} -body {
1499  $OBJ which color
1500} -returnCodes error -result {unknown method "which": must be clay, destroy or do}
1501
1502test clay-mixin-a-0002 {Test and standard method from an ancestor} {
1503  $OBJ do this really cool thing
1504} {I did this really cool thing}
1505
1506$OBJ clay mixinmap species ::TEST::animal
1507test clay-mixin-b-0001 {Test that an ensemble is created during a mixin} {
1508  $OBJ which color
1509} {unknown}
1510
1511test clay-mixin-b-0002 {Test that an ensemble is created during a mixin} {
1512  $OBJ which sound
1513} {unknown}
1514
1515test clay-mixin-b-0003 {Test that an ensemble is created during a mixin} \
1516  -body {$OBJ which flavor} -returnCodes {error} \
1517  -result {unknown method which flavor. Valid: color sound}
1518
1519###
1520# Test Modified: 2018-10-21
1521###
1522test clay-mixin-b-0004 {Test that mixins resolve in the correct order} {
1523  $OBJ clay ancestors
1524} {::TEST::animal ::TEST::thing ::clay::object}
1525
1526###
1527# Replacing a mixin replaces the behaviors
1528###
1529$OBJ clay mixinmap species ::TEST::vegetable
1530test clay-mixin-c-0001 {Test that an ensemble is created during a mixin} {
1531  $OBJ which color
1532} {unknown}
1533test clay-mixin-c-0002 {Test that an ensemble is created during a mixin} \
1534  -body {$OBJ which sound} \
1535  -returnCodes {error} \
1536  -result {unknown method which sound. Valid: color flavor}
1537test clay-mixin-c-0003 {Test that an ensemble is created during a mixin} {
1538  $OBJ which flavor
1539} {unknown}
1540###
1541# Test Modified: 2018-10-21
1542###
1543test clay-mixin-c-0004 {Test that mixins resolve in the correct order} {
1544  $OBJ clay ancestors
1545} {::TEST::vegetable ::TEST::thing ::clay::object}
1546
1547###
1548# Replacing a mixin
1549$OBJ clay mixinmap species ::TEST::species.cat
1550test clay-mixin-e-0001 {Test that an ensemble is created during a mixin} {
1551  $OBJ which color
1552} {unknown}
1553test clay-mixin-e-0002a {Test that an ensemble is created during a mixin} {
1554  $OBJ sound
1555} {meow}
1556test clay-mixin-e-0002b {Test that an ensemble is created during a mixin} {
1557  $OBJ clay get sound
1558} {meow}
1559test clay-mixin-e-0002 {Test that an ensemble is created during a mixin} {
1560  $OBJ which sound
1561} {meow}
1562test clay-mixin-e-0003 {Test that an ensemble is created during a mixin} \
1563  -body {$OBJ which flavor} -returnCodes {error} \
1564  -result {unknown method which flavor. Valid: color sound}
1565###
1566# Test Modified: 2018-10-30, 2018-10-21, 2018-10-10
1567###
1568test clay-mixin-e-0004 {Test that clay data follows the rules of inheritence and order of mixin} {
1569  $OBJ clay ancestors
1570} {::TEST::species.cat ::TEST::animal ::TEST::thing ::clay::object}
1571
1572$OBJ clay mixinmap coloring ::TEST::coloring.calico
1573test clay-mixin-f-0001 {Test that an ensemble is created during a mixin} {
1574  $OBJ which color
1575} {calico}
1576test clay-mixin-f-0002 {Test that an ensemble is created during a mixin} {
1577  $OBJ which sound
1578} {meow}
1579test clay-mixin-f-0003 {Test that an ensemble is created during a mixin} \
1580  -body {$OBJ which flavor} -returnCodes {error} \
1581  -result {unknown method which flavor. Valid: color sound}
1582
1583###
1584# Test modified 2018-10-30, 2018-10-21, 2018-10-10
1585###
1586test clay-mixin-f-0004 {Test that clay data follows the rules of inheritence and order of mixin} {
1587  $OBJ clay ancestors
1588} {::TEST::coloring.calico ::TEST::species.cat ::TEST::animal ::TEST::thing ::clay::object}
1589
1590test clay-mixin-f-0005 {Test that clay data from a mixin works} {
1591  $OBJ clay provenance  color
1592} {::TEST::coloring.calico}
1593
1594###
1595# Test variable initialization
1596###
1597::clay::define ::TEST::has_var {
1598  Variable my_variable 10
1599
1600  method get_my_variable {} {
1601    my variable my_variable
1602    return $my_variable
1603  }
1604}
1605
1606set OBJ [::TEST::has_var new]
1607test clay-class-variable-0001 {Test that the parser injected the right value in the right place for clay to catch it} {
1608  $OBJ clay get variable/ my_variable
1609} {10}
1610
1611# Modified 2018-10-30 (order is different)
1612test clay-class-variable-0002 {Test that the parser injected the right value in the right place for clay to catch it} {
1613  $OBJ clay get variable
1614} {my_variable 10 DestroyEvent 0}
1615
1616# Modified 2018-10-30 (order is different)
1617test clay-class-variable-0003 {Test that the parser injected the right value in the right place for clay to catch it} {
1618  $OBJ clay dget variable
1619} {. {} my_variable 10 DestroyEvent 0}
1620
1621test clay-class-variable-0004 {Test that variables declared in the class definition are initialized} {
1622  $OBJ get_my_variable
1623} 10
1624
1625###
1626# Test array initialization
1627###
1628::clay::define ::TEST::has_array {
1629  Array my_array {timeout 10}
1630
1631  method get_my_array {field} {
1632    my variable my_array
1633    return $my_array($field)
1634  }
1635}
1636
1637set OBJ [::TEST::has_array new]
1638test clay-class-array-0001 {Test that the parser injected the right value in the right place for clay to catch it} {
1639  $OBJ clay get array
1640} {my_array {timeout 10}}
1641
1642test clay-class-array-0002 {Test that the parser injected the right value in the right place for clay to catch it} {
1643  $OBJ clay dget array
1644} {. {} my_array {. {} timeout 10}}
1645
1646test clay-class-array-0003 {Test that variables declared in the class definition are initialized} {
1647  $OBJ get_my_array timeout
1648} 10
1649
1650::clay::define ::TEST::has_more_array {
1651  superclass ::TEST::has_array
1652  Array my_array {color blue}
1653}
1654test clay-class-array-0008 {Test that the parser injected the right value in the right place for clay to catch it} {
1655  ::TEST::has_more_array clay get array
1656} {my_array {color blue}}
1657
1658test clay-class-array-0009 {Test that the parser injected the right value in the right place for clay to catch it} {
1659  ::TEST::has_more_array clay find array
1660} {my_array {timeout 10 color blue}}
1661
1662# Modified 2018-10-30 (order is different)
1663set BOBJ [::TEST::has_more_array new]
1664test clay-class-array-0004 {Test that the parser injected the right value in the right place for clay to catch it} {
1665  $BOBJ clay get array
1666} {my_array {color blue timeout 10}}
1667
1668# Modified 2018-10-30 (order is different)
1669test clay-class-array-0005 {Test that the parser injected the right value in the right place for clay to catch it} {
1670  $BOBJ clay dget array
1671} {. {} my_array {. {} color blue timeout 10}}
1672
1673test clay-class-arrau-0006 {Test that variables declared in the class definition are initialized} {
1674  $BOBJ get_my_array timeout
1675} 10
1676test clay-class-arrau-0007 {Test that variables declared in the class definition are initialized} {
1677  $BOBJ get_my_array color
1678} blue
1679
1680::clay::define ::TEST::has_empty_array {
1681  Array my_array {}
1682
1683  method my_array_exists {} {
1684    my variable my_array
1685    return [info exists my_array]
1686  }
1687  method get {field} {
1688    my variable my_array
1689    return $my_array($field)
1690  }
1691  method set {field value} {
1692    my variable my_array
1693    set my_array($field) $value
1694  }
1695}
1696
1697test clay-class-array-0008 {Test that an declaration of an array with no values produces and empty array} {
1698  set COBJ [::TEST::has_empty_array new]
1699  $COBJ my_array_exists
1700} 1
1701
1702test clay-class-array-0009 {Test that an declaration of an array with no values produces and empty array} {
1703  $COBJ set test "A random value"
1704  $COBJ get test
1705} {A random value}
1706###
1707# Test dict initialization
1708###
1709::clay::define ::TEST::has_dict {
1710  Dict my_dict {timeout 10}
1711
1712  method get_my_dict {args} {
1713    my variable my_dict
1714    if {[llength $args]==0} {
1715      return $my_dict
1716    }
1717    return [dict get $my_dict {*}$args]
1718  }
1719
1720}
1721
1722set OBJ [::TEST::has_dict new]
1723test clay-class-dict-0001 {Test that the parser injected the right value in the right place for clay to catch it} {
1724  $OBJ clay get dict
1725} {my_dict {timeout 10}}
1726
1727test clay-class-dict-0002 {Test that the parser injected the right value in the right place for clay to catch it} {
1728  $OBJ clay dget dict
1729} {. {} my_dict {. {} timeout 10}}
1730
1731test clay-class-dict-0003 {Test that variables declared in the class definition are initialized} {
1732  $OBJ get_my_dict timeout
1733} 10
1734
1735test clay-class-dict-0004 {Test that an empty dict is annotated} {
1736  $OBJ clay get dict
1737} {my_dict {timeout 10}}
1738
1739
1740::clay::define ::TEST::has_more_dict {
1741  superclass ::TEST::has_dict
1742  Dict my_dict {color blue}
1743}
1744set BOBJ [::TEST::has_more_dict new]
1745
1746# Modified 2018-10-30
1747test clay-class-dict-0004 {Test that the parser injected the right value in the right place for clay to catch it} {
1748  $BOBJ clay get dict
1749} {my_dict {color blue timeout 10}}
1750
1751# Modified 2018-10-30
1752test clay-class-dict-0005 {Test that the parser injected the right value in the right place for clay to catch it} {
1753  $BOBJ clay dget dict
1754} {. {} my_dict {. {} color blue timeout 10}}
1755
1756test clay-class-dict-0006 {Test that variables declared in the class definition are initialized} {
1757  $BOBJ get_my_dict timeout
1758} 10
1759
1760test clay-class-dict-0007 {Test that variables declared in the class definition are initialized} {
1761  $BOBJ get_my_dict color
1762} blue
1763
1764::clay::define ::TEST::has_empty_dict {
1765  Dict my_empty_dict {}
1766
1767  method get_my_empty_dict {args} {
1768    my variable my_empty_dict
1769    if {[llength $args]==0} {
1770      return $my_empty_dict
1771    }
1772    return [dict get $my_empty_dict {*}$args]
1773  }
1774}
1775
1776set COBJ [::TEST::has_empty_dict new]
1777
1778test clay-class-dict-0008 {Test that the parser injected the right value in the right place for clay to catch it} {
1779  $COBJ clay dget dict
1780} {my_empty_dict {. {}}}
1781
1782test clay-class-dict-0009 {Test that an empty dict is initialized} {
1783  $COBJ get_my_empty_dict
1784} {}
1785
1786###
1787# Test object delegation
1788###
1789::clay::define ::TEST::organelle {
1790  method add args {
1791    set total 0
1792    foreach item $args {
1793      set total [expr {$total+$item}]
1794    }
1795    return $total
1796  }
1797}
1798::clay::define ::TEST::master {
1799  constructor {} {
1800    set mysub [namespace current]::sub
1801    ::TEST::organelle create $mysub
1802    my clay delegate sub $mysub
1803  }
1804}
1805
1806set OBJ [::TEST::master new]
1807###
1808# Test that delegation is working
1809###
1810test clay-delegation-0001 {Test an array driven ensemble} {
1811  $OBJ <sub> add 5 5
1812} 10
1813
1814
1815###
1816# Test the Ensemble keyword
1817###
1818::clay::define ::TEST::with_ensemble {
1819
1820  Ensemble myensemble {pattern args} {
1821    set ensemble [self method]
1822    set emap [my clay ensemble_map $ensemble]
1823    set mlist [dict keys $emap [string tolower $pattern]]
1824    if {[llength $mlist] != 1} {
1825      error "Couldn't figure out what to do with $pattern"
1826    }
1827    set method [lindex $mlist 0]
1828    set argspec [dict get $emap $method argspec]
1829    set body    [dict get $emap $method body]
1830    if {$argspec ni {args {}}} {
1831      ::clay::dynamic_arguments $ensemble $method [list $argspec] {*}$args
1832    }
1833    eval $body
1834  }
1835
1836  Ensemble myensemble::go args {
1837    return 1
1838  }
1839}
1840
1841::clay::define ::TEST::with_ensemble.dance {
1842  Ensemble myensemble::dance args {
1843    return 1
1844  }
1845}
1846::clay::define ::TEST::with_ensemble.cannot_dance {
1847  Ensemble myensemble::dance args {
1848    return 0
1849  }
1850}
1851
1852set OBJA [::clay::object new]
1853set OBJB [::clay::object new]
1854
1855$OBJA clay mixinmap \
1856  core ::TEST::with_ensemble \
1857  friends ::TEST::with_ensemble.dance
1858
1859$OBJB clay mixinmap \
1860  core ::TEST::with_ensemble \
1861  friends ::TEST::with_ensemble.cannot_dance
1862}
1863
1864set testnum 0
1865
1866set matrix {
1867  go {
1868    OBJA 1
1869    OBJB 1
1870  }
1871  dance {
1872    OBJA 1
1873    OBJB 0
1874  }
1875}
1876foreach {action output} $matrix {
1877  putb result "# Test $action"
1878  foreach {object value} $output {
1879    set map [dict create %object% $object %action% $action %value% $value]
1880    dict set map %testnum% [format %04d [incr testnum]]
1881    putb result $map {test clay-dynamic-ensemble-%testnum% {Test ensemble with static method} {
1882  $%object% myensemble %action%
1883} {%value%}}
1884  }
1885}
1886
1887putb result {
1888
1889###
1890# Class method testing
1891###
1892
1893clay::class create WidgetClass {
1894  Class_Method working {} {
1895    return {Works}
1896  }
1897
1898  Class_Method unknown args {
1899    set tkpath [lindex $args 0]
1900    if {[string index $tkpath 0] eq "."} {
1901      set obj [my new $tkpath {*}[lrange $args 1 end]]
1902      $obj tkalias $tkpath
1903      return $tkpath
1904    }
1905    next {*}$args
1906  }
1907
1908  constructor {TkPath args} {
1909    my variable hull
1910    set hull $TkPath
1911    my clay delegate hull $TkPath
1912  }
1913
1914  method tkalias tkname {
1915    set oldname $tkname
1916    my variable tkalias
1917    set tkalias $tkname
1918    set self [self]
1919    set hullwidget [::info object namespace $self]::tkwidget
1920    my clay delegate tkwidget $hullwidget
1921    #rename ::$tkalias $hullwidget
1922    my clay delegate hullwidget $hullwidget
1923    #::tool::object_rename [self] ::$tkalias
1924    rename [self] ::$tkalias
1925    #my Hull_Bind $tkname
1926    return $hullwidget
1927  }
1928}
1929
1930test tool-class-method-000 {Test that class methods actually work...} {
1931  WidgetClass working
1932} {Works}
1933
1934test tool-class-method-001 {Test Tk style creator} {
1935  WidgetClass .foo
1936  .foo clay delegate hull
1937} {.foo}
1938
1939::clay::define WidgetNewClass {
1940  superclass WidgetClass
1941}
1942
1943test tool-class-method-002 {Test Tk style creator inherited by morph} {
1944  WidgetNewClass .bar
1945  .bar clay delegate hull
1946} {.bar}
1947
1948
1949
1950###
1951# Test ensemble inheritence
1952###
1953clay::define NestedClassA {
1954  Ensemble do::family {} {
1955    return NestedClassA
1956  }
1957  Ensemble do::something {} {
1958    return A
1959  }
1960  Ensemble do::whop {} {
1961    return A
1962  }
1963}
1964clay::define NestedClassB {
1965  superclass NestedClassA
1966  Ensemble do::family {} {
1967    set r [next family]
1968    lappend r NestedClassB
1969    return $r
1970  }
1971  Ensemble do::whop {} {
1972    return B
1973  }
1974}
1975clay::define NestedClassC {
1976  superclass NestedClassB
1977
1978  Ensemble do::somethingelse {} {
1979    return C
1980  }
1981}
1982clay::define NestedClassD {
1983  superclass NestedClassB
1984
1985  Ensemble do::somethingelse {} {
1986    return D
1987  }
1988}
1989
1990clay::define NestedClassE {
1991  superclass NestedClassD NestedClassC
1992}
1993
1994clay::define NestedClassF {
1995  superclass NestedClassC NestedClassD
1996}
1997
1998NestedClassC create NestedObjectC
1999
2000###
2001# These tests no longer work because method ensembles are now dynamically
2002# generated by object, that are not attached to the class anymore
2003#
2004####
2005#test tool-ensemble-001 {Test that an ensemble can access [next] even if no object of the ancestor class have been instantiated} {
2006#  NestedObjectC do family
2007#} {::NestedClassA ::NestedClassB ::NestedClassC}
2008
2009test tool-ensemble-002 {Test that a later ensemble definition trumps a more primitive one} {
2010  NestedObjectC do whop
2011} {B}
2012test tool-ensemble-003 {Test that an ensemble definitions in an ancestor carry over} {
2013  NestedObjectC do something
2014} {A}
2015
2016NestedClassE create NestedObjectE
2017NestedClassF create NestedObjectF
2018
2019
2020test tool-ensemble-004 {Test that ensembles follow the same rules for inheritance as methods} {
2021  NestedObjectE do somethingelse
2022} {D}
2023
2024test tool-ensemble-005 {Test that ensembles follow the same rules for inheritance as methods} {
2025  NestedObjectF do somethingelse
2026} {C}
2027
2028###
2029# Set of tests to exercise the mixinmap system
2030###
2031clay::define MixinMainClass {
2032  Variable mainvar unchanged
2033
2034  Ensemble test::which {} {
2035    my variable mainvar
2036    return $mainvar
2037  }
2038
2039  Ensemble test::main args {
2040    puts [list this is main $method $args]
2041  }
2042
2043}
2044
2045set mixoutscript {my test untool $class}
2046set mixinscript {my test tool $class}
2047clay::define MixinTool {
2048  Variable toolvar unchanged.mixin
2049  clay set mixin/ unmap-script $mixoutscript
2050  clay set mixin/ map-script $mixinscript
2051  clay set mixin/ name {Generic Tool}
2052
2053  Ensemble test::untool class {
2054    my variable toolvar mainvar
2055    set mainvar {}
2056    set toolvar {}
2057  }
2058
2059  Ensemble test::tool class {
2060    my variable toolvar mainvar
2061    set mainvar [$class clay get mixin name]
2062    set toolvar [$class clay get mixin name]
2063  }
2064}
2065
2066clay::define MixinToolA {
2067  superclass MixinTool
2068
2069  clay set mixin/ name {Tool A}
2070}
2071
2072clay::define MixinToolB {
2073  superclass MixinTool
2074
2075  clay set mixin/ name {Tool B}
2076
2077  method test_newfunc {} {
2078    return "B"
2079  }
2080}
2081
2082test tool-mixinspec-001 {Test application of mixin specs} {
2083  MixinTool clay get mixin map-script
2084} $mixinscript
2085
2086test tool-mixinspec-002 {Test application of mixin specs} {
2087  MixinToolA clay get mixin map-script
2088} {}
2089
2090test tool-mixinspec-003 {Test application of mixin specs} {
2091  MixinToolA clay find mixin map-script
2092} $mixinscript
2093
2094test tool-mixinspec-004 {Test application of mixin specs} {
2095  MixinToolB clay find mixin map-script
2096} $mixinscript
2097
2098
2099MixinMainClass create mixintest
2100
2101test tool-mixinmap-001 {Test object prior to mixins} {
2102  mixintest test which
2103} {unchanged}
2104
2105mixintest clay mixinmap tool MixinToolA
2106test tool-mixinmap-002 {Test mixin map script ran} {
2107  mixintest test which
2108} {Tool A}
2109
2110mixintest clay mixinmap tool MixinToolB
2111
2112test tool-mixinmap-003 {Test mixin map script ran} {
2113  mixintest test which
2114} {Tool B}
2115
2116test tool-mixinmap-003 {Test mixin map script ran} {
2117  mixintest test_newfunc
2118} {B}
2119
2120mixintest clay mixinmap tool {}
2121test tool-mixinmap-004 {Test object prior to mixins} {
2122  mixintest test which
2123} {}
2124}
2125
2126###
2127# Test clay mixinslots
2128###
2129putb result {
2130
2131clay::define ::clay::object {
2132  method path {} {
2133    return [self class]
2134  }
2135}
2136
2137
2138clay::define ::MixinRoot {
2139  clay set opts core   root
2140  clay set opts option unset
2141  clay set opts color  unset
2142
2143  Ensemble info::root {} {
2144    return MixinRoot
2145  }
2146  Ensemble info::shade {} {
2147    return avacodo
2148  }
2149  Ensemble info::default {} {
2150    return Undefined
2151  }
2152
2153  method did {} {
2154    return MixinRoot
2155  }
2156
2157  method path {} {
2158    return [list [self class] {*}[next]]
2159  }
2160}
2161
2162clay::define ::MixinOption1 {
2163  clay set opts option option1
2164
2165  Ensemble info::option {} {
2166    return MixinOption1
2167  }
2168  Ensemble info::other {} {
2169    return MixinOption1
2170  }
2171
2172  method did {} {
2173    return MixinOption1
2174  }
2175
2176  method path {} {
2177    return [list [self class] {*}[next]]
2178  }
2179}
2180
2181clay::define ::MixinOption2 {
2182  superclass ::MixinOption1
2183
2184  clay set opts option option2
2185
2186  Ensemble info::option {} {
2187    return MixinOption2
2188  }
2189
2190  method did {} {
2191    return MixinOption2
2192  }
2193
2194  method path {} {
2195    return [list [self class] {*}[next]]
2196  }
2197}
2198
2199
2200clay::define ::MixinColor1 {
2201  clay set opts color blue
2202
2203  Ensemble info::color {} {
2204    return MixinColor1
2205  }
2206  Ensemble info::shade {} {
2207    return blue
2208  }
2209
2210  method did {} {
2211    return MixinColor1
2212  }
2213
2214  method path {} {
2215    return [list [self class] {*}[next]]
2216  }
2217}
2218
2219clay::define ::MixinColor2 {
2220  clay set opts color green
2221
2222  Ensemble info::color {} {
2223    return MixinColor2
2224  }
2225  Ensemble info::shade {} {
2226    return green
2227  }
2228
2229  method did {} {
2230    return MixinColor2
2231  }
2232
2233  method path {} {
2234    return [list [self class] {*}[next]]
2235  }
2236}
2237
2238set obj [clay::object new]
2239
2240$obj clay mixinmap root ::MixinRoot
2241}
2242set testnum 0
2243set batnum  0
2244
2245set obj {$obj}
2246set template {
2247test tool-prototype-%battery%-%test% {%comment%} {
2248  %obj% %method%
2249} {%answer%}
2250}
2251set map {}
2252
2253dict set map %obj% {$obj}
2254dict set map %battery% [format %04d [incr batnum]]
2255dict set map %comment% {Mixin core}
2256
2257foreach {method answer} {
2258  {info root} {MixinRoot}
2259  {info option} {Undefined}
2260  {info color} {Undefined}
2261  {info other} {Undefined}
2262  {info shade} {avacodo}
2263  {did} {MixinRoot}
2264  {path} {::MixinRoot ::clay::object}
2265  {clay get opts} {core root option unset color unset}
2266  {clay get opts core} root
2267  {clay get opts option} unset
2268  {clay get opts color} unset
2269  {clay ancestors} {::MixinRoot ::clay::object}
2270} {
2271  set testid [format %04d [incr testnum]]
2272  dict set map %test% $testid
2273  dict set map %method% $method
2274  dict set map %answer% $answer
2275  putb result $map $template
2276}
2277
2278set testnum 0
2279putb result {$obj clay mixinmap option ::MixinOption1}
2280dict set map %battery% [format %04d [incr batnum]]
2281dict set map %comment% {Mixin option1}
2282foreach {method answer} {
2283  {info root} {MixinRoot}
2284  {info option} {MixinOption1}
2285  {info color} {Undefined}
2286  {info other} {MixinOption1}
2287  {info shade} {avacodo}
2288  {did} {MixinOption1}
2289  {path} {::MixinOption1 ::MixinRoot ::clay::object}
2290  {clay get opts} {option option1 core root color unset}
2291  {clay get opts core} root
2292  {clay get opts option} option1
2293  {clay get opts color} unset
2294  {clay ancestors} {::MixinOption1 ::MixinRoot ::clay::object}
2295} {
2296  set testid [format %04d [incr testnum]]
2297  dict set map %test% $testid
2298  dict set map %method% $method
2299  dict set map %answer% $answer
2300  putb result $map $template
2301}
2302
2303set testnum 0
2304putb result {
2305set obj2 [clay::object new]
2306$obj2 clay mixinmap root ::MixinRoot option ::MixinOption1
2307}
2308putb result {$obj clay mixinmap option ::MixinOption1}
2309dict set map %obj% {$obj2}
2310dict set map %battery% [format %04d [incr batnum]]
2311dict set map %comment% {Mixin option1 - clean object}
2312foreach {method answer} {
2313  {info root} {MixinRoot}
2314  {info option} {MixinOption1}
2315  {info color} {Undefined}
2316  {info other} {MixinOption1}
2317  {info shade} {avacodo}
2318  {did} {MixinOption1}
2319  {path} {::MixinOption1 ::MixinRoot ::clay::object}
2320  {clay get opts} {option option1 core root color unset}
2321  {clay get opts core} root
2322  {clay get opts option} option1
2323  {clay get opts color} unset
2324  {clay ancestors} {::MixinOption1 ::MixinRoot ::clay::object}
2325} {
2326  set testid [format %04d [incr testnum]]
2327  dict set map %test% $testid
2328  dict set map %method% $method
2329  dict set map %answer% $answer
2330  putb result $map $template
2331}
2332
2333set testnum 0
2334putb result {$obj clay mixinmap option ::MixinOption2}
2335dict set map %battery% [format %04d [incr batnum]]
2336dict set map %comment% {Mixin option2}
2337dict set map %obj% {$obj}
2338foreach {method answer} {
2339  {info root} {MixinRoot}
2340  {info option} {MixinOption2}
2341  {info color} {Undefined}
2342  {info other} {MixinOption1}
2343  {info shade} {avacodo}
2344  {did} {MixinOption2}
2345  {path} {::MixinOption2 ::MixinOption1 ::MixinRoot ::clay::object}
2346  {clay get opts} {option option2 core root color unset}
2347  {clay get opts core} root
2348  {clay get opts option} option2
2349  {clay get opts color} unset
2350  {clay ancestors} {::MixinOption2 ::MixinOption1 ::MixinRoot ::clay::object}
2351} {
2352  set testid [format %04d [incr testnum]]
2353  dict set map %test% $testid
2354  dict set map %method% $method
2355  dict set map %answer% $answer
2356  putb result $map $template
2357}
2358
2359set testnum 0
2360putb result {$obj clay mixinmap color MixinColor1}
2361dict set map %battery% [format %04d [incr batnum]]
2362dict set map %comment% {Mixin color1}
2363foreach {method answer} {
2364  {info root} {MixinRoot}
2365  {info option} {MixinOption2}
2366  {info color} {MixinColor1}
2367  {info other} {MixinOption1}
2368  {info shade} {blue}
2369  {did} {MixinColor1}
2370  {path} {::MixinColor1 ::MixinOption2 ::MixinOption1 ::MixinRoot ::clay::object}
2371  {clay get opts} {color blue option option2 core root}
2372  {clay get opts core} root
2373  {clay get opts option} option2
2374  {clay get opts color} blue
2375  {clay ancestors} {::MixinColor1 ::MixinOption2 ::MixinOption1 ::MixinRoot ::clay::object}
2376} {
2377  set testid [format %04d [incr testnum]]
2378  dict set map %test% $testid
2379  dict set map %method% $method
2380  dict set map %answer% $answer
2381  putb result $map $template
2382}
2383set testnum 0
2384putb result {$obj clay mixinmap color MixinColor2}
2385dict set map %battery% [format %04d [incr batnum]]
2386dict set map %comment% {Mixin color2}
2387foreach {method answer} {
2388  {info root} {MixinRoot}
2389  {info option} {MixinOption2}
2390  {info color} {MixinColor2}
2391  {info other} {MixinOption1}
2392  {info shade} {green}
2393  {clay get opts} {color green option option2 core root}
2394  {clay get opts core} root
2395  {clay get opts option} option2
2396  {clay get opts color} green
2397  {clay ancestors} {::MixinColor2 ::MixinOption2 ::MixinOption1 ::MixinRoot ::clay::object}
2398} {
2399  set testid [format %04d [incr testnum]]
2400  dict set map %test% $testid
2401  dict set map %method% $method
2402  dict set map %answer% $answer
2403  putb result $map $template
2404}
2405
2406set testnum 0
2407putb result {$obj clay mixinmap option MixinOption1}
2408dict set map %battery% [format %04d [incr batnum]]
2409dict set map %comment% {Mixin color2 + Option1}
2410foreach {method answer} {
2411  {info root} {MixinRoot}
2412  {info option} {MixinOption1}
2413  {info color} {MixinColor2}
2414  {info other} {MixinOption1}
2415  {info shade} {green}
2416  {clay get opts} {color green option option1 core root}
2417  {clay get opts core} root
2418  {clay get opts option} option1
2419  {clay get opts color} green
2420  {clay ancestors} {::MixinColor2 ::MixinOption1 ::MixinRoot ::clay::object}
2421} {
2422  set testid [format %04d [incr testnum]]
2423  dict set map %test% $testid
2424  dict set map %method% $method
2425  dict set map %answer% $answer
2426  putb result $map $template
2427}
2428
2429set testnum 0
2430putb result {$obj clay mixinmap option {}}
2431dict set map %battery% [format %04d [incr batnum]]
2432dict set map %comment% {Mixin color2 + no option}
2433foreach {method answer} {
2434  {info root} {MixinRoot}
2435  {info option} {Undefined}
2436  {info color} {MixinColor2}
2437  {info other} {Undefined}
2438  {info shade} {green}
2439  {clay get opts} {color green core root option unset}
2440  {clay get opts core} root
2441  {clay get opts option} unset
2442  {clay get opts color} green
2443  {clay ancestors} {::MixinColor2 ::MixinRoot ::clay::object}
2444} {
2445  set testid [format %04d [incr testnum]]
2446  dict set map %test% $testid
2447  dict set map %method% $method
2448  dict set map %answer% $answer
2449  putb result $map $template
2450}
2451
2452set testnum 0
2453putb result {$obj clay mixinmap color {}}
2454dict set map %battery% [format %04d [incr batnum]]
2455dict set map %comment% {Mixin core (return to normal)}
2456foreach {method answer} {
2457  {info root} {MixinRoot}
2458  {info option} {Undefined}
2459  {info color} {Undefined}
2460  {info other} {Undefined}
2461  {info shade} {avacodo}
2462  {clay get opts} {core root option unset color unset}
2463  {clay get opts core} root
2464  {clay get opts option} unset
2465  {clay get opts color} unset
2466  {clay ancestors} {::MixinRoot ::clay::object}
2467} {
2468  set testid [format %04d [incr testnum]]
2469  dict set map %test% $testid
2470  dict set map %method% $method
2471  dict set map %answer% $answer
2472  putb result $map $template
2473}
2474
2475putb result {
2476###
2477# Tip479 Tests
2478###
2479clay::define tip479class {
2480
2481  Method newitem dictargs {
2482    id {type: number}
2483    color {default: green}
2484    shape {options: {round square}}
2485    flavor {default: grape}
2486  } {
2487    my variable items
2488    foreach {f v} $args {
2489      dict set items $id $f $v
2490    }
2491    if {"color" ni [dict keys $args]} {
2492      dict set items $id color $color
2493    }
2494    return [dict get $items $id]
2495  }
2496
2497  method itemget {id field} {
2498    my variable items
2499    return [dict get $id $field]
2500  }
2501}
2502
2503set obj [tip479class new]
2504test tip479-001 {Test that a later ensemble definition trumps a more primitive one} {
2505  $obj newitem id 1 color orange shape round
2506} {id 1 color orange shape round}
2507
2508# Fail because we left off a mandatory argument
2509test tip479-002 {Test that a later ensemble definition trumps a more primitive one} \
2510  -errorCode NONE -body {
2511  $obj newitem id 2
2512} -result {shape is required}
2513
2514###
2515# Leave off a value that has a default
2516# note: Method had special handling for color, but not flavor
2517###
2518test tip479-003 {Test that a later ensemble definition trumps a more primitive one} {
2519  $obj newitem id 3 shape round
2520} {id 3 shape round color green}
2521
2522###
2523# Add extra arguments
2524###
2525test tip479-004 {Test that a later ensemble definition trumps a more primitive one} {
2526  $obj newitem id 4 shape round trim leather
2527} {id 4 shape round trim leather color green}
2528
2529clay::define tip479classE {
2530
2531  Ensemble item::new dictargs {
2532    id {type: number}
2533    color {default: green}
2534    shape {options: {round square}}
2535    flavor {default: grape}
2536  } {
2537    my variable items
2538    foreach {f v} $args {
2539      dict set items $id $f $v
2540    }
2541    if {"color" ni [dict keys $args]} {
2542      dict set items $id color $color
2543    }
2544    return [dict get $items $id]
2545  }
2546
2547  Ensemble item::get {id field} {
2548    my variable items
2549    return [dict get $id $field]
2550  }
2551}
2552
2553
2554set obj [tip479classE new]
2555test tip479-001 {Test that a later ensemble definition trumps a more primitive one} {
2556  $obj item new id 1 color orange shape round
2557} {id 1 color orange shape round}
2558
2559# Fail because we left off a mandatory argument
2560test tip479-002 {Test that a later ensemble definition trumps a more primitive one} \
2561  -errorCode NONE -body {
2562  $obj item new id 2
2563} -result {shape is required}
2564
2565###
2566# Leave off a value that has a default
2567# note: Method had special handling for color, but not flavor
2568###
2569test tip479-003 {Test that a later ensemble definition trumps a more primitive one} {
2570  $obj item new id 3 shape round
2571} {id 3 shape round color green}
2572
2573###
2574# Add extra arguments
2575###
2576test tip479-004 {Test that a later ensemble definition trumps a more primitive one} {
2577  $obj item new id 4 shape round trim leather
2578} {id 4 shape round trim leather color green}
2579
2580}
2581
2582###
2583# TESTS NEEDED:
2584# destructor
2585###
2586
2587putb result {
2588testsuiteCleanup
2589
2590# Local variables:
2591# mode: tcl
2592# indent-tabs-mode: nil
2593# End:
2594}
2595return $result
2596