How To Add New Features To DejaGnu Testing Framework

如何为 DejaGnu 添加新特性

DejaGnu 是一个非常好的开源的测试工具,尤其是做 GNU 开源工具链的软件开发,更离不开 DejaGnu 的支持,因为 binutils, gcc, gdb 自带的测试用例都是使用 DejaGnu 进行测试的。但是,DejaGnu 也有一些不足之处。因为它是开源的,我们可以修改它来满足我们的需求。

DejaGnu is a very good open-source testing framework, expecially in GNU development tool-chain developing. Because binutils, gcc, gdb use DejaGnu to test themselves. However, DejaGnu can not cover all aspects. Luckily it is open-sourced, so we can modify it to meet our demand.

我曾经想把下面代码提交到 DejaGnu,但是很遗憾,它的维护者似乎并不希望添加这些特性。所以我只好写在这里,希望能对某些人有所帮助。

I have tried to commit the fellowing code to DejaGnu, but unfortunately its maintainers did not want these features. So I write them down, in the hope that them will be useful.

1. DejaGnu 的官方介绍
1. Introduction From DejaGNU’s website

DejaGnu is a framework for testing other programs. Its purpose is to provide a single front end for all tests. Think of it as a custom library of Tcl procedures crafted to support writing a test harness. A test harness is the testing infrastructure that is created to support a specific program or tool. Each program can have multiple testsuites, all supported by a single test harness. DejaGnu is written in Expect, which in turn uses Tcl -- Tool command language.

2. 怎样给 runtest 命令添加新的选项
2. How to add new options to runtest command

runtest 有很多选项,可以通过 runtest --help 查看。下面是向 runtest 添加两个选项的代码,"--pr"是带优先权的测试,通过在 site.exp 或者 unix.exp 等其它设置文件中添加 pre_test_list 变量(list类型)来实现优先测试 pre_test_list 中的测试用例;"--sm"是 smoking test,意思是可以在抽一支烟的时间内测完的用例列表,也是通过在设置文件中添加 smoking_test_list 变量(list类型)来实现。

"runtest" command has many options, you can use command "runtest --help" to get more information. The following code adds two options to runtest. "--pr" is for test with priority on some testcases, via setting a list variable "pre_test_list" in configure files like site.exp and unix.exp. "--sm" is for smoking test, set a list variable "smoking_test_list" in the same place as --pr.

在 dejagnu/runtest.exp 中,添加下面的代码:
Adds the following code to dejagnu/runtest.exp:

/* 在最后一个 switch -glob -- $option 的 swith 体中,添加:*/
/* Find the last "switch -glob -- $option", adds the fellowing to it’s switch body:*/
# Options handler.
"--pr*" { # (--priority) run preferential tests first, then others
if {[info exists pre_test_list]} {
set SOLREX_testlist $pre_test_list
# Flag to control priority and smoking test.
# 0: run before whole test
# 1: run only listed test
set SOLREX_flag 0
verbose "Running preferential tests $SOLREX_testlist"
} else {
warning "Variable pre_test_list is null. runtest as default."

"--sm*" { # (--smoking) run smoking tests
if {[info exists smoking_test_list]} {
if { $smoking_test_list != ""} {
set SOLREX_testlist $smoking_test_list
set SOLREX_flag 1
verbose "Running smoking tests $SOLREX_testlist"
} else {
warning "Variable smoking_test_list is null."
exit 0
} else {
warning "Variable smoking_test_list is not defined. "
exit 1

/* 在最后一个 set testlist 的循环语句后,添加下面代码: */
/* Find the last "set testlist " statement, jump out of the foreach loop, adds: */
# Exe code for --pr and --sm.
# If SOLREX_testlist is defined, run these *.exp files listed in it first.
# code in foreach loop bellow is copied from above.
if {[info exists SOLREX_testlist] } {
foreach x $SOLREX_testlist {
verbose "trying to glob ${srcdir}/${x}" 2
set s [glob -nocomplain ${srcdir}/$x]
if { $s != "" } {
lappend testlist $s
} else {
set f [exec find $srcdir -name "$x"]
set f [split $f "
set f [lindex $f 0]
set s [glob -nocomplain ${srcdir}/$f]
if { $s!= "" } {
lappend testlist $s
if { $SOLREX_flag == 0 } {
foreach test_name $testlist {
if { ${ignoretests} != "" } {
if { 0 <= [lsearch ${ignoretests} [file tail ${test_name}]]} {
set subdir [file dirname $test_name]
set p [expr {[string length $srcdir] - 1}]
while {0 < $p && [string index $srcdir $p] == "/"} {
incr p -1
if {[string range $subdir 0 $p] == $srcdir} {
set subdir [string range $subdir [expr {$p + 1}] end]
regsub "^/" $subdir "" subdir
set runtests [list [file tail $test_name] ""]
runtest $test_name
lappend ignoretests [file tail $test_name]
set testlist ""

/* 在 usage 函数中,添加下面两句:*/
/* Find "proc usage", adds following two lines in it: */
# Help information.
send_user " --priority (-pr) Run tests in ’pre_test_list’ first and orderly, defined in ’site.exp’
send_user " --smoking (-sm) Run tests in ’smoking_test_list’ only, defined in ’site.exp’

3. 怎样给 DejaGnu 添加新的开发板描述文件
3. How to Add Board Description File to DejaGnu

DejaGnu 在baseboards目录下内建了很多开发板描述文件,例如:arm-sim.exp, mips-sim.exp,可以在这个目录下新建或修改原有文件来为你的开发板配置一个正确的环境。

DejaGnu have many board description files in directory dejagnu/baseboards, i.e. arm-sim.exp, mips-sim.exp. You can add or modify file to get a right configuration for your board.

Example board description file :

$cat dejagnu/baseboards/gdbserver-sample.exp
......ignored headers......
# gdbserver running over ssh.
/* 加载 tool-and-target-specific interface 配置文件,这个文件应该在当前测试环境的 config 目录下,可以在它里重写很多库函数。*/
/* Load tool-and-target-specific interface file, it should be in your testhome’s config dir. You can overwrite library functions in this file. */
load_generic_config "gdbserver"

/* 下面都是很明了的了,但是需要说明的一点是 board_info 并不是一个 struct, 可以自定义很多内容。比如 GDB 就定义了许多变量,例如:gdb_prompt, mathlib 等等。 你也可以为自己定义一些变量,只需要在这里 set_board_info xxxx yyyy, 当你要使用时,先测试它是否存在:[target_info exists xxxx],然后取得它的值:set YYYY [target_info xxxx]。*/
/* The following is self-explained. But maybe you are glad to know that "board_info" is not a struct, you can define variables in it. For example, GDB defines many variables, i.e. gdb_prompt, mathlib. Of course you can define youself some variables for configuration use. Put "set_board_info xxxx yyyy" here first. When you want to use xxxx, you test if it exits: "[target_info exists xxxx]", then get xxxx’s value: "set YYYY [target_info xxxx]" */

process_multilib_options ""

# The default compiler for this target.
set_board_info compiler "[find_gcc]"

#set_board_info compiler "/opt/src/gcc/install-30/bin/gcc"
#set_board_info c++compiler "/opt/src/gcc/install-30/bin/g++"

set_board_info rsh_prog /usr/bin/ssh
set_board_info rcp_prog /usr/bin/scp
set_board_info protocol standard
set_board_info hostname
set_board_info username dan
set_board_info gdb_server_prog /home/dan/gdb/mv/obj/gdb/gdbserver/gdbserver

# We will be using the standard GDB remote protocol
set_board_info gdb_protocol "remote"

# Path to the gdbserver executable, if required.
set_board_info gdb_server_prog "../gdbserver/gdbserver"

# Name of the computer whose socket will be used, if required.
set_board_info sockethost "voltaire:"

# Port ID to use for socket connection
# set_board_info gdb,socketport "4004"

# Use techniques appropriate to a stub
set_board_info use_gdb_stub 1

# This gdbserver can only run a process once per session.
set_board_info gdb,do_reload_on_run 1

# There’s no support for argument-passing (yet).
set_board_info noargs 1

# Can’t do input (or output) in the current gdbserver.
set_board_info gdb,noinferiorio 1

# Can’t do hardware watchpoints, in general
set_board_info gdb,no_hardware_watchpoints 1