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:

  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: 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