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
{} attach_vfs
{}
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 }
78 if { $opt(attach_vfs
) ne
"" } {
79 attach_mockvfs
$name [lindex $opt(attach_vfs
) 0]
83 proc copy_array_to_test_interpreter
{ sicmd dest
{src
{}} } {
84 if { $src eq
{} } { set src
$dest }
86 $sicmd eval array set [list $dest [array get src_array
]]
88 proc delete_test_interpreter
{ name
} {
92 proc reset_mock_trace
{} {
93 global mock_call_trace
94 set mock_call_trace
[list]
96 proc dump_mock_trace
{} {
97 global mock_call_trace
98 puts "<<< mocked calls recorded"
99 foreach cell
$mock_call_trace {
100 puts " [lindex $cell 0]"
101 if { [llength $cell] > 1 } {
102 puts " -> [lindex $cell 1]"
105 puts ">>> mocked calls recorded"
107 proc get_mock_trace
{} {
108 global mock_call_trace
109 return $mock_call_trace
111 proc find_mock_calls
{ prefix
} {
112 global mock_call_trace
114 foreach cell
$mock_call_trace {
115 if { [string match
"${prefix}*" [lindex $cell 0]] } {
122 proc relay_link_call
{ name args
} {
123 eval [list $name] $args
125 proc establish_link
{ sicmd name
} {
126 $sicmd alias
$name relay_link_call
$name
129 proc record_mock_call
{ name args
} {
130 global mock_call_trace
131 lappend mock_call_trace
[list [linsert $args 0 $name]]
134 proc establish_mock_log_alias
{ sicmd name
} {
135 $sicmd alias logcall_
$name record_mock_call
$name
137 proc establish_mock
{ sicmd name arglist retexpr
} {
138 establish_mock_log_alias
$sicmd $name
141 foreach arg
$arglist { lappend sargl
[format {$%s
} $arg] }
143 if { [lindex $arglist end
] eq
"args" } {
145 "eval \[list logcall_$name [join [lrange $sargl 0 end-1]]\] \$args"
148 "logcall_$name [join $sargl]"
151 $sicmd eval [subst -nocommands {
152 proc $name {$arglist} {
159 proc relay_shim_call
{ name args
} {
160 global mock_call_trace
161 set retval
[eval [list $name] $args]
162 lappend mock_call_trace
[list [linsert $args 0 $name] [list $retval]]
165 proc establish_shim
{ sicmd name
} {
166 $sicmd alias
$name relay_shim_call
$name
169 proc match_argpat
{ argpat call
} {
171 foreach {pos qre
} $argpat {
172 set qre
[regsub -all {\M
\s
+(?
=[^
*+?
\s
])} $qre {\s
+}]
173 set qre
[regsub -all {([*+?
])\s
+(?
=[^
*+?
\s
])} $qre {\1\s
+} ]
174 set out
[lindex $call 0 $pos]
175 verbose
"matching: ^$qre$"
176 verbose
" against: $out"
177 if { ![regexp "^$qre$" $out] } { set result
0 }
182 # test_proc_with_mocks testName sicmd testCode {
184 # prefix mode:[*U[:digit:]] { [argument pattern]... }
185 # prefix mode:[!] { }
186 # prefix mode:[C] [ { count } | count ]
189 proc test_proc_with_mocks
{ name sicmd code args
} {
193 foreach { key value
} $args {
194 if { ![info exists opt
($key)] } {
195 error "test_proc_with_mocks: unknown option $key"
197 set opt
($key) [strip_comment_lines
$value]
200 verbose
"-------- begin test: $name"
206 foreach { prefix callpos argpat
} $opt(check_calls
) {
207 set calls
[find_mock_calls
$prefix]
209 verbose
"checking: \[$callpos\] $prefix"
210 if { $callpos eq
"*" } {
211 # succeed if any call matches both prefix and argpat
213 foreach { call
} $calls {
214 verbose
" step: [lindex $call 0]"
215 if { [match_argpat
$argpat $call] } {
220 if { $innerresult ne
"pass" } {
224 } elseif
{ $callpos eq
"!" } {
225 # succeed if no calls match prefix
226 if { [llength $calls] != 0 } {
230 } elseif
{ $callpos eq
"C" } {
231 # succeed if exactly N calls match prefix
232 if { [llength $calls] != [lindex $argpat 0] } {
236 } elseif
{ $callpos eq
"U" } {
237 # prefix selects one unique call
238 if { [llength $calls] != 1 } {
239 verbose
" expected unique call... failed!"
243 if { ![match_argpat
$argpat [lindex $calls 0]] } {
247 } elseif
{ [llength $calls] > $callpos } {
248 if { ![match_argpat
$argpat [lindex $calls $callpos]] } {
253 verbose
" expected trace record not found... failed!"
260 verbose
"-------- end test: $name"