Add test mode for "dejagnu help" command
[dejagnu.git] / testsuite / runtest.libs / mockutil.tcl
bloba8fa2fd70b14d60b73746dad8052ffeb16d5c6fa
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 } {
30 array set opt {
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
42 } else {
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
49 set argspec [list]
50 foreach arg [uplevel info args $proc] {
51 if { [uplevel info default $proc $arg value] } {
52 lappend argspec [list $arg $value]
53 } else {
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
64 } else {
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 return $name
80 proc copy_array_to_test_interpreter { sicmd dest {src {}} } {
81 if { $src eq {} } { set src $dest }
82 upvar $src src_array
83 $sicmd eval array set [list $dest [array get src_array]]
85 proc delete_test_interpreter { name } {
86 interp delete $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
110 set result [list]
111 foreach cell $mock_call_trace {
112 if { [string match "${prefix}*" [lindex $cell 0]] } {
113 lappend result $cell
116 return $result
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]]
129 return
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
137 set sargl [list]
138 foreach arg $arglist { lappend sargl [format {$%s} $arg] }
140 if { [lindex $arglist end] eq "args" } {
141 set log_call \
142 "eval \[list logcall_$name [join [lrange $sargl 0 end-1]]\] \$args"
143 } else {
144 set log_call \
145 "logcall_$name [join $sargl]"
148 $sicmd eval [subst -nocommands {
149 proc $name {$arglist} {
150 $log_call
151 return $retexpr
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]]
160 return $retval
162 proc establish_shim { sicmd name } {
163 $sicmd alias $name relay_shim_call $name
166 proc match_argpat { argpat call } {
167 set result 1
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 }
176 return $result
179 proc test_proc_with_mocks { name sicmd code args } {
180 array set opt {
181 check_calls {}
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"
191 reset_mock_trace
192 $sicmd eval $code
193 dump_mock_trace
195 set result pass
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
202 set innerresult fail
203 foreach { call } $calls {
204 verbose " step: [lindex $call 0]"
205 if { [match_argpat $argpat $call] } {
206 set innerresult pass
207 break
210 if { $innerresult ne "pass" } {
211 verbose " failed!"
212 set result fail
214 } elseif { $callpos eq "!" } {
215 # succeed if no calls match prefix
216 if { [llength $calls] != 0 } {
217 verbose " failed!"
218 set result fail
220 } elseif { $callpos eq "U" } {
221 # prefix selects one unique call
222 if { [llength $calls] != 1 } {
223 error "expected unique call"
224 return
226 if { ![match_argpat $argpat [lindex $calls 0]] } {
227 verbose " failed!"
228 set result fail
230 } elseif { [llength $calls] > $callpos } {
231 if { ![match_argpat $argpat [lindex $calls $callpos]] } {
232 verbose " failed!"
233 set result fail
235 } else {
236 error "failed to select trace record"
237 return
241 $result $name
242 verbose "-------- end test: $name"
246 #EOF