home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 April / PCWorld_2001-04_cd.bin / Software / TemaCD / webclean / !!!python!!! / BeOpen-Python-2.0.exe / TCLTEST.TCL < prev    next >
Encoding:
Text File  |  2000-04-10  |  57.8 KB  |  1,906 lines

  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, etc. used
  6. #       by Tcl tests.  See the tcltest man page for more details.
  7. #       
  8. #       This design was based on the Tcl testing approach designed and
  9. #       initially implemented by Mary Ann May-Pumphrey of Sun Microsystems. 
  10. #
  11. # Copyright (c) 1994-1997 Sun Microsystems, Inc.
  12. # Copyright (c) 1998-1999 by Scriptics Corporation.
  13. # All rights reserved.
  14. # RCS: @(#) $Id: tcltest.tcl,v 1.24 2000/04/11 01:04:19 welch Exp $
  15.  
  16. package provide tcltest 1.0
  17.  
  18. # create the "tcltest" namespace for all testing variables and procedures
  19.  
  20. namespace eval tcltest { 
  21.  
  22.     # Export the public tcltest procs
  23.     set procList [list test cleanupTests saveState restoreState \
  24.         normalizeMsg makeFile removeFile makeDirectory removeDirectory \
  25.         viewFile bytestring safeFetch threadReap getMatchingFiles \
  26.         loadTestedCommands normalizePath]
  27.     foreach proc $procList {
  28.     namespace export $proc
  29.     }
  30.  
  31.     # ::tcltest::verbose defaults to "b"
  32.     if {![info exists verbose]} {
  33.     variable verbose "b"
  34.     }
  35.  
  36.     # Match and skip patterns default to the empty list, except for
  37.     # matchFiles, which defaults to all .test files in the testsDirectory
  38.  
  39.     if {![info exists match]} {
  40.     variable match {}
  41.     }
  42.     if {![info exists skip]} {
  43.     variable skip {}
  44.     }
  45.     if {![info exists matchFiles]} {
  46.     variable matchFiles {*.test}
  47.     }
  48.     if {![info exists skipFiles]} {
  49.     variable skipFiles {}
  50.     }
  51.  
  52.     # By default, don't save core files
  53.     if {![info exists preserveCore]} {
  54.     variable preserveCore 0
  55.     }
  56.  
  57.     # output goes to stdout by default
  58.     if {![info exists outputChannel]} {
  59.     variable outputChannel stdout
  60.     }
  61.  
  62.     # errors go to stderr by default
  63.     if {![info exists errorChannel]} {
  64.     variable errorChannel stderr
  65.     }
  66.  
  67.     # debug output doesn't get printed by default; debug level 1 spits
  68.     # up only the tests that were skipped because they didn't match or were 
  69.     # specifically skipped.  A debug level of 2 would spit up the tcltest
  70.     # variables and flags provided; a debug level of 3 causes some additional
  71.     # output regarding operations of the test harness.  The tcltest package
  72.     # currently implements only up to debug level 3.
  73.     if {![info exists debug]} {
  74.     variable debug 0
  75.     }
  76.  
  77.     # Save any arguments that we might want to pass through to other programs. 
  78.     # This is used by the -args flag.
  79.     if {![info exists parameters]} {
  80.     variable parameters {}
  81.     }
  82.  
  83.     # Count the number of files tested (0 if all.tcl wasn't called).
  84.     # The all.tcl file will set testSingleFile to false, so stats will
  85.     # not be printed until all.tcl calls the cleanupTests proc.
  86.     # The currentFailure var stores the boolean value of whether the
  87.     # current test file has had any failures.  The failFiles list
  88.     # stores the names of test files that had failures.
  89.  
  90.     if {![info exists numTestFiles]} {
  91.     variable numTestFiles 0
  92.     }
  93.     if {![info exists testSingleFile]} {
  94.     variable testSingleFile true
  95.     }
  96.     if {![info exists currentFailure]} {
  97.     variable currentFailure false
  98.     }
  99.     if {![info exists failFiles]} {
  100.     variable failFiles {}
  101.     }
  102.  
  103.     # Tests should remove all files they create.  The test suite will
  104.     # check the current working dir for files created by the tests.
  105.     # ::tcltest::filesMade keeps track of such files created using the
  106.     # ::tcltest::makeFile and ::tcltest::makeDirectory procedures.
  107.     # ::tcltest::filesExisted stores the names of pre-existing files.
  108.  
  109.     if {![info exists filesMade]} {
  110.     variable filesMade {}
  111.     }
  112.     if {![info exists filesExisted]} {
  113.     variable filesExisted {}
  114.     }
  115.  
  116.     # ::tcltest::numTests will store test files as indices and the list
  117.     # of files (that should not have been) left behind by the test files.
  118.  
  119.     if {![info exists createdNewFiles]} {
  120.     variable createdNewFiles
  121.     array set ::tcltest::createdNewFiles {}
  122.     }
  123.  
  124.     # initialize ::tcltest::numTests array to keep track fo the number of
  125.     # tests that pass, fail, and are skipped.
  126.  
  127.     if {![info exists numTests]} {
  128.     variable numTests
  129.     array set ::tcltest::numTests \
  130.         [list Total 0 Passed 0 Skipped 0 Failed    0] 
  131.     }
  132.  
  133.     # initialize ::tcltest::skippedBecause array to keep track of
  134.     # constraints that kept tests from running; a constraint name of
  135.     # "userSpecifiedSkip" means that the test appeared on the list of tests
  136.     # that matched the -skip value given to the flag; "userSpecifiedNonMatch"
  137.     # means that the test didn't match the argument given to the -match flag;
  138.     # both of these constraints are counted only if ::tcltest::debug is set to
  139.     # true. 
  140.  
  141.     if {![info exists skippedBecause]} {
  142.     variable skippedBecause
  143.     array set ::tcltest::skippedBecause {}
  144.     }
  145.  
  146.     # initialize the ::tcltest::testConstraints array to keep track of valid
  147.     # predefined constraints (see the explanation for the
  148.     # ::tcltest::initConstraints proc for more details).
  149.  
  150.     if {![info exists testConstraints]} {
  151.     variable testConstraints
  152.     array set ::tcltest::testConstraints {}
  153.     }
  154.  
  155.     # Don't run only the constrained tests by default
  156.  
  157.     if {![info exists limitConstraints]} {
  158.     variable limitConstraints false
  159.     }
  160.  
  161.     # A test application has to know how to load the tested commands into
  162.     # the interpreter.
  163.  
  164.     if {![info exists loadScript]} {
  165.     variable loadScript {}
  166.     }
  167.  
  168.     # tests that use threads need to know which is the main thread
  169.  
  170.     if {![info exists mainThread]} {
  171.     variable mainThread 1
  172.     if {[info commands thread::id] != {}} {
  173.         set mainThread [thread::id]
  174.     } elseif {[info commands testthread] != {}} {
  175.         set mainThread [testthread id]
  176.     }
  177.     }
  178.  
  179.     # save the original environment so that it can be restored later
  180.     
  181.     if {![info exists originalEnv]} {
  182.     variable originalEnv
  183.     array set ::tcltest::originalEnv [array get ::env]
  184.     }
  185.  
  186.     # Set ::tcltest::workingDirectory to [pwd]. The default output directory
  187.     # for Tcl tests is the working directory.
  188.  
  189.     if {![info exists workingDirectory]} {
  190.     variable workingDirectory [pwd]
  191.     }
  192.     if {![info exists temporaryDirectory]} {
  193.     variable temporaryDirectory $workingDirectory
  194.     }
  195.  
  196.     # Tests should not rely on the current working directory.
  197.     # Files that are part of the test suite should be accessed relative to 
  198.     # ::tcltest::testsDirectory.
  199.  
  200.     if {![info exists testsDirectory]} {
  201.     set oldpwd [pwd]
  202.     catch {cd [file join [file dirname [info script]] .. .. tests]}
  203.     variable testsDirectory [pwd]
  204.     cd $oldpwd
  205.     unset oldpwd
  206.     }
  207.  
  208.     # the variables and procs that existed when ::tcltest::saveState was
  209.     # called are stored in a variable of the same name
  210.     if {![info exists saveState]} {
  211.     variable saveState {}
  212.     }
  213.  
  214.     # Internationalization support
  215.     if {![info exists isoLocale]} {
  216.     variable isoLocale fr
  217.         switch $tcl_platform(platform) {
  218.         "unix" {
  219.  
  220.         # Try some 'known' values for some platforms:
  221.  
  222.         switch -exact -- $tcl_platform(os) {
  223.             "FreeBSD" {
  224.             set ::tcltest::isoLocale fr_FR.ISO_8859-1
  225.             }
  226.             HP-UX {
  227.             set ::tcltest::isoLocale fr_FR.iso88591
  228.             }
  229.             Linux -
  230.             IRIX {
  231.             set ::tcltest::isoLocale fr
  232.             }
  233.             default {
  234.  
  235.             # Works on SunOS 4 and Solaris, and maybe others...
  236.             # define it to something else on your system
  237.             #if you want to test those.
  238.  
  239.             set ::tcltest::isoLocale iso_8859_1
  240.             }
  241.         }
  242.         }
  243.         "windows" {
  244.         set ::tcltest::isoLocale French
  245.         }
  246.     }
  247.     }
  248.  
  249.     # Set the location of the execuatble
  250.     if {![info exists tcltest]} {
  251.     variable tcltest [info nameofexecutable]
  252.     }
  253.  
  254.     # save the platform information so it can be restored later
  255.     if {![info exists originalTclPlatform]} {
  256.     variable originalTclPlatform [array get tcl_platform]
  257.     }
  258.  
  259.     # If a core file exists, save its modification time.
  260.     if {![info exists coreModificationTime]} {
  261.     if {[file exists [file join $::tcltest::workingDirectory core]]} {
  262.         variable coreModificationTime [file mtime [file join \
  263.             $::tcltest::workingDirectory core]]
  264.     }
  265.     }
  266.  
  267.     # Tcl version numbers
  268.     if {![info exists version]} {
  269.     variable version 8.3
  270.     }
  271.     if {![info exists patchLevel]} {
  272.     variable patchLevel 8.3.0
  273.     }
  274. }   
  275.  
  276. # ::tcltest::Debug* --
  277. #
  278. #     Internal helper procedures to write out debug information
  279. #     dependent on the chosen level. A test shell may overide
  280. #     them, f.e. to redirect the output into a different
  281. #     channel, or even into a GUI.
  282.  
  283. # ::tcltest::DebugPuts --
  284. #
  285. #     Prints the specified string if the current debug level is
  286. #     higher than the provided level argument.
  287. #
  288. # Arguments:
  289. #     level   The lowest debug level triggering the output
  290. #     string  The string to print out.
  291. #
  292. # Results:
  293. #     Prints the string. Nothing else is allowed.
  294. #
  295.  
  296. proc ::tcltest::DebugPuts {level string} {
  297.     variable debug
  298.     if {$debug >= $level} {
  299.     puts $string
  300.     }
  301. }
  302.  
  303. # ::tcltest::DebugPArray --
  304. #
  305. #     Prints the contents of the specified array if the current
  306. #       debug level is higher than the provided level argument
  307. #
  308. # Arguments:
  309. #     level           The lowest debug level triggering the output
  310. #     arrayvar        The name of the array to print out.
  311. #
  312. # Results:
  313. #     Prints the contents of the array. Nothing else is allowed.
  314. #
  315.  
  316. proc ::tcltest::DebugPArray {level arrayvar} {
  317.     variable debug
  318.  
  319.     if {$debug >= $level} {
  320.     catch {upvar  $arrayvar $arrayvar}
  321.     parray $arrayvar
  322.     }
  323. }
  324.  
  325. # ::tcltest::DebugDo --
  326. #
  327. #     Executes the script if the current debug level is greater than
  328. #       the provided level argument
  329. #
  330. # Arguments:
  331. #     level   The lowest debug level triggering the execution.
  332. #     script  The tcl script executed upon a debug level high enough.
  333. #
  334. # Results:
  335. #     Arbitrary side effects, dependent on the executed script.
  336. #
  337.  
  338. proc ::tcltest::DebugDo {level script} {
  339.     variable debug
  340.  
  341.     if {$debug >= $level} {
  342.     uplevel $script
  343.     }
  344. }
  345.  
  346. # ::tcltest::AddToSkippedBecause --
  347. #
  348. #    Increments the variable used to track how many tests were skipped
  349. #       because of a particular constraint.
  350. #
  351. # Arguments:
  352. #    constraint     The name of the constraint to be modified
  353. #
  354. # Results:
  355. #    Modifies ::tcltest::skippedBecause; sets the variable to 1 if didn't
  356. #       previously exist - otherwise, it just increments it.
  357.  
  358. proc ::tcltest::AddToSkippedBecause { constraint } {
  359.     # add the constraint to the list of constraints that kept tests
  360.     # from running
  361.  
  362.     if {[info exists ::tcltest::skippedBecause($constraint)]} {
  363.     incr ::tcltest::skippedBecause($constraint)
  364.     } else {
  365.     set ::tcltest::skippedBecause($constraint) 1
  366.     }
  367.     return
  368. }
  369.  
  370. # ::tcltest::PrintError --
  371. #
  372. #    Prints errors to ::tcltest::errorChannel and then flushes that
  373. #       channel, making sure that all messages are < 80 characters per line.
  374. #
  375. # Arguments:
  376. #    errorMsg     String containing the error to be printed
  377. #
  378.  
  379. proc ::tcltest::PrintError {errorMsg} {
  380.     set InitialMessage "Error:  "
  381.     set InitialMsgLen  [string length $InitialMessage]
  382.     puts -nonewline $::tcltest::errorChannel $InitialMessage
  383.  
  384.     # Keep track of where the end of the string is.
  385.     set endingIndex [string length $errorMsg]
  386.  
  387.     if {$endingIndex < 80} {
  388.     puts $::tcltest::errorChannel $errorMsg
  389.     } else {
  390.     # Print up to 80 characters on the first line, including the
  391.     # InitialMessage. 
  392.     set beginningIndex [string last " " [string range $errorMsg 0 \
  393.         [expr {80 - $InitialMsgLen}]]]
  394.     puts $::tcltest::errorChannel [string range $errorMsg 0 $beginningIndex]
  395.  
  396.     while {$beginningIndex != "end"} {
  397.         puts -nonewline $::tcltest::errorChannel \
  398.             [string repeat " " $InitialMsgLen]  
  399.         if {[expr {$endingIndex - $beginningIndex}] < 72} {
  400.         puts $::tcltest::errorChannel [string trim \
  401.             [string range $errorMsg $beginningIndex end]]
  402.         set beginningIndex end
  403.         } else {
  404.         set newEndingIndex [expr [string last " " [string range \
  405.             $errorMsg $beginningIndex \
  406.             [expr {$beginningIndex + 72}]]] + $beginningIndex]
  407.         if {($newEndingIndex <= 0) \
  408.             || ($newEndingIndex <= $beginningIndex)} {
  409.             set newEndingIndex end
  410.         }
  411.         puts $::tcltest::errorChannel [string trim \
  412.             [string range $errorMsg \
  413.             $beginningIndex $newEndingIndex]]
  414.         set beginningIndex $newEndingIndex
  415.         }
  416.     }
  417.     }
  418.     flush $::tcltest::errorChannel
  419.     return
  420. }
  421.  
  422. if {[namespace inscope ::tcltest info procs initConstraintsHook] == {}} {
  423.     proc ::tcltest::initConstraintsHook {} {}
  424. }
  425.  
  426. # ::tcltest::initConstraints --
  427. #
  428. # Check Constraintsuration information that will determine which tests
  429. # to run.  To do this, create an array ::tcltest::testConstraints.  Each
  430. # element has a 0 or 1 value.  If the element is "true" then tests
  431. # with that constraint will be run, otherwise tests with that constraint
  432. # will be skipped.  See the tcltest man page for the list of built-in
  433. # constraints defined in this procedure.
  434. #
  435. # Arguments:
  436. #    none
  437. #
  438. # Results:
  439. #    The ::tcltest::testConstraints array is reset to have an index for
  440. #    each built-in test constraint.
  441.  
  442. proc ::tcltest::initConstraints {} {
  443.     global tcl_platform tcl_interactive tk_version
  444.  
  445.     # The following trace procedure makes it so that we can safely refer to
  446.     # non-existent members of the ::tcltest::testConstraints array without
  447.     # causing an error.  Instead, reading a non-existent member will return 0.
  448.     # This is necessary because tests are allowed to use constraint "X" without
  449.     # ensuring that ::tcltest::testConstraints("X") is defined.
  450.  
  451.     trace variable ::tcltest::testConstraints r ::tcltest::safeFetch
  452.  
  453.     proc ::tcltest::safeFetch {n1 n2 op} {
  454.     if {($n2 != {}) && ([info exists ::tcltest::testConstraints($n2)] == 0)} {
  455.         set ::tcltest::testConstraints($n2) 0
  456.     }
  457.     }
  458.  
  459.     ::tcltest::initConstraintsHook
  460.  
  461.     set ::tcltest::testConstraints(unixOnly) \
  462.         [string equal $tcl_platform(platform) "unix"]
  463.     set ::tcltest::testConstraints(macOnly) \
  464.         [string equal $tcl_platform(platform) "macintosh"]
  465.     set ::tcltest::testConstraints(pcOnly) \
  466.         [string equal $tcl_platform(platform) "windows"]
  467.  
  468.     set ::tcltest::testConstraints(unix) $::tcltest::testConstraints(unixOnly)
  469.     set ::tcltest::testConstraints(mac) $::tcltest::testConstraints(macOnly)
  470.     set ::tcltest::testConstraints(pc) $::tcltest::testConstraints(pcOnly)
  471.  
  472.     set ::tcltest::testConstraints(unixOrPc) \
  473.         [expr {$::tcltest::testConstraints(unix) \
  474.         || $::tcltest::testConstraints(pc)}]
  475.     set ::tcltest::testConstraints(macOrPc) \
  476.         [expr {$::tcltest::testConstraints(mac) \
  477.         || $::tcltest::testConstraints(pc)}]
  478.     set ::tcltest::testConstraints(macOrUnix) \
  479.         [expr {$::tcltest::testConstraints(mac) \
  480.         || $::tcltest::testConstraints(unix)}]
  481.  
  482.     set ::tcltest::testConstraints(nt) [string equal $tcl_platform(os) \
  483.         "Windows NT"]
  484.     set ::tcltest::testConstraints(95) [string equal $tcl_platform(os) \
  485.         "Windows 95"]
  486.     set ::tcltest::testConstraints(98) [string equal $tcl_platform(os) \
  487.         "Windows 98"]
  488.  
  489.     # The following Constraints switches are used to mark tests that should
  490.     # work, but have been temporarily disabled on certain platforms because
  491.     # they don't and we haven't gotten around to fixing the underlying
  492.     # problem. 
  493.  
  494.     set ::tcltest::testConstraints(tempNotPc) \
  495.         [expr {!$::tcltest::testConstraints(pc)}]
  496.     set ::tcltest::testConstraints(tempNotMac) \
  497.         [expr {!$::tcltest::testConstraints(mac)}]
  498.     set ::tcltest::testConstraints(tempNotUnix) \
  499.         [expr {!$::tcltest::testConstraints(unix)}]
  500.  
  501.     # The following Constraints switches are used to mark tests that crash on
  502.     # certain platforms, so that they can be reactivated again when the
  503.     # underlying problem is fixed.
  504.  
  505.     set ::tcltest::testConstraints(pcCrash) \
  506.         [expr {!$::tcltest::testConstraints(pc)}]
  507.     set ::tcltest::testConstraints(macCrash) \
  508.         [expr {!$::tcltest::testConstraints(mac)}]
  509.     set ::tcltest::testConstraints(unixCrash) \
  510.         [expr {!$::tcltest::testConstraints(unix)}]
  511.  
  512.     # Skip empty tests
  513.  
  514.     set ::tcltest::testConstraints(emptyTest) 0
  515.  
  516.     # By default, tests that expose known bugs are skipped.
  517.  
  518.     set ::tcltest::testConstraints(knownBug) 0
  519.  
  520.     # By default, non-portable tests are skipped.
  521.  
  522.     set ::tcltest::testConstraints(nonPortable) 0
  523.  
  524.     # Some tests require user interaction.
  525.  
  526.     set ::tcltest::testConstraints(userInteraction) 0
  527.  
  528.     # Some tests must be skipped if the interpreter is not in interactive mode
  529.     
  530.     if {[info exists tcl_interactive]} {
  531.     set ::tcltest::testConstraints(interactive) $::tcl_interactive
  532.     } else {
  533.     set ::tcltest::testConstraints(interactive) 0
  534.     }
  535.  
  536.     # Some tests can only be run if the installation came from a CD image
  537.     # instead of a web image
  538.     # Some tests must be skipped if you are running as root on Unix.
  539.     # Other tests can only be run if you are running as root on Unix.
  540.  
  541.     set ::tcltest::testConstraints(root) 0
  542.     set ::tcltest::testConstraints(notRoot) 1
  543.     set user {}
  544.     if {[string equal $tcl_platform(platform) "unix"]} {
  545.     catch {set user [exec whoami]}
  546.     if {[string equal $user ""]} {
  547.         catch {regexp {^[^(]*\(([^)]*)\)} [exec id] dummy user}
  548.     }
  549.     if {([string equal $user "root"]) || ([string equal $user ""])} {
  550.         set ::tcltest::testConstraints(root) 1
  551.         set ::tcltest::testConstraints(notRoot) 0
  552.     }
  553.     }
  554.  
  555.     # Set nonBlockFiles constraint: 1 means this platform supports
  556.     # setting files into nonblocking mode.
  557.  
  558.     if {[catch {set f [open defs r]}]} {
  559.     set ::tcltest::testConstraints(nonBlockFiles) 1
  560.     } else {
  561.     if {[catch {fconfigure $f -blocking off}] == 0} {
  562.         set ::tcltest::testConstraints(nonBlockFiles) 1
  563.     } else {
  564.         set ::tcltest::testConstraints(nonBlockFiles) 0
  565.     }
  566.     close $f
  567.     }
  568.  
  569.     # Set asyncPipeClose constraint: 1 means this platform supports
  570.     # async flush and async close on a pipe.
  571.     #
  572.     # Test for SCO Unix - cannot run async flushing tests because a
  573.     # potential problem with select is apparently interfering.
  574.     # (Mark Diekhans).
  575.  
  576.     if {[string equal $tcl_platform(platform) "unix"]} {
  577.     if {[catch {exec uname -X | fgrep {Release = 3.2v}}] == 0} {
  578.         set ::tcltest::testConstraints(asyncPipeClose) 0
  579.     } else {
  580.         set ::tcltest::testConstraints(asyncPipeClose) 1
  581.     }
  582.     } else {
  583.     set ::tcltest::testConstraints(asyncPipeClose) 1
  584.     }
  585.  
  586.     # Test to see if we have a broken version of sprintf with respect
  587.     # to the "e" format of floating-point numbers.
  588.  
  589.     set ::tcltest::testConstraints(eformat) 1
  590.     if {![string equal "[format %g 5e-5]" "5e-05"]} {
  591.     set ::tcltest::testConstraints(eformat) 0
  592.     }
  593.  
  594.     # Test to see if execed commands such as cat, echo, rm and so forth are
  595.     # present on this machine.
  596.  
  597.     set ::tcltest::testConstraints(unixExecs) 1
  598.     if {[string equal $tcl_platform(platform) "macintosh"]} {
  599.     set ::tcltest::testConstraints(unixExecs) 0
  600.     }
  601.     if {($::tcltest::testConstraints(unixExecs) == 1) && \
  602.         ([string equal $tcl_platform(platform) "windows"])} {
  603.     if {[catch {exec cat defs}] == 1} {
  604.         set ::tcltest::testConstraints(unixExecs) 0
  605.     }
  606.     if {($::tcltest::testConstraints(unixExecs) == 1) && \
  607.         ([catch {exec echo hello}] == 1)} {
  608.         set ::tcltest::testConstraints(unixExecs) 0
  609.     }
  610.     if {($::tcltest::testConstraints(unixExecs) == 1) && \
  611.         ([catch {exec sh -c echo hello}] == 1)} {
  612.         set ::tcltest::testConstraints(unixExecs) 0
  613.     }
  614.     if {($::tcltest::testConstraints(unixExecs) == 1) && \
  615.         ([catch {exec wc defs}] == 1)} {
  616.         set ::tcltest::testConstraints(unixExecs) 0
  617.     }
  618.     if {$::tcltest::testConstraints(unixExecs) == 1} {
  619.         exec echo hello > removeMe
  620.         if {[catch {exec rm removeMe}] == 1} {
  621.         set ::tcltest::testConstraints(unixExecs) 0
  622.         }
  623.     }
  624.     if {($::tcltest::testConstraints(unixExecs) == 1) && \
  625.         ([catch {exec sleep 1}] == 1)} {
  626.         set ::tcltest::testConstraints(unixExecs) 0
  627.     }
  628.     if {($::tcltest::testConstraints(unixExecs) == 1) && \
  629.         ([catch {exec fgrep unixExecs defs}] == 1)} {
  630.         set ::tcltest::testConstraints(unixExecs) 0
  631.     }
  632.     if {($::tcltest::testConstraints(unixExecs) == 1) && \
  633.         ([catch {exec ps}] == 1)} {
  634.         set ::tcltest::testConstraints(unixExecs) 0
  635.     }
  636.     if {($::tcltest::testConstraints(unixExecs) == 1) && \
  637.         ([catch {exec echo abc > removeMe}] == 0) && \
  638.         ([catch {exec chmod 644 removeMe}] == 1) && \
  639.         ([catch {exec rm removeMe}] == 0)} {
  640.         set ::tcltest::testConstraints(unixExecs) 0
  641.     } else {
  642.         catch {exec rm -f removeMe}
  643.     }
  644.     if {($::tcltest::testConstraints(unixExecs) == 1) && \
  645.         ([catch {exec mkdir removeMe}] == 1)} {
  646.         set ::tcltest::testConstraints(unixExecs) 0
  647.     } else {
  648.         catch {exec rm -r removeMe}
  649.     }
  650.     }
  651.  
  652.     # Locate tcltest executable
  653.  
  654.     if {![info exists tk_version]} {
  655.     set tcltest [info nameofexecutable]
  656.  
  657.     if {$tcltest == "{}"} {
  658.         set tcltest {}
  659.     }
  660.     }
  661.  
  662.     set ::tcltest::testConstraints(stdio) 0
  663.     catch {
  664.     catch {file delete -force tmp}
  665.     set f [open tmp w]
  666.     puts $f {
  667.         exit
  668.     }
  669.     close $f
  670.  
  671.     set f [open "|[list $tcltest tmp]" r]
  672.     close $f
  673.     
  674.     set ::tcltest::testConstraints(stdio) 1
  675.     }
  676.     catch {file delete -force tmp}
  677.  
  678.     # Deliberately call socket with the wrong number of arguments.  The error
  679.     # message you get will indicate whether sockets are available on this
  680.     # system. 
  681.  
  682.     catch {socket} msg
  683.     set ::tcltest::testConstraints(socket) \
  684.         [expr {$msg != "sockets are not available on this system"}]
  685.     
  686.     # Check for internationalization
  687.  
  688.     if {[info commands testlocale] == ""} {
  689.     # No testlocale command, no tests...
  690.     set ::tcltest::testConstraints(hasIsoLocale) 0
  691.     } else {
  692.     set ::tcltest::testConstraints(hasIsoLocale) \
  693.         [string length [::tcltest::set_iso8859_1_locale]]
  694.     ::tcltest::restore_locale
  695.     }
  696. }   
  697.  
  698. # ::tcltest::PrintUsageInfoHook
  699. #
  700. #       Hook used for customization of display of usage information.
  701. #
  702.  
  703. if {[namespace inscope ::tcltest info procs PrintUsageInfoHook] == {}} {
  704.     proc ::tcltest::PrintUsageInfoHook {} {}
  705. }
  706.  
  707. # ::tcltest::PrintUsageInfo
  708. #
  709. #    Prints out the usage information for package tcltest.  This can be
  710. #       customized with the redefinition of ::tcltest::PrintUsageInfoHook.
  711. #
  712. # Arguments:
  713. #    none
  714. #
  715.  
  716. proc ::tcltest::PrintUsageInfo {} {
  717.     puts [format "Usage: [file tail [info nameofexecutable]] \
  718.         script ?-help? ?flag value? ... \n\
  719.         Available flags (and valid input values) are: \n\
  720.         -help          \t Display this usage information. \n\
  721.         -verbose level \t Takes any combination of the values \n\
  722.         \t                 'p', 's' and 'b'.  Test suite will \n\
  723.         \t                 display all passed tests if 'p' is \n\
  724.         \t                 specified, all skipped tests if 's' \n\
  725.         \t                 is specified, and the bodies of \n\
  726.         \t                 failed tests if 'b' is specified. \n\
  727.         \t                 The default value is 'b'. \n\
  728.         -constraints list\t Do not skip the listed constraints\n\
  729.         -limitconstraints bool\t Only run tests with the constraints\n\
  730.         \t                 listed in -constraints.\n\
  731.         -match pattern \t Run all tests within the specified \n\
  732.         \t                 files that match the glob pattern \n\
  733.         \t                 given. \n\
  734.         -skip pattern  \t Skip all tests within the set of \n\
  735.         \t                 specified tests (via -match) and \n\
  736.         \t                 files that match the glob pattern \n\
  737.         \t                 given. \n\
  738.         -file pattern  \t Run tests in all test files that \n\
  739.         \t                 match the glob pattern given. \n\
  740.         -notfile pattern\t Skip all test files that match the \n\
  741.         \t                 glob pattern given. \n\
  742.         -preservecore level \t If 2, save any core files produced \n\
  743.         \t                 during testing in the directory \n\
  744.         \t                 specified by -tmpdir. If 1, notify the\n\
  745.         \t                 user if core files are created. The default \n\
  746.         \t                 is $::tcltest::preserveCore. \n\
  747.         -tmpdir directory\t Save temporary files in the specified\n\
  748.         \t                 directory.  The default value is \n\
  749.         \t                 $::tcltest::temporaryDirectory. \n\
  750.         -testdir directories\t Search tests in the specified\n\
  751.         \t                 directories.  The default value is \n\
  752.         \t                 $::tcltest::testsDirectory. \n\
  753.         -outfile file    \t Send output from test runs to the \n\
  754.         \t                 specified file.  The default is \n\
  755.         \t                 stdout. \n\
  756.         -errfile file    \t Send errors from test runs to the \n\
  757.         \t                 specified file.  The default is \n\
  758.         \t                 stderr. \n\
  759.         -loadfile file   \t Read the script to load the tested \n\
  760.         \t                 commands from the specified file. \n\
  761.         -load script     \t Specifies the script to load the tested \n\
  762.         \t                 commands. \n\
  763.         -debug level     \t Internal debug flag."]
  764.     ::tcltest::PrintUsageInfoHook
  765.     return
  766. }
  767.  
  768. # ::tcltest::CheckDirectory --
  769. #
  770. #     This procedure checks whether the specified path is a readable
  771. #     and/or writable directory. If one of the conditions is not
  772. #     satisfied an error is printed and the application aborted. The
  773. #     procedure assumes that the caller already checked the existence
  774. #     of the path.
  775. #
  776. # Arguments
  777. #     rw      Information what attributes to check. Allowed values:
  778. #             r, w, rw, wr. If 'r' is part of the value the directory
  779. #             must be readable. 'w' associates to 'writable'.
  780. #     dir     The directory to check.
  781. #     errMsg  The string to prepend to the actual error message before
  782. #             printing it.
  783. #
  784. # Results
  785. #     none
  786. #
  787.  
  788. proc ::tcltest::CheckDirectory {rw dir errMsg} {
  789.     # Allowed values for 'rw': r, w, rw, wr
  790.  
  791.     if {![file isdir $dir]} { 
  792.     ::tcltest::PrintError "$errMsg \"$dir\" is not a directory"
  793.     exit 1
  794.     } elseif {([string first w $rw] >= 0) && ![file writable $dir]} {
  795.     ::tcltest::PrintError "$errMsg \"$dir\" is not writeable"
  796.     exit 1
  797.     } elseif {([string first r $rw] >= 0) && ![file readable $dir]} {
  798.     ::tcltest::PrintError "$errMsg \"$dir\" is not readable"
  799.     exit 1
  800.     }
  801. }
  802.  
  803. # ::tcltest::normalizePath --
  804. #
  805. #     This procedure resolves any symlinks in the path thus creating a
  806. #     path without internal redirection. It assumes that the incoming
  807. #     path is absolute.
  808. #
  809. # Arguments
  810. #     pathVar contains the name of the variable containing the path to modify.
  811. #
  812. # Results
  813. #     The path is modified in place.
  814. #
  815.  
  816. proc ::tcltest::normalizePath {pathVar} {
  817.     upvar $pathVar path
  818.  
  819.     set oldpwd [pwd]
  820.     catch {cd $path}
  821.     set path [pwd]
  822.     cd $oldpwd
  823. }
  824.  
  825. # ::tcltest::MakeAbsolutePath --
  826. #
  827. #     This procedure checks whether the incoming path is absolute or not.
  828. #     Makes it absolute if it was not.
  829. #
  830. # Arguments
  831. #     pathVar contains the name of the variable containing the path to modify.
  832. #     prefix  is optional, contains the path to use to make the other an
  833. #             absolute one. The current working directory is used if it was
  834. #             not specified.
  835. #
  836. # Results
  837. #     The path is modified in place.
  838. #
  839.  
  840. proc ::tcltest::MakeAbsolutePath {pathVar {prefix {}}} {
  841.     upvar $pathVar path
  842.  
  843.     if {![string equal [file pathtype $path] "absolute"]} { 
  844.     if {$prefix == {}} {
  845.         set prefix [pwd]
  846.     }
  847.  
  848.     set path [file join $prefix $path] 
  849.     }
  850. }
  851.  
  852. # ::tcltest::processCmdLineArgsFlagsHook --
  853. #
  854. #    This hook is used to add to the list of command line arguments that are
  855. #       processed by ::tcltest::processCmdLineArgs. 
  856. #
  857.  
  858. if {[namespace inscope ::tcltest info procs processCmdLineArgsAddFlagsHook] == {}} {
  859.     proc ::tcltest::processCmdLineArgsAddFlagsHook {} {}
  860. }
  861.  
  862. # ::tcltest::processCmdLineArgsHook --
  863. #
  864. #    This hook is used to actually process the flags added by
  865. #       ::tcltest::processCmdLineArgsAddFlagsHook.
  866. #
  867. # Arguments:
  868. #    flags      The flags that have been pulled out of argv
  869. #
  870.  
  871. if {[namespace inscope ::tcltest info procs processCmdLineArgsHook] == {}} {
  872.     proc ::tcltest::processCmdLineArgsHook {flag} {}
  873. }
  874.  
  875. # ::tcltest::processCmdLineArgs --
  876. #
  877. #    Use command line args to set the verbose, skip, and
  878. #    match, outputChannel, errorChannel, debug, and temporaryDirectory
  879. #       variables.   
  880. #
  881. #       This procedure must be run after constraints are initialized, because
  882. #       some constraints can be overridden.
  883. #
  884. # Arguments:
  885. #    none
  886. #
  887. # Results:
  888. #    Sets the above-named variables in the tcltest namespace.
  889.  
  890. proc ::tcltest::processCmdLineArgs {} {
  891.     global argv
  892.  
  893.     # The "argv" var doesn't exist in some cases, so use {}.
  894.  
  895.     if {(![info exists argv]) || ([llength $argv] < 1)} {
  896.     set flagArray {}
  897.     } else {
  898.     set flagArray $argv
  899.     }
  900.     
  901.     # Allow for 1-char abbreviations, where applicable (e.g., -match == -m).
  902.     # Note that -verbose cannot be abbreviated to -v in wish because it
  903.     # conflicts with the wish option -visual.
  904.  
  905.     # Process -help first
  906.     if {([lsearch -exact $flagArray {-help}] != -1) || \
  907.         ([lsearch -exact $flagArray {-h}] != -1)} {
  908.     ::tcltest::PrintUsageInfo
  909.     exit 1
  910.     }
  911.  
  912.     if {[catch {array set flag $flagArray}]} {
  913.     ::tcltest::PrintError "odd number of arguments specified on command line: \ 
  914.     $argv"
  915.     ::tcltest::PrintUsageInfo
  916.     exit 1
  917.     }
  918.  
  919.     # -help is not listed since it has already been processed
  920.     lappend defaultFlags -verbose -match -skip -constraints \
  921.         -outfile -errfile -debug -tmpdir -file -notfile \
  922.         -preservecore -limitconstraints -args -testdir \
  923.         -load -loadfile
  924.     set defaultFlags [concat $defaultFlags \
  925.         [ ::tcltest::processCmdLineArgsAddFlagsHook ]]
  926.  
  927.     foreach arg $defaultFlags {
  928.     set abbrev [string range $arg 0 1]
  929.     if {([info exists flag($abbrev)]) && \
  930.         ([lsearch -exact $flagArray $arg] < [lsearch -exact \
  931.         $flagArray $abbrev])} { 
  932.         set flag($arg) $flag($abbrev)
  933.     }
  934.     }
  935.  
  936.     # Set ::tcltest::parameters to the arg of the -args flag, if given
  937.     if {[info exists flag(-args)]} {
  938.     set ::tcltest::parameters $flag(-args)
  939.     }
  940.  
  941.     # Set ::tcltest::verbose to the arg of the -verbose flag, if given
  942.  
  943.     if {[info exists flag(-verbose)]} {
  944.     set ::tcltest::verbose $flag(-verbose)
  945.     }
  946.  
  947.     # Set ::tcltest::match to the arg of the -match flag, if given.  
  948.  
  949.     if {[info exists flag(-match)]} {
  950.     set ::tcltest::match $flag(-match)
  951.     } 
  952.  
  953.     # Set ::tcltest::skip to the arg of the -skip flag, if given
  954.  
  955.     if {[info exists flag(-skip)]} {
  956.     set ::tcltest::skip $flag(-skip)
  957.     }
  958.  
  959.     # Handle the -file and -notfile flags
  960.     if {[info exists flag(-file)]} {
  961.     set ::tcltest::matchFiles $flag(-file)
  962.     }
  963.     if {[info exists flag(-notfile)]} {
  964.     set ::tcltest::skipFiles $flag(-notfile)
  965.     }
  966.  
  967.     # Use the -constraints flag, if given, to turn on constraints that are
  968.     # turned off by default: userInteractive knownBug nonPortable.  This
  969.     # code fragment must be run after constraints are initialized.
  970.  
  971.     if {[info exists flag(-constraints)]} {
  972.     foreach elt $flag(-constraints) {
  973.         set ::tcltest::testConstraints($elt) 1
  974.     }
  975.     }
  976.  
  977.     # Use the -limitconstraints flag, if given, to tell the harness to limit
  978.     # tests run to those that were specified using the -constraints flag.  If
  979.     # the -constraints flag was not specified, print out an error and exit.
  980.     if {[info exists flag(-limitconstraints)]} {
  981.     if {![info exists flag(-constraints)]} {
  982.         puts "You can only use the -limitconstraints flag with \
  983.             -constraints"
  984.         exit 1
  985.     }
  986.     set ::tcltest::limitConstraints $flag(-limitconstraints)
  987.     foreach elt [array names ::tcltest::testConstraints] {
  988.         if {[lsearch -exact $flag(-constraints) $elt] == -1} {
  989.         set ::tcltest::testConstraints($elt) 0
  990.         }
  991.     }
  992.     }
  993.  
  994.     # Set the ::tcltest::temporaryDirectory to the arg of -tmpdir, if
  995.     # given.
  996.     # 
  997.     # If the path is relative, make it absolute.  If the file exists but
  998.     # is not a dir, then return an error.
  999.     #
  1000.     # If ::tcltest::temporaryDirectory does not already exist, create it.
  1001.     # If you cannot create it, then return an error.
  1002.  
  1003.     set tmpDirError ""
  1004.     if {[info exists flag(-tmpdir)]} {
  1005.     set ::tcltest::temporaryDirectory $flag(-tmpdir)
  1006.     
  1007.     MakeAbsolutePath ::tcltest::temporaryDirectory
  1008.     set tmpDirError "bad argument \"$flag(-tmpdir)\" to -tmpdir: "
  1009.     }
  1010.     if {[file exists $::tcltest::temporaryDirectory]} {
  1011.     ::tcltest::CheckDirectory rw $::tcltest::temporaryDirectory $tmpDirError
  1012.     } else {
  1013.     file mkdir $::tcltest::temporaryDirectory
  1014.     }
  1015.  
  1016.     normalizePath ::tcltest::temporaryDirectory
  1017.  
  1018.     # Set the ::tcltest::testsDirectory to the arg of -testdir, if
  1019.     # given.
  1020.     # 
  1021.     # If the path is relative, make it absolute.  If the file exists but
  1022.     # is not a dir, then return an error.
  1023.     #
  1024.     # If ::tcltest::temporaryDirectory does not already exist return an error.
  1025.     
  1026.     set testDirError ""
  1027.     if {[info exists flag(-testdir)]} {
  1028.     set ::tcltest::testsDirectory $flag(-testdir)
  1029.     
  1030.     MakeAbsolutePath ::tcltest::testsDirectory
  1031.     set testDirError "bad argument \"$flag(-testdir)\" to -testdir: "
  1032.     }
  1033.     if {[file exists $::tcltest::testsDirectory]} {
  1034.     ::tcltest::CheckDirectory r $::tcltest::testsDirectory $testDirError
  1035.     } else {
  1036.     ::tcltest::PrintError "$testDirError \"$::tcltest::testsDirectory\" \
  1037.         does not exist"
  1038.     exit 1
  1039.     }
  1040.     
  1041.     normalizePath ::tcltest::testsDirectory
  1042.     
  1043.     # Save the names of files that already exist in
  1044.     # the output directory.
  1045.     foreach file [glob -nocomplain \
  1046.         [file join $::tcltest::temporaryDirectory *]] {
  1047.     lappend ::tcltest::filesExisted [file tail $file]
  1048.     }
  1049.  
  1050.     # If an alternate error or output files are specified, change the
  1051.     # default channels.
  1052.  
  1053.     if {[info exists flag(-outfile)]} {
  1054.     set tmp $flag(-outfile)
  1055.     MakeAbsolutePath tmp $::tcltest::temporaryDirectory
  1056.     set ::tcltest::outputChannel [open $tmp w]
  1057.     } 
  1058.  
  1059.     if {[info exists flag(-errfile)]} {
  1060.     set tmp $flag(-errfile)
  1061.     MakeAbsolutePath tmp $::tcltest::temporaryDirectory
  1062.     set ::tcltest::errorChannel [open $tmp w]
  1063.     }
  1064.  
  1065.     # If a load script was specified, either directly or through
  1066.     # a file, remember it for later usage.
  1067.     
  1068.     if {[info exists flag(-load)] &&  \
  1069.         ([lsearch -exact $flagArray -load] > \
  1070.         [lsearch -exact $flagArray -loadfile])} {
  1071.         set ::tcltest::loadScript $flag(-load)
  1072.     }
  1073.     
  1074.     if {[info exists flag(-loadfile)] && \
  1075.         ([lsearch -exact $flagArray -loadfile] > \
  1076.         [lsearch -exact $flagArray -load]) } {
  1077.     set tmp $flag(-loadfile)
  1078.     MakeAbsolutePath tmp $::tcltest::temporaryDirectory
  1079.     set tmp [open $tmp r]
  1080.     set ::tcltest::loadScript [read $tmp]
  1081.     close $tmp
  1082.     }
  1083.  
  1084.     # If the user specifies debug testing, print out extra information during
  1085.     # the run.
  1086.     if {[info exists flag(-debug)]} {
  1087.     set ::tcltest::debug $flag(-debug)
  1088.     }
  1089.  
  1090.     # Handle -preservecore
  1091.     if {[info exists flag(-preservecore)]} {
  1092.     set ::tcltest::preserveCore $flag(-preservecore)
  1093.     }
  1094.  
  1095.     # Call the hook
  1096.     ::tcltest::processCmdLineArgsHook [array get flag]
  1097.  
  1098.     # Spit out everything you know if we're at a debug level 2 or greater
  1099.  
  1100.     DebugPuts    2 "Flags passed into tcltest:"
  1101.     DebugPArray  2 flag
  1102.     DebugPuts    2 "::tcltest::debug              = $::tcltest::debug"
  1103.     DebugPuts    2 "::tcltest::testsDirectory     = $::tcltest::testsDirectory"
  1104.     DebugPuts    2 "::tcltest::workingDirectory   = $::tcltest::workingDirectory"
  1105.     DebugPuts    2 "::tcltest::temporaryDirectory = $::tcltest::temporaryDirectory"
  1106.     DebugPuts    2 "::tcltest::outputChannel      = $::tcltest::outputChannel"
  1107.     DebugPuts    2 "::tcltest::errorChannel       = $::tcltest::errorChannel"
  1108.     DebugPuts    2 "Original environment (::tcltest::originalEnv):"
  1109.     DebugPArray  2 ::tcltest::originalEnv
  1110.     DebugPuts    2 "Constraints:"
  1111.     DebugPArray  2 ::tcltest::testConstraints
  1112. }
  1113.  
  1114. # ::tcltest::loadTestedCommands --
  1115. #
  1116. #     Uses the specified script to load the commands to test. Allowed to
  1117. #     be empty, as the tested commands could have been compiled into the
  1118. #     interpreter.
  1119. #
  1120. # Arguments
  1121. #     none
  1122. #
  1123. # Results
  1124. #     none
  1125.  
  1126. proc ::tcltest::loadTestedCommands {} {
  1127.     if {$::tcltest::loadScript == {}} {
  1128.     return
  1129.     }
  1130.     
  1131.     uplevel #0 $::tcltest::loadScript
  1132. }
  1133.  
  1134. # ::tcltest::cleanupTests --
  1135. #
  1136. # Remove files and dirs created using the makeFile and makeDirectory
  1137. # commands since the last time this proc was invoked.
  1138. #
  1139. # Print the names of the files created without the makeFile command
  1140. # since the tests were invoked.
  1141. #
  1142. # Print the number tests (total, passed, failed, and skipped) since the
  1143. # tests were invoked.
  1144. # Restore original environment (as reported by special variable env).
  1145.  
  1146. proc ::tcltest::cleanupTests {{calledFromAllFile 0}} {
  1147.  
  1148.     set testFileName [file tail [info script]]
  1149.  
  1150.     # Call the cleanup hook
  1151.     ::tcltest::cleanupTestsHook 
  1152.  
  1153.     # Remove files and directories created by the :tcltest::makeFile and
  1154.     # ::tcltest::makeDirectory procedures.
  1155.     # Record the names of files in ::tcltest::workingDirectory that were not
  1156.     # pre-existing, and associate them with the test file that created them.
  1157.  
  1158.     if {!$calledFromAllFile} {
  1159.     foreach file $::tcltest::filesMade {
  1160.         if {[file exists $file]} {
  1161.         catch {file delete -force $file}
  1162.         }
  1163.     }
  1164.     set currentFiles {}
  1165.     foreach file [glob -nocomplain \
  1166.         [file join $::tcltest::temporaryDirectory *]] {
  1167.         lappend currentFiles [file tail $file]
  1168.     }
  1169.     set newFiles {}
  1170.     foreach file $currentFiles {
  1171.         if {[lsearch -exact $::tcltest::filesExisted $file] == -1} {
  1172.         lappend newFiles $file
  1173.         }
  1174.     }
  1175.     set ::tcltest::filesExisted $currentFiles
  1176.     if {[llength $newFiles] > 0} {
  1177.         set ::tcltest::createdNewFiles($testFileName) $newFiles
  1178.     }
  1179.     }
  1180.  
  1181.     if {$calledFromAllFile || $::tcltest::testSingleFile} {
  1182.  
  1183.     # print stats
  1184.  
  1185.     puts -nonewline $::tcltest::outputChannel "$testFileName:"
  1186.     foreach index [list "Total" "Passed" "Skipped" "Failed"] {
  1187.         puts -nonewline $::tcltest::outputChannel \
  1188.             "\t$index\t$::tcltest::numTests($index)"
  1189.     }
  1190.     puts $::tcltest::outputChannel ""
  1191.  
  1192.     # print number test files sourced
  1193.     # print names of files that ran tests which failed
  1194.  
  1195.     if {$calledFromAllFile} {
  1196.         puts $::tcltest::outputChannel \
  1197.             "Sourced $::tcltest::numTestFiles Test Files."
  1198.         set ::tcltest::numTestFiles 0
  1199.         if {[llength $::tcltest::failFiles] > 0} {
  1200.         puts $::tcltest::outputChannel \
  1201.             "Files with failing tests: $::tcltest::failFiles"
  1202.         set ::tcltest::failFiles {}
  1203.         }
  1204.     }
  1205.  
  1206.     # if any tests were skipped, print the constraints that kept them
  1207.     # from running.
  1208.  
  1209.     set constraintList [array names ::tcltest::skippedBecause]
  1210.     if {[llength $constraintList] > 0} {
  1211.         puts $::tcltest::outputChannel \
  1212.             "Number of tests skipped for each constraint:"
  1213.         foreach constraint [lsort $constraintList] {
  1214.         puts $::tcltest::outputChannel \
  1215.             "\t$::tcltest::skippedBecause($constraint)\t$constraint"
  1216.         unset ::tcltest::skippedBecause($constraint)
  1217.         }
  1218.     }
  1219.  
  1220.     # report the names of test files in ::tcltest::createdNewFiles, and
  1221.     # reset the array to be empty.
  1222.  
  1223.     set testFilesThatTurded [lsort [array names ::tcltest::createdNewFiles]]
  1224.     if {[llength $testFilesThatTurded] > 0} {
  1225.         puts $::tcltest::outputChannel "Warning: files left behind:"
  1226.         foreach testFile $testFilesThatTurded {
  1227.         puts $::tcltest::outputChannel \
  1228.             "\t$testFile:\t$::tcltest::createdNewFiles($testFile)"
  1229.         unset ::tcltest::createdNewFiles($testFile)
  1230.         }
  1231.     }
  1232.  
  1233.     # reset filesMade, filesExisted, and numTests
  1234.  
  1235.     set ::tcltest::filesMade {}
  1236.     foreach index [list "Total" "Passed" "Skipped" "Failed"] {
  1237.         set ::tcltest::numTests($index) 0
  1238.     }
  1239.  
  1240.     # exit only if running Tk in non-interactive mode
  1241.  
  1242.     global tk_version tcl_interactive
  1243.     if {[info exists tk_version] && ![info exists tcl_interactive]} {
  1244.         exit
  1245.     }
  1246.     } else {
  1247.  
  1248.     # if we're deferring stat-reporting until all files are sourced,
  1249.     # then add current file to failFile list if any tests in this file
  1250.     # failed
  1251.  
  1252.     incr ::tcltest::numTestFiles
  1253.     if {($::tcltest::currentFailure) && \
  1254.         ([lsearch -exact $::tcltest::failFiles $testFileName] == -1)} {
  1255.         lappend ::tcltest::failFiles $testFileName
  1256.     }
  1257.     set ::tcltest::currentFailure false
  1258.  
  1259.     # restore the environment to the state it was in before this package
  1260.     # was loaded
  1261.  
  1262.     set newEnv {}
  1263.     set changedEnv {}
  1264.     set removedEnv {}
  1265.     foreach index [array names ::env] {
  1266.         if {![info exists ::tcltest::originalEnv($index)]} {
  1267.         lappend newEnv $index
  1268.         unset ::env($index)
  1269.         } else {
  1270.         if {$::env($index) != $::tcltest::originalEnv($index)} {
  1271.             lappend changedEnv $index
  1272.             set ::env($index) $::tcltest::originalEnv($index)
  1273.         }
  1274.         }
  1275.     }
  1276.     foreach index [array names ::tcltest::originalEnv] {
  1277.         if {![info exists ::env($index)]} {
  1278.         lappend removedEnv $index
  1279.         set ::env($index) $::tcltest::originalEnv($index)
  1280.         }
  1281.     }
  1282.     if {[llength $newEnv] > 0} {
  1283.         puts $::tcltest::outputChannel \
  1284.             "env array elements created:\t$newEnv"
  1285.     }
  1286.     if {[llength $changedEnv] > 0} {
  1287.         puts $::tcltest::outputChannel \
  1288.             "env array elements changed:\t$changedEnv"
  1289.     }
  1290.     if {[llength $removedEnv] > 0} {
  1291.         puts $::tcltest::outputChannel \
  1292.             "env array elements removed:\t$removedEnv"
  1293.     }
  1294.  
  1295.     set changedTclPlatform {}
  1296.     foreach index [array names ::tcltest::originalTclPlatform] {
  1297.         if {$::tcl_platform($index) != \
  1298.             $::tcltest::originalTclPlatform($index)} { 
  1299.         lappend changedTclPlatform $index
  1300.         set ::tcl_platform($index) \
  1301.             $::tcltest::originalTclPlatform($index) 
  1302.         }
  1303.     }
  1304.     if {[llength $changedTclPlatform] > 0} {
  1305.         puts $::tcltest::outputChannel \
  1306.             "tcl_platform array elements changed:\t$changedTclPlatform"
  1307.     } 
  1308.  
  1309.     if {[file exists [file join $::tcltest::workingDirectory core]]} {
  1310.         if {$::tcltest::preserveCore > 1} {
  1311.         puts $::tcltest::outputChannel "produced core file! \
  1312.             Moving file to: \
  1313.             [file join $::tcltest::temporaryDirectory core-$name]"
  1314.         flush $::tcltest::outputChannel
  1315.         catch {file rename -force \
  1316.             [file join $::tcltest::workingDirectory core] \
  1317.             [file join $::tcltest::temporaryDirectory \
  1318.             core-$name]} msg
  1319.         if {[string length $msg] > 0} {
  1320.             ::tcltest::PrintError "Problem renaming file: $msg"
  1321.         }
  1322.         } else {
  1323.         # Print a message if there is a core file and (1) there
  1324.         # previously wasn't one or (2) the new one is different from
  1325.         # the old one. 
  1326.  
  1327.         if {[info exists ::tcltest::coreModificationTime]} {
  1328.             if {$::tcltest::coreModificationTime != [file mtime \
  1329.                 [file join $::tcltest::workingDirectory core]]} {
  1330.             puts $::tcltest::outputChannel "A core file was created!"
  1331.             }
  1332.         } else {
  1333.             puts $::tcltest::outputChannel "A core file was created!"
  1334.         } 
  1335.         }
  1336.     }
  1337.     }
  1338. }
  1339.  
  1340. # ::tcltest::cleanupTestsHook --
  1341. #
  1342. #    This hook allows a harness that builds upon tcltest to specify
  1343. #       additional things that should be done at cleanup.
  1344. #
  1345.  
  1346. if {[namespace inscope ::tcltest info procs cleanupTestsHook] == {}} {
  1347.     proc ::tcltest::cleanupTestsHook {} {}
  1348. }
  1349.  
  1350. # test --
  1351. #
  1352. # This procedure runs a test and prints an error message if the test fails.
  1353. # If ::tcltest::verbose has been set, it also prints a message even if the
  1354. # test succeeds.  The test will be skipped if it doesn't match the
  1355. # ::tcltest::match variable, if it matches an element in
  1356. # ::tcltest::skip, or if one of the elements of "constraints" turns
  1357. # out not to be true.
  1358. #
  1359. # Arguments:
  1360. # name -        Name of test, in the form foo-1.2.
  1361. # description -        Short textual description of the test, to
  1362. #            help humans understand what it does.
  1363. # constraints -        A list of one or more keywords, each of
  1364. #            which must be the name of an element in
  1365. #            the array "::tcltest::testConstraints".  If any of these
  1366. #            elements is zero, the test is skipped.
  1367. #            This argument may be omitted.
  1368. # script -        Script to run to carry out the test.  It must
  1369. #            return a result that can be checked for
  1370. #            correctness.
  1371. # expectedAnswer -    Expected result from script.
  1372.  
  1373. proc ::tcltest::test {name description script expectedAnswer args} {
  1374.  
  1375.     DebugPuts 3 "Running $name ($description)"
  1376.  
  1377.     incr ::tcltest::numTests(Total)
  1378.  
  1379.     # skip the test if it's name matches an element of skip
  1380.  
  1381.     foreach pattern $::tcltest::skip {
  1382.     if {[string match $pattern $name]} {
  1383.         incr ::tcltest::numTests(Skipped)
  1384.         DebugDo 1 {::tcltest::AddToSkippedBecause userSpecifiedSkip}
  1385.         return
  1386.     }
  1387.     }
  1388.  
  1389.     # skip the test if it's name doesn't match any element of match
  1390.  
  1391.     if {[llength $::tcltest::match] > 0} {
  1392.     set ok 0
  1393.     foreach pattern $::tcltest::match {
  1394.         if {[string match $pattern $name]} {
  1395.         set ok 1
  1396.         break
  1397.         }
  1398.         }
  1399.     if {!$ok} {
  1400.         incr ::tcltest::numTests(Skipped)
  1401.         DebugDo 1 {::tcltest::AddToSkippedBecause userSpecifiedNonMatch}
  1402.         return
  1403.     }
  1404.     }
  1405.  
  1406.     set i [llength $args]
  1407.     if {$i == 0} {
  1408.     set constraints {}
  1409.     # If we're limited to the listed constraints and there aren't any
  1410.     # listed, then we shouldn't run the test.
  1411.     if {$::tcltest::limitConstraints} {
  1412.         ::tcltest::AddToSkippedBecause userSpecifiedLimitConstraint
  1413.         incr ::tcltest::numTests(Skipped)
  1414.         return
  1415.     }
  1416.     } elseif {$i == 1} {
  1417.  
  1418.     # "constraints" argument exists;  shuffle arguments down, then
  1419.     # make sure that the constraints are satisfied.
  1420.  
  1421.     set constraints $script
  1422.     set script $expectedAnswer
  1423.     set expectedAnswer [lindex $args 0]
  1424.     set doTest 0
  1425.     if {[string match {*[$\[]*} $constraints] != 0} {
  1426.         # full expression, e.g. {$foo > [info tclversion]}
  1427.         catch {set doTest [uplevel #0 expr $constraints]}
  1428.     } elseif {[regexp {[^.a-zA-Z0-9 ]+} $constraints] != 0} {
  1429.         # something like {a || b} should be turned into 
  1430.         # $::tcltest::testConstraints(a) || $::tcltest::testConstraints(b).
  1431.          regsub -all {[.\w]+} $constraints \
  1432.             {$::tcltest::testConstraints(&)} c
  1433.         catch {set doTest [eval expr $c]}
  1434.     } else {
  1435.         # just simple constraints such as {unixOnly fonts}.
  1436.         set doTest 1
  1437.         foreach constraint $constraints {
  1438.         if {(![info exists ::tcltest::testConstraints($constraint)]) \
  1439.             || (!$::tcltest::testConstraints($constraint))} {
  1440.             set doTest 0
  1441.  
  1442.             # store the constraint that kept the test from running
  1443.             set constraints $constraint
  1444.             break
  1445.         }
  1446.         }
  1447.     }
  1448.     if {$doTest == 0} {
  1449.         if {[string first s $::tcltest::verbose] != -1} {
  1450.         puts $::tcltest::outputChannel "++++ $name SKIPPED: $constraints"
  1451.         }
  1452.  
  1453.         incr ::tcltest::numTests(Skipped)
  1454.         ::tcltest::AddToSkippedBecause $constraints
  1455.         return    
  1456.     }
  1457.     } else {
  1458.     error "wrong # args: must be \"test name description ?constraints? script expectedAnswer\""
  1459.     }   
  1460.  
  1461.     # Save information about the core file.  You need to restore the original
  1462.     # tcl_platform environment because some of the tests mess with tcl_platform.
  1463.  
  1464.     if {$::tcltest::preserveCore} {
  1465.     set currentTclPlatform [array get tcl_platform]
  1466.     array set tcl_platform $::tcltest::originalTclPlatform
  1467.     if {[file exists [file join $::tcltest::workingDirectory core]]} {
  1468.         set coreModTime [file mtime [file join \
  1469.             $::tcltest::workingDirectory core]]
  1470.     }
  1471.     array set tcl_platform $currentTclPlatform
  1472.     }
  1473.  
  1474.     # If there is no "memory" command (because memory debugging isn't
  1475.     # enabled), then don't attempt to use the command.
  1476.     
  1477.     if {[info commands memory] != {}} {
  1478.     memory tag $name
  1479.     }
  1480.  
  1481.     set code [catch {uplevel $script} actualAnswer]
  1482.     if {([string equal $actualAnswer $expectedAnswer]) && ($code == 0)} {
  1483.     incr ::tcltest::numTests(Passed)
  1484.     if {[string first p $::tcltest::verbose] != -1} {
  1485.         puts $::tcltest::outputChannel "++++ $name PASSED"
  1486.     }
  1487.     } else {
  1488.     incr ::tcltest::numTests(Failed)
  1489.     set ::tcltest::currentFailure true
  1490.     if {[string first b $::tcltest::verbose] == -1} {
  1491.         set script ""
  1492.     }
  1493.     puts $::tcltest::outputChannel "\n==== $name $description FAILED"
  1494.     if {$script != ""} {
  1495.         puts $::tcltest::outputChannel "==== Contents of test case:"
  1496.         puts $::tcltest::outputChannel $script
  1497.     }
  1498.     if {$code != 0} {
  1499.         if {$code == 1} {
  1500.         puts $::tcltest::outputChannel "==== Test generated error:"
  1501.         puts $::tcltest::outputChannel $actualAnswer
  1502.         } elseif {$code == 2} {
  1503.         puts $::tcltest::outputChannel "==== Test generated return exception;  result was:"
  1504.         puts $::tcltest::outputChannel $actualAnswer
  1505.         } elseif {$code == 3} {
  1506.         puts $::tcltest::outputChannel "==== Test generated break exception"
  1507.         } elseif {$code == 4} {
  1508.         puts $::tcltest::outputChannel "==== Test generated continue exception"
  1509.         } else {
  1510.         puts $::tcltest::outputChannel "==== Test generated exception $code;  message was:"
  1511.         puts $::tcltest::outputChannel $actualAnswer
  1512.         }
  1513.     } else {
  1514.         puts $::tcltest::outputChannel "---- Result was:\n$actualAnswer"
  1515.     }
  1516.     puts $::tcltest::outputChannel "---- Result should have been:\n$expectedAnswer"
  1517.     puts $::tcltest::outputChannel "==== $name FAILED\n"
  1518.     }
  1519.     if {$::tcltest::preserveCore} {
  1520.     set currentTclPlatform [array get tcl_platform]
  1521.     if {[file exists [file join $::tcltest::workingDirectory core]]} {
  1522.         if {$::tcltest::preserveCore > 1} {
  1523.         puts $::tcltest::outputChannel "==== $name produced core file! \
  1524.             Moving file to: \
  1525.             [file join $::tcltest::temporaryDirectory core-$name]"
  1526.         catch {file rename -force \
  1527.             [file join $::tcltest::workingDirectory core] \
  1528.             [file join $::tcltest::temporaryDirectory \
  1529.             core-$name]} msg
  1530.         if {[string length $msg] > 0} {
  1531.             ::tcltest::PrintError "Problem renaming file: $msg"
  1532.         }
  1533.         } else {
  1534.         # Print a message if there is a core file and (1) there
  1535.         # previously wasn't one or (2) the new one is different from
  1536.         # the old one. 
  1537.  
  1538.         if {[info exists coreModTime]} {
  1539.             if {$coreModTime != [file mtime \
  1540.                 [file join $::tcltest::workingDirectory core]]} {
  1541.             puts $::tcltest::outputChannel "==== $name produced core file!"
  1542.             }
  1543.         } else {
  1544.             puts $::tcltest::outputChannel "==== $name produced core file!"
  1545.         } 
  1546.         }
  1547.     }
  1548.     array set tcl_platform $currentTclPlatform
  1549.     }
  1550. }
  1551.  
  1552. # ::tcltest::getMatchingFiles
  1553. #
  1554. #       Looks at the patterns given to match and skip files
  1555. #       and uses them to put together a list of the tests that will be run.
  1556. #
  1557. # Arguments:
  1558. #       none
  1559. #
  1560. # Results:
  1561. #       The constructed list is returned to the user.  This will primarily
  1562. #       be used in 'all.tcl' files.
  1563.  
  1564. proc ::tcltest::getMatchingFiles {args} {
  1565.     set matchingFiles {}
  1566.     if {[llength $args]} {
  1567.     set searchDirectory $args
  1568.     } else {
  1569.     set searchDirectory [list $::tcltest::testsDirectory]
  1570.     }
  1571.     # Find the matching files in the list of directories and then remove the
  1572.     # ones that match the skip pattern
  1573.     foreach directory $searchDirectory {
  1574.     set matchFileList {}
  1575.     foreach match $::tcltest::matchFiles {
  1576.         set matchFileList [concat $matchFileList \
  1577.             [glob -nocomplain [file join $directory $match]]]
  1578.     }
  1579.     if {[string compare {} $::tcltest::skipFiles]} {
  1580.         set skipFileList {}
  1581.         foreach skip $::tcltest::skipFiles {
  1582.         set skipFileList [concat $skipFileList \
  1583.             [glob -nocomplain [file join $directory $skip]]]
  1584.         }
  1585.         foreach file $matchFileList {
  1586.         # Only include files that don't match the skip pattern and
  1587.         # aren't SCCS lock files.
  1588.         if {([lsearch -exact $skipFileList $file] == -1) && \
  1589.             (![string match l.*.test [file tail $file]])} {
  1590.             lappend matchingFiles $file
  1591.         }
  1592.         }
  1593.     } else {
  1594.         set matchingFiles [concat $matchingFiles $matchFileList]
  1595.     }
  1596.     }
  1597.     if {[string equal $matchingFiles {}]} {
  1598.     ::tcltest::PrintError "No test files remain after applying \
  1599.         your match and skip patterns!"
  1600.     }
  1601.     return $matchingFiles
  1602. }
  1603.  
  1604. # The following two procs are used in the io tests.
  1605.  
  1606. proc ::tcltest::openfiles {} {
  1607.     if {[catch {testchannel open} result]} {
  1608.     return {}
  1609.     }
  1610.     return $result
  1611. }
  1612.  
  1613. proc ::tcltest::leakfiles {old} {
  1614.     if {[catch {testchannel open} new]} {
  1615.         return {}
  1616.     }
  1617.     set leak {}
  1618.     foreach p $new {
  1619.         if {[lsearch $old $p] < 0} {
  1620.         lappend leak $p
  1621.     }
  1622.     }
  1623.     return $leak
  1624. }
  1625.  
  1626. # ::tcltest::saveState --
  1627. #
  1628. #    Save information regarding what procs and variables exist.
  1629. #
  1630. # Arguments:
  1631. #    none
  1632. #
  1633. # Results:
  1634. #    Modifies the variable ::tcltest::saveState
  1635.  
  1636. proc ::tcltest::saveState {} {
  1637.     uplevel #0 {set ::tcltest::saveState [list [info procs] [info vars]]}
  1638.     DebugPuts  2 "::tcltest::saveState: $::tcltest::saveState"
  1639. }
  1640.  
  1641. # ::tcltest::restoreState --
  1642. #
  1643. #    Remove procs and variables that didn't exist before the call to
  1644. #       ::tcltest::saveState.
  1645. #
  1646. # Arguments:
  1647. #    none
  1648. #
  1649. # Results:
  1650. #    Removes procs and variables from your environment if they don't exist
  1651. #       in the ::tcltest::saveState variable.
  1652.  
  1653. proc ::tcltest::restoreState {} {
  1654.     foreach p [info procs] {
  1655.     if {([lsearch [lindex $::tcltest::saveState 0] $p] < 0) && \
  1656.         (![string equal ::tcltest::$p [namespace origin $p]])} {
  1657.         
  1658.         DebugPuts 3 "::tcltest::restoreState: Removing proc $p"
  1659.         rename $p {}
  1660.     }
  1661.     }
  1662.     foreach p [uplevel #0 {info vars}] {
  1663.     if {[lsearch [lindex $::tcltest::saveState 1] $p] < 0} {
  1664.         DebugPuts 3 "::tcltest::restoreState: Removing variable $p"
  1665.         uplevel #0 "catch {unset $p}"
  1666.     }
  1667.     }
  1668. }
  1669.  
  1670. # ::tcltest::normalizeMsg --
  1671. #
  1672. #    Removes "extra" newlines from a string.
  1673. #
  1674. # Arguments:
  1675. #    msg        String to be modified
  1676. #
  1677.  
  1678. proc ::tcltest::normalizeMsg {msg} {
  1679.     regsub "\n$" [string tolower $msg] "" msg
  1680.     regsub -all "\n\n" $msg "\n" msg
  1681.     regsub -all "\n\}" $msg "\}" msg
  1682.     return $msg
  1683. }
  1684.  
  1685. # makeFile --
  1686. #
  1687. # Create a new file with the name <name>, and write <contents> to it.
  1688. #
  1689. # If this file hasn't been created via makeFile since the last time
  1690. # cleanupTests was called, add it to the $filesMade list, so it will
  1691. # be removed by the next call to cleanupTests.
  1692. #
  1693. proc ::tcltest::makeFile {contents name} {
  1694.     global tcl_platform
  1695.     
  1696.     DebugPuts 3 "::tcltest::makeFile: putting $contents into $name"
  1697.  
  1698.     set fullName [file join $::tcltest::temporaryDirectory $name]
  1699.     set fd [open $fullName w]
  1700.  
  1701.     fconfigure $fd -translation lf
  1702.  
  1703.     if {[string equal [string index $contents end] "\n"]} {
  1704.     puts -nonewline $fd $contents
  1705.     } else {
  1706.     puts $fd $contents
  1707.     }
  1708.     close $fd
  1709.  
  1710.     if {[lsearch -exact $::tcltest::filesMade $fullName] == -1} {
  1711.     lappend ::tcltest::filesMade $fullName
  1712.     }
  1713.     return $fullName
  1714. }
  1715.  
  1716. # ::tcltest::removeFile --
  1717. #
  1718. #    Removes the named file from the filesystem
  1719. #
  1720. # Arguments:
  1721. #    name     file to be removed
  1722. #
  1723.  
  1724. proc ::tcltest::removeFile {name} {
  1725.     DebugPuts 3 "::tcltest::removeFile: removing $name"
  1726.     file delete [file join $::tcltest::temporaryDirectory $name]
  1727. }
  1728.  
  1729. # makeDirectory --
  1730. #
  1731. # Create a new dir with the name <name>.
  1732. #
  1733. # If this dir hasn't been created via makeDirectory since the last time
  1734. # cleanupTests was called, add it to the $directoriesMade list, so it will
  1735. # be removed by the next call to cleanupTests.
  1736. #
  1737. proc ::tcltest::makeDirectory {name} {
  1738.     file mkdir $name
  1739.  
  1740.     set fullName [file join [pwd] $name]
  1741.     if {[lsearch -exact $::tcltest::filesMade $fullName] == -1} {
  1742.     lappend ::tcltest::filesMade $fullName
  1743.     }
  1744. }
  1745.  
  1746. # ::tcltest::removeDirectory --
  1747. #
  1748. #    Removes a named directory from the file system.
  1749. #
  1750. # Arguments:
  1751. #    name    Name of the directory to remove
  1752. #
  1753.  
  1754. proc ::tcltest::removeDirectory {name} {
  1755.     file delete -force $name
  1756. }
  1757.  
  1758. proc ::tcltest::viewFile {name} {
  1759.     global tcl_platform
  1760.     if {([string equal $tcl_platform(platform) "macintosh"]) || \
  1761.         ($::tcltest::testConstraints(unixExecs) == 0)} {
  1762.     set f [open [file join $::tcltest::temporaryDirectory $name]]
  1763.     set data [read -nonewline $f]
  1764.     close $f
  1765.     return $data
  1766.     } else {
  1767.     exec cat [file join $::tcltest::temporaryDirectory $name]
  1768.     }
  1769. }
  1770.  
  1771. # grep --
  1772. #
  1773. # Evaluate a given expression against each element of a list and return all
  1774. # elements for which the expression evaluates to true.  For the purposes of
  1775. # this proc, use of the keyword "CURRENT_ELEMENT" will flag the proc to use the
  1776. # value of the current element within the expression.  This is equivalent to
  1777. # the perl grep command where CURRENT_ELEMENT would be the name for the special
  1778. # variable $_.
  1779. #
  1780. # Examples of usage would be:
  1781. #   set subList [grep {CURRENT_ELEMENT == 1} $listOfNumbers]
  1782. #   set subList [grep {regexp {abc} CURRENT_ELEMENT} $listOfStrings]
  1783. #
  1784. # Use of the CURRENT_ELEMENT keyword is optional.  If it is left out, it is
  1785. # assumed to be the final argument to the expression provided.
  1786. # Example:
  1787. #   grep {regexp a} $someList   
  1788. #
  1789. proc ::tcltest::grep { expression searchList } {
  1790.     foreach element $searchList {
  1791.     if {[regsub -all CURRENT_ELEMENT $expression $element \
  1792.         newExpression] == 0} { 
  1793.         set newExpression "$expression {$element}"
  1794.     }
  1795.     if {[eval $newExpression] == 1} {
  1796.         lappend returnList $element
  1797.     }
  1798.     }
  1799.     if {[info exists returnList]} {
  1800.     return $returnList
  1801.     }
  1802.     return
  1803. }
  1804.  
  1805. #
  1806. # Construct a string that consists of the requested sequence of bytes,
  1807. # as opposed to a string of properly formed UTF-8 characters.  
  1808. # This allows the tester to 
  1809. # 1. Create denormalized or improperly formed strings to pass to C procedures 
  1810. #    that are supposed to accept strings with embedded NULL bytes.
  1811. # 2. Confirm that a string result has a certain pattern of bytes, for instance
  1812. #    to confirm that "\xe0\0" in a Tcl script is stored internally in 
  1813. #    UTF-8 as the sequence of bytes "\xc3\xa0\xc0\x80".
  1814. #
  1815. # Generally, it's a bad idea to examine the bytes in a Tcl string or to
  1816. # construct improperly formed strings in this manner, because it involves
  1817. # exposing that Tcl uses UTF-8 internally.
  1818.  
  1819. proc ::tcltest::bytestring {string} {
  1820.     encoding convertfrom identity $string
  1821. }
  1822.  
  1823. #
  1824. # Internationalization / ISO support procs     -- dl
  1825. #
  1826. proc ::tcltest::set_iso8859_1_locale {} {
  1827.     if {[info commands testlocale] != ""} {
  1828.     set ::tcltest::previousLocale [testlocale ctype]
  1829.     testlocale ctype $::tcltest::isoLocale
  1830.     }
  1831.     return
  1832. }
  1833.  
  1834. proc ::tcltest::restore_locale {} {
  1835.     if {[info commands testlocale] != ""} {
  1836.     testlocale ctype $::tcltest::previousLocale
  1837.     }
  1838.     return
  1839. }
  1840.  
  1841. # threadReap --
  1842. #
  1843. #    Kill all threads except for the main thread.
  1844. #    Do nothing if testthread is not defined.
  1845. #
  1846. # Arguments:
  1847. #    none.
  1848. #
  1849. # Results:
  1850. #    Returns the number of existing threads.
  1851. proc ::tcltest::threadReap {} {
  1852.     if {[info commands testthread] != {}} {
  1853.  
  1854.     # testthread built into tcltest
  1855.  
  1856.     testthread errorproc ThreadNullError
  1857.     while {[llength [testthread names]] > 1} {
  1858.         foreach tid [testthread names] {
  1859.         if {$tid != $::tcltest::mainThread} {
  1860.             catch {testthread send -async $tid {testthread exit}}
  1861.         }
  1862.         }
  1863.         ## Enter a bit a sleep to give the threads enough breathing
  1864.         ## room to kill themselves off, otherwise the end up with a
  1865.         ## massive queue of repeated events
  1866.         after 1
  1867.     }
  1868.     testthread errorproc ThreadError
  1869.     return [llength [testthread names]]
  1870.     } elseif {[info commands thread::id] != {}} {
  1871.     
  1872.     # Thread extension
  1873.  
  1874.     thread::errorproc ThreadNullError
  1875.     while {[llength [thread::names]] > 1} {
  1876.         foreach tid [thread::names] {
  1877.         if {$tid != $::tcltest::mainThread} {
  1878.             catch {thread::send -async $tid {thread::exit}}
  1879.         }
  1880.         }
  1881.         ## Enter a bit a sleep to give the threads enough breathing
  1882.         ## room to kill themselves off, otherwise the end up with a
  1883.         ## massive queue of repeated events
  1884.         after 1
  1885.     }
  1886.     thread::errorproc ThreadError
  1887.     return [llength [thread::names]]
  1888.     } else {
  1889.     return 1
  1890.     }
  1891. }
  1892.  
  1893. # Initialize the constraints and set up command line arguments 
  1894. namespace eval tcltest {
  1895.     # Ensure that we have a minimal auto_path so we don't pick up extra junk.
  1896.     set ::auto_path [list [info library]]
  1897.  
  1898.     ::tcltest::initConstraints
  1899.     if {[namespace children ::tcltest] == {}} {
  1900.     ::tcltest::processCmdLineArgs
  1901.     }
  1902. }
  1903.