1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
|
%prep
storepath=($path)
mkdir command.tmp command.tmp/dir1 command.tmp/dir2
cd command.tmp
print '#!/bin/sh\necho This is top' >tstcmd
print '#!/bin/sh\necho This is dir1' >dir1/tstcmd
print '#!/bin/sh\necho This is dir2' >dir2/tstcmd
print -n '#!sh\necho This is slashless' >tstcmd-slashless
print -n '#!echo foo\necho This is arg' >tstcmd-arg
print '#!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnyyy' >tstcmd-interp-too-long
print '#!/bin/sh\necho should not execute; exit 1' >xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn
chmod 755 tstcmd dir1/tstcmd dir2/tstcmd
chmod 755 tstcmd-slashless tstcmd-arg tstcmd-interp-too-long
chmod 755 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn
%test
./tstcmd
0:./prog execution
>This is top
path=($ZTST_testdir/command.tmp/dir1
$ZTST_testdir/command.tmp/dir2
.)
tstcmd
path=($storepath)
0:path (1)
>This is dir1
path=(. command.tmp/dir{1,2})
tstcmd
path=($storepath)
0:path (2)
>This is top
PATH=/bin:${ZTST_testdir}/command.tmp/ tstcmd-slashless
0:path (3)
>This is slashless
PATH=/bin:${ZTST_testdir}/command.tmp tstcmd-arg
0:path (4)
*>foo */command.tmp/tstcmd-arg
path=(/bin ${ZTST_testdir}/command.tmp/)
tstcmd-interp-too-long 2>&1; echo "status $?"
path=($storepath)
0:path (5)
*>*tstcmd-interp-too-long: bad interpreter: x*xn: no such file or directory
>status 127
functst() { print $# arguments:; print -l $*; }
functst "Eines Morgens" "als Gregor Samsa"
functst ""
functst "aus unrühigen Träumen erwachte"
foo="fand er sich in seinem Bett"
bar=
rod="zu einem ungeheuren Ungeziefer verwandelt."
functst $foo $bar $rod
# set up alias for next test
alias foo='print This is alias one'
0:function argument passing
>2 arguments:
>Eines Morgens
>als Gregor Samsa
>1 arguments:
>
>1 arguments:
>aus unrühigen Träumen erwachte
>2 arguments:
>fand er sich in seinem Bett
>zu einem ungeheuren Ungeziefer verwandelt.
alias foo='print This is alias two'
fn() { foo; }
fn
0:Aliases in functions
>This is alias one
foo='Global foo'
traptst() { local foo="Local foo"; trap 'print $foo' EXIT; }
traptst
0:EXIT trap environment
>Global foo
functst() { return 0; print Ha ha; return 1; }
functst
0:return (1)
functst() { return 1; print Ho ho; return 0; }
functst
1:return (2)
unfunction functst
fpath=(.)
print "print This is functst." >functst
autoload functst
functst
0:autoloading (1)
>This is functst.
unfunction functst
print "functst() { print This, too, is functst; }; print Hello." >functst
typeset -fu functst
functst
functst
0:autoloading with initialization
>Hello.
>This, too, is functst
unfunction functst
print "print Yet another version" >functst
functst() { autoload -X; }
functst
0:autoloading via -X
>Yet another version
chpwd() { print Changed to $PWD; }
cd .
unfunction chpwd
0q:chpwd
>Changed to $ZTST_testdir/command.tmp
chpwd() { print chpwd: changed to $PWD; }
chpwdfn1() { print chpwdfn1: changed to $PWD; }
chpwdfn2() { print chpwdfn2: changed to $PWD; }
chpwd_functions=(chpwdfn1 '' chpwdnonexistentfn chpwdfn2)
cd .
unfunction chpwd
unset chpwd_functions
0q:chpwd_functions
>chpwd: changed to $ZTST_testdir/command.tmp
>chpwdfn1: changed to $ZTST_testdir/command.tmp
>chpwdfn2: changed to $ZTST_testdir/command.tmp
# Hard to test periodic, precmd and preexec non-interactively.
fn() { TRAPEXIT() { print Exit; }; }
fn
0:TRAPEXIT
>Exit
unsetopt DEBUG_BEFORE_CMD
unfunction fn
print 'TRAPDEBUG() {
print Line $LINENO
}
:
unfunction TRAPDEBUG
' > fn
autoload fn
fn
rm fn
0:TRAPDEBUG
>Line 1
>Line 1
unsetopt DEBUG_BEFORE_CMD
unfunction fn
print 'trap '\''print Line $LINENO'\'' DEBUG
:
trap - DEBUG
' > fn
autoload fn
fn
rm fn
0:trap DEBUG
>Line 1
>Line 2
TRAPZERR() { print Command failed; }
true
false
true
false
unfunction TRAPZERR
0:TRAPZERR
>Command failed
>Command failed
trap 'print Command failed again.' ZERR
true
false
true
false
trap - ZERR
0:trap ZERR
>Command failed again.
>Command failed again.
false
sleep 1000 &
print $?
kill $!
0:Status reset by starting a backgrounded command
>0
{ setopt MONITOR } 2>/dev/null
[[ -o MONITOR ]] || print -u $ZTST_fd 'Unable to change MONITOR option'
repeat 2048; do (return 2 |
return 1 |
while true; do
false
break
done;
print "${pipestatus[@]}")
ZTST_hashmark
done | sort | uniq -c | sed 's/^ *//'
0:Check whether '$pipestatus[]' behaves.
>2048 2 1 0
F:This test checks for a bug in '$pipestatus[]' handling. If it breaks then
F:the bug is still there or it reappeared. See workers-29973 for details.
{ setopt MONITOR } 2>/dev/null
externFunc() { awk >/dev/null 2>&1; true; }
false | true | false | true | externFunc
echo $pipestatus
0:Check $pipestatus with a known difficult case
>1 0 1 0 0
F:This similar test was triggering a reproducible failure with pipestatus.
{ unsetopt MONITOR } 2>/dev/null
coproc { read -et 5 || { print -u $ZTST_fd KILLED; kill -HUP -$$ } }
print -u $ZTST_fd 'This test takes 5 seconds to fail...'
{ printf "%d\n" {1..20000} } 2>/dev/null | ( read -e )
hang(){ printf "%d\n" {2..20000} | cat }; hang 2>/dev/null | ( read -e )
print -p done
read -et 6 -p
0:Bug regression: piping a shell construct to an external process may hang
>1
>2
>done
F:This test checks for a file descriptor leak that could cause the left
F:side of a pipe to block on write after the right side has exited
{ setopt MONITOR } 2>/dev/null
if [[ -o MONITOR ]]
then
( while :; do print "This is a line"; done ) | () : &
sleep 1
jobs -l
else
print -u $ZTST_fd "Skipping pipe leak test, requires MONITOR option"
print "[0] 0 0"
fi
0:Bug regression: piping to anonymous function; piping to backround function
*>\[<->\] <-> <->
F:This test checks for two different bugs, a parser segfault piping to an
F:anonymous function, and a descriptor leak when backgrounding a pipeline
print "autoload_redir() { print Autoloaded ksh style; } >autoload.log" >autoload_redir
autoload -Uk autoload_redir
autoload_redir
print No output yet
cat autoload.log
functions autoload_redir
0:
>No output yet
>Autoloaded ksh style
>autoload_redir () {
> print Autoloaded ksh style
>} > autoload.log
# This tests that we record the status of processes that have already exited
# for when we wait for them.
#
# Actually, we don't guarantee here that the jobs have already exited, but
# the order of the waits means it's highly likely we do need to recall a
# previous status, barring accidents which shouldn't happen very often. In
# other words, we rely on the test working repeatedly rather than just
# once. The monitor option is irrelevant to the logic, so we'll make
# our job easier by turning it off.
{ unsetopt MONITOR } 2>/dev/null
(exit 1) &
one=$!
(exit 2) &
two=$!
(exit 3) &
three=$!
wait $three
print $?
wait $two
print $?
wait $one
print $?
0:The status of recently exited background jobs is recorded
>3
>2
>1
# Regression test for workers/34060 (patch in 34065)
setopt ERR_EXIT NULL_GLOB
if false; then :; else echo if:$?; fi
if false; then :; else for x in _*_; do :; done; echo for:$?; fi
0:False "if" condition handled correctly by "for" loops with ERR_EXIT
>if:1
>for:0
# Regression test for workers/34065 (uses setopt from preceding test)
select x; do :; done; echo $?
select x in; do :; done; echo $?
select x in _*_; do :; done; echo $?
unsetopt ERR_EXIT NULL_GLOB
0:The status of "select" is zero when the loop body does not execute
>0
>0
>0
# Regression test for workers/36392
print -u $ZTST_fd 'This test takes 3 seconds and hangs the shell when it fails...'
callfromchld() { true && { print CHLD } }
TRAPCHLD() { callfromchld }
sleep 2 & sleep 3; print OK
0:Background job exit does not affect reaping foreground job
>CHLD
>OK
# Regression test for workers/39839 and workers/39844
() { if return 11; then :; fi }; echo $?
() { while return 13; do :; done }; echo $?
() { until return 17; do :; done }; echo $?
() { until false; do return 19; done }; echo $?
0:"return" in "if" or "while" conditional
>11
>13
>17
>19
|