The most common ways to extend the DejaGnu framework are: adding a suite
of tests for a new tool to be tested; adding support for testing on a
new target; and porting runtest
to a new host.
In general, the best way to learn how to write (code or even prose) is
to read something similar. This principle applies to test cases and to
test suites. Unfortunately, well-established test suites have a way of
developing their own conventions: as test writers become more
experienced with DejaGnu and with Tcl, they accumulate more utilities,
and take advantage of more and more features of expect
and Tcl in
general.
Inspecting such established test suites may make the prospect of creating an entirely new test suite appear overwhelming. Nevertheless, it is quite straightforward to get a new test suite going.
There is one test suite that is guaranteed not to grow more elaborate
over time: both it and the tool it tests were created expressly to
illustrate what it takes to get started with DejaGnu. The
`example/' directory of the DejaGnu distribution contains both an
interactive tool called calc
, and a test suite for it. Reading
this test suite, and experimenting with it, is a good way to supplement
the information in this section. (Thanks to Robert Lupton for creating
calc
and its test suite--and also the first version of this
section of the manual!)
To help orient you further in this task, here is an outline of the steps to begin building a test suite for a program example.
testsuite
):
eg$ cd testsuite/
target_abbrev
; this value is the link to the init file you will
write soon. (For simplicity, we assume the environment is Unix, and use
`unix' as the value.)
What else is needed in `configure.in' depends on the requirements
of your tool, your intended test environments, and which
configure
system you use. This example is a minimal
configure.in
for use with Cygnus Configure. (For an alternative
based on the FSF autoconf
system, see the calc
example
distributed with DejaGnu.) Replace example with the name of your
program:
# This file is a shell script fragment # for use with Cygnus configure. srctrigger="example.0" srcname="The DejaGnu example tests" # per-host: # per-target: # everything defaults to unix for a target target_abbrev=unix # post-target:
configure
to
build your `Makefile'. Its leading section should as usual contain
the values that configure
may override:
srcdir = . prefix = /usr/local exec_prefix = $(prefix) bindir = $(exec_prefix)/bin libdir = $(exec_prefix)/lib tooldir = $(libdir)/$(target_alias) datadir = $(exec_prefix)/lib/dejagnu RUNTEST = runtest RUNTESTFLAGS = FLAGS_TO_PASS = #### host, target, site specific Makefile frags come in here.This should be followed by the standard targets at your site. To begin with, they need not do anything--for example, these definitions will do:
all: info: install-info: install: uninstall: clean: -rm -f *~ core *.info*It is also a good idea to make sure your `Makefile' can rebuild itself if `Makefile.in' changes, with a target like this (which works for either Cygnus or FSF Configure):
Makefile : $(srcdir)/Makefile.in $(host_makefile_frag) \ $(target_makefile_frag) $(SHELL) ./config.statusYou also need to include two targets important to DejaGnu:
check
,
to run the tests, and site.exp
, to set up the Tcl copies of
configuration-dependent values. The check
target must run
`runtest --tool example':
check: site.exp all $(RUNTEST) $(RUNTESTFLAGS) $(FLAGS_TO_PASS) \ --tool example --srcdir $(srcdir)The
site.exp
target should usually set up (among other things!) a
Tcl variable for the name of your program:
site.exp: ./config.status Makefile @echo "Making a new config file..." -@rm -f ./tmp? @touch site.exp -@mv site.exp site.bak @echo "## these variables are automatically\ generated by make ##" > ./tmp0 @echo "# Do not edit here. If you wish to\ override these values" >> ./tmp0 @echo "# add them to the last section" >> ./tmp0 @echo "set host_os ${host_os}" >> ./tmp0 @echo "set host_alias ${host_alias}" >> ./tmp0 @echo "set host_cpu ${host_cpu}" >> ./tmp0 @echo "set host_vendor ${host_vendor}" >> ./tmp0 @echo "set target_os ${target_os}" >> ./tmp0 @echo "set target_alias ${target_alias}" >> ./tmp0 @echo "set target_cpu ${target_cpu}" >> ./tmp0 @echo "set target_vendor ${target_vendor}" >> ./tmp0 @echo "set host_triplet ${host_canonical}" >> ./tmp0 @echo "set target_triplet ${target_canonical}">>./tmp0 @echo "set tool binutils" >> ./tmp0 @echo "set srcdir ${srcdir}" >> ./tmp0 @echo "set objdir `pwd`" >> ./tmp0 @echo "set examplename example" >> ./tmp0 @echo "## All variables above are generated by\ configure. Do Not Edit ##" >> ./tmp0 @cat ./tmp0 > site.exp @sed < site.bak \ -e '1,/^## All variables above are.*##/ d' \ >> site.exp -@rm -f ./tmp?
eg$ mkdir config
target_abbrev
value, so call it `config/unix.exp'.
This is the file that contains the target-dependent procedures;
fortunately, most of them do not have to do very much in order for
runtest
to run.
If example is not interactive, you can get away with this minimal
`unix.exp' to begin with:
proc foo_exit {} {} proc foo_version {} {}If example is interactive, however, you might as well define a start routine and invoke it by using an init file like this:
proc foo_exit {} {} proc foo_version {} {} proc foo_start {} { global examplename spawn $examplename expect { -re "" {} } } foo_start
eg$ mkdir example.0
send_user "Testing: one, two...\n"
eg$ configure(You may have to specify more of a path, if a suitable
configure
is not available in your execution path.)
Test Run By rhl on Fri Jan 29 16:25:44 EST 1993 === example tests === Running ./example.0/first-try.exp ... Testing: one, two... === example Summary ===There is no output in the summary, because so far the example does not call any of the procedures that establish a test outcome.
DejaGnu has some additional requirements for target support, beyond the
general-purpose provisions of Cygnus configure
. runtest
must actively communicate with the target, rather than simply generating
or managing code for the target architecture. Therefore, each tool
requires an initialization module for each target. For new targets, you
must supply a few Tcl procedures to adapt DejaGnu to the target. This
permits DejaGnu itself to remain target independent. See section Initialization module, for a discussion of the naming
conventions that enable DejaGnu to locate and use init files.
Usually the best way to write a new initialization module is to edit an existing initialization module; some trial and error will be required. If necessary, you can use the `--debug' option to see what is really going on.
When you code an initialization module, be generous in printing
information controlled by the verbose
procedure (see section DejaGnu procedures).
Most of the work is in getting the communications right. Communications code (for several situations involving IP networks or serial lines) is available in a DejaGnu library file, `lib/remote.exp'. See section Library procedures.
If you suspect a communication problem, try running the connection
interactively from expect
. (There are three ways of running
expect
as an interactive interpreter. You can run expect
with no arguments, and control it completely interactively; or you can
use `expect -i' together with other command-line options and
arguments; or you can run the command interpreter
from any
expect
procedure. Use return
to get back to the calling
procedure (if any), or return -tcl
to make the calling procedure
itself return to its caller; use exit
or end-of-file to leave
expect
altogether.) Run the program whose name is recorded in
`$connectmode', with the arguments in `$targetname', to
establish a connection. You should at least be able to get a prompt
from any target that is physically connected.
The task of porting DejaGnu is basically that of porting Tcl and
expect
. Tcl and expect
, as distributed with DejaGnu, both
use autoconf
; they should port automatically to most Unix
systems.
Once Tcl and expect
are ported, DejaGnu should run. Most system
dependencies are taken care of by using expect
as the main
command shell.