Testing and Debugging Tcl/Tk Scripts
Testing and debugging a program is one of the most tedious parts of
computer programming. The testing and debugging phase of a project
can easily take more time than it took to write the application.
Testing includes both checking that the code runs at all, that it
runs correctly under all circumstances, and that it runs the same way
it did before you made changes.
Tcl's error diagnostics make it easy to track down coding errors;
the modular nature of Tcl code makes it easy to do unit testing of
functions, and the tcl test package makes it easy to write integrated
regression test suites.
Debugging Code
The first step to debugging a Tcl script is to examine the Tcl
error output closely. Tcl provides a verbose error information that
lead you to the exact line where a coding error occurs.
Tcl error messages consist of a set of lines. The first line will
describe the immediate cause of the error (Incorrect number of arguments,
invalid argument, undefined variable, etc). The rest of the message
describes more details about where the error occurs.
For example, this procedure has a fairly common error - the closing
brace and bracket are in the wrong order:
proc hasError {a} {
return [expr {$a+2]}
}
The error message is:
missing close-bracket
while executing
"return [expr {$a+2]}
"
(procedure "hasError" line 2)
invoked from within
"hasError 1"
The first line describes the error (missing curly bracket), and the
rest of the lines show the exact line in the program (return
[expr {$a+2]}
), and where that line occurs (second line in the
hasError
procedure)
Here are a few of the common error messages and a description of the
code that generates them.
wrong # args: should be ...
set result "a b"
button .b -text set -command "set x $result"
This example looks reasonable, however, the command to be evaluated
when the button is clicked is created by substituting the the
$result
and concatenating the values into a string
resembling this: set x a b
.
The recommended way of creating a button like this is to use the
list
command to maintain grouping:
set result "a b"
button .b -text set -command [list set x $result]
missing "
missing close-brace
missing close-bracket
- Your code has an unterminated string that starts with a double-quote.
The usual causes of this are typos (not hitting shift fast enough and
having a double quote at one end of a string and a single quote at the other),
having a space after a back-slash line continuation character, or mismatching
the open/close pairs of a set of nested quotes, braces and brackets.
Here are some examples of lines that would generate one of these errors.
set rtn "there is a space after the backslash \
causing this to generate an error"
puts "Missing close quote
puts "mismatched quote and brace}
puts "should be double quote to close'
can't read "...": no such variable
- There is a
$varName
in your code for which
varName
has never been set.
This can happen:
- because you were reworking code and forgot to initialize a variable.
- because a global scope variable is referenced in a procedure without
declaring it global.
- because a procedure invoked from a widget did not declare the widget's
-textvariable
to be in global scope.
- because code is being evaluated outside the scope in which it was
created. Code connected with a button, after event, etc is evaluated in
the global scope (unless a namespace or class scope is assigned) even if
the widget is created within a procedure or method. Procedure
scope local variables will no longer exist after the GUI has taken control of
the application.
set globalVar 1
# Fails because of missing "global globalVar"
proc hasError {} {
puts "$globalVar"
}
# The entry widget is OK, but the button will cause an error.
entry .e -textvariable globalVar
button .b -text "generateError" -command hasError
# This button references a local variable.
proc makeBadButton {} {
set xx "local variable"
button .b -text "throw error" -command "puts $xx"
grid .b
}
invalid command name "..."
- The first word on a command line is not a valid command or procedure name.
This is most often caused by mis-typing a command name, or forgetting to
source
or package require
the Tcl code that
defines a command or procedure.
syntax error in expression "...": variable references require preceding $
invalid bareword "..."
- This error is generated by the expr command. It's caused when
you try to do arithmetic on a string. The usual causes are that you
forgot to put a dollar-sign on a variable name or that a variable
that should hold a number was assigned a string value.
-
Interactive Debugging
tkcon
tkcon is an application written by Jeff Hobbs to provide a more
advanced interactive console to Tcl/Tk applications. It includes
a color-coded display, history, multiple consoles, socket connections
and more.
When you start tkcon
, whether it's embedded or standalone,
you'll see a display that resembles this:
You can type commands into the console, and output will be printed to
the console. You can examine variables, define and run procedures
and more, as shown below.
TkCon with Unix/Linux
On a Unix/Linux/MacOSX platform, you can start the tkcon
application and attach to a running wish script. At that point,
you can examine variables, windows, window hierarchies, procedure
bodies, and more as will be discussed below.
One advantage of attaching tkcon to an application, rather than
embedding tkcon is that you maintain a history of commands. If you
are debugging an application, you may find yourself doing the sequence
of Start Target Application, Run Test In tkcon, Examine Results, Edit
over and over again. Being able to just up-arrow to rerun a previous
test saves a bunch of retyping and potentially mis-typing.
Embed tkcon
You can embed tkcon into your application regardless of the run-time
platform. This provides a complete command line interface for your
application that you can use to examine variables, run procedures,
source new code (perhaps to correct a bug), and more.
In order to embed tkcon
into an application you need to
include these two lines in the application:
source tkcon
tkcon show
Putting this into a menu or button like this makes it easy to access
tkcon
when an application needs attention:
$menu add command -label $lbl -command "source tkcon; tkcon show"
Using tkcon
Running tkcon
within an existing program gives you a
chance to look at global variables, check the state of windows, examine
procedure bodies, bindings, set traces and more.
Assuming that you've opened tkcon
because a program
has reported a failure here are some steps to take:
-
puts $errorInfo
to examine the error information after closing an error window.
-
puts $argv
to confirm that command line arguments are correct.
-
puts $argc
to confirm that command line argument count is correct.
-
parray $GlobalStateArray to check the state of the global state array.
- Use the
trace
command to put a trace on a critical variable and repeat whatever caused the error.
- Use the
source
command to load a modified procedure and repeat whatever caused the error.
- Use the
info body
command to examine a procedure to confirm that the expected version of a procedure is included in the executable.
You can examine any global variables from tkcon, but you can't access
procedure local variables.
Since Tcl is an interpreted language, you can modify a procedure that's
causing problems, use the source
command to reload just
that procedure and repeat a test.
Duplicating GUI debugger behavior without a debugger in Tcl/Tk
Several of the common features of a GUI debugger can be duplicated in
a Tcl/Tk application by modifying the code and rerunning a test.
- Breakpoint
-
- Variable display
-
- Stack trace
-
Static checking
prolint
nagelfar
frink
Other Tricks
use rename
puts/logging
whereami
trace