summaryrefslogtreecommitdiff
path: root/itcl/itcl/tests/old/inherit.test
blob: 2e3f0a2c13497d17728d821a876f952d1abe9706 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#
# Tests for inheritance and scope handling
# ----------------------------------------------------------------------
#   AUTHOR:  Michael J. McLennan
#            Bell Labs Innovations for Lucent Technologies
#            mmclennan@lucent.com
#            http://www.tcltk.com/itcl
#
#      RCS:  $Id$
# ----------------------------------------------------------------------
#            Copyright (c) 1993-1998  Lucent Technologies, Inc.
# ======================================================================
# See the file "license.terms" for information on usage and
# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.

# ----------------------------------------------------------------------
#  MULTIPLE BASE-CLASS ERROR DETECTION
# ----------------------------------------------------------------------
test {Cannot inherit from the same base class more than once} {
	catch "VirtualErr" errmsg
	set errmsg
} {
	[string match {*class "::VirtualErr" inherits base class "::Foo" more than once:
  VirtualErr->Mongrel->FooBar->Foo
  VirtualErr->Foo
  VirtualErr->BarFoo->Foo} $result]
}

# ----------------------------------------------------------------------
#  CONSTRUCTION
# ----------------------------------------------------------------------
test {Constructors should be invoked implicitly} {
	set WATCH ""
	concat [Mongrel m] / $WATCH
} {
	$result == "m / ::Geek ::Bar ::Foo ::FooBar ::Mongrel"
}

test {Initialization of shadowed variables works properly} {
	concat [m info public blit -value] / [m info public Foo::blit -value]
} {
	$result == "nonnull / <undefined>"
}

# ----------------------------------------------------------------------
#  PUBLIC VARIABLES
# ----------------------------------------------------------------------
test {Inherited "config" method works on derived classes} {
	m config -blit xyz -Foo::blit pdq
} {
	$result == "Mongrel::blit Foo::blit"
}

test {Inherited "config" method works on derived classes} {
	m config -blit xyz -Foo::blit pdq
	concat [m info public blit -value] / [m info public Foo::blit -value]
} {
	$result == "xyz / pdq"
}

test {Inherited "config" method works on derived classes} {
	m config -tag #0000
} {
	$result == "Mongrel::tag"
}

# ----------------------------------------------------------------------
#  INHERITANCE INFO
# ----------------------------------------------------------------------
test {Info: class} {
	m info class
} {
	$result == "::Mongrel"
}

test {Info: inherit} {
	m info inherit
} {
	$result == "::FooBar ::Geek"
}

test {Info: heritage} {
	m info heritage
} {
	$result == "::Mongrel ::FooBar ::Foo ::Bar ::Geek"
}

test {Built-in "isa" method} {
	set status 1
	foreach c [m info heritage] {
		set status [expr {$status && [m isa $c]}]
	}
	set status
} {
	$result == 1
}

test {Built-in "isa" method} {
    itcl_class Watermelon {}
	m isa Watermelon
} {
	$result == 0
}

# ----------------------------------------------------------------------
#  SCOPE MANIPULATION
# ----------------------------------------------------------------------
test {commands normally execute in the scope of their class} {
	m Foo::do {namespace current}
} {
	$result == "Foo says '::Foo'"
}

test {"virtual" command moves scope to most specific class} {
	m Foo::do {virtual namespace current}
} {
	$result == "Foo says '::Mongrel'"
}

test {"previous" command moves scope upward in hierarchy} {
	m do {virtual previous namespace current}
} {
	$result == "Foo says '::FooBar'"
}

test {"previous" command can be chained} {
	m do {virtual previous previous namespace current}
} {
	$result == "Foo says '::Foo'"
}

# ----------------------------------------------------------------------
#  METHOD INVOCATION
# ----------------------------------------------------------------------
test {Simple method names are assigned based on heritage} {
	m do {concat "$this ([virtual info class]) at scope [namespace current]"}
} {
	$result == "Foo says '::m (Mongrel) at scope ::Foo'"
}

test {Explicit scoping can be used to reach shadowed members} {
	m Geek::do {concat "$this ([virtual info class]) at scope [namespace current]"}
} {
	$result == "Geek says '::m (Mongrel) at scope ::Geek'"
}

test {Methods execute in local scope of class, e.g., Foo::do} {
	m config -blit abc -Foo::blit def
	m Foo::do {set blit xyz}
	concat [m info public blit -value] / [m info public Foo::blit -value]
} {
	$result == "abc / xyz"
}

# ----------------------------------------------------------------------
#  DESTRUCTION
# ----------------------------------------------------------------------
test {Destructors should be invoked implicitly} {
	set WATCH ""
	concat [m delete] / $WATCH
} {
	$result == "/ ::Mongrel ::FooBar ::Foo ::Bar ::Geek"
}

# ----------------------------------------------------------------------
#  OBJECT INFO
# ----------------------------------------------------------------------
foreach obj [itcl_info objects] {
	$obj delete
}
Mongrel m
FooBar fb
Foo f
Geek g

test {Object queries can be restricted by object name} {
	itcl_info objects f*
} {
	[test_cmp_lists $result {f fb}]
}

test {Object queries can be restricted to specific classes} {
	itcl_info objects -class Foo
} {
	$result == "f"
}

test {Object queries can be restricted by object heritage} {
	itcl_info objects -isa Foo
} {
	[test_cmp_lists $result {m f fb}]
}

test {Object queries can be restricted by object name / specific classes} {
	itcl_info objects f* -class Foo
} {
	$result == "f"
}

test {Object queries can be restricted by object name / object heritage} {
	itcl_info objects f* -isa Foo
} {
	[test_cmp_lists $result {f fb}]
}

# ----------------------------------------------------------------------
#  ERROR HANDLING ACROSS CLASS BOUNDARIES
# ----------------------------------------------------------------------
Mongrel m1
FooBar fb2

test {Errors and detected and reported across class boundaries} {
	set status [catch {m1 do {fb2 do {error "test"}}} mesg]
	format "$mesg $status"
} {
	$result == "test 1"
}

test {Stack trace unwinds properly across class boundaries} {
	catch {m1 do {fb2 do {error "test"}}} mesg
	format "$errorInfo"
} {
	$result == {test
    while executing
"error "test""
    ("eval" body line 1)
    invoked from within
"eval $cmds"
    invoked from within
"return "Foo says '[eval $cmds]..."
    (object "::fb2" method "::Foo::do" body line 2)
    invoked from within
"fb2 do {error "test"}"
    ("eval" body line 1)
    invoked from within
"eval $cmds"
    invoked from within
"return "Foo says '[eval $cmds]..."
    (object "::m1" method "::Foo::do" body line 2)
    invoked from within
"m1 do {fb2 do {error "test"}}"}
}

test {Stack trace unwinds properly across class boundaries} {
	catch {m1 do {fb2 do {error "test" "some error"}}} mesg
	format "$errorInfo"
} {
	$result == {some error
    ("eval" body line 1)
    invoked from within
"eval $cmds"
    invoked from within
"return "Foo says '[eval $cmds]..."
    (object "::fb2" method "::Foo::do" body line 2)
    invoked from within
"fb2 do {error "test" "some error"}"
    ("eval" body line 1)
    invoked from within
"eval $cmds"
    invoked from within
"return "Foo says '[eval $cmds]..."
    (object "::m1" method "::Foo::do" body line 2)
    invoked from within
"m1 do {fb2 do {error "test" "some error"}}"}
}

test {Error codes are preserved across class boundaries} {
	catch {m1 do {fb2 do {error "test" "some error" CODE-BLUE}}} mesg
	format "$errorCode"
} {
	$result == "CODE-BLUE"
}