source: proiecte/pmake3d/make3d_original/Make3dSingleImageStanford_version0.1/third_party/vrippack-0.31/lib/linux/tcl8.4/tcltest2.2/tcltest.tcl @ 37

Last change on this file since 37 was 37, checked in by (none), 14 years ago

Added original make3d

File size: 95.8 KB
Line 
1# tcltest.tcl --
2#
3#       This file contains support code for the Tcl test suite.  It
4#       defines the tcltest namespace and finds and defines the output
5#       directory, constraints available, output and error channels,
6#       etc. used by Tcl tests.  See the tcltest man page for more
7#       details.
8#
9#       This design was based on the Tcl testing approach designed and
10#       initially implemented by Mary Ann May-Pumphrey of Sun
11#       Microsystems.
12#
13# Copyright (c) 1994-1997 Sun Microsystems, Inc.
14# Copyright (c) 1998-1999 by Scriptics Corporation.
15# Copyright (c) 2000 by Ajuba Solutions
16# Contributions from Don Porter, NIST, 2002.  (not subject to US copyright)
17# All rights reserved.
18#
19# RCS: @(#) $Id: tcltest.tcl,v 1.78.2.10 2004/05/26 16:24:37 dgp Exp $
20
21package require Tcl 8.3         ;# uses [glob -directory]
22namespace eval tcltest {
23
24    # When the version number changes, be sure to update the pkgIndex.tcl file,
25    # and the install directory in the Makefiles.  When the minor version
26    # changes (new feature) be sure to update the man page as well.
27    variable Version 2.2.6
28
29    # Compatibility support for dumb variables defined in tcltest 1
30    # Do not use these.  Call [package provide Tcl] and [info patchlevel]
31    # yourself.  You don't need tcltest to wrap it for you.
32    variable version [package provide Tcl]
33    variable patchLevel [info patchlevel]
34
35##### Export the public tcltest procs; several categories
36    #
37    # Export the main functional commands that do useful things
38    namespace export cleanupTests loadTestedCommands makeDirectory \
39        makeFile removeDirectory removeFile runAllTests test
40
41    # Export configuration commands that control the functional commands
42    namespace export configure customMatch errorChannel interpreter \
43            outputChannel testConstraint
44
45    # Export commands that are duplication (candidates for deprecation)
46    namespace export bytestring         ;# dups [encoding convertfrom identity]
47    namespace export debug              ;#      [configure -debug]
48    namespace export errorFile          ;#      [configure -errfile]
49    namespace export limitConstraints   ;#      [configure -limitconstraints]
50    namespace export loadFile           ;#      [configure -loadfile]
51    namespace export loadScript         ;#      [configure -load]
52    namespace export match              ;#      [configure -match]
53    namespace export matchFiles         ;#      [configure -file]
54    namespace export matchDirectories   ;#      [configure -relateddir]
55    namespace export normalizeMsg       ;#      application of [customMatch]
56    namespace export normalizePath      ;#      [file normalize] (8.4)
57    namespace export outputFile         ;#      [configure -outfile]
58    namespace export preserveCore       ;#      [configure -preservecore]
59    namespace export singleProcess      ;#      [configure -singleproc]
60    namespace export skip               ;#      [configure -skip]
61    namespace export skipFiles          ;#      [configure -notfile]
62    namespace export skipDirectories    ;#      [configure -asidefromdir]
63    namespace export temporaryDirectory ;#      [configure -tmpdir]
64    namespace export testsDirectory     ;#      [configure -testdir]
65    namespace export verbose            ;#      [configure -verbose]
66    namespace export viewFile           ;#      binary encoding [read]
67    namespace export workingDirectory   ;#      [cd] [pwd]
68
69    # Export deprecated commands for tcltest 1 compatibility
70    namespace export getMatchingFiles mainThread restoreState saveState \
71            threadReap
72
73    # tcltest::normalizePath --
74    #
75    #     This procedure resolves any symlinks in the path thus creating
76    #     a path without internal redirection. It assumes that the
77    #     incoming path is absolute.
78    #
79    # Arguments
80    #     pathVar - name of variable containing path to modify.
81    #
82    # Results
83    #     The path is modified in place.
84    #
85    # Side Effects:
86    #     None.
87    #
88    proc normalizePath {pathVar} {
89        upvar $pathVar path
90        set oldpwd [pwd]
91        catch {cd $path}
92        set path [pwd]
93        cd $oldpwd
94        return $path
95    }
96
97##### Verification commands used to test values of variables and options
98    #
99    # Verification command that accepts everything
100    proc AcceptAll {value} {
101        return $value
102    }
103
104    # Verification command that accepts valid Tcl lists
105    proc AcceptList { list } {
106        return [lrange $list 0 end]
107    }
108
109    # Verification command that accepts a glob pattern
110    proc AcceptPattern { pattern } {
111        return [AcceptAll $pattern]
112    }
113
114    # Verification command that accepts integers
115    proc AcceptInteger { level } {
116        return [incr level 0]
117    }
118
119    # Verification command that accepts boolean values
120    proc AcceptBoolean { boolean } {
121        return [expr {$boolean && $boolean}]
122    }
123
124    # Verification command that accepts (syntactically) valid Tcl scripts
125    proc AcceptScript { script } {
126        if {![info complete $script]} {
127            return -code error "invalid Tcl script: $script"
128        }
129        return $script
130    }
131
132    # Verification command that accepts (converts to) absolute pathnames
133    proc AcceptAbsolutePath { path } {
134        return [file join [pwd] $path]
135    }
136
137    # Verification command that accepts existing readable directories
138    proc AcceptReadable { path } {
139        if {![file readable $path]} {
140            return -code error "\"$path\" is not readable"
141        }
142        return $path
143    }
144    proc AcceptDirectory { directory } {
145        set directory [AcceptAbsolutePath $directory]
146        if {![file exists $directory]} {
147            return -code error "\"$directory\" does not exist"
148        }
149        if {![file isdir $directory]} {
150            return -code error "\"$directory\" is not a directory"
151        }
152        return [AcceptReadable $directory]
153    }
154
155##### Initialize internal arrays of tcltest, but only if the caller
156    # has not already pre-initialized them.  This is done to support
157    # compatibility with older tests that directly access internals
158    # rather than go through command interfaces.
159    #
160    proc ArrayDefault {varName value} {
161        variable $varName
162        if {[array exists $varName]} {
163            return
164        }
165        if {[info exists $varName]} {
166            # Pre-initialized value is a scalar: destroy it!
167            unset $varName
168        }
169        array set $varName $value
170    }
171
172    # save the original environment so that it can be restored later
173    ArrayDefault originalEnv [array get ::env]
174
175    # initialize numTests array to keep track of the number of tests
176    # that pass, fail, and are skipped.
177    ArrayDefault numTests [list Total 0 Passed 0 Skipped 0 Failed 0]
178
179    # createdNewFiles will store test files as indices and the list of
180    # files (that should not have been) left behind by the test files
181    # as values.
182    ArrayDefault createdNewFiles {}
183
184    # initialize skippedBecause array to keep track of constraints that
185    # kept tests from running; a constraint name of "userSpecifiedSkip"
186    # means that the test appeared on the list of tests that matched the
187    # -skip value given to the flag; "userSpecifiedNonMatch" means that
188    # the test didn't match the argument given to the -match flag; both
189    # of these constraints are counted only if tcltest::debug is set to
190    # true.
191    ArrayDefault skippedBecause {}
192
193    # initialize the testConstraints array to keep track of valid
194    # predefined constraints (see the explanation for the
195    # InitConstraints proc for more details).
196    ArrayDefault testConstraints {}
197
198##### Initialize internal variables of tcltest, but only if the caller
199    # has not already pre-initialized them.  This is done to support
200    # compatibility with older tests that directly access internals
201    # rather than go through command interfaces.
202    #
203    proc Default {varName value {verify AcceptAll}} {
204        variable $varName
205        if {![info exists $varName]} {
206            variable $varName [$verify $value]
207        } else {
208            variable $varName [$verify [set $varName]]
209        }
210    }
211
212    # Save any arguments that we might want to pass through to other
213    # programs.  This is used by the -args flag.
214    # FINDUSER
215    Default parameters {}
216
217    # Count the number of files tested (0 if runAllTests wasn't called).
218    # runAllTests will set testSingleFile to false, so stats will
219    # not be printed until runAllTests calls the cleanupTests proc.
220    # The currentFailure var stores the boolean value of whether the
221    # current test file has had any failures.  The failFiles list
222    # stores the names of test files that had failures.
223    Default numTestFiles 0 AcceptInteger
224    Default testSingleFile true AcceptBoolean
225    Default currentFailure false AcceptBoolean
226    Default failFiles {} AcceptList
227
228    # Tests should remove all files they create.  The test suite will
229    # check the current working dir for files created by the tests.
230    # filesMade keeps track of such files created using the makeFile and
231    # makeDirectory procedures.  filesExisted stores the names of
232    # pre-existing files.
233    #
234    # Note that $filesExisted lists only those files that exist in
235    # the original [temporaryDirectory].
236    Default filesMade {} AcceptList
237    Default filesExisted {} AcceptList
238    proc FillFilesExisted {} {
239        variable filesExisted
240
241        # Save the names of files that already exist in the scratch directory.
242        foreach file [glob -nocomplain -directory [temporaryDirectory] *] {
243            lappend filesExisted [file tail $file]
244        }
245
246        # After successful filling, turn this into a no-op.
247        proc FillFilesExisted args {}
248    }
249
250    # Kept only for compatibility
251    Default constraintsSpecified {} AcceptList
252    trace variable constraintsSpecified r {set ::tcltest::constraintsSpecified \
253                [array names ::tcltest::testConstraints] ;# }
254
255    # tests that use threads need to know which is the main thread
256    Default mainThread 1
257    variable mainThread
258    if {[info commands thread::id] != {}} {
259        set mainThread [thread::id]
260    } elseif {[info commands testthread] != {}} {
261        set mainThread [testthread id]
262    }
263
264    # Set workingDirectory to [pwd]. The default output directory for
265    # Tcl tests is the working directory.  Whenever this value changes
266    # change to that directory.
267    variable workingDirectory
268    trace variable workingDirectory w \
269            [namespace code {cd $workingDirectory ;#}]
270
271    Default workingDirectory [pwd] AcceptAbsolutePath
272    proc workingDirectory { {dir ""} } {
273        variable workingDirectory
274        if {[llength [info level 0]] == 1} {
275            return $workingDirectory
276        }
277        set workingDirectory [AcceptAbsolutePath $dir]
278    }
279
280    # Set the location of the execuatble
281    Default tcltest [info nameofexecutable]
282    trace variable tcltest w [namespace code {testConstraint stdio \
283            [eval [ConstraintInitializer stdio]] ;#}]
284
285    # save the platform information so it can be restored later
286    Default originalTclPlatform [array get ::tcl_platform]
287
288    # If a core file exists, save its modification time.
289    if {[file exists [file join [workingDirectory] core]]} {
290        Default coreModTime \
291                [file mtime [file join [workingDirectory] core]]
292    }
293
294    # stdout and stderr buffers for use when we want to store them
295    Default outData {}
296    Default errData {}
297
298    # keep track of test level for nested test commands
299    variable testLevel 0
300
301    # the variables and procs that existed when saveState was called are
302    # stored in a variable of the same name
303    Default saveState {}
304
305    # Internationalization support -- used in [SetIso8859_1_Locale] and
306    # [RestoreLocale]. Those commands are used in cmdIL.test.
307
308    if {![info exists [namespace current]::isoLocale]} {
309        variable isoLocale fr
310        switch -- $::tcl_platform(platform) {
311            "unix" {
312
313                # Try some 'known' values for some platforms:
314
315                switch -exact -- $::tcl_platform(os) {
316                    "FreeBSD" {
317                        set isoLocale fr_FR.ISO_8859-1
318                    }
319                    HP-UX {
320                        set isoLocale fr_FR.iso88591
321                    }
322                    Linux -
323                    IRIX {
324                        set isoLocale fr
325                    }
326                    default {
327
328                        # Works on SunOS 4 and Solaris, and maybe
329                        # others...  Define it to something else on your
330                        # system if you want to test those.
331
332                        set isoLocale iso_8859_1
333                    }
334                }
335            }
336            "windows" {
337                set isoLocale French
338            }
339        }
340    }
341
342    variable ChannelsWeOpened; array set ChannelsWeOpened {}
343    # output goes to stdout by default
344    Default outputChannel stdout
345    proc outputChannel { {filename ""} } {
346        variable outputChannel
347        variable ChannelsWeOpened
348
349        # This is very subtle and tricky, so let me try to explain.
350        # (Hopefully this longer comment will be clear when I come
351        # back in a few months, unlike its predecessor :) )
352        #
353        # The [outputChannel] command (and underlying variable) have to
354        # be kept in sync with the [configure -outfile] configuration
355        # option ( and underlying variable Option(-outfile) ).  This is
356        # accomplished with a write trace on Option(-outfile) that will
357        # update [outputChannel] whenver a new value is written.  That
358        # much is easy.
359        #
360        # The trick is that in order to maintain compatibility with
361        # version 1 of tcltest, we must allow every configuration option
362        # to get its inital value from command line arguments.  This is
363        # accomplished by setting initial read traces on all the
364        # configuration options to parse the command line option the first
365        # time they are read.  These traces are cancelled whenever the
366        # program itself calls [configure].
367        #
368        # OK, then so to support tcltest 1 compatibility, it seems we want
369        # to get the return from [outputFile] to trigger the read traces,
370        # just in case.
371        #
372        # BUT!  A little known feature of Tcl variable traces is that
373        # traces are disabled during the handling of other traces.  So,
374        # if we trigger read traces on Option(-outfile) and that triggers
375        # command line parsing which turns around and sets an initial
376        # value for Option(-outfile) -- <whew!> -- the write trace that
377        # would keep [outputChannel] in sync with that new initial value
378        # would not fire!
379        #
380        # SO, finally, as a workaround, instead of triggering read traces
381        # by invoking [outputFile], we instead trigger the same set of
382        # read traces by invoking [debug].  Any command that reads a
383        # configuration option would do.  [debug] is just a handy one.
384        # The end result is that we support tcltest 1 compatibility and
385        # keep outputChannel and -outfile in sync in all cases.
386        debug
387
388        if {[llength [info level 0]] == 1} {
389            return $outputChannel
390        }
391        if {[info exists ChannelsWeOpened($outputChannel)]} {
392            close $outputChannel
393            unset ChannelsWeOpened($outputChannel)
394        }
395        switch -exact -- $filename {
396            stderr -
397            stdout {
398                set outputChannel $filename
399            }
400            default {
401                set outputChannel [open $filename a]
402                set ChannelsWeOpened($outputChannel) 1
403
404                # If we created the file in [temporaryDirectory], then
405                # [cleanupTests] will delete it, unless we claim it was
406                # already there.
407                set outdir [normalizePath [file dirname \
408                        [file join [pwd] $filename]]]
409                if {[string equal $outdir [temporaryDirectory]]} {
410                    variable filesExisted
411                    FillFilesExisted
412                    set filename [file tail $filename]
413                    if {[lsearch -exact $filesExisted $filename] == -1} {
414                        lappend filesExisted $filename
415                    }
416                }
417            }
418        }
419        return $outputChannel
420    }
421
422    # errors go to stderr by default
423    Default errorChannel stderr
424    proc errorChannel { {filename ""} } {
425        variable errorChannel
426        variable ChannelsWeOpened
427
428        # This is subtle and tricky.  See the comment above in
429        # [outputChannel] for a detailed explanation.
430        debug
431
432        if {[llength [info level 0]] == 1} {
433            return $errorChannel
434        }
435        if {[info exists ChannelsWeOpened($errorChannel)]} {
436            close $errorChannel
437            unset ChannelsWeOpened($errorChannel)
438        }
439        switch -exact -- $filename {
440            stderr -
441            stdout {
442                set errorChannel $filename
443            }
444            default {
445                set errorChannel [open $filename a]
446                set ChannelsWeOpened($errorChannel) 1
447
448                # If we created the file in [temporaryDirectory], then
449                # [cleanupTests] will delete it, unless we claim it was
450                # already there.
451                set outdir [normalizePath [file dirname \
452                        [file join [pwd] $filename]]]
453                if {[string equal $outdir [temporaryDirectory]]} {
454                    variable filesExisted
455                    FillFilesExisted
456                    set filename [file tail $filename]
457                    if {[lsearch -exact $filesExisted $filename] == -1} {
458                        lappend filesExisted $filename
459                    }
460                }
461            }
462        }
463        return $errorChannel
464    }
465
466##### Set up the configurable options
467    #
468    # The configurable options of the package
469    variable Option; array set Option {}
470
471    # Usage strings for those options
472    variable Usage; array set Usage {}
473
474    # Verification commands for those options
475    variable Verify; array set Verify {}
476
477    # Initialize the default values of the configurable options that are
478    # historically associated with an exported variable.  If that variable
479    # is already set, support compatibility by accepting its pre-set value.
480    # Use [trace] to establish ongoing connection between the deprecated
481    # exported variable and the modern option kept as a true internal var.
482    # Also set up usage string and value testing for the option.
483    proc Option {option value usage {verify AcceptAll} {varName {}}} {
484        variable Option
485        variable Verify
486        variable Usage
487        variable OptionControlledVariables
488        set Usage($option) $usage
489        set Verify($option) $verify
490        if {[catch {$verify $value} msg]} {
491            return -code error $msg
492        } else {
493            set Option($option) $msg
494        }
495        if {[string length $varName]} {
496            variable $varName
497            if {[info exists $varName]} {
498                if {[catch {$verify [set $varName]} msg]} {
499                    return -code error $msg
500                } else {
501                    set Option($option) $msg
502                }
503                unset $varName
504            }
505            namespace eval [namespace current] \
506                    [list upvar 0 Option($option) $varName]
507            # Workaround for Bug (now Feature Request) 572889.  Grrrr....
508            # Track all the variables tied to options
509            lappend OptionControlledVariables $varName
510            # Later, set auto-configure read traces on all
511            # of them, since a single trace on Option does not work.
512            proc $varName {{value {}}} [subst -nocommands {
513                if {[llength [info level 0]] == 2} {
514                    Configure $option [set value]
515                }
516                return [Configure $option]
517            }]
518        }
519    }
520
521    proc MatchingOption {option} {
522        variable Option
523        set match [array names Option $option*]
524        switch -- [llength $match] {
525            0 {
526                set sorted [lsort [array names Option]]
527                set values [join [lrange $sorted 0 end-1] ", "]
528                append values ", or [lindex $sorted end]"
529                return -code error "unknown option $option: should be\
530                        one of $values"
531            }
532            1 {
533                return [lindex $match 0]
534            }
535            default {
536                # Exact match trumps ambiguity
537                if {[lsearch -exact $match $option] >= 0} {
538                    return $option
539                }
540                set values [join [lrange $match 0 end-1] ", "]
541                append values ", or [lindex $match end]"
542                return -code error "ambiguous option $option:\
543                        could match $values"
544            }
545        }
546    }
547
548    proc EstablishAutoConfigureTraces {} {
549        variable OptionControlledVariables
550        foreach varName [concat $OptionControlledVariables Option] {
551            variable $varName
552            trace variable $varName r [namespace code {ProcessCmdLineArgs ;#}]
553        }
554    }
555
556    proc RemoveAutoConfigureTraces {} {
557        variable OptionControlledVariables
558        foreach varName [concat $OptionControlledVariables Option] {
559            variable $varName
560            foreach pair [trace vinfo $varName] {
561                foreach {op cmd} $pair break
562                if {[string equal r $op]
563                        && [string match *ProcessCmdLineArgs* $cmd]} {
564                    trace vdelete $varName $op $cmd
565                }
566            }
567        }
568        # Once the traces are removed, this can become a no-op
569        proc RemoveAutoConfigureTraces {} {}
570    }
571
572    proc Configure args {
573        variable Option
574        variable Verify
575        set n [llength $args]
576        if {$n == 0} {
577            return [lsort [array names Option]]
578        }
579        if {$n == 1} {
580            if {[catch {MatchingOption [lindex $args 0]} option]} {
581                return -code error $option
582            }
583            return $Option($option)
584        }
585        while {[llength $args] > 1} {
586            if {[catch {MatchingOption [lindex $args 0]} option]} {
587                return -code error $option
588            }
589            if {[catch {$Verify($option) [lindex $args 1]} value]} {
590                return -code error "invalid $option\
591                        value \"[lindex $args 1]\": $value"
592            }
593            set Option($option) $value
594            set args [lrange $args 2 end]
595        }
596        if {[llength $args]} {
597            if {[catch {MatchingOption [lindex $args 0]} option]} {
598                return -code error $option
599            }
600            return -code error "missing value for option $option"
601        }
602    }
603    proc configure args {
604        RemoveAutoConfigureTraces
605        set code [catch {eval Configure $args} msg]
606        return -code $code $msg
607    }
608   
609    proc AcceptVerbose { level } {
610        set level [AcceptList $level]
611        if {[llength $level] == 1} {
612            if {![regexp {^(pass|body|skip|start|error)$} $level]} {
613                # translate single characters abbreviations to expanded list
614                set level [string map {p pass b body s skip t start e error} \
615                        [split $level {}]]
616            }
617        }
618        set valid [list]
619        foreach v $level {
620            if {[regexp {^(pass|body|skip|start|error)$} $v]} {
621                lappend valid $v
622            }
623        }
624        return $valid
625    }
626
627    proc IsVerbose {level} {
628        variable Option
629        return [expr {[lsearch -exact $Option(-verbose) $level] != -1}]
630    }
631
632    # Default verbosity is to show bodies of failed tests
633    Option -verbose {body error} {
634        Takes any combination of the values 'p', 's', 'b', 't' and 'e'.
635        Test suite will display all passed tests if 'p' is specified, all
636        skipped tests if 's' is specified, the bodies of failed tests if
637        'b' is specified, and when tests start if 't' is specified.
638        ErrorInfo is displayed if 'e' is specified.
639    } AcceptVerbose verbose
640
641    # Match and skip patterns default to the empty list, except for
642    # matchFiles, which defaults to all .test files in the
643    # testsDirectory and matchDirectories, which defaults to all
644    # directories.
645    Option -match * {
646        Run all tests within the specified files that match one of the
647        list of glob patterns given.
648    } AcceptList match
649
650    Option -skip {} {
651        Skip all tests within the specified tests (via -match) and files
652        that match one of the list of glob patterns given.
653    } AcceptList skip
654
655    Option -file *.test {
656        Run tests in all test files that match the glob pattern given.
657    } AcceptPattern matchFiles
658
659    # By default, skip files that appear to be SCCS lock files.
660    Option -notfile l.*.test {
661        Skip all test files that match the glob pattern given.
662    } AcceptPattern skipFiles
663
664    Option -relateddir * {
665        Run tests in directories that match the glob pattern given.
666    } AcceptPattern matchDirectories
667
668    Option -asidefromdir {} {
669        Skip tests in directories that match the glob pattern given.
670    } AcceptPattern skipDirectories
671
672    # By default, don't save core files
673    Option -preservecore 0 {
674        If 2, save any core files produced during testing in the directory
675        specified by -tmpdir. If 1, notify the user if core files are
676        created.
677    } AcceptInteger preserveCore
678
679    # debug output doesn't get printed by default; debug level 1 spits
680    # up only the tests that were skipped because they didn't match or
681    # were specifically skipped.  A debug level of 2 would spit up the
682    # tcltest variables and flags provided; a debug level of 3 causes
683    # some additional output regarding operations of the test harness.
684    # The tcltest package currently implements only up to debug level 3.
685    Option -debug 0 {
686        Internal debug level
687    } AcceptInteger debug
688
689    proc SetSelectedConstraints args {
690        variable Option
691        foreach c $Option(-constraints) {
692            testConstraint $c 1
693        }
694    }
695    Option -constraints {} {
696        Do not skip the listed constraints listed in -constraints.
697    } AcceptList
698    trace variable Option(-constraints) w \
699            [namespace code {SetSelectedConstraints ;#}]
700
701    # Don't run only the "-constraint" specified tests by default
702    proc ClearUnselectedConstraints args {
703        variable Option
704        variable testConstraints
705        if {!$Option(-limitconstraints)} {return}
706        foreach c [array names testConstraints] {
707            if {[lsearch -exact $Option(-constraints) $c] == -1} {
708                testConstraint $c 0
709            }
710        }
711    }
712    Option -limitconstraints false {
713        whether to run only tests with the constraints
714    } AcceptBoolean limitConstraints
715    trace variable Option(-limitconstraints) w \
716            [namespace code {ClearUnselectedConstraints ;#}]
717
718    # A test application has to know how to load the tested commands
719    # into the interpreter.
720    Option -load {} {
721        Specifies the script to load the tested commands.
722    } AcceptScript loadScript
723
724    # Default is to run each test file in a separate process
725    Option -singleproc 0 {
726        whether to run all tests in one process
727    } AcceptBoolean singleProcess
728
729    proc AcceptTemporaryDirectory { directory } {
730        set directory [AcceptAbsolutePath $directory]
731        if {![file exists $directory]} {
732            file mkdir $directory
733        }
734        set directory [AcceptDirectory $directory]
735        if {![file writable $directory]} {
736            if {[string equal [workingDirectory] $directory]} {
737                # Special exception: accept the default value
738                # even if the directory is not writable
739                return $directory
740            }
741            return -code error "\"$directory\" is not writeable"
742        }
743        return $directory
744    }
745
746    # Directory where files should be created
747    Option -tmpdir [workingDirectory] {
748        Save temporary files in the specified directory.
749    } AcceptTemporaryDirectory temporaryDirectory
750    trace variable Option(-tmpdir) w \
751            [namespace code {normalizePath Option(-tmpdir) ;#}]
752
753    # Tests should not rely on the current working directory.
754    # Files that are part of the test suite should be accessed relative
755    # to [testsDirectory]
756    Option -testdir [workingDirectory] {
757        Search tests in the specified directory.
758    } AcceptDirectory testsDirectory
759    trace variable Option(-testdir) w \
760            [namespace code {normalizePath Option(-testdir) ;#}]
761
762    proc AcceptLoadFile { file } {
763        if {[string equal "" $file]} {return $file}
764        set file [file join [temporaryDirectory] $file]
765        return [AcceptReadable $file]
766    }
767    proc ReadLoadScript {args} {
768        variable Option
769        if {[string equal "" $Option(-loadfile)]} {return}
770        set tmp [open $Option(-loadfile) r]
771        loadScript [read $tmp]
772        close $tmp
773    }
774    Option -loadfile {} {
775        Read the script to load the tested commands from the specified file.
776    } AcceptLoadFile loadFile
777    trace variable Option(-loadfile) w [namespace code ReadLoadScript]
778
779    proc AcceptOutFile { file } {
780        if {[string equal stderr $file]} {return $file}
781        if {[string equal stdout $file]} {return $file}
782        return [file join [temporaryDirectory] $file]
783    }
784
785    # output goes to stdout by default
786    Option -outfile stdout {
787        Send output from test runs to the specified file.
788    } AcceptOutFile outputFile
789    trace variable Option(-outfile) w \
790            [namespace code {outputChannel $Option(-outfile) ;#}]
791
792    # errors go to stderr by default
793    Option -errfile stderr {
794        Send errors from test runs to the specified file.
795    } AcceptOutFile errorFile
796    trace variable Option(-errfile) w \
797            [namespace code {errorChannel $Option(-errfile) ;#}]
798
799}
800
801#####################################################################
802
803# tcltest::Debug* --
804#
805#     Internal helper procedures to write out debug information
806#     dependent on the chosen level. A test shell may overide
807#     them, f.e. to redirect the output into a different
808#     channel, or even into a GUI.
809
810# tcltest::DebugPuts --
811#
812#     Prints the specified string if the current debug level is
813#     higher than the provided level argument.
814#
815# Arguments:
816#     level   The lowest debug level triggering the output
817#     string  The string to print out.
818#
819# Results:
820#     Prints the string. Nothing else is allowed.
821#
822# Side Effects:
823#     None.
824#
825
826proc tcltest::DebugPuts {level string} {
827    variable debug
828    if {$debug >= $level} {
829        puts $string
830    }
831    return
832}
833
834# tcltest::DebugPArray --
835#
836#     Prints the contents of the specified array if the current
837#       debug level is higher than the provided level argument
838#
839# Arguments:
840#     level           The lowest debug level triggering the output
841#     arrayvar        The name of the array to print out.
842#
843# Results:
844#     Prints the contents of the array. Nothing else is allowed.
845#
846# Side Effects:
847#     None.
848#
849
850proc tcltest::DebugPArray {level arrayvar} {
851    variable debug
852
853    if {$debug >= $level} {
854        catch {upvar  $arrayvar $arrayvar}
855        parray $arrayvar
856    }
857    return
858}
859
860# Define our own [parray] in ::tcltest that will inherit use of the [puts]
861# defined in ::tcltest.  NOTE: Ought to construct with [info args] and
862# [info default], but can't be bothered now.  If [parray] changes, then
863# this will need changing too.
864auto_load ::parray
865proc tcltest::parray {a {pattern *}} [info body ::parray]
866
867# tcltest::DebugDo --
868#
869#     Executes the script if the current debug level is greater than
870#       the provided level argument
871#
872# Arguments:
873#     level   The lowest debug level triggering the execution.
874#     script  The tcl script executed upon a debug level high enough.
875#
876# Results:
877#     Arbitrary side effects, dependent on the executed script.
878#
879# Side Effects:
880#     None.
881#
882
883proc tcltest::DebugDo {level script} {
884    variable debug
885
886    if {$debug >= $level} {
887        uplevel 1 $script
888    }
889    return
890}
891
892#####################################################################
893
894proc tcltest::Warn {msg} {
895    puts [outputChannel] "WARNING: $msg"
896}
897
898# tcltest::mainThread
899#
900#     Accessor command for tcltest variable mainThread.
901#
902proc tcltest::mainThread { {new ""} } {
903    variable mainThread
904    if {[llength [info level 0]] == 1} {
905        return $mainThread
906    }
907    set mainThread $new
908}
909
910# tcltest::testConstraint --
911#
912#       sets a test constraint to a value; to do multiple constraints,
913#       call this proc multiple times.  also returns the value of the
914#       named constraint if no value was supplied.
915#
916# Arguments:
917#       constraint - name of the constraint
918#       value - new value for constraint (should be boolean) - if not
919#               supplied, this is a query
920#
921# Results:
922#       content of tcltest::testConstraints($constraint)
923#
924# Side effects:
925#       none
926
927proc tcltest::testConstraint {constraint {value ""}} {
928    variable testConstraints
929    variable Option
930    DebugPuts 3 "entering testConstraint $constraint $value"
931    if {[llength [info level 0]] == 2} {
932        return $testConstraints($constraint)
933    }
934    # Check for boolean values
935    if {[catch {expr {$value && $value}} msg]} {
936        return -code error $msg
937    }
938    if {[limitConstraints] 
939            && [lsearch -exact $Option(-constraints) $constraint] == -1} {
940        set value 0
941    }
942    set testConstraints($constraint) $value
943}
944
945# tcltest::interpreter --
946#
947#       the interpreter name stored in tcltest::tcltest
948#
949# Arguments:
950#       executable name
951#
952# Results:
953#       content of tcltest::tcltest
954#
955# Side effects:
956#       None.
957
958proc tcltest::interpreter { {interp ""} } {
959    variable tcltest
960    if {[llength [info level 0]] == 1} {
961        return $tcltest
962    }
963    if {[string equal {} $interp]} {
964        set tcltest {}
965    } else {
966        set tcltest $interp
967    }
968}
969
970#####################################################################
971
972# tcltest::AddToSkippedBecause --
973#
974#       Increments the variable used to track how many tests were
975#       skipped because of a particular constraint.
976#
977# Arguments:
978#       constraint     The name of the constraint to be modified
979#
980# Results:
981#       Modifies tcltest::skippedBecause; sets the variable to 1 if
982#       didn't previously exist - otherwise, it just increments it.
983#
984# Side effects:
985#       None.
986
987proc tcltest::AddToSkippedBecause { constraint {value 1}} {
988    # add the constraint to the list of constraints that kept tests
989    # from running
990    variable skippedBecause
991
992    if {[info exists skippedBecause($constraint)]} {
993        incr skippedBecause($constraint) $value
994    } else {
995        set skippedBecause($constraint) $value
996    }
997    return
998}
999
1000# tcltest::PrintError --
1001#
1002#       Prints errors to tcltest::errorChannel and then flushes that
1003#       channel, making sure that all messages are < 80 characters per
1004#       line.
1005#
1006# Arguments:
1007#       errorMsg     String containing the error to be printed
1008#
1009# Results:
1010#       None.
1011#
1012# Side effects:
1013#       None.
1014
1015proc tcltest::PrintError {errorMsg} {
1016    set InitialMessage "Error:  "
1017    set InitialMsgLen  [string length $InitialMessage]
1018    puts -nonewline [errorChannel] $InitialMessage
1019
1020    # Keep track of where the end of the string is.
1021    set endingIndex [string length $errorMsg]
1022
1023    if {$endingIndex < (80 - $InitialMsgLen)} {
1024        puts [errorChannel] $errorMsg
1025    } else {
1026        # Print up to 80 characters on the first line, including the
1027        # InitialMessage.
1028        set beginningIndex [string last " " [string range $errorMsg 0 \
1029                [expr {80 - $InitialMsgLen}]]]
1030        puts [errorChannel] [string range $errorMsg 0 $beginningIndex]
1031
1032        while {![string equal end $beginningIndex]} {
1033            puts -nonewline [errorChannel] \
1034                    [string repeat " " $InitialMsgLen]
1035            if {($endingIndex - $beginningIndex)
1036                    < (80 - $InitialMsgLen)} {
1037                puts [errorChannel] [string trim \
1038                        [string range $errorMsg $beginningIndex end]]
1039                break
1040            } else {
1041                set newEndingIndex [expr {[string last " " \
1042                        [string range $errorMsg $beginningIndex \
1043                                [expr {$beginningIndex
1044                                        + (80 - $InitialMsgLen)}]
1045                ]] + $beginningIndex}]
1046                if {($newEndingIndex <= 0)
1047                        || ($newEndingIndex <= $beginningIndex)} {
1048                    set newEndingIndex end
1049                }
1050                puts [errorChannel] [string trim \
1051                        [string range $errorMsg \
1052                            $beginningIndex $newEndingIndex]]
1053                set beginningIndex $newEndingIndex
1054            }
1055        }
1056    }
1057    flush [errorChannel]
1058    return
1059}
1060
1061# tcltest::SafeFetch --
1062#
1063#        The following trace procedure makes it so that we can safely
1064#        refer to non-existent members of the testConstraints array
1065#        without causing an error.  Instead, reading a non-existent
1066#        member will return 0. This is necessary because tests are
1067#        allowed to use constraint "X" without ensuring that
1068#        testConstraints("X") is defined.
1069#
1070# Arguments:
1071#       n1 - name of the array (testConstraints)
1072#       n2 - array key value (constraint name)
1073#       op - operation performed on testConstraints (generally r)
1074#
1075# Results:
1076#       none
1077#
1078# Side effects:
1079#       sets testConstraints($n2) to 0 if it's referenced but never
1080#       before used
1081
1082proc tcltest::SafeFetch {n1 n2 op} {
1083    variable testConstraints
1084    DebugPuts 3 "entering SafeFetch $n1 $n2 $op"
1085    if {[string equal {} $n2]} {return}
1086    if {![info exists testConstraints($n2)]} {
1087        if {[catch {testConstraint $n2 [eval [ConstraintInitializer $n2]]}]} {
1088            testConstraint $n2 0
1089        }
1090    }
1091}
1092
1093# tcltest::ConstraintInitializer --
1094#
1095#       Get or set a script that when evaluated in the tcltest namespace
1096#       will return a boolean value with which to initialize the
1097#       associated constraint.
1098#
1099# Arguments:
1100#       constraint - name of the constraint initialized by the script
1101#       script - the initializer script
1102#
1103# Results
1104#       boolean value of the constraint - enabled or disabled
1105#
1106# Side effects:
1107#       Constraint is initialized for future reference by [test]
1108proc tcltest::ConstraintInitializer {constraint {script ""}} {
1109    variable ConstraintInitializer
1110    DebugPuts 3 "entering ConstraintInitializer $constraint $script"
1111    if {[llength [info level 0]] == 2} {
1112        return $ConstraintInitializer($constraint)
1113    }
1114    # Check for boolean values
1115    if {![info complete $script]} {
1116        return -code error "ConstraintInitializer must be complete script"
1117    }
1118    set ConstraintInitializer($constraint) $script
1119}
1120
1121# tcltest::InitConstraints --
1122#
1123# Call all registered constraint initializers to force initialization
1124# of all known constraints.
1125# See the tcltest man page for the list of built-in constraints defined
1126# in this procedure.
1127#
1128# Arguments:
1129#       none
1130#
1131# Results:
1132#       The testConstraints array is reset to have an index for each
1133#       built-in test constraint.
1134#
1135# Side Effects:
1136#       None.
1137#
1138
1139proc tcltest::InitConstraints {} {
1140    variable ConstraintInitializer
1141    initConstraintsHook
1142    foreach constraint [array names ConstraintInitializer] {
1143        testConstraint $constraint
1144    }
1145}
1146
1147proc tcltest::DefineConstraintInitializers {} {
1148    ConstraintInitializer singleTestInterp {singleProcess}
1149
1150    # All the 'pc' constraints are here for backward compatibility and
1151    # are not documented.  They have been replaced with equivalent 'win'
1152    # constraints.
1153
1154    ConstraintInitializer unixOnly \
1155            {string equal $::tcl_platform(platform) unix}
1156    ConstraintInitializer macOnly \
1157            {string equal $::tcl_platform(platform) macintosh}
1158    ConstraintInitializer pcOnly \
1159            {string equal $::tcl_platform(platform) windows}
1160    ConstraintInitializer winOnly \
1161            {string equal $::tcl_platform(platform) windows}
1162
1163    ConstraintInitializer unix {testConstraint unixOnly}
1164    ConstraintInitializer mac {testConstraint macOnly}
1165    ConstraintInitializer pc {testConstraint pcOnly}
1166    ConstraintInitializer win {testConstraint winOnly}
1167
1168    ConstraintInitializer unixOrPc \
1169            {expr {[testConstraint unix] || [testConstraint pc]}}
1170    ConstraintInitializer macOrPc \
1171            {expr {[testConstraint mac] || [testConstraint pc]}}
1172    ConstraintInitializer unixOrWin \
1173            {expr {[testConstraint unix] || [testConstraint win]}}
1174    ConstraintInitializer macOrWin \
1175            {expr {[testConstraint mac] || [testConstraint win]}}
1176    ConstraintInitializer macOrUnix \
1177            {expr {[testConstraint mac] || [testConstraint unix]}}
1178
1179    ConstraintInitializer nt {string equal $::tcl_platform(os) "Windows NT"}
1180    ConstraintInitializer 95 {string equal $::tcl_platform(os) "Windows 95"}
1181    ConstraintInitializer 98 {string equal $::tcl_platform(os) "Windows 98"}
1182
1183    # The following Constraints switches are used to mark tests that
1184    # should work, but have been temporarily disabled on certain
1185    # platforms because they don't and we haven't gotten around to
1186    # fixing the underlying problem.
1187
1188    ConstraintInitializer tempNotPc {expr {![testConstraint pc]}}
1189    ConstraintInitializer tempNotWin {expr {![testConstraint win]}}
1190    ConstraintInitializer tempNotMac {expr {![testConstraint mac]}}
1191    ConstraintInitializer tempNotUnix {expr {![testConstraint unix]}}
1192
1193    # The following Constraints switches are used to mark tests that
1194    # crash on certain platforms, so that they can be reactivated again
1195    # when the underlying problem is fixed.
1196
1197    ConstraintInitializer pcCrash {expr {![testConstraint pc]}}
1198    ConstraintInitializer winCrash {expr {![testConstraint win]}}
1199    ConstraintInitializer macCrash {expr {![testConstraint mac]}}
1200    ConstraintInitializer unixCrash {expr {![testConstraint unix]}}
1201
1202    # Skip empty tests
1203
1204    ConstraintInitializer emptyTest {format 0}
1205
1206    # By default, tests that expose known bugs are skipped.
1207
1208    ConstraintInitializer knownBug {format 0}
1209
1210    # By default, non-portable tests are skipped.
1211
1212    ConstraintInitializer nonPortable {format 0}
1213
1214    # Some tests require user interaction.
1215
1216    ConstraintInitializer userInteraction {format 0}
1217
1218    # Some tests must be skipped if the interpreter is not in
1219    # interactive mode
1220
1221    ConstraintInitializer interactive \
1222            {expr {[info exists ::tcl_interactive] && $::tcl_interactive}}
1223
1224    # Some tests can only be run if the installation came from a CD
1225    # image instead of a web image.  Some tests must be skipped if you
1226    # are running as root on Unix.  Other tests can only be run if you
1227    # are running as root on Unix.
1228
1229    ConstraintInitializer root {expr \
1230            {[string equal unix $::tcl_platform(platform)]
1231            && ([string equal root $::tcl_platform(user)]
1232                || [string equal "" $::tcl_platform(user)])}}
1233    ConstraintInitializer notRoot {expr {![testConstraint root]}}
1234
1235    # Set nonBlockFiles constraint: 1 means this platform supports
1236    # setting files into nonblocking mode.
1237
1238    ConstraintInitializer nonBlockFiles {
1239            set code [expr {[catch {set f [open defs r]}] 
1240                    || [catch {fconfigure $f -blocking off}]}]
1241            catch {close $f}
1242            set code
1243    }
1244
1245    # Set asyncPipeClose constraint: 1 means this platform supports
1246    # async flush and async close on a pipe.
1247    #
1248    # Test for SCO Unix - cannot run async flushing tests because a
1249    # potential problem with select is apparently interfering.
1250    # (Mark Diekhans).
1251
1252    ConstraintInitializer asyncPipeClose {expr {
1253            !([string equal unix $::tcl_platform(platform)] 
1254            && ([catch {exec uname -X | fgrep {Release = 3.2v}}] == 0))}}
1255
1256    # Test to see if we have a broken version of sprintf with respect
1257    # to the "e" format of floating-point numbers.
1258
1259    ConstraintInitializer eformat {string equal [format %g 5e-5] 5e-05}
1260
1261    # Test to see if execed commands such as cat, echo, rm and so forth
1262    # are present on this machine.
1263
1264    ConstraintInitializer unixExecs {
1265        set code 1
1266        if {[string equal macintosh $::tcl_platform(platform)]} {
1267            set code 0
1268        }
1269        if {[string equal windows $::tcl_platform(platform)]} {
1270            if {[catch {
1271                set file _tcl_test_remove_me.txt
1272                makeFile {hello} $file
1273            }]} {
1274                set code 0
1275            } elseif {
1276                [catch {exec cat $file}] ||
1277                [catch {exec echo hello}] ||
1278                [catch {exec sh -c echo hello}] ||
1279                [catch {exec wc $file}] ||
1280                [catch {exec sleep 1}] ||
1281                [catch {exec echo abc > $file}] ||
1282                [catch {exec chmod 644 $file}] ||
1283                [catch {exec rm $file}] ||
1284                [llength [auto_execok mkdir]] == 0 ||
1285                [llength [auto_execok fgrep]] == 0 ||
1286                [llength [auto_execok grep]] == 0 ||
1287                [llength [auto_execok ps]] == 0
1288            } {
1289                set code 0
1290            }
1291            removeFile $file
1292        }
1293        set code
1294    }
1295
1296    ConstraintInitializer stdio {
1297        set code 0
1298        if {![catch {set f [open "|[list [interpreter]]" w]}]} {
1299            if {![catch {puts $f exit}]} {
1300                if {![catch {close $f}]} {
1301                    set code 1
1302                }
1303            }
1304        }
1305        set code
1306    }
1307
1308    # Deliberately call socket with the wrong number of arguments.  The
1309    # error message you get will indicate whether sockets are available
1310    # on this system.
1311
1312    ConstraintInitializer socket {
1313        catch {socket} msg
1314        string compare $msg "sockets are not available on this system"
1315    }
1316
1317    # Check for internationalization
1318    ConstraintInitializer hasIsoLocale {
1319        if {[llength [info commands testlocale]] == 0} {
1320            set code 0
1321        } else {
1322            set code [string length [SetIso8859_1_Locale]]
1323            RestoreLocale
1324        }
1325        set code
1326    }
1327
1328}
1329#####################################################################
1330
1331# Usage and command line arguments processing.
1332
1333# tcltest::PrintUsageInfo
1334#
1335#       Prints out the usage information for package tcltest.  This can
1336#       be customized with the redefinition of [PrintUsageInfoHook].
1337#
1338# Arguments:
1339#       none
1340#
1341# Results:
1342#       none
1343#
1344# Side Effects:
1345#       none
1346proc tcltest::PrintUsageInfo {} {
1347    puts [Usage]
1348    PrintUsageInfoHook
1349}
1350
1351proc tcltest::Usage { {option ""} } {
1352    variable Usage
1353    variable Verify
1354    if {[llength [info level 0]] == 1} {
1355        set msg "Usage: [file tail [info nameofexecutable]] script "
1356        append msg "?-help? ?flag value? ... \n"
1357        append msg "Available flags (and valid input values) are:"
1358
1359        set max 0
1360        set allOpts [concat -help [Configure]]
1361        foreach opt $allOpts {
1362            set foo [Usage $opt]
1363            foreach [list x type($opt) usage($opt)] $foo break
1364            set line($opt) "  $opt $type($opt)  "
1365            set length($opt) [string length $line($opt)]
1366            if {$length($opt) > $max} {set max $length($opt)}
1367        }
1368        set rest [expr {72 - $max}]
1369        foreach opt $allOpts {
1370            append msg \n$line($opt)
1371            append msg [string repeat " " [expr {$max - $length($opt)}]]
1372            set u [string trim $usage($opt)]
1373            catch {append u "  (default: \[[Configure $opt]])"}
1374            regsub -all {\s*\n\s*} $u " " u
1375            while {[string length $u] > $rest} {
1376                set break [string wordstart $u $rest]
1377                if {$break == 0} {
1378                    set break [string wordend $u 0]
1379                }
1380                append msg [string range $u 0 [expr {$break - 1}]]
1381                set u [string trim [string range $u $break end]]
1382                append msg \n[string repeat " " $max]
1383            }
1384            append msg $u
1385        }
1386        return $msg\n
1387    } elseif {[string equal -help $option]} {
1388        return [list -help "" "Display this usage information."]
1389    } else {
1390        set type [lindex [info args $Verify($option)] 0]
1391        return [list $option $type $Usage($option)]
1392    }
1393}
1394
1395# tcltest::ProcessFlags --
1396#
1397#       process command line arguments supplied in the flagArray - this
1398#       is called by processCmdLineArgs.  Modifies tcltest variables
1399#       according to the content of the flagArray.
1400#
1401# Arguments:
1402#       flagArray - array containing name/value pairs of flags
1403#
1404# Results:
1405#       sets tcltest variables according to their values as defined by
1406#       flagArray
1407#
1408# Side effects:
1409#       None.
1410
1411proc tcltest::ProcessFlags {flagArray} {
1412    # Process -help first
1413    if {[lsearch -exact $flagArray {-help}] != -1} {
1414        PrintUsageInfo
1415        exit 1
1416    }
1417
1418    if {[llength $flagArray] == 0} {
1419        RemoveAutoConfigureTraces
1420    } else {
1421        set args $flagArray
1422        while {[llength $args]>1 && [catch {eval configure $args} msg]} {
1423
1424            # Something went wrong parsing $args for tcltest options
1425            # Check whether the problem is "unknown option"
1426            if {[regexp {^unknown option (\S+):} $msg -> option]} {
1427                # Could be this is an option the Hook knows about
1428                set moreOptions [processCmdLineArgsAddFlagsHook]
1429                if {[lsearch -exact $moreOptions $option] == -1} {
1430                    # Nope.  Report the error, including additional options,
1431                    # but keep going
1432                    if {[llength $moreOptions]} {
1433                        append msg ", "
1434                        append msg [join [lrange $moreOptions 0 end-1] ", "]
1435                        append msg "or [lindex $moreOptions end]"
1436                    }
1437                    Warn $msg
1438                }
1439            } else {
1440                # error is something other than "unknown option"
1441                # notify user of the error; and exit
1442                puts [errorChannel] $msg
1443                exit 1
1444            }
1445
1446            # To recover, find that unknown option and remove up to it.
1447            # then retry
1448            while {![string equal [lindex $args 0] $option]} {
1449                set args [lrange $args 2 end]
1450            }
1451            set args [lrange $args 2 end]
1452        }
1453        if {[llength $args] == 1} {
1454            puts [errorChannel] \
1455                    "missing value for option [lindex $args 0]"
1456            exit 1
1457        }
1458    }
1459
1460    # Call the hook
1461    array set flag $flagArray
1462    processCmdLineArgsHook [array get flag]
1463    return
1464}
1465
1466# tcltest::ProcessCmdLineArgs --
1467#
1468#       This procedure must be run after constraint initialization is
1469#       set up (by [DefineConstraintInitializers]) because some constraints
1470#       can be overridden.
1471#
1472#       Perform configuration according to the command-line options.
1473#
1474# Arguments:
1475#       none
1476#
1477# Results:
1478#       Sets the above-named variables in the tcltest namespace.
1479#
1480# Side Effects:
1481#       None.
1482#
1483
1484proc tcltest::ProcessCmdLineArgs {} {
1485    variable originalEnv
1486    variable testConstraints
1487
1488    # The "argv" var doesn't exist in some cases, so use {}.
1489    if {![info exists ::argv]} {
1490        ProcessFlags {}
1491    } else {
1492        ProcessFlags $::argv
1493    }
1494
1495    # Spit out everything you know if we're at a debug level 2 or
1496    # greater
1497    DebugPuts 2 "Flags passed into tcltest:"
1498    if {[info exists ::env(TCLTEST_OPTIONS)]} {
1499        DebugPuts 2 \
1500                "    ::env(TCLTEST_OPTIONS): $::env(TCLTEST_OPTIONS)"
1501    }
1502    if {[info exists ::argv]} {
1503        DebugPuts 2 "    argv: $::argv"
1504    }
1505    DebugPuts    2 "tcltest::debug              = [debug]"
1506    DebugPuts    2 "tcltest::testsDirectory     = [testsDirectory]"
1507    DebugPuts    2 "tcltest::workingDirectory   = [workingDirectory]"
1508    DebugPuts    2 "tcltest::temporaryDirectory = [temporaryDirectory]"
1509    DebugPuts    2 "tcltest::outputChannel      = [outputChannel]"
1510    DebugPuts    2 "tcltest::errorChannel       = [errorChannel]"
1511    DebugPuts    2 "Original environment (tcltest::originalEnv):"
1512    DebugPArray  2 originalEnv
1513    DebugPuts    2 "Constraints:"
1514    DebugPArray  2 testConstraints
1515}
1516
1517#####################################################################
1518
1519# Code to run the tests goes here.
1520
1521# tcltest::TestPuts --
1522#
1523#       Used to redefine puts in test environment.  Stores whatever goes
1524#       out on stdout in tcltest::outData and stderr in errData before
1525#       sending it on to the regular puts.
1526#
1527# Arguments:
1528#       same as standard puts
1529#
1530# Results:
1531#       none
1532#
1533# Side effects:
1534#       Intercepts puts; data that would otherwise go to stdout, stderr,
1535#       or file channels specified in outputChannel and errorChannel
1536#       does not get sent to the normal puts function.
1537namespace eval tcltest::Replace {
1538    namespace export puts
1539}
1540proc tcltest::Replace::puts {args} {
1541    variable [namespace parent]::outData
1542    variable [namespace parent]::errData
1543    switch [llength $args] {
1544        1 {
1545            # Only the string to be printed is specified
1546            append outData [lindex $args 0]\n
1547            return
1548            # return [Puts [lindex $args 0]]
1549        }
1550        2 {
1551            # Either -nonewline or channelId has been specified
1552            if {[string equal -nonewline [lindex $args 0]]} {
1553                append outData [lindex $args end]
1554                return
1555                # return [Puts -nonewline [lindex $args end]]
1556            } else {
1557                set channel [lindex $args 0]
1558                set newline \n
1559            }
1560        }
1561        3 {
1562            if {[string equal -nonewline [lindex $args 0]]} {
1563                # Both -nonewline and channelId are specified, unless
1564                # it's an error.  -nonewline is supposed to be argv[0].
1565                set channel [lindex $args 1]
1566                set newline ""
1567            }
1568        }
1569    }
1570
1571    if {[info exists channel]} {
1572        if {[string equal $channel [[namespace parent]::outputChannel]]
1573                || [string equal $channel stdout]} {
1574            append outData [lindex $args end]$newline
1575            return
1576        } elseif {[string equal $channel [[namespace parent]::errorChannel]]
1577                || [string equal $channel stderr]} {
1578            append errData [lindex $args end]$newline
1579            return
1580        }
1581    }
1582
1583    # If we haven't returned by now, we don't know how to handle the
1584    # input.  Let puts handle it.
1585    return [eval Puts $args]
1586}
1587
1588# tcltest::Eval --
1589#
1590#       Evaluate the script in the test environment.  If ignoreOutput is
1591#       false, store data sent to stderr and stdout in outData and
1592#       errData.  Otherwise, ignore this output altogether.
1593#
1594# Arguments:
1595#       script             Script to evaluate
1596#       ?ignoreOutput?     Indicates whether or not to ignore output
1597#                          sent to stdout & stderr
1598#
1599# Results:
1600#       result from running the script
1601#
1602# Side effects:
1603#       Empties the contents of outData and errData before running a
1604#       test if ignoreOutput is set to 0.
1605
1606proc tcltest::Eval {script {ignoreOutput 1}} {
1607    variable outData
1608    variable errData
1609    DebugPuts 3 "[lindex [info level 0] 0] called"
1610    if {!$ignoreOutput} {
1611        set outData {}
1612        set errData {}
1613        rename ::puts [namespace current]::Replace::Puts
1614        namespace eval :: \
1615                [list namespace import [namespace origin Replace::puts]]
1616        namespace import Replace::puts
1617    }
1618    set result [uplevel 1 $script]
1619    if {!$ignoreOutput} {
1620        namespace forget puts
1621        namespace eval :: namespace forget puts
1622        rename [namespace current]::Replace::Puts ::puts
1623    }
1624    return $result
1625}
1626
1627# tcltest::CompareStrings --
1628#
1629#       compares the expected answer to the actual answer, depending on
1630#       the mode provided.  Mode determines whether a regexp, exact,
1631#       glob or custom comparison is done.
1632#
1633# Arguments:
1634#       actual - string containing the actual result
1635#       expected - pattern to be matched against
1636#       mode - type of comparison to be done
1637#
1638# Results:
1639#       result of the match
1640#
1641# Side effects:
1642#       None.
1643
1644proc tcltest::CompareStrings {actual expected mode} {
1645    variable CustomMatch
1646    if {![info exists CustomMatch($mode)]} {
1647        return -code error "No matching command registered for `-match $mode'"
1648    }
1649    set match [namespace eval :: $CustomMatch($mode) [list $expected $actual]]
1650    if {[catch {expr {$match && $match}} result]} {
1651        return -code error "Invalid result from `-match $mode' command: $result"
1652    }
1653    return $match
1654}
1655
1656# tcltest::customMatch --
1657#
1658#       registers a command to be called when a particular type of
1659#       matching is required.
1660#
1661# Arguments:
1662#       nickname - Keyword for the type of matching
1663#       cmd - Incomplete command that implements that type of matching
1664#               when completed with expected string and actual string
1665#               and then evaluated.
1666#
1667# Results:
1668#       None.
1669#
1670# Side effects:
1671#       Sets the variable tcltest::CustomMatch
1672
1673proc tcltest::customMatch {mode script} {
1674    variable CustomMatch
1675    if {![info complete $script]} {
1676        return -code error \
1677                "invalid customMatch script; can't evaluate after completion"
1678    }
1679    set CustomMatch($mode) $script
1680}
1681
1682# tcltest::SubstArguments list
1683#
1684# This helper function takes in a list of words, then perform a
1685# substitution on the list as though each word in the list is a separate
1686# argument to the Tcl function.  For example, if this function is
1687# invoked as:
1688#
1689#      SubstArguments {$a {$a}}
1690#
1691# Then it is as though the function is invoked as:
1692#
1693#      SubstArguments $a {$a}
1694#
1695# This code is adapted from Paul Duffin's function "SplitIntoWords".
1696# The original function can be found  on:
1697#
1698#      http://purl.org/thecliff/tcl/wiki/858.html
1699#
1700# Results:
1701#     a list containing the result of the substitution
1702#
1703# Exceptions:
1704#     An error may occur if the list containing unbalanced quote or
1705#     unknown variable.
1706#
1707# Side Effects:
1708#     None.
1709#
1710
1711proc tcltest::SubstArguments {argList} {
1712
1713    # We need to split the argList up into tokens but cannot use list
1714    # operations as they throw away some significant quoting, and
1715    # [split] ignores braces as it should.  Therefore what we do is
1716    # gradually build up a string out of whitespace seperated strings.
1717    # We cannot use [split] to split the argList into whitespace
1718    # separated strings as it throws away the whitespace which maybe
1719    # important so we have to do it all by hand.
1720
1721    set result {}
1722    set token ""
1723
1724    while {[string length $argList]} {
1725        # Look for the next word containing a quote: " { }
1726        if {[regexp -indices {[^ \t\n]*[\"\{\}]+[^ \t\n]*} \
1727                $argList all]} {
1728            # Get the text leading up to this word, but not including
1729            # this word, from the argList.
1730            set text [string range $argList 0 \
1731                    [expr {[lindex $all 0] - 1}]]
1732            # Get the word with the quote
1733            set word [string range $argList \
1734                    [lindex $all 0] [lindex $all 1]]
1735
1736            # Remove all text up to and including the word from the
1737            # argList.
1738            set argList [string range $argList \
1739                    [expr {[lindex $all 1] + 1}] end]
1740        } else {
1741            # Take everything up to the end of the argList.
1742            set text $argList
1743            set word {}
1744            set argList {}
1745        }
1746
1747        if {$token != {}} {
1748            # If we saw a word with quote before, then there is a
1749            # multi-word token starting with that word.  In this case,
1750            # add the text and the current word to this token.
1751            append token $text $word
1752        } else {
1753            # Add the text to the result.  There is no need to parse
1754            # the text because it couldn't be a part of any multi-word
1755            # token.  Then start a new multi-word token with the word
1756            # because we need to pass this token to the Tcl parser to
1757            # check for balancing quotes
1758            append result $text
1759            set token $word
1760        }
1761
1762        if { [catch {llength $token} length] == 0 && $length == 1} {
1763            # The token is a valid list so add it to the result.
1764            # lappend result [string trim $token]
1765            append result \{$token\}
1766            set token {}
1767        }
1768    }
1769
1770    # If the last token has not been added to the list then there
1771    # is a problem.
1772    if { [string length $token] } {
1773        error "incomplete token \"$token\""
1774    }
1775
1776    return $result
1777}
1778
1779
1780# tcltest::test --
1781#
1782# This procedure runs a test and prints an error message if the test
1783# fails.  If verbose has been set, it also prints a message even if the
1784# test succeeds.  The test will be skipped if it doesn't match the
1785# match variable, if it matches an element in skip, or if one of the
1786# elements of "constraints" turns out not to be true.
1787#
1788# If testLevel is 1, then this is a top level test, and we record
1789# pass/fail information; otherwise, this information is not logged and
1790# is not added to running totals.
1791#
1792# Attributes:
1793#   Only description is a required attribute.  All others are optional.
1794#   Default values are indicated.
1795#
1796#   constraints -       A list of one or more keywords, each of which
1797#                       must be the name of an element in the array
1798#                       "testConstraints".  If any of these elements is
1799#                       zero, the test is skipped. This attribute is
1800#                       optional; default is {}
1801#   body -              Script to run to carry out the test.  It must
1802#                       return a result that can be checked for
1803#                       correctness.  This attribute is optional;
1804#                       default is {}
1805#   result -            Expected result from script.  This attribute is
1806#                       optional; default is {}.
1807#   output -            Expected output sent to stdout.  This attribute
1808#                       is optional; default is {}.
1809#   errorOutput -       Expected output sent to stderr.  This attribute
1810#                       is optional; default is {}.
1811#   returnCodes -       Expected return codes.  This attribute is
1812#                       optional; default is {0 2}.
1813#   setup -             Code to run before $script (above).  This
1814#                       attribute is optional; default is {}.
1815#   cleanup -           Code to run after $script (above).  This
1816#                       attribute is optional; default is {}.
1817#   match -             specifies type of matching to do on result,
1818#                       output, errorOutput; this must be a string
1819#                       previously registered by a call to [customMatch].
1820#                       The strings exact, glob, and regexp are pre-registered
1821#                       by the tcltest package.  Default value is exact.
1822#
1823# Arguments:
1824#   name -              Name of test, in the form foo-1.2.
1825#   description -       Short textual description of the test, to
1826#                       help humans understand what it does.
1827#
1828# Results:
1829#       None.
1830#
1831# Side effects:
1832#       Just about anything is possible depending on the test.
1833#
1834
1835proc tcltest::test {name description args} {
1836    global tcl_platform
1837    variable testLevel
1838    variable coreModTime
1839    DebugPuts 3 "test $name $args"
1840    DebugDo 1 {
1841        variable TestNames
1842        catch {
1843            puts "test name '$name' re-used; prior use in $TestNames($name)"
1844        }
1845        set TestNames($name) [info script]
1846    }
1847
1848    FillFilesExisted
1849    incr testLevel
1850
1851    # Pre-define everything to null except output and errorOutput.  We
1852    # determine whether or not to trap output based on whether or not
1853    # these variables (output & errorOutput) are defined.
1854    foreach item {constraints setup cleanup body result returnCodes
1855            match} {
1856        set $item {}
1857    }
1858
1859    # Set the default match mode
1860    set match exact
1861
1862    # Set the default match values for return codes (0 is the standard
1863    # expected return value if everything went well; 2 represents
1864    # 'return' being used in the test script).
1865    set returnCodes [list 0 2]
1866
1867    # The old test format can't have a 3rd argument (constraints or
1868    # script) that starts with '-'.
1869    if {[string match -* [lindex $args 0]]
1870            || ([llength $args] <= 1)} {
1871        if {[llength $args] == 1} {
1872            set list [SubstArguments [lindex $args 0]]
1873            foreach {element value} $list {
1874                set testAttributes($element) $value
1875            }
1876            foreach item {constraints match setup body cleanup \
1877                    result returnCodes output errorOutput} {
1878                if {[info exists testAttributes(-$item)]} {
1879                    set testAttributes(-$item) [uplevel 1 \
1880                            ::concat $testAttributes(-$item)]
1881                }
1882            }
1883        } else {
1884            array set testAttributes $args
1885        }
1886
1887        set validFlags {-setup -cleanup -body -result -returnCodes \
1888                -match -output -errorOutput -constraints}
1889
1890        foreach flag [array names testAttributes] {
1891            if {[lsearch -exact $validFlags $flag] == -1} {
1892                incr testLevel -1
1893                set sorted [lsort $validFlags]
1894                set options [join [lrange $sorted 0 end-1] ", "]
1895                append options ", or [lindex $sorted end]"
1896                return -code error "bad option \"$flag\": must be $options"
1897            }
1898        }
1899
1900        # store whatever the user gave us
1901        foreach item [array names testAttributes] {
1902            set [string trimleft $item "-"] $testAttributes($item)
1903        }
1904
1905        # Check the values supplied for -match
1906        variable CustomMatch
1907        if {[lsearch [array names CustomMatch] $match] == -1} {
1908            incr testLevel -1
1909            set sorted [lsort [array names CustomMatch]]
1910            set values [join [lrange $sorted 0 end-1] ", "]
1911            append values ", or [lindex $sorted end]"
1912            return -code error "bad -match value \"$match\":\
1913                    must be $values"
1914        }
1915
1916        # Replace symbolic valies supplied for -returnCodes
1917        foreach {strcode numcode} {ok 0 normal 0 error 1 return 2 break 3 continue 4} {
1918            set returnCodes [string map -nocase [list $strcode $numcode] $returnCodes]
1919        }
1920    } else {
1921        # This is parsing for the old test command format; it is here
1922        # for backward compatibility.
1923        set result [lindex $args end]
1924        if {[llength $args] == 2} {
1925            set body [lindex $args 0]
1926        } elseif {[llength $args] == 3} {
1927            set constraints [lindex $args 0]
1928            set body [lindex $args 1]
1929        } else {
1930            incr testLevel -1
1931            return -code error "wrong # args:\
1932                    should be \"test name desc ?options?\""
1933        }
1934    }
1935
1936    if {[Skipped $name $constraints]} {
1937        incr testLevel -1
1938        return
1939    }
1940
1941    # Save information about the core file. 
1942    if {[preserveCore]} {
1943        if {[file exists [file join [workingDirectory] core]]} {
1944            set coreModTime [file mtime [file join [workingDirectory] core]]
1945        }
1946    }
1947
1948    # First, run the setup script
1949    set code [catch {uplevel 1 $setup} setupMsg]
1950    set setupFailure [expr {$code != 0}]
1951
1952    # Only run the test body if the setup was successful
1953    if {!$setupFailure} {
1954
1955        # Verbose notification of $body start
1956        if {[IsVerbose start]} {
1957            puts [outputChannel] "---- $name start"
1958            flush [outputChannel]
1959        }
1960
1961        set command [list [namespace origin RunTest] $name $body]
1962        if {[info exists output] || [info exists errorOutput]} {
1963            set testResult [uplevel 1 [list [namespace origin Eval] $command 0]]
1964        } else {
1965            set testResult [uplevel 1 [list [namespace origin Eval] $command 1]]
1966        }
1967        foreach {actualAnswer returnCode} $testResult break
1968    }
1969
1970    # Always run the cleanup script
1971    set code [catch {uplevel 1 $cleanup} cleanupMsg]
1972    set cleanupFailure [expr {$code != 0}]
1973
1974    set coreFailure 0
1975    set coreMsg ""
1976    # check for a core file first - if one was created by the test,
1977    # then the test failed
1978    if {[preserveCore]} {
1979        if {[file exists [file join [workingDirectory] core]]} {
1980            # There's only a test failure if there is a core file
1981            # and (1) there previously wasn't one or (2) the new
1982            # one is different from the old one.
1983            if {[info exists coreModTime]} {
1984                if {$coreModTime != [file mtime \
1985                        [file join [workingDirectory] core]]} {
1986                    set coreFailure 1
1987                }
1988            } else {
1989                set coreFailure 1
1990            }
1991       
1992            if {([preserveCore] > 1) && ($coreFailure)} {
1993                append coreMsg "\nMoving file to:\
1994                    [file join [temporaryDirectory] core-$name]"
1995                catch {file rename -force \
1996                    [file join [workingDirectory] core] \
1997                    [file join [temporaryDirectory] core-$name]
1998                } msg
1999                if {[string length $msg] > 0} {
2000                    append coreMsg "\nError:\
2001                        Problem renaming core file: $msg"
2002                }
2003            }
2004        }
2005    }
2006
2007    # check if the return code matched the expected return code
2008    set codeFailure 0
2009    if {!$setupFailure && [lsearch -exact $returnCodes $returnCode] == -1} {
2010        set codeFailure 1
2011    }
2012
2013    # If expected output/error strings exist, we have to compare
2014    # them.  If the comparison fails, then so did the test.
2015    set outputFailure 0
2016    variable outData
2017    if {[info exists output] && !$codeFailure} {
2018        if {[set outputCompare [catch {
2019            CompareStrings $outData $output $match
2020        } outputMatch]] == 0} {
2021            set outputFailure [expr {!$outputMatch}]
2022        } else {
2023            set outputFailure 1
2024        }
2025    }
2026
2027    set errorFailure 0
2028    variable errData
2029    if {[info exists errorOutput] && !$codeFailure} {
2030        if {[set errorCompare [catch {
2031            CompareStrings $errData $errorOutput $match
2032        } errorMatch]] == 0} {
2033            set errorFailure [expr {!$errorMatch}]
2034        } else {
2035            set errorFailure 1
2036        }
2037    }
2038
2039    # check if the answer matched the expected answer
2040    # Only check if we ran the body of the test (no setup failure)
2041    if {$setupFailure || $codeFailure} {
2042        set scriptFailure 0
2043    } elseif {[set scriptCompare [catch {
2044        CompareStrings $actualAnswer $result $match
2045    } scriptMatch]] == 0} {
2046        set scriptFailure [expr {!$scriptMatch}]
2047    } else {
2048        set scriptFailure 1
2049    }
2050
2051    # if we didn't experience any failures, then we passed
2052    variable numTests
2053    if {!($setupFailure || $cleanupFailure || $coreFailure
2054            || $outputFailure || $errorFailure || $codeFailure
2055            || $scriptFailure)} {
2056        if {$testLevel == 1} {
2057            incr numTests(Passed)
2058            if {[IsVerbose pass]} {
2059                puts [outputChannel] "++++ $name PASSED"
2060            }
2061        }
2062        incr testLevel -1
2063        return
2064    }
2065
2066    # We know the test failed, tally it...
2067    if {$testLevel == 1} {
2068        incr numTests(Failed)
2069    }
2070
2071    # ... then report according to the type of failure
2072    variable currentFailure true
2073    if {![IsVerbose body]} {
2074        set body ""
2075    }   
2076    puts [outputChannel] "\n==== $name\
2077            [string trim $description] FAILED"
2078    if {[string length $body]} {
2079        puts [outputChannel] "==== Contents of test case:"
2080        puts [outputChannel] $body
2081    }
2082    if {$setupFailure} {
2083        puts [outputChannel] "---- Test setup\
2084                failed:\n$setupMsg"
2085    }
2086    if {$scriptFailure} {
2087        if {$scriptCompare} {
2088            puts [outputChannel] "---- Error testing result: $scriptMatch"
2089        } else {
2090            puts [outputChannel] "---- Result was:\n$actualAnswer"
2091            puts [outputChannel] "---- Result should have been\
2092                    ($match matching):\n$result"
2093        }
2094    }
2095    if {$codeFailure} {
2096        switch -- $returnCode {
2097            0 { set msg "Test completed normally" }
2098            1 { set msg "Test generated error" }
2099            2 { set msg "Test generated return exception" }
2100            3 { set msg "Test generated break exception" }
2101            4 { set msg "Test generated continue exception" }
2102            default { set msg "Test generated exception" }
2103        }
2104        puts [outputChannel] "---- $msg; Return code was: $returnCode"
2105        puts [outputChannel] "---- Return code should have been\
2106                one of: $returnCodes"
2107        if {[IsVerbose error]} {
2108            if {[info exists ::errorInfo]} {
2109                puts [outputChannel] "---- errorInfo: $::errorInfo"
2110                puts [outputChannel] "---- errorCode: $::errorCode"
2111            }
2112        }
2113    }
2114    if {$outputFailure} {
2115        if {$outputCompare} {
2116            puts [outputChannel] "---- Error testing output: $outputMatch"
2117        } else {
2118            puts [outputChannel] "---- Output was:\n$outData"
2119            puts [outputChannel] "---- Output should have been\
2120                    ($match matching):\n$output"
2121        }
2122    }
2123    if {$errorFailure} {
2124        if {$errorCompare} {
2125            puts [outputChannel] "---- Error testing errorOutput: $errorMatch"
2126        } else {
2127            puts [outputChannel] "---- Error output was:\n$errData"
2128            puts [outputChannel] "---- Error output should have\
2129                    been ($match matching):\n$errorOutput"
2130        }
2131    }
2132    if {$cleanupFailure} {
2133        puts [outputChannel] "---- Test cleanup failed:\n$cleanupMsg"
2134    }
2135    if {$coreFailure} {
2136        puts [outputChannel] "---- Core file produced while running\
2137                test!  $coreMsg"
2138    }
2139    puts [outputChannel] "==== $name FAILED\n"
2140
2141    incr testLevel -1
2142    return
2143}
2144
2145# Skipped --
2146#
2147# Given a test name and it constraints, returns a boolean indicating
2148# whether the current configuration says the test should be skipped.
2149#
2150# Side Effects:  Maintains tally of total tests seen and tests skipped.
2151#
2152proc tcltest::Skipped {name constraints} {
2153    variable testLevel
2154    variable numTests
2155    variable testConstraints
2156
2157    if {$testLevel == 1} {
2158        incr numTests(Total)
2159    }
2160    # skip the test if it's name matches an element of skip
2161    foreach pattern [skip] {
2162        if {[string match $pattern $name]} {
2163            if {$testLevel == 1} {
2164                incr numTests(Skipped)
2165                DebugDo 1 {AddToSkippedBecause userSpecifiedSkip}
2166            }
2167            return 1
2168        }
2169    }
2170    # skip the test if it's name doesn't match any element of match
2171    set ok 0
2172    foreach pattern [match] {
2173        if {[string match $pattern $name]} {
2174            set ok 1
2175            break
2176        }
2177    }
2178    if {!$ok} {
2179        if {$testLevel == 1} {
2180            incr numTests(Skipped)
2181            DebugDo 1 {AddToSkippedBecause userSpecifiedNonMatch}
2182        }
2183        return 1
2184    }
2185    if {[string equal {} $constraints]} {
2186        # If we're limited to the listed constraints and there aren't
2187        # any listed, then we shouldn't run the test.
2188        if {[limitConstraints]} {
2189            AddToSkippedBecause userSpecifiedLimitConstraint
2190            if {$testLevel == 1} {
2191                incr numTests(Skipped)
2192            }
2193            return 1
2194        }
2195    } else {
2196        # "constraints" argument exists;
2197        # make sure that the constraints are satisfied.
2198
2199        set doTest 0
2200        if {[string match {*[$\[]*} $constraints] != 0} {
2201            # full expression, e.g. {$foo > [info tclversion]}
2202            catch {set doTest [uplevel #0 expr $constraints]}
2203        } elseif {[regexp {[^.a-zA-Z0-9 \n\r\t]+} $constraints] != 0} {
2204            # something like {a || b} should be turned into
2205            # $testConstraints(a) || $testConstraints(b).
2206            regsub -all {[.\w]+} $constraints {$testConstraints(&)} c
2207            catch {set doTest [eval expr $c]}
2208        } elseif {![catch {llength $constraints}]} {
2209            # just simple constraints such as {unixOnly fonts}.
2210            set doTest 1
2211            foreach constraint $constraints {
2212                if {(![info exists testConstraints($constraint)]) \
2213                        || (!$testConstraints($constraint))} {
2214                    set doTest 0
2215
2216                    # store the constraint that kept the test from
2217                    # running
2218                    set constraints $constraint
2219                    break
2220                }
2221            }
2222        }
2223       
2224        if {$doTest == 0} {
2225            if {[IsVerbose skip]} {
2226                puts [outputChannel] "++++ $name SKIPPED: $constraints"
2227            }
2228
2229            if {$testLevel == 1} {
2230                incr numTests(Skipped)
2231                AddToSkippedBecause $constraints
2232            }
2233            return 1
2234        }
2235    }
2236    return 0
2237}
2238
2239# RunTest --
2240#
2241# This is where the body of a test is evaluated.  The combination of
2242# [RunTest] and [Eval] allows the output and error output of the test
2243# body to be captured for comparison against the expected values.
2244
2245proc tcltest::RunTest {name script} {
2246    DebugPuts 3 "Running $name {$script}"
2247
2248    # If there is no "memory" command (because memory debugging isn't
2249    # enabled), then don't attempt to use the command.
2250
2251    if {[llength [info commands memory]] == 1} {
2252        memory tag $name
2253    }
2254
2255    set code [catch {uplevel 1 $script} actualAnswer]
2256
2257    return [list $actualAnswer $code]
2258}
2259
2260#####################################################################
2261
2262# tcltest::cleanupTestsHook --
2263#
2264#       This hook allows a harness that builds upon tcltest to specify
2265#       additional things that should be done at cleanup.
2266#
2267
2268if {[llength [info commands tcltest::cleanupTestsHook]] == 0} {
2269    proc tcltest::cleanupTestsHook {} {}
2270}
2271
2272# tcltest::cleanupTests --
2273#
2274# Remove files and dirs created using the makeFile and makeDirectory
2275# commands since the last time this proc was invoked.
2276#
2277# Print the names of the files created without the makeFile command
2278# since the tests were invoked.
2279#
2280# Print the number tests (total, passed, failed, and skipped) since the
2281# tests were invoked.
2282#
2283# Restore original environment (as reported by special variable env).
2284#
2285# Arguments:
2286#      calledFromAllFile - if 0, behave as if we are running a single
2287#      test file within an entire suite of tests.  if we aren't running
2288#      a single test file, then don't report status.  check for new
2289#      files created during the test run and report on them.  if 1,
2290#      report collated status from all the test file runs.
2291#
2292# Results:
2293#      None.
2294#
2295# Side Effects:
2296#      None
2297#
2298
2299proc tcltest::cleanupTests {{calledFromAllFile 0}} {
2300    variable filesMade
2301    variable filesExisted
2302    variable createdNewFiles
2303    variable testSingleFile
2304    variable numTests
2305    variable numTestFiles
2306    variable failFiles
2307    variable skippedBecause
2308    variable currentFailure
2309    variable originalEnv
2310    variable originalTclPlatform
2311    variable coreModTime
2312
2313    FillFilesExisted
2314    set testFileName [file tail [info script]]
2315
2316    # Call the cleanup hook
2317    cleanupTestsHook
2318
2319    # Remove files and directories created by the makeFile and
2320    # makeDirectory procedures.  Record the names of files in
2321    # workingDirectory that were not pre-existing, and associate them
2322    # with the test file that created them.
2323
2324    if {!$calledFromAllFile} {
2325        foreach file $filesMade {
2326            if {[file exists $file]} {
2327                DebugDo 1 {Warn "cleanupTests deleting $file..."}
2328                catch {file delete -force $file}
2329            }
2330        }
2331        set currentFiles {}
2332        foreach file [glob -nocomplain \
2333                -directory [temporaryDirectory] *] {
2334            lappend currentFiles [file tail $file]
2335        }
2336        set newFiles {}
2337        foreach file $currentFiles {
2338            if {[lsearch -exact $filesExisted $file] == -1} {
2339                lappend newFiles $file
2340            }
2341        }
2342        set filesExisted $currentFiles
2343        if {[llength $newFiles] > 0} {
2344            set createdNewFiles($testFileName) $newFiles
2345        }
2346    }
2347
2348    if {$calledFromAllFile || $testSingleFile} {
2349
2350        # print stats
2351
2352        puts -nonewline [outputChannel] "$testFileName:"
2353        foreach index [list "Total" "Passed" "Skipped" "Failed"] {
2354            puts -nonewline [outputChannel] \
2355                    "\t$index\t$numTests($index)"
2356        }
2357        puts [outputChannel] ""
2358
2359        # print number test files sourced
2360        # print names of files that ran tests which failed
2361
2362        if {$calledFromAllFile} {
2363            puts [outputChannel] \
2364                    "Sourced $numTestFiles Test Files."
2365            set numTestFiles 0
2366            if {[llength $failFiles] > 0} {
2367                puts [outputChannel] \
2368                        "Files with failing tests: $failFiles"
2369                set failFiles {}
2370            }
2371        }
2372
2373        # if any tests were skipped, print the constraints that kept
2374        # them from running.
2375
2376        set constraintList [array names skippedBecause]
2377        if {[llength $constraintList] > 0} {
2378            puts [outputChannel] \
2379                    "Number of tests skipped for each constraint:"
2380            foreach constraint [lsort $constraintList] {
2381                puts [outputChannel] \
2382                        "\t$skippedBecause($constraint)\t$constraint"
2383                unset skippedBecause($constraint)
2384            }
2385        }
2386
2387        # report the names of test files in createdNewFiles, and reset
2388        # the array to be empty.
2389
2390        set testFilesThatTurded [lsort [array names createdNewFiles]]
2391        if {[llength $testFilesThatTurded] > 0} {
2392            puts [outputChannel] "Warning: files left behind:"
2393            foreach testFile $testFilesThatTurded {
2394                puts [outputChannel] \
2395                        "\t$testFile:\t$createdNewFiles($testFile)"
2396                unset createdNewFiles($testFile)
2397            }
2398        }
2399
2400        # reset filesMade, filesExisted, and numTests
2401
2402        set filesMade {}
2403        foreach index [list "Total" "Passed" "Skipped" "Failed"] {
2404            set numTests($index) 0
2405        }
2406
2407        # exit only if running Tk in non-interactive mode
2408        # This should be changed to determine if an event
2409        # loop is running, which is the real issue.
2410        # Actually, this doesn't belong here at all.  A package
2411        # really has no business [exit]-ing an application.
2412        if {![catch {package present Tk}] && ![testConstraint interactive]} {
2413            exit
2414        }
2415    } else {
2416
2417        # if we're deferring stat-reporting until all files are sourced,
2418        # then add current file to failFile list if any tests in this
2419        # file failed
2420
2421        if {$currentFailure \
2422                && ([lsearch -exact $failFiles $testFileName] == -1)} {
2423            lappend failFiles $testFileName
2424        }
2425        set currentFailure false
2426
2427        # restore the environment to the state it was in before this package
2428        # was loaded
2429
2430        set newEnv {}
2431        set changedEnv {}
2432        set removedEnv {}
2433        foreach index [array names ::env] {
2434            if {![info exists originalEnv($index)]} {
2435                lappend newEnv $index
2436                unset ::env($index)
2437            } else {
2438                if {$::env($index) != $originalEnv($index)} {
2439                    lappend changedEnv $index
2440                    set ::env($index) $originalEnv($index)
2441                }
2442            }
2443        }
2444        foreach index [array names originalEnv] {
2445            if {![info exists ::env($index)]} {
2446                lappend removedEnv $index
2447                set ::env($index) $originalEnv($index)
2448            }
2449        }
2450        if {[llength $newEnv] > 0} {
2451            puts [outputChannel] \
2452                    "env array elements created:\t$newEnv"
2453        }
2454        if {[llength $changedEnv] > 0} {
2455            puts [outputChannel] \
2456                    "env array elements changed:\t$changedEnv"
2457        }
2458        if {[llength $removedEnv] > 0} {
2459            puts [outputChannel] \
2460                    "env array elements removed:\t$removedEnv"
2461        }
2462
2463        set changedTclPlatform {}
2464        foreach index [array names originalTclPlatform] {
2465            if {$::tcl_platform($index) \
2466                    != $originalTclPlatform($index)} {
2467                lappend changedTclPlatform $index
2468                set ::tcl_platform($index) $originalTclPlatform($index)
2469            }
2470        }
2471        if {[llength $changedTclPlatform] > 0} {
2472            puts [outputChannel] "tcl_platform array elements\
2473                    changed:\t$changedTclPlatform"
2474        }
2475
2476        if {[file exists [file join [workingDirectory] core]]} {
2477            if {[preserveCore] > 1} {
2478                puts "rename core file (> 1)"
2479                puts [outputChannel] "produced core file! \
2480                        Moving file to: \
2481                        [file join [temporaryDirectory] core-$testFileName]"
2482                catch {file rename -force \
2483                        [file join [workingDirectory] core] \
2484                        [file join [temporaryDirectory] core-$testFileName]
2485                } msg
2486                if {[string length $msg] > 0} {
2487                    PrintError "Problem renaming file: $msg"
2488                }
2489            } else {
2490                # Print a message if there is a core file and (1) there
2491                # previously wasn't one or (2) the new one is different
2492                # from the old one.
2493
2494                if {[info exists coreModTime]} {
2495                    if {$coreModTime != [file mtime \
2496                            [file join [workingDirectory] core]]} {
2497                        puts [outputChannel] "A core file was created!"
2498                    }
2499                } else {
2500                    puts [outputChannel] "A core file was created!"
2501                }
2502            }
2503        }
2504    }
2505    flush [outputChannel]
2506    flush [errorChannel]
2507    return
2508}
2509
2510#####################################################################
2511
2512# Procs that determine which tests/test files to run
2513
2514# tcltest::GetMatchingFiles
2515#
2516#       Looks at the patterns given to match and skip files and uses
2517#       them to put together a list of the tests that will be run.
2518#
2519# Arguments:
2520#       directory to search
2521#
2522# Results:
2523#       The constructed list is returned to the user.  This will
2524#       primarily be used in 'all.tcl' files.  It is used in
2525#       runAllTests.
2526#
2527# Side Effects:
2528#       None
2529
2530# a lower case version is needed for compatibility with tcltest 1.0
2531proc tcltest::getMatchingFiles args {eval GetMatchingFiles $args}
2532
2533proc tcltest::GetMatchingFiles { args } {
2534    if {[llength $args]} {
2535        set dirList $args
2536    } else {
2537        # Finding tests only in [testsDirectory] is normal operation.
2538        # This procedure is written to accept multiple directory arguments
2539        # only to satisfy version 1 compatibility.
2540        set dirList [list [testsDirectory]]
2541    }
2542
2543    set matchingFiles [list]
2544    foreach directory $dirList {
2545
2546        # List files in $directory that match patterns to run.
2547        set matchFileList [list]
2548        foreach match [matchFiles] {
2549            set matchFileList [concat $matchFileList \
2550                    [glob -directory $directory -nocomplain -- $match]]
2551        }
2552
2553        # List files in $directory that match patterns to skip.
2554        set skipFileList [list]
2555        foreach skip [skipFiles] {
2556            set skipFileList [concat $skipFileList \
2557                    [glob -directory $directory -nocomplain -- $skip]]
2558        }
2559
2560        # Add to result list all files in match list and not in skip list
2561        foreach file $matchFileList {
2562            if {[lsearch -exact $skipFileList $file] == -1} {
2563                lappend matchingFiles $file
2564            }
2565        }
2566    }
2567
2568    if {[llength $matchingFiles] == 0} {
2569        PrintError "No test files remain after applying your match and\
2570                skip patterns!"
2571    }
2572    return $matchingFiles
2573}
2574
2575# tcltest::GetMatchingDirectories --
2576#
2577#       Looks at the patterns given to match and skip directories and
2578#       uses them to put together a list of the test directories that we
2579#       should attempt to run.  (Only subdirectories containing an
2580#       "all.tcl" file are put into the list.)
2581#
2582# Arguments:
2583#       root directory from which to search
2584#
2585# Results:
2586#       The constructed list is returned to the user.  This is used in
2587#       the primary all.tcl file.
2588#
2589# Side Effects:
2590#       None.
2591
2592proc tcltest::GetMatchingDirectories {rootdir} {
2593
2594    # Determine the skip list first, to avoid [glob]-ing over subdirectories
2595    # we're going to throw away anyway.  Be sure we skip the $rootdir if it
2596    # comes up to avoid infinite loops.
2597    set skipDirs [list $rootdir]
2598    foreach pattern [skipDirectories] {
2599        foreach path [glob -directory $rootdir -nocomplain -- $pattern] {
2600            if {[file isdirectory $path]} {
2601                lappend skipDirs $path
2602            }
2603        }
2604    }
2605
2606    # Now step through the matching directories, prune out the skipped ones
2607    # as you go.
2608    set matchDirs [list]
2609    foreach pattern [matchDirectories] {
2610        foreach path [glob -directory $rootdir -nocomplain -- $pattern] {
2611            if {[file isdirectory $path]} {
2612                if {[lsearch -exact $skipDirs $path] == -1} {
2613                    set matchDirs [concat $matchDirs \
2614                            [GetMatchingDirectories $path]]
2615                    if {[file exists [file join $path all.tcl]]} {
2616                        lappend matchDirs $path
2617                    }
2618                }
2619            }
2620        }
2621    }
2622
2623    if {[llength $matchDirs] == 0} {
2624        DebugPuts 1 "No test directories remain after applying match\
2625                and skip patterns!"
2626    }
2627    return $matchDirs
2628}
2629
2630# tcltest::runAllTests --
2631#
2632#       prints output and sources test files according to the match and
2633#       skip patterns provided.  after sourcing test files, it goes on
2634#       to source all.tcl files in matching test subdirectories.
2635#
2636# Arguments:
2637#       shell being tested
2638#
2639# Results:
2640#       None.
2641#
2642# Side effects:
2643#       None.
2644
2645proc tcltest::runAllTests { {shell ""} } {
2646    variable testSingleFile
2647    variable numTestFiles
2648    variable numTests
2649    variable failFiles
2650
2651    FillFilesExisted
2652    if {[llength [info level 0]] == 1} {
2653        set shell [interpreter]
2654    }
2655
2656    set testSingleFile false
2657
2658    puts [outputChannel] "Tests running in interp:  $shell"
2659    puts [outputChannel] "Tests located in:  [testsDirectory]"
2660    puts [outputChannel] "Tests running in:  [workingDirectory]"
2661    puts [outputChannel] "Temporary files stored in\
2662            [temporaryDirectory]"
2663
2664    # [file system] first available in Tcl 8.4
2665    if {![catch {file system [testsDirectory]} result]
2666            && ![string equal native [lindex $result 0]]} {
2667        # If we aren't running in the native filesystem, then we must
2668        # run the tests in a single process (via 'source'), because
2669        # trying to run then via a pipe will fail since the files don't
2670        # really exist.
2671        singleProcess 1
2672    }
2673
2674    if {[singleProcess]} {
2675        puts [outputChannel] \
2676                "Test files sourced into current interpreter"
2677    } else {
2678        puts [outputChannel] \
2679                "Test files run in separate interpreters"
2680    }
2681    if {[llength [skip]] > 0} {
2682        puts [outputChannel] "Skipping tests that match:  [skip]"
2683    }
2684    puts [outputChannel] "Running tests that match:  [match]"
2685
2686    if {[llength [skipFiles]] > 0} {
2687        puts [outputChannel] \
2688                "Skipping test files that match:  [skipFiles]"
2689    }
2690    if {[llength [matchFiles]] > 0} {
2691        puts [outputChannel] \
2692                "Only running test files that match:  [matchFiles]"
2693    }
2694
2695    set timeCmd {clock format [clock seconds]}
2696    puts [outputChannel] "Tests began at [eval $timeCmd]"
2697
2698    # Run each of the specified tests
2699    foreach file [lsort [GetMatchingFiles]] {
2700        set tail [file tail $file]
2701        puts [outputChannel] $tail
2702        flush [outputChannel]
2703
2704        if {[singleProcess]} {
2705            incr numTestFiles
2706            uplevel 1 [list ::source $file]
2707        } else {
2708            # Pass along our configuration to the child processes.
2709            # EXCEPT for the -outfile, because the parent process
2710            # needs to read and process output of children.
2711            set childargv [list]
2712            foreach opt [Configure] {
2713                if {[string equal $opt -outfile]} {continue}
2714                lappend childargv $opt [Configure $opt]
2715            }
2716            set cmd [linsert $childargv 0 | $shell $file]
2717            if {[catch {
2718                incr numTestFiles
2719                set pipeFd [open $cmd "r"]
2720                while {[gets $pipeFd line] >= 0} {
2721                    if {[regexp [join {
2722                            {^([^:]+):\t}
2723                            {Total\t([0-9]+)\t}
2724                            {Passed\t([0-9]+)\t}
2725                            {Skipped\t([0-9]+)\t}
2726                            {Failed\t([0-9]+)}
2727                            } ""] $line null testFile \
2728                            Total Passed Skipped Failed]} {
2729                        foreach index {Total Passed Skipped Failed} {
2730                            incr numTests($index) [set $index]
2731                        }
2732                        if {$Failed > 0} {
2733                            lappend failFiles $testFile
2734                        }
2735                    } elseif {[regexp [join {
2736                            {^Number of tests skipped }
2737                            {for each constraint:}
2738                            {|^\t(\d+)\t(.+)$}
2739                            } ""] $line match skipped constraint]} {
2740                        if {[string match \t* $match]} {
2741                            AddToSkippedBecause $constraint $skipped
2742                        }
2743                    } else {
2744                        puts [outputChannel] $line
2745                    }
2746                }
2747                close $pipeFd
2748            } msg]} {
2749                puts [outputChannel] "Test file error: $msg"
2750                # append the name of the test to a list to be reported
2751                # later
2752                lappend testFileFailures $file
2753            }
2754        }
2755    }
2756
2757    # cleanup
2758    puts [outputChannel] "\nTests ended at [eval $timeCmd]"
2759    cleanupTests 1
2760    if {[info exists testFileFailures]} {
2761        puts [outputChannel] "\nTest files exiting with errors:  \n"
2762        foreach file $testFileFailures {
2763            puts [outputChannel] "  [file tail $file]\n"
2764        }
2765    }
2766
2767    # Checking for subdirectories in which to run tests
2768    foreach directory [GetMatchingDirectories [testsDirectory]] {
2769        set dir [file tail $directory]
2770        puts [outputChannel] [string repeat ~ 44]
2771        puts [outputChannel] "$dir test began at [eval $timeCmd]\n"
2772       
2773        uplevel 1 [list ::source [file join $directory all.tcl]]
2774       
2775        set endTime [eval $timeCmd]
2776        puts [outputChannel] "\n$dir test ended at $endTime"
2777        puts [outputChannel] ""
2778        puts [outputChannel] [string repeat ~ 44]
2779    }
2780    return
2781}
2782
2783#####################################################################
2784
2785# Test utility procs - not used in tcltest, but may be useful for
2786# testing.
2787
2788# tcltest::loadTestedCommands --
2789#
2790#     Uses the specified script to load the commands to test. Allowed to
2791#     be empty, as the tested commands could have been compiled into the
2792#     interpreter.
2793#
2794# Arguments
2795#     none
2796#
2797# Results
2798#     none
2799#
2800# Side Effects:
2801#     none.
2802
2803proc tcltest::loadTestedCommands {} {
2804    variable l
2805    if {[string equal {} [loadScript]]} {
2806        return
2807    }
2808
2809    return [uplevel 1 [loadScript]]
2810}
2811
2812# tcltest::saveState --
2813#
2814#       Save information regarding what procs and variables exist.
2815#
2816# Arguments:
2817#       none
2818#
2819# Results:
2820#       Modifies the variable saveState
2821#
2822# Side effects:
2823#       None.
2824
2825proc tcltest::saveState {} {
2826    variable saveState
2827    uplevel 1 [list ::set [namespace which -variable saveState]] \
2828            {[::list [::info procs] [::info vars]]}
2829    DebugPuts  2 "[lindex [info level 0] 0]: $saveState"
2830    return
2831}
2832
2833# tcltest::restoreState --
2834#
2835#       Remove procs and variables that didn't exist before the call to
2836#       [saveState].
2837#
2838# Arguments:
2839#       none
2840#
2841# Results:
2842#       Removes procs and variables from your environment if they don't
2843#       exist in the saveState variable.
2844#
2845# Side effects:
2846#       None.
2847
2848proc tcltest::restoreState {} {
2849    variable saveState
2850    foreach p [uplevel 1 {::info procs}] {
2851        if {([lsearch [lindex $saveState 0] $p] < 0)
2852                && ![string equal [namespace current]::$p \
2853                [uplevel 1 [list ::namespace origin $p]]]} {
2854
2855            DebugPuts 2 "[lindex [info level 0] 0]: Removing proc $p"
2856            uplevel 1 [list ::catch [list ::rename $p {}]]
2857        }
2858    }
2859    foreach p [uplevel 1 {::info vars}] {
2860        if {[lsearch [lindex $saveState 1] $p] < 0} {
2861            DebugPuts 2 "[lindex [info level 0] 0]:\
2862                    Removing variable $p"
2863            uplevel 1 [list ::catch [list ::unset $p]]
2864        }
2865    }
2866    return
2867}
2868
2869# tcltest::normalizeMsg --
2870#
2871#       Removes "extra" newlines from a string.
2872#
2873# Arguments:
2874#       msg        String to be modified
2875#
2876# Results:
2877#       string with extra newlines removed
2878#
2879# Side effects:
2880#       None.
2881
2882proc tcltest::normalizeMsg {msg} {
2883    regsub "\n$" [string tolower $msg] "" msg
2884    set msg [string map [list "\n\n" "\n"] $msg]
2885    return [string map [list "\n\}" "\}"] $msg]
2886}
2887
2888# tcltest::makeFile --
2889#
2890# Create a new file with the name <name>, and write <contents> to it.
2891#
2892# If this file hasn't been created via makeFile since the last time
2893# cleanupTests was called, add it to the $filesMade list, so it will be
2894# removed by the next call to cleanupTests.
2895#
2896# Arguments:
2897#       contents        content of the new file
2898#       name            name of the new file
2899#       directory       directory name for new file
2900#
2901# Results:
2902#       absolute path to the file created
2903#
2904# Side effects:
2905#       None.
2906
2907proc tcltest::makeFile {contents name {directory ""}} {
2908    variable filesMade
2909    FillFilesExisted
2910
2911    if {[llength [info level 0]] == 3} {
2912        set directory [temporaryDirectory]
2913    }
2914
2915    set fullName [file join $directory $name]
2916
2917    DebugPuts 3 "[lindex [info level 0] 0]:\
2918             putting ``$contents'' into $fullName"
2919
2920    set fd [open $fullName w]
2921    fconfigure $fd -translation lf
2922    if {[string equal [string index $contents end] \n]} {
2923        puts -nonewline $fd $contents
2924    } else {
2925        puts $fd $contents
2926    }
2927    close $fd
2928
2929    if {[lsearch -exact $filesMade $fullName] == -1} {
2930        lappend filesMade $fullName
2931    }
2932    return $fullName
2933}
2934
2935# tcltest::removeFile --
2936#
2937#       Removes the named file from the filesystem
2938#
2939# Arguments:
2940#       name          file to be removed
2941#       directory     directory from which to remove file
2942#
2943# Results:
2944#       return value from [file delete]
2945#
2946# Side effects:
2947#       None.
2948
2949proc tcltest::removeFile {name {directory ""}} {
2950    variable filesMade
2951    FillFilesExisted
2952    if {[llength [info level 0]] == 2} {
2953        set directory [temporaryDirectory]
2954    }
2955    set fullName [file join $directory $name]
2956    DebugPuts 3 "[lindex [info level 0] 0]: removing $fullName"
2957    set idx [lsearch -exact $filesMade $fullName]
2958    set filesMade [lreplace $filesMade $idx $idx]
2959    if {$idx == -1} {
2960        DebugDo 1 {
2961            Warn "removeFile removing \"$fullName\":\n  not created by makeFile"
2962        }
2963    } 
2964    if {![file isfile $fullName]} {
2965        DebugDo 1 {
2966            Warn "removeFile removing \"$fullName\":\n  not a file"
2967        }
2968    }
2969    return [file delete $fullName]
2970}
2971
2972# tcltest::makeDirectory --
2973#
2974# Create a new dir with the name <name>.
2975#
2976# If this dir hasn't been created via makeDirectory since the last time
2977# cleanupTests was called, add it to the $directoriesMade list, so it
2978# will be removed by the next call to cleanupTests.
2979#
2980# Arguments:
2981#       name            name of the new directory
2982#       directory       directory in which to create new dir
2983#
2984# Results:
2985#       absolute path to the directory created
2986#
2987# Side effects:
2988#       None.
2989
2990proc tcltest::makeDirectory {name {directory ""}} {
2991    variable filesMade
2992    FillFilesExisted
2993    if {[llength [info level 0]] == 2} {
2994        set directory [temporaryDirectory]
2995    }
2996    set fullName [file join $directory $name]
2997    DebugPuts 3 "[lindex [info level 0] 0]: creating $fullName"
2998    file mkdir $fullName
2999    if {[lsearch -exact $filesMade $fullName] == -1} {
3000        lappend filesMade $fullName
3001    }
3002    return $fullName
3003}
3004
3005# tcltest::removeDirectory --
3006#
3007#       Removes a named directory from the file system.
3008#
3009# Arguments:
3010#       name          Name of the directory to remove
3011#       directory     Directory from which to remove
3012#
3013# Results:
3014#       return value from [file delete]
3015#
3016# Side effects:
3017#       None
3018
3019proc tcltest::removeDirectory {name {directory ""}} {
3020    variable filesMade
3021    FillFilesExisted
3022    if {[llength [info level 0]] == 2} {
3023        set directory [temporaryDirectory]
3024    }
3025    set fullName [file join $directory $name]
3026    DebugPuts 3 "[lindex [info level 0] 0]: deleting $fullName"
3027    set idx [lsearch -exact $filesMade $fullName]
3028    set filesMade [lreplace $filesMade $idx $idx]
3029    if {$idx == -1} {
3030        DebugDo 1 {
3031            Warn "removeDirectory removing \"$fullName\":\n  not created\
3032                    by makeDirectory"
3033        }
3034    } 
3035    if {![file isdirectory $fullName]} {
3036        DebugDo 1 {
3037            Warn "removeDirectory removing \"$fullName\":\n  not a directory"
3038        }
3039    }
3040    return [file delete -force $fullName]
3041}
3042
3043# tcltest::viewFile --
3044#
3045#       reads the content of a file and returns it
3046#
3047# Arguments:
3048#       name of the file to read
3049#       directory in which file is located
3050#
3051# Results:
3052#       content of the named file
3053#
3054# Side effects:
3055#       None.
3056
3057proc tcltest::viewFile {name {directory ""}} {
3058    FillFilesExisted
3059    if {[llength [info level 0]] == 2} {
3060        set directory [temporaryDirectory]
3061    }
3062    set fullName [file join $directory $name]
3063    set f [open $fullName]
3064    set data [read -nonewline $f]
3065    close $f
3066    return $data
3067}
3068
3069# tcltest::bytestring --
3070#
3071# Construct a string that consists of the requested sequence of bytes,
3072# as opposed to a string of properly formed UTF-8 characters.
3073# This allows the tester to
3074# 1. Create denormalized or improperly formed strings to pass to C
3075#    procedures that are supposed to accept strings with embedded NULL
3076#    bytes.
3077# 2. Confirm that a string result has a certain pattern of bytes, for
3078#    instance to confirm that "\xe0\0" in a Tcl script is stored
3079#    internally in UTF-8 as the sequence of bytes "\xc3\xa0\xc0\x80".
3080#
3081# Generally, it's a bad idea to examine the bytes in a Tcl string or to
3082# construct improperly formed strings in this manner, because it involves
3083# exposing that Tcl uses UTF-8 internally.
3084#
3085# Arguments:
3086#       string being converted
3087#
3088# Results:
3089#       result fom encoding
3090#
3091# Side effects:
3092#       None
3093
3094proc tcltest::bytestring {string} {
3095    return [encoding convertfrom identity $string]
3096}
3097
3098# tcltest::OpenFiles --
3099#
3100#       used in io tests, uses testchannel
3101#
3102# Arguments:
3103#       None.
3104#
3105# Results:
3106#       ???
3107#
3108# Side effects:
3109#       None.
3110
3111proc tcltest::OpenFiles {} {
3112    if {[catch {testchannel open} result]} {
3113        return {}
3114    }
3115    return $result
3116}
3117
3118# tcltest::LeakFiles --
3119#
3120#       used in io tests, uses testchannel
3121#
3122# Arguments:
3123#       None.
3124#
3125# Results:
3126#       ???
3127#
3128# Side effects:
3129#       None.
3130
3131proc tcltest::LeakFiles {old} {
3132    if {[catch {testchannel open} new]} {
3133        return {}
3134    }
3135    set leak {}
3136    foreach p $new {
3137        if {[lsearch $old $p] < 0} {
3138            lappend leak $p
3139        }
3140    }
3141    return $leak
3142}
3143
3144#
3145# Internationalization / ISO support procs     -- dl
3146#
3147
3148# tcltest::SetIso8859_1_Locale --
3149#
3150#       used in cmdIL.test, uses testlocale
3151#
3152# Arguments:
3153#       None.
3154#
3155# Results:
3156#       None.
3157#
3158# Side effects:
3159#       None.
3160
3161proc tcltest::SetIso8859_1_Locale {} {
3162    variable previousLocale
3163    variable isoLocale
3164    if {[info commands testlocale] != ""} {
3165        set previousLocale [testlocale ctype]
3166        testlocale ctype $isoLocale
3167    }
3168    return
3169}
3170
3171# tcltest::RestoreLocale --
3172#
3173#       used in cmdIL.test, uses testlocale
3174#
3175# Arguments:
3176#       None.
3177#
3178# Results:
3179#       None.
3180#
3181# Side effects:
3182#       None.
3183
3184proc tcltest::RestoreLocale {} {
3185    variable previousLocale
3186    if {[info commands testlocale] != ""} {
3187        testlocale ctype $previousLocale
3188    }
3189    return
3190}
3191
3192# tcltest::threadReap --
3193#
3194#       Kill all threads except for the main thread.
3195#       Do nothing if testthread is not defined.
3196#
3197# Arguments:
3198#       none.
3199#
3200# Results:
3201#       Returns the number of existing threads.
3202#
3203# Side Effects:
3204#       none.
3205#
3206
3207proc tcltest::threadReap {} {
3208    if {[info commands testthread] != {}} {
3209
3210        # testthread built into tcltest
3211
3212        testthread errorproc ThreadNullError
3213        while {[llength [testthread names]] > 1} {
3214            foreach tid [testthread names] {
3215                if {$tid != [mainThread]} {
3216                    catch {
3217                        testthread send -async $tid {testthread exit}
3218                    }
3219                }
3220            }
3221            ## Enter a bit a sleep to give the threads enough breathing
3222            ## room to kill themselves off, otherwise the end up with a
3223            ## massive queue of repeated events
3224            after 1
3225        }
3226        testthread errorproc ThreadError
3227        return [llength [testthread names]]
3228    } elseif {[info commands thread::id] != {}} {
3229       
3230        # Thread extension
3231
3232        thread::errorproc ThreadNullError
3233        while {[llength [thread::names]] > 1} {
3234            foreach tid [thread::names] {
3235                if {$tid != [mainThread]} {
3236                    catch {thread::send -async $tid {thread::exit}}
3237                }
3238            }
3239            ## Enter a bit a sleep to give the threads enough breathing
3240            ## room to kill themselves off, otherwise the end up with a
3241            ## massive queue of repeated events
3242            after 1
3243        }
3244        thread::errorproc ThreadError
3245        return [llength [thread::names]]
3246    } else {
3247        return 1
3248    }
3249    return 0
3250}
3251
3252# Initialize the constraints and set up command line arguments
3253namespace eval tcltest {
3254    # Define initializers for all the built-in contraint definitions
3255    DefineConstraintInitializers
3256
3257    # Set up the constraints in the testConstraints array to be lazily
3258    # initialized by a registered initializer, or by "false" if no
3259    # initializer is registered.
3260    trace variable testConstraints r [namespace code SafeFetch]
3261
3262    # Only initialize constraints at package load time if an
3263    # [initConstraintsHook] has been pre-defined.  This is only
3264    # for compatibility support.  The modern way to add a custom
3265    # test constraint is to just call the [testConstraint] command
3266    # straight away, without all this "hook" nonsense.
3267    if {[string equal [namespace current] \
3268            [namespace qualifiers [namespace which initConstraintsHook]]]} {
3269        InitConstraints
3270    } else {
3271        proc initConstraintsHook {} {}
3272    }
3273
3274    # Define the standard match commands
3275    customMatch exact   [list string equal]
3276    customMatch glob    [list string match]
3277    customMatch regexp  [list regexp --]
3278
3279    # If the TCLTEST_OPTIONS environment variable exists, configure
3280    # tcltest according to the option values it specifies.  This has
3281    # the effect of resetting tcltest's default configuration.
3282    proc ConfigureFromEnvironment {} {
3283        upvar #0 env(TCLTEST_OPTIONS) options
3284        if {[catch {llength $options} msg]} {
3285            Warn "invalid TCLTEST_OPTIONS \"$options\":\n  invalid\
3286                    Tcl list: $msg"
3287            return
3288        }
3289        if {[llength $::env(TCLTEST_OPTIONS)] % 2} {
3290            Warn "invalid TCLTEST_OPTIONS: \"$options\":\n  should be\
3291                    -option value ?-option value ...?"
3292            return
3293        }
3294        if {[catch {eval Configure $::env(TCLTEST_OPTIONS)} msg]} {
3295            Warn "invalid TCLTEST_OPTIONS: \"$options\":\n  $msg"
3296            return
3297        }
3298    }
3299    if {[info exists ::env(TCLTEST_OPTIONS)]} {
3300        ConfigureFromEnvironment
3301    }
3302
3303    proc LoadTimeCmdLineArgParsingRequired {} {
3304        set required false
3305        if {[info exists ::argv] && [lsearch -exact $::argv -help] != -1} {
3306            # The command line asks for -help, so give it (and exit)
3307            # right now.  ([configure] does not process -help)
3308            set required true
3309        }
3310        foreach hook { PrintUsageInfoHook processCmdLineArgsHook
3311                        processCmdLineArgsAddFlagsHook } {
3312            if {[string equal [namespace current] [namespace qualifiers \
3313                    [namespace which $hook]]]} {
3314                set required true
3315            } else {
3316                proc $hook args {}
3317            }
3318        }
3319        return $required
3320    }
3321
3322    # Only initialize configurable options from the command line arguments
3323    # at package load time if necessary for backward compatibility.  This
3324    # lets the tcltest user call [configure] for themselves if they wish.
3325    # Traces are established for auto-configuration from the command line
3326    # if any configurable options are accessed before the user calls
3327    # [configure].
3328    if {[LoadTimeCmdLineArgParsingRequired]} {
3329        ProcessCmdLineArgs
3330    } else {
3331        EstablishAutoConfigureTraces
3332    }
3333
3334    package provide [namespace tail [namespace current]] $Version
3335}
Note: See TracBrowser for help on using the repository browser.