2929namespace eval tk {
3030 namespace eval test {
3131
32+ # auto_ns_vars --
33+ #
34+ # Each new namespace automatically holds several variables upvar'ed
35+ # from the global namespace. Notably:
36+ #
37+ # argc argv argv0 auto_index auto_path env tcl_interactive tcl_library \
38+ # tcl_patchLevel tcl_pkgPath tcl_platform tcl_rcFileName tcl_version
39+ #
40+ # proc testutils (see below) needs to know about them to keep track of
41+ # newly created variables.
42+ #
43+ variable auto_ns_vars [namespace eval tmp {info vars}]
44+ namespace delete tmp
45+
3246 proc assert {expr } {
3347 if {! [uplevel 1 [list expr $expr ]]} {
3448 return -code error " assertion failed: \" [ uplevel 1 [list subst -nocommands $expr ] ]\" "
@@ -192,6 +206,86 @@ namespace eval tk {
192206 unset _pause($num )
193207 }
194208
209+ # testutils --
210+ #
211+ # Takes care of importing/forgetting utility procs with any associated
212+ # variables from a specific test domain (functional area). It hides
213+ # details/peculiarities from the test writer.
214+ #
215+ # The "import" subcmd invokes any proc "init" defined in the doamin-
216+ # specific namespace. See also the explanation of this mehanism below
217+ # the header for the section "DEFINITIONS OF UTILITY PROCS PER
218+ # FUNCTIONAL AREA" in this file.
219+ #
220+ # Arguments:
221+ # subCmd : "import" or "forget"
222+ # args : a sequence of domains that need to be imported/forgotten,
223+ # optionally preceded by the option -nocommands or -novars.
224+ #
225+ proc testutils {subCmd args} {
226+ variable importedVars
227+
228+ set usage " [ lindex [info level 0] 0] import|forget ?-nocommands|-novars? domain ?domain domain ...?"
229+ set argc [llength $args ]
230+ if {$argc < 1} {
231+ return -code error $usage
232+ }
233+
234+ set option [lindex $args 0]
235+ if {$option ni " -nocommands -novars" } {
236+ set option {}
237+ }
238+ if {($subCmd ni " import forget" ) || (($option ne " " ) && ($argc < 2))} {
239+ return -code error $usage
240+ }
241+ if {($subCmd eq " forget" ) && ($option ne " " )} {
242+ return -code error " options \" -nocommands\" and \" -novars\" are not valid with subCmd \" forget\" "
243+ }
244+
245+ set domains [expr {$option eq " " ?$args :[lrange $args 1 end]}]
246+ foreach domain $domains {
247+ if {! [namespace exists ::tk::test::$domain ]} {
248+ return -code error " Tk test domain \" $domain \" doesn't exist"
249+ }
250+ switch -- $subCmd {
251+ import {
252+ if {$domain ni [array names importedVars]} {
253+ if {$option ne " -nocommands" } {
254+ uplevel 1 [list namespace import -force ::tk::test::${domain} ::*]
255+ set importedVars($domain ) [list ]
256+ }
257+ if {$option ne " -novars" } {
258+ variable auto_ns_vars
259+ if {[namespace inscope ::tk::test::$domain {info procs init}] eq " init" } {
260+ ::tk::test::${domain} ::init
261+ }
262+ foreach varName [namespace inscope ::tk::test::$domain {info vars}] {
263+ if {$varName ni $auto_ns_vars } {
264+ uplevel 1 [list upvar #0 ::tk::test::${domain} ::$varName $varName ]
265+ lappend importedVars($domain ) $varName
266+ }
267+ }
268+ }
269+ } else {
270+ if {[namespace inscope ::tk::test::$domain {info procs init}] eq " init" } {
271+ ::tk::test::${domain} ::init
272+ }
273+ }
274+ }
275+ forget {
276+ if {! [info exists importedVars($domain )]} {
277+ return -code error " domain \" $domain \" was not imported"
278+ }
279+ uplevel 1 [list namespace forget ::tk::test::${domain} ::*]
280+ foreach varName $importedVars($domain) {
281+ uplevel 1 unset -nocomplain $varName
282+ }
283+ unset importedVars($domain )
284+ }
285+ }
286+ }
287+ }
288+
195289 namespace export *
196290 }
197291}
@@ -203,6 +297,40 @@ namespace import -force tk::test::*
203297# DEFINITIONS OF UTILITY PROCS PER FUNCTIONAL AREA
204298#
205299
300+ #
301+ # INIT PROCS, IMPORTING UTILITY PROCS AND ASSOCIATED NAMESPACE VARIABLES,
302+ # AND AUTO-INITIALIZATION
303+ #
304+ # Some utility procs from specific functional areas store state in a namespace
305+ # variable that is also accessed from the namespace in which the tests are
306+ # executed (the "executing namespace"). Some tests require such variables
307+ # to be initialized.
308+ #
309+ # When such variables are imported into the "executing namespace" through
310+ # an "upvar" command, and the test file unsets these variables as part of a
311+ # cleanup operation, this results in the deletion of the target variable
312+ # inside the specific domain namespace. This, in turn, poses a problem for
313+ # the next test file, which presumes that the variable is initialized.
314+ #
315+ # The proc "testutils" deals with this upvar issue as follows:
316+ #
317+ # If a namespace for a specific functional area holds a proc "init", the
318+ # "testutils import xxx" will invoke it to carry out the initialization of
319+ # such namespace variables and subsequently imports them into the executing
320+ # namespace using "upvar" (import with auto-initialization).
321+ # Upon test file cleanup "testutils forget xxx" will remove the imported
322+ # utility procs with the associated namespace variables, and unset the upvar'ed
323+ # variable in both the source and target namespace, including their link. The
324+ # link and initialization will be recreated for the next namespace upon
325+ # "testutils import yyy".
326+ #
327+ # Test writers that create a new utility procs that use a namespace variable
328+ # that is also accessed by a test file, need to add the initialization
329+ # statements to the init proc. Just placing them inside the "namespace eval"
330+ # scope for the specific domain (outside the init proc) isn't enough because
331+ # that foregoes the importing of the namespace variables and their automatic
332+ # re-initialization.
333+ #
206334namespace eval ::tk::test::button {
207335 proc bogusTrace args {
208336 error " trace aborted"
@@ -388,6 +516,11 @@ namespace eval ::tk::test::colors {
388516
389517namespace eval ::tk::test::dialog {
390518
519+ proc init {} {
520+ variable dialogType none
521+ variable testDialog
522+ }
523+
391524 proc Click {button} {
392525 variable testDialog
393526 if {$button ni " ok cancel apply" } {
@@ -456,7 +589,6 @@ namespace eval ::tk::test::dialog {
456589 }
457590 }
458591
459- variable dialogType none
460592 proc setDialogType {type} {
461593 variable dialogType $type
462594 }
@@ -534,7 +666,8 @@ namespace eval ::tk::test::dialog {
534666
535667 ::tk::test::createStdAccessProc testDialogFont
536668
537- namespace export *
669+ namespace export Click PressButton SendButtonPress setDialogType testDialog \
670+ testDialogFont ToPressButton
538671}
539672
540673
@@ -599,7 +732,6 @@ namespace eval ::tk::test::geometry {
599732}
600733
601734namespace eval ::tk::test::image {
602- variable ImageNames
603735
604736 proc imageCleanup {} {
605737 variable ImageNames
@@ -774,10 +906,12 @@ namespace eval ::tk::test::select {
774906}
775907
776908namespace eval ::tk::test::text {
777- variable fixedFont {Courier -12}
778- variable fixedWidth [font measure $fixedFont m]
779- variable fixedHeight [font metrics $fixedFont -linespace]
780- variable fixedAscent [font metrics $fixedFont -ascent]
909+ proc init {} {
910+ variable fixedFont {Courier -12}
911+ variable fixedWidth [font measure $fixedFont m]
912+ variable fixedHeight [font metrics $fixedFont -linespace]
913+ variable fixedAscent [font metrics $fixedFont -ascent]
914+ }
781915
782916 # full border size of the text widget, i.e. first x or y coordinate inside the text widget
783917 # warning: -padx is supposed to be the same as -pady (same border size horizontally and
@@ -803,7 +937,7 @@ namespace eval ::tk::test::text {
803937 return [expr {[bo $w ] + ($l - 1) * $fixedHeight }]
804938 }
805939
806- namespace export *
940+ namespace export bo xchar xw yline
807941}
808942
809943# EOF
0 commit comments