1 # Copyright (C) 2022 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 emulating a partial
22 # filesystem while running isolated tests of DejaGnu procedures in a slave
23 # interpreter. These are designed to be run in the child process used by
24 # the DejaGnu library tests. Intended use is with mockutil.tcl.
26 # This implementation is by no means complete, but is sufficient for the
27 # purposes of internal DejaGnu unit tests and will be expanded as needed.
29 proc create_mockvfs
{ vfsname
} {
38 # create_mock_file vfsname {filename contents}...
39 proc create_mock_file
{ vfsname args
} {
42 foreach {filename contents
} $args {
43 if { [regexp -- {\A
\n\s
+} $contents indent
] } {
44 regsub "\\A$indent" $contents "" contents
45 regsub -all -- $indent $contents "\n" contents
46 regsub {\n\s
+\Z
} $contents "\n" contents
48 set vfs
(file,data
,$filename) $contents
49 set vfs
(file,length
,$filename) [string length
$contents]
53 # Install mockvfs procedure aliases in slave interpreter
54 proc attach_mockvfs
{ sicmd vfsname
} {
55 # supply operations for file name operations
56 foreach cmd
{ file glob open } {
57 $sicmd alias
$cmd "mockvfs_op_${cmd}" $vfsname $sicmd
59 # override I/O channel-using commands present in a safe interpreter
61 close eof flush gets puts read seek tell
64 $sicmd alias
$cmd "mockvfs_op_${cmd}" $vfsname $sicmd
66 # DejaGnu uses Expect instead of the Tcl event loop at this time, so
67 # fconfigure, fcopy, and fileevent are left untouched for now.
68 # The mock VFS does not have a current directory, so cd is omitted.
71 # operations normally not available in safe interpreters:
72 proc mockvfs_op_file
{ vfsname sicmd op args
} {
77 set name
[lindex $args 0]
78 set point
[string last
/ $name]
79 if { $point == -1 } { return .
}
80 return [string range
$name 0 [expr {$point-1}]]
83 set name
[lindex $args 0]
84 set point
[string last
/ $name]
85 if { $point == -1 } { return $name }
86 return [string range
$name [expr {$point+1}] end
]
89 error "mockvfs: file $op not implemented"
93 proc mockvfs_op_glob
{ vfsname sicmd args
} {
96 error "mockvfs: glob not implemented"
98 proc mockvfs_op_open
{ vfsname sicmd
99 fileName
{access r
} {permissions
0666} } {
100 upvar #0 $vfsname vfs
102 if { ! [info exists vfs
(file,data
,$fileName)] } {
103 error "couldn't open \"$fileName\": no such file or directory"
106 switch -glob -- $access {
109 *WR
* { error "couldn't open \"$fileName\": read-only file system" }
112 set fnum
$vfs(chan
,hint
)
113 while { [info exists vfs
(chan
,mock
${fnum
},pos
)] } { incr fnum
}
114 set vfs
(chan
,hint
) $fnum
115 set handle mock
${fnum
}
116 set vfs
(chan
,$handle,pos
) 0
117 set vfs
(chan
,$handle,file) $fileName
122 # operations normally available in safe interpreters:
123 proc mockvfs_op_close
{ vfsname sicmd chan
} {
124 if { ! [string match mock
* $chan] } {
125 return [$sicmd invokehidden
close $chan]
128 upvar #0 $vfsname vfs
130 if { [info exists vfs
(chan
,$chan,pos
)] } {
131 array unset vfs chan
,$chan,*
132 scan $chan mock
%d fnum
133 if { $vfs(chan
,hint
) > $fnum } { set vfs
(chan
,hint
) $fnum }
135 error "can not find channel named \"$chan\""
138 proc mockvfs_op_eof
{ vfsname sicmd chan
} {
139 if { ! [string match mock
* $chan] } {
140 return [$sicmd invokehidden
eof $chan]
143 upvar #0 $vfsname vfs
145 if { ! [info exists vfs
(chan
,$chan,pos
)] } {
146 error "can not find channel named \"$chan\""
149 if { $vfs(chan
,$chan,pos
) >= $vfs(file,length
,$vfs(chan
,$chan,file)) } {
155 proc mockvfs_op_flush
{ vfsname sicmd chan
} {
156 if { ! [string match mock
* $chan] } {
157 return [$sicmd invokehidden
flush $chan]
159 # do nothing for mockvfs channels
161 proc mockvfs_op_gets
{ vfsname sicmd chan args
} {
162 if { ! [string match mock
* $chan] } {
163 return [eval [list $sicmd invokehidden
gets] $args]
166 upvar #0 $vfsname vfs
167 if { [llength $args] > 1 } {
168 error "too many arguments to gets: gets $chan $args"
169 } elseif
{ [llength $args] == 1 } {
170 set outvar
[lindex $args 0]
173 if { ! [info exists vfs
(chan
,$chan,pos
)] } {
174 error "can not find channel named \"$chan\""
177 if { $vfs(chan
,$chan,pos
) >= $vfs(file,length
,$vfs(chan
,$chan,file)) } {
182 set bound
[string first
"\n" $vfs(file,data
,$vfs(chan
,$chan,file)) \
183 $vfs(chan
,$chan,pos
)]
184 if { $bound == -1 } {
185 # no newline found before eof; return last partial line
186 set output
[string range
$vfs(file,data
,$vfs(chan
,$chan,file)) \
187 $vfs(chan
,$chan,pos
) end
]
188 set outcnt
[string length
$output]
189 set vfs
(chan
,$chan,pos
) $vfs(file,length
,$vfs(chan
,$chan,file))
192 set output
[string range
$vfs(file,data
,$vfs(chan
,$chan,file)) \
193 $vfs(chan
,$chan,pos
) [expr {$bound-1}]]
194 set outcnt
[string length
$output]
195 incr vfs
(chan
,$chan,pos
) [expr {1+$outcnt}]
199 if { [info exists outvar
] } {
200 $sicmd eval [list set $outvar $output]
206 proc mockvfs_op_read
{ vfsname sicmd chan args
} {
207 if { ! [string match mock
* $chan] } {
208 return [eval [list $sicmd invokehidden
read] $args]
211 upvar #0 $vfsname vfs
213 if { ! [info exists vfs
(chan
,$chan,pos
)] } {
214 error "can not find channel named \"$chan\""
217 error "mockvfs: read not implemented"
219 proc mockvfs_op_puts
{ vfsname sicmd args
} {
220 if { [llength $args] < 2
221 ||
! [string match mock
* [lindex $args end-1
]] } {
222 return [eval [list $sicmd invokehidden
puts] $args]
225 upvar #0 $vfsname vfs
227 if { ! [info exists vfs
(chan
,$chan,pos
)] } {
228 error "can not find channel named \"$chan\""
231 error "mockvfs is currently read-only"
233 proc mockvfs_op_seek
{ vfsname sicmd chan args
} {
234 if { ! [string match mock
* $chan] } {
235 return [eval [list $sicmd invokehidden
seek] $args]
238 upvar #0 $vfsname vfs
240 if { ! [info exists vfs
(chan
,$chan,pos
)] } {
241 error "can not find channel named \"$chan\""
244 error "mockvfs: seek not implemented"
246 proc mockvfs_op_tell
{ vfsname sicmd chan args
} {
247 if { ! [string match mock
* $chan] } {
248 return [eval [list $sicmd invokehidden
tell] $args]
251 upvar #0 $vfsname vfs
253 if { ! [info exists vfs
(chan
,$chan,pos
)] } {
254 error "can not find channel named \"$chan\""
257 error "mockvfs: tell not implemented"