t_builtins.sh revision 1.1 1 # $NetBSD: t_builtins.sh,v 1.1 2018/12/05 02:45:06 kre Exp $
2 #
3 # Copyright (c) 2017 The NetBSD Foundation, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16 # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 # POSSIBILITY OF SUCH DAMAGE.
26 #
27 # the implementation of "sh" to test
28 : ${TEST_SH:="/bin/sh"}
29
30 #
31 # This file tests the various sh builtin utilities.
32 #
33 # Those utilities that are really external programs, which are builtin in
34 # for (mostly) performance (printf, kill, test, ...), are tested elsewhere.
35 # We do test the builtin "echo" here as (in NetBSD) it is different than
36 # the external one.
37 #
38 # The (mostly special) builtins which appear to be more syntax than command
39 # are tested in other test programs, rather than here (break, continue...)
40 #
41 # And finally, those which are fundamental to the operation of the shell,
42 # like wait, set, shift, ... are also tested in other test programs where
43 # all their operations can be more thoroughly verified.
44 #
45 # This leaves those which need to be built in (cd, umask, ...) but whose
46 # purpose is mostly to alter the environment in which the shell operates
47 # of that of the commands it runs. These tests act in co-operation with
48 # other tests exist here (where thy do) by not duplicating tests run
49 # elsewhere (ulimit is one example) but just adding to those.
50 # One day these might be unified.
51 #
52 # We do test both standard use of the builtins (where they are standard)
53 # and NetBSD sh extensions (when run on a shell with no support, such tests
54 # should be skipped.)
55 #
56
57 # Utility function able to test whether most of the builtins exist in
58 # the shell being tested.
59 have_builtin()
60 {
61 ${TEST_SH} -c "( $3 $1 $4 ) >/dev/null 2>&1" &&
62 LC_ALL=C ${TEST_SH} -c \
63 'case "$( (type '"$1"') 2>&1)" in
64 (*built*) exit 0 ;;
65 esac
66 exit 1' ||
67 {
68 test -z "$2" && atf_skip "${TEST_SH} has no '$1$5' built-in"
69 return 1;
70 }
71
72 return 0
73 }
74
75 ### Helper functions
76
77 nl='
78 '
79 reset()
80 {
81 TEST_NUM=0
82 TEST_FAILURES=''
83 TEST_FAIL_COUNT=0
84 TEST_ID="$1"
85
86 # These are used in check()
87 atf_require_prog tr
88 atf_require_prog printf
89 atf_require_prog mktemp
90 }
91
92 # Test run & validate.
93 #
94 # $1 is the command to run (via sh -c)
95 # $2 is the expected output
96 # $3 is the expected exit status from sh
97 # $4 is optional extra data for the error msg (if there is one)
98 #
99 # Stderr is exxpected to be empty, unless the expected exit code ($3) is != 0
100 # in which case some message there is expected (and nothing is a failure).
101 # When non-zero exit is expected, we note a different (non-zero) value
102 # observed, but do not fail the test because of that.
103
104 check()
105 {
106 fail=false
107 TEMP_FILE=$( mktemp OUT.XXXXXX )
108 TEST_NUM=$(( $TEST_NUM + 1 ))
109 MSG=
110
111 # our local shell (ATF_SHELL) better do quoting correctly...
112 # some of the tests expect us to expand $nl internally...
113 CMD="$1"
114
115 # determine what the test generates, preserving trailing \n's
116 result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" && printf X )"
117 STATUS=$?
118 result="${result%X}"
119
120
121 if [ "${STATUS}" -ne "$3" ]; then
122 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
123 MSG="${MSG} expected exit code $3, got ${STATUS}"
124
125 # don't actually fail just because of wrong exit code
126 # unless we either expected, or received "good"
127 # or something else is detected as incorrect as well.
128 case "$3/${STATUS}" in
129 (*/0|0/*) fail=true;;
130 esac
131 fi
132
133 if [ "$3" -eq 0 ]; then
134 if [ -s "${TEMP_FILE}" ]; then
135 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
136 MSG="${MSG} Messages produced on stderr unexpected..."
137 MSG="${MSG}${nl}$( cat "${TEMP_FILE}" )"
138 fail=true
139 fi
140 else
141 if ! [ -s "${TEMP_FILE}" ]; then
142 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
143 MSG="${MSG} Expected messages on stderr,"
144 MSG="${MSG} nothing produced"
145 fail=true
146 fi
147 fi
148 rm -f "${TEMP_FILE}"
149
150 if [ "$2" != "${result}" ]
151 then
152 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
153 MSG="${MSG} Expected: <<$2>>, received: <<$result>>"
154 fail=true
155 fi
156
157 if $fail
158 then
159 if [ -n "$4" ]; then
160 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM] Note: ${4}"
161 fi
162 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
163 MSG="${MSG} Full command: <<${CMD}>>"
164 fi
165
166 $fail && test -n "$TEST_ID" && {
167 TEST_FAILURES="${TEST_FAILURES}${TEST_FAILURES:+${nl}}"
168 TEST_FAILURES="${TEST_FAILURES}${TEST_ID}[$TEST_NUM]:"
169 TEST_FAILURES="${TEST_FAILURES} Test of <<$1>> failed.";
170 TEST_FAILURES="${TEST_FAILURES}${nl}${MSG}"
171 TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 ))
172 return 0
173 }
174 $fail && atf_fail "Test[$TEST_NUM] failed: $(
175 # ATF does not like newlines in messages, so change them...
176 printf '%s' "${MSG}" | tr '\n' ';'
177 )"
178 return 0
179 }
180
181 results()
182 {
183 test -n "$1" && atf_expect_fail "$1"
184
185 test -z "${TEST_ID}" && return 0
186 test -z "${TEST_FAILURES}" && return 0
187
188 echo >&2 "=========================================="
189 echo >&2 "While testing '${TEST_ID}'"
190 echo >&2 " - - - - - - - - - - - - - - - - -"
191 echo >&2 "${TEST_FAILURES}"
192
193 atf_fail \
194 "Test ${TEST_ID}: $TEST_FAIL_COUNT (of $TEST_NUM) subtests failed - see stderr"
195 }
196
197 ####### End helpers
198
199 atf_test_case colon
200 colon_head() {
201 atf_set "descr" "Tests the shell special builtin ':' command"
202 }
203 colon_body() {
204 have_builtin : || return 0
205
206 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c ":"
207
208 # ':' is a special builtin, so we should exit on redirect error
209 # and variable assignments should persist (stupid, but it is the rule)
210
211 atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
212 ": >/foo/bar; printf %s No-exit-BUG"
213 atf_check -s exit:0 -e empty -o inline:OK ${TEST_SH} -c \
214 'X=BUG; X=OK : ; printf %s "${X}"'
215 }
216
217 atf_test_case echo
218 echo_head() {
219 atf_set "descr" "Tests the shell builtin version of echo"
220 }
221 echo_body() {
222 have_builtin echo || return 0
223
224 unset NETBSD_SHELL 2>/dev/null
225 if test -z "$( ${TEST_SH} -c 'printf %s "${NETBSD_SHELL}"')"; then
226 atf_skip \
227 "${TEST_SH%% *} is not the NetBSD shell, this test is for it alone"
228 return 0
229 fi
230
231 reset echo
232
233 check 'echo "hello world"' "hello world${nl}" 0
234 check 'echo hello world' "hello world${nl}" 0
235 check 'echo -n "hello world"' "hello world" 0
236 check 'IFS=:; echo hello world' "hello world${nl}" 0
237 check 'IFS=; echo hello world' "hello world${nl}" 0
238
239 check 'echo -e "hello world"' "hello world${nl}" 0
240 check 'echo -e hello world' "hello world${nl}" 0
241 check 'IFS=:; echo -e hello world' "hello world${nl}" 0
242
243 # only one of the options is used
244 check 'echo -e -n "hello world"' "-n hello world${nl}" 0
245 check 'echo -n -e "hello world"' "-e hello world" 0
246 # and only when it is alone
247 check 'echo -en "hello world"' "-en hello world${nl}" 0
248 check 'echo -ne "hello world"' "-ne hello world${nl}" 0
249
250 # echo is specifically required to *not* support --
251 check 'echo -- "hello world"' "-- hello world${nl}" 0
252
253 # similarly any other unknown option is simply part of the output
254 for OPT in a b c v E N Q V 0 1 2 @ , \? \[ \] \( \; . \* -help -version
255 do
256 check "echo '-${OPT}' foo" "-${OPT} foo${nl}" 0
257 done
258
259 # Now test the \\ expansions, with and without -e
260
261 # We rely upon printf %b (tested elsewhere, not only a sh test)
262 # to verify the output when the \\ is supposed to be expanded.
263
264 for E in '' -e
265 do
266 for B in a b c e f n r t v \\ 04 010 012 0177
267 do
268 S="test string with \\${B} in it"
269 if [ -z "${E}" ]; then
270 R="${S}${nl}"
271 else
272 R="$(printf '%b\nX' "${S}")"
273 R=${R%X}
274 fi
275 check "echo $E '${S}'" "${R}" 0
276 done
277 done
278
279 results
280 }
281
282 atf_test_case eval
283 eval_head() {
284 atf_set "descr" "Tests the shell special builtin 'eval'"
285 }
286 eval_body() {
287 have_builtin eval || return 0
288
289 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'eval "exit 0"'
290 atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c 'eval "exit 1"'
291 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'eval exit 0'
292
293 atf_check -s exit:0 -e empty -o inline:abc ${TEST_SH} -c \
294 'X=a Y=b Z=c; for V in X Y Z; do eval "printf %s \$$V"; done'
295 atf_check -s exit:0 -e empty -o inline:abc ${TEST_SH} -c \
296 'X=a Y=b Z=c; for V in X Y Z; do eval printf %s \$$V; done'
297 atf_check -s exit:0 -e empty -o inline:XYZ ${TEST_SH} -c \
298 'for V in X Y Z; do eval "${V}=${V}"; done; printf %s "$X$Y$Z"'
299 }
300
301 atf_test_case exec
302 exec_head() {
303 atf_set "descr" "Tests the shell special builtin 'exec'"
304 }
305 exec_body() {
306 have_builtin exec || return 0
307
308 atf_check -s exit:0 -e empty -o inline:OK ${TEST_SH} -c \
309 'exec printf OK; printf BROKEN; exit 3'
310 atf_check -s exit:3 -e empty -o inline:OKOK ${TEST_SH} -c \
311 '(exec printf OK); printf OK; exit 3'
312 }
313
314 atf_test_case export
315 export_head() {
316 atf_set "descr" "Tests the shell builtin 'export'"
317 }
318 export_body() {
319 have_builtin export || return 0
320
321 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export VAR'
322 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export VAR=abc'
323 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export V A R'
324 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
325 'export V A=1 R=2'
326
327 atf_require_prog printenv
328
329 atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \
330 'unset VAR || exit 7; export VAR; printenv VAR'
331 atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \
332 'unset VAR || exit 7; export VAR=; printenv VAR'
333 atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \
334 'unset VAR || exit 7; VAR=; export VAR; printenv VAR'
335 atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \
336 'unset VAR || exit 7; export VAR; VAR=; printenv VAR'
337 atf_check -s exit:0 -e empty -o inline:XYZ\\n ${TEST_SH} -c \
338 'unset VAR || exit 7; export VAR=XYZ; printenv VAR'
339 atf_check -s exit:0 -e empty -o inline:ABC\\n ${TEST_SH} -c \
340 'VAR=ABC; export VAR; printenv VAR'
341 atf_check -s exit:0 -e empty -o inline:ABC\\n ${TEST_SH} -c \
342 'unset VAR || exit 7; export VAR; VAR=ABC; printenv VAR'
343 atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \
344 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR'
345 atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \
346 'unset VAR || exit 7; export VAR;
347 VAR=ABC; printenv VAR; VAR=XYZ; printenv VAR'
348
349 atf_check -s exit:1 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \
350 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR;
351 unset VAR; printenv VAR; VAR=PQR; printenv VAR'
352 atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\nVAR=unset\\nMNO\\n \
353 ${TEST_SH} -c \
354 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR;
355 unset VAR; printf %s\\n "VAR=${VAR-unset}"; printenv VAR;
356 VAR=PQR; printenv VAR; VAR=MNO; export VAR; printenv VAR'
357 }
358
359 atf_test_case export_nbsd
360 export_nbsd_head() {
361 atf_set "descr" "Tests NetBSD extensions to the shell builtin 'export'"
362 }
363 export_nbsd_body() {
364 have_builtin "export" "" "" "-n foo" ' -n' || return 0
365
366 atf_require_prog printenv
367
368 atf_check -s exit:1 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \
369 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR;
370 export -n VAR; printenv VAR; VAR=PQR; printenv VAR'
371
372 atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\nVAR=XYZ\\nMNO\\n \
373 ${TEST_SH} -c \
374 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR;
375 export -n VAR; printf %s\\n "VAR=${VAR-unset}"; printenv VAR;
376 VAR=PQR; printenv VAR; VAR=MNO; export VAR; printenv VAR'
377
378 have_builtin "export" "" "" -x ' -x' || return 0
379
380 atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \
381 'export VAR=exported; export -x VAR; printenv VAR'
382 atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \
383 'export VAR=exported; export -x VAR; VAR=local; printenv VAR'
384 atf_check -s exit:0 -e empty -o inline:once\\nx\\n ${TEST_SH} -c \
385 'export VAR=exported
386 export -x VAR
387 VAR=once printenv VAR
388 printenv VAR || printf %s\\n x'
389
390 atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
391 'export VAR=exported; export -x VAR; export VAR=FOO'
392 }
393
394 atf_test_case getopts
395 getopts_head() {
396 atf_set "descr" "Tests the shell builtin 'getopts'"
397 }
398 getopts_body() {
399 have_builtin getopts "" "f() {" "a x; }; f -a" || return 0
400 }
401
402 atf_test_case jobs
403 jobs_head() {
404 atf_set "descr" "Tests the shell builting 'jobs' command"
405 }
406 jobs_body() {
407 have_builtin jobs || return 0
408
409 atf_require_prog sleep
410
411 # note that POSIX requires that we reference $! otherwise
412 # the shell is not required to remember the process...
413
414 atf_check -s exit:0 -e empty -o match:sleep -o match:Running \
415 ${TEST_SH} -c 'sleep 1 & P=$!; jobs; wait'
416 atf_check -s exit:0 -e empty -o match:sleep -o match:Done \
417 ${TEST_SH} -c 'sleep 1 & P=$!; sleep 2; jobs; wait'
418 }
419
420 atf_test_case read
421 read_head() {
422 atf_set "descr" "Tests the shell builtin read command"
423 }
424 read_body() {
425 have_builtin read "" "echo x|" "var" || return 0
426 }
427
428 atf_test_case readonly
429 readonly_head() {
430 atf_set "descr" "Tests the shell builtin 'readonly'"
431 }
432 readonly_body() {
433 have_builtin readonly || return 0
434
435 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly VAR'
436 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly VAR=abc'
437 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly V A R'
438 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly V A=1 R=2'
439
440 atf_check -s exit:0 -e empty -o inline:unset ${TEST_SH} -c \
441 'unset VAR; readonly VAR; printf %s ${VAR-unset}'
442 atf_check -s exit:0 -e empty -o inline:set ${TEST_SH} -c \
443 'unset VAR; readonly VAR=set; printf %s ${VAR-unset}'
444 atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
445 'readonly VAR=initial; VAR=new; printf %s "${VAR}"'
446
447 # don't test stderr, some shells inist on generating a message for an
448 # unset of a readonly var (rather than simply having unset make $?=1)
449
450 atf_check -s not-exit:0 -e empty -o empty ${TEST_SH} -c \
451 'unset VAR; readonly VAR=set;
452 unset VAR 2>/dev/null && printf %s ${VAR:-XX}'
453 atf_check -s not-exit:0 -e ignore -o empty ${TEST_SH} -c \
454 'unset VAR; readonly VAR=set; unset VAR && printf %s ${VAR:-XX}'
455 atf_check -s exit:0 -e ignore -o inline:set ${TEST_SH} -c \
456 'unset VAR; readonly VAR=set; unset VAR; printf %s ${VAR-unset}'
457 }
458
459 atf_test_case cd_pwd
460 cd_pwd_head() {
461 atf_set "descr" "Tests the shell builtins 'cd' & 'pwd'"
462 }
463 cd_pwd_body() {
464 have_builtin cd "" "HOME=/;" || return 0
465 have_builtin pwd || return 0
466 }
467
468 atf_test_case true_false
469 true_false_head() {
470 atf_set "descr" "Tests the 'true' and 'false' shell builtin commands"
471 }
472 true_false_body() {
473 have_builtin true || return 0
474
475 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c true
476
477 # true is not a special builtin, so errors do not cause exit
478 # but we should still get an error from the broken redirect
479 # and the exit status of true should be false...
480
481 atf_check -s exit:0 -e not-empty -o inline:OK ${TEST_SH} -c \
482 "true >/foo/bar && printf %s NOT-; printf %s OK"
483
484 # and var-assigns should not affect the current sh env
485
486 atf_check -s exit:0 -e empty -o inline:IS-OK ${TEST_SH} -c \
487 'X=OK; X=BROKEN true && printf %s IS-; printf %s "${X}"'
488
489 have_builtin false "" ! || return 0
490
491 atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c false
492 }
493
494 atf_test_case type
495 type_head() {
496 atf_set "descr" "Tests the sh builtin 'type' command"
497 }
498 type_body() {
499 have_builtin type "" "" type || return 0
500 }
501
502 # This currently has its own t_ulimit - either merge that here,
503 # or delete this one and keep that... ulimit -n is also tested in
504 # the t_redir tests, as that affects the shell's use of file descriptors
505 atf_test_case ulimit
506 ulimit_head() {
507 atf_set "descr" "Tests the sh builtin 'ulimit'"
508 }
509 ulimit_body() {
510 have_builtin ulimit || return 0
511 }
512
513 atf_test_case umask
514 umask_head() {
515 atf_set "descr" "Tests the sh builtin 'umask'"
516 }
517 umask_body() {
518 have_builtin umask || return 0
519
520 atf_require_prog touch
521 atf_require_prog stat
522 atf_require_prog rm
523 atf_require_prog chmod
524
525 reset umask
526
527 # 8 octal digits
528 for M in 0 1 2 3 4 5 6 7
529 do
530 # Test numbers start: 1 25 49 73 97 121 145 169
531
532 # 8 combinations of each to test (64 inner loops)
533 # 3 tests in each loop, hence 192 subtests in all
534
535 # Test numbers from loop above, plus (below) and the next 2
536 #+ 1 4 7 10 13
537 for T in "0${M}" "00${M}" "0${M}0" "0${M}00" "0${M}${M}" \
538 "0${M}${M}0" "0${M}${M}${M}" "0${M}0${M}"
539 #+ 16 19 22
540 do
541 # umask turns bits off, calculate which bits will be on...
542
543 D=$(( 0777 & ~ T )) # for directories
544 F=$(( $D & ~ 0111 )) # and files with no 'x' bits
545
546 # Note: $(( )) always produces decimal, so we test that format
547 # (see '%d' in printf of stat result)
548
549 # need chmod or we might have no perm to rmdir TD
550 { chmod +rwx TF TFT TD; rm -fr TF TFT TD; } 2>/dev/null || :
551
552 # check that the umask applies to files created by the shell
553 check \
554 "umask $T; > TF; printf %d \$(stat -nf %#Lp TF)" \
555 "$F" 0 "$F is $(printf %#o $F)" # 1 4 7 10 ...
556
557 # and to files created by commands that the shell runs
558 check \
559 "umask $T; touch TFT; printf %d \$(stat -nf %#Lp TFT)" \
560 "$F" 0 "$F is $(printf %#o $F)" # 2 5 8 11 ...
561
562 # and to directories created b ... (directories keep 'x')
563 check \
564 "umask $T; mkdir TD; printf %d \$(stat -nf %#Lp TD)" \
565 "$D" 0 "$D is $(printf %#o $D)" # 3 6 9 12 ...
566 done
567 done
568
569 # Now add a few more tests with less regular u/g/m masks
570 # In here, include tests where umask value has no leading '0'
571
572 # 10 loops, the same 3 tests in each loop, 30 more subtests
573 # from 193 .. 222
574
575 # 193 196 199 202 205 208 211 214 217 220
576 for T in 013 047 722 0772 027 123 421 0124 0513 067
577 do
578 D=$(( 0777 & ~ 0$T ))
579 F=$(( $D & ~ 0111 ))
580
581 { chmod +rwx TF TFT TD; rm -fr TF TFT TD; } 2>/dev/null || :
582
583 check \
584 "umask $T; > TF; printf %d \$(stat -nf %#Lp TF)" \
585 "$F" 0 "$F is $(printf %#o $F)" # +0
586
587 check \
588 "umask $T; touch TFT; printf %d \$(stat -nf %#Lp TFT)" \
589 "$F" 0 "$F is $(printf %#o $F)" # +1
590
591 check \
592 "umask $T; mkdir TD; printf %d \$(stat -nf %#Lp TD)" \
593 "$D" 0 "$D is $(printf %#o $D)" # +2
594 done
595
596 results
597 }
598
599 atf_test_case unset
600 unset_head() {
601 atf_set "descr" "Tests the sh builtin 'unset'"
602 }
603 unset_body() {
604 have_builtin unset || return 0
605 }
606
607 atf_test_case hash
608 hash_head() {
609 atf_set "descr" "Tests the sh builtin 'hash' (ash extension)"
610 }
611 hash_body() {
612 have_builtin hash || return 0
613 }
614
615 atf_test_case jobid
616 jobid_head() {
617 atf_set "descr" "Tests sh builtin 'jobid' (NetBSD extension)"
618 }
619 jobid_body() {
620
621 # have_builtin jobid || return 0 No simple jobid command test
622 $TEST_SH -c '(exit 0)& jobid $!' >/dev/null 2>&1 || {
623 atf_skip "${TEST_SH} has no 'jobid' built-in"
624 return 0
625 }
626 }
627
628 atf_test_case let
629 let_head() {
630 atf_set "descr" "Tests the sh builtin 'let' (common extension from ksh)"
631 }
632 let_body() {
633 have_builtin let "" "" 1 || return 0
634 }
635
636 atf_test_case local
637 local_head() {
638 atf_set "descr" "Tests the shell builtin 'local' (common extension)"
639 }
640 local_body() {
641 have_builtin local "" "f () {" "X; }; f" || return 0
642 }
643
644 atf_test_case setvar
645 setvar_head() {
646 atf_set "descr" "Tests the shell builtin 'setvar' (BSD extension)"
647 }
648 setvar_body() {
649 have_builtin setvar || return 0
650
651 atf_check -s exit:0 -e empty -o inline:foo ${TEST_SH} -c \
652 'unset PQ && setvar PQ foo; printf %s "${PQ-not set}"'
653 atf_check -s exit:0 -e empty -o inline:abcd ${TEST_SH} -c \
654 'for x in a b c d; do setvar "$x" "$x"; done;
655 printf %s "$a$b$c$d"'
656 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
657 'a=1; b=2; c=3; d=4
658 for x in a b c d; do setvar "$x" ""; done;
659 printf %s "$a$b$c$d"'
660 }
661
662 atf_test_case fdflags
663 fdflags_head() {
664 atf_set "descr" \
665 "Tests basic operation of sh builtin 'fdflags' (NetBSD extension)"
666 }
667 fdflags_body() {
668 have_builtin fdflags || return 0
669 }
670
671 atf_test_case fdflags__s
672 fdflags__s_head() {
673 atf_set "descr" "Checks setting/clearing flags on file descriptors"
674 }
675 fdflags__s_body() {
676 have_builtin fdflags || return 0
677 }
678
679 atf_test_case fdflags__v
680 fdflags__v_head() {
681 atf_set "descr" "Checks verbose operation of fdflags"
682 }
683 fdflags__v_body() {
684 have_builtin fdflags || return 0
685 }
686
687 atf_test_case fdflags__v_s
688 fdflags__v_s_head() {
689 atf_set "descr" "tests verbose operation of fdflags -s"
690 }
691 fdflags__v_s_body() {
692 have_builtin fdflags || return 0
693 }
694
695 atf_test_case fdflags_multiple_fd
696 fdflags_multiple_fd_head() {
697 atf_set "descr" "Checks operation of fdflags with more than one fd"
698 }
699 fdflags_multiple_fd_body() {
700 have_builtin fdflags || return 0
701 }
702
703 atf_test_case fdflags_one_flag_at_a_time
704 fdflags_one_flag_at_a_time_head() {
705 atf_set "descr" "Tests all possible fdflags flags, and combinations"
706 }
707 fdflags_one_flag_at_a_time_body() {
708 have_builtin fdflags || return 0
709 }
710
711 atf_test_case fdflags_save_restore
712 fdflags_save_restore_head() {
713 atf_set "descr" 'Verify that fd flags can be saved and restored'
714 }
715 fdflags_save_restore_body() {
716 have_builtin fdflags || return 0
717 }
718
719 atf_test_case fdflags_names_abbreviated
720 fdflags_names_abbreviated_head() {
721 atf_set "descr" 'Tests using abbreviated names for fdflags'
722 }
723 fdflags_names_abbreviated_body() {
724 have_builtin fdflags || return 0
725 }
726
727 atf_test_case fdflags_xx_errors
728 fdflags_xx_errors_head() {
729 atf_set "descr" 'Check various erroneous fdflags uses'
730 }
731 fdflags_xx_errors_body() {
732 have_builtin fdflags || return 0
733 }
734
735
736 atf_init_test_cases() {
737
738 # "standard" builtin commands in sh
739
740 # no tests of the "very special" (almost syntax) builtins
741 # (break/continue/return) - they're tested enough elsewhere
742
743 atf_add_test_case cd_pwd
744 atf_add_test_case colon
745 atf_add_test_case echo
746 atf_add_test_case eval
747 atf_add_test_case exec
748 atf_add_test_case export
749 atf_add_test_case getopts
750 atf_add_test_case jobs
751 atf_add_test_case read
752 atf_add_test_case readonly
753 atf_add_test_case true_false
754 atf_add_test_case type
755 atf_add_test_case ulimit
756 atf_add_test_case umask
757 atf_add_test_case unset
758
759 # exit/wait/set/shift/trap/alias/unalias/. should have their own tests
760 # fc/times/fg/bg/% are too messy to contemplate for now
761 # command ?? (probably should have some tests)
762
763 # Note that builtin versions of, printf, kill, ... are tested separately
764 # (these are all "optional" builtins)
765 # (echo is tested here because NetBSD sh builtin echo and /bin/echo
766 # are different)
767
768 atf_add_test_case export_nbsd
769 atf_add_test_case hash
770 atf_add_test_case jobid
771 atf_add_test_case let
772 atf_add_test_case local
773 atf_add_test_case setvar
774 # inputrc should probably be tested in libedit tests (somehow)
775
776 # fdflags has a bunch of test cases
777
778 # Always run one test, so we get at least "skipped" result
779 atf_add_test_case fdflags
780
781 # but no need to say "skipped" lots more times...
782 have_builtin fdflags available && {
783 atf_add_test_case fdflags__s
784 atf_add_test_case fdflags__v
785 atf_add_test_case fdflags__v_s
786 atf_add_test_case fdflags_multiple_fd
787 atf_add_test_case fdflags_names_abbreviated
788 atf_add_test_case fdflags_one_flag_at_a_time
789 atf_add_test_case fdflags_save_restore
790 atf_add_test_case fdflags_xx_errors
791 }
792 return 0
793 }
794