1 # Copyright (C) 2019 Free Software Foundation, Inc.
3 # This file is part of DejaGnu.
5 # DejaGnu is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # DejaGnu is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with DejaGnu; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
19 # This file was written by Jacob Bachmeyer.
21 # This library provides convenience procedures for running isolated tests
22 # of DejaGnu procedures in a slave interpreter. These are designed to be
23 # run in the child process used by the DejaGnu library tests.
25 proc strip_comment_lines
{ text } {
26 regsub -all -- {\n[[:space
:]]*#[^\r\n]*[\r\n]+} $text "\n"
29 proc create_test_interpreter
{ name opts
} {
31 copy_arrays
{} copy_procs
{} copy_vars
{}
32 link_channels
{} link_procs
{} shim_procs
{} mocks
{} vars
{}
34 array set opt
[strip_comment_lines
$opts]
36 interp create
-safe -- $name
37 foreach array $opt(copy_arrays
) { # inlined due to upvar
38 if { [llength $array] == 2 } {
39 upvar [lindex $array 1] src_array
40 } elseif
{ [llength $array] == 1 } {
41 upvar [lindex $array 0] src_array
43 error "bogus copy_arrays directive: $array"
45 $name eval array set [list [lindex $array 0] [array get src_array
]]
47 foreach proc $opt(copy_procs
) { # inlined due to uplevel
48 # proc reconstruction adapted from Tcl info(n) manpage
50 foreach arg
[uplevel info args
$proc] {
51 if { [uplevel info default $proc $arg value
] } {
52 lappend argspec
[list $arg $value]
54 lappend argspec
[list $arg]
57 $name eval proc $proc [list $argspec] [list [uplevel info body
$proc]]
59 foreach var
$opt(copy_vars
) { # inlined due to upvar
60 if { [llength $var] == 2 } {
61 upvar [lindex $var 1] src_var
62 } elseif
{ [llength $var] == 1 } {
63 upvar [lindex $var 0] src_var
65 error "bogus copy_vars directive: $var"
67 $name eval set [list [lindex $var 0] $src_var]
69 foreach {varname var
} $opt(vars
) {
70 $name eval set [list $varname $var]
72 foreach {mockname arglist retexpr
} $opt(mocks
) {
73 establish_mock
$name $mockname $arglist $retexpr
75 foreach chan
$opt(link_channels
) { interp share
{} $chan $name }
76 foreach link
$opt(link_procs
) { establish_link
$name $link }
77 foreach shim
$opt(shim_procs
) { establish_shim
$name $shim }
80 proc copy_array_to_test_interpreter
{ sicmd dest
{src
{}} } {
81 if { $src eq
{} } { set src
$dest }
83 $sicmd eval array set [list $dest [array get src_array
]]
85 proc delete_test_interpreter
{ name
} {
89 proc reset_mock_trace
{} {
90 global mock_call_trace
91 set mock_call_trace
[list]
93 proc dump_mock_trace
{} {
94 global mock_call_trace
95 puts "<<< mocked calls recorded"
96 foreach cell
$mock_call_trace {
97 puts " [lindex $cell 0]"
98 if { [llength $cell] > 1 } {
99 puts " -> [lindex $cell 1]"
102 puts ">>> mocked calls recorded"
104 proc get_mock_trace
{} {
105 global mock_call_trace
106 return $mock_call_trace
108 proc find_mock_calls
{ prefix
} {
109 global mock_call_trace
111 foreach cell
$mock_call_trace {
112 if { [string match
"${prefix}*" [lindex $cell 0]] } {
119 proc relay_link_call
{ name args
} {
120 eval [list $name] $args
122 proc establish_link
{ sicmd name
} {
123 $sicmd alias
$name relay_link_call
$name
126 proc record_mock_call
{ name args
} {
127 global mock_call_trace
128 lappend mock_call_trace
[list [linsert $args 0 $name]]
131 proc establish_mock_log_alias
{ sicmd name
} {
132 $sicmd alias logcall_
$name record_mock_call
$name
134 proc establish_mock
{ sicmd name arglist retexpr
} {
135 establish_mock_log_alias
$sicmd $name
138 foreach arg
$arglist { lappend sargl
[format {$%s
} $arg] }
140 if { [lindex $arglist end
] eq
"args" } {
142 "eval \[list logcall_$name [join [lrange $sargl 0 end-1]]\] \$args"
145 "logcall_$name [join $sargl]"
148 $sicmd eval [subst -nocommands {
149 proc $name {$arglist} {
156 proc relay_shim_call
{ name args
} {
157 global mock_call_trace
158 set retval
[eval [list $name] $args]
159 lappend mock_call_trace
[list [linsert $args 0 $name] [list $retval]]
162 proc establish_shim
{ sicmd name
} {
163 $sicmd alias
$name relay_shim_call
$name
166 proc match_argpat
{ argpat call
} {
168 foreach {pos qre
} $argpat {
169 set qre
[regsub -all {\M
\s
+(?
=[^
*+?
\s
])} $qre {\s
+}]
170 set qre
[regsub -all {([*+?
])\s
+(?
=[^
*+?
\s
])} $qre {\1\s
+} ]
171 set out
[lindex $call 0 $pos]
172 verbose
"matching: ^$qre$"
173 verbose
" against: $out"
174 if { ![regexp "^$qre$" $out] } { set result
0 }
179 proc test_proc_with_mocks
{ name sicmd code args
} {
183 foreach { key value
} $args {
184 if { ![info exists opt
($key)] } {
185 error "test_proc_with_mocks: unknown option $key"
187 set opt
($key) [strip_comment_lines
$value]
190 verbose
"-------- begin test: $name"
196 foreach { prefix callpos argpat
} $opt(check_calls
) {
197 set calls
[find_mock_calls
$prefix]
199 verbose
"checking: \[$callpos\] $prefix"
200 if { $callpos eq
"*" } {
201 # succeed if any call matches both prefix and argpat
203 foreach { call
} $calls {
204 verbose
" step: [lindex $call 0]"
205 if { [match_argpat
$argpat $call] } {
210 if { $innerresult ne
"pass" } {
214 } elseif
{ $callpos eq
"!" } {
215 # succeed if no calls match prefix
216 if { [llength $calls] != 0 } {
220 } elseif
{ $callpos eq
"U" } {
221 # prefix selects one unique call
222 if { [llength $calls] != 1 } {
223 error "expected unique call"
226 if { ![match_argpat
$argpat [lindex $calls 0]] } {
230 } elseif
{ [llength $calls] > $callpos } {
231 if { ![match_argpat
$argpat [lindex $calls $callpos]] } {
236 error "failed to select trace record"
242 verbose
"-------- end test: $name"