t_option.sh revision 1.1 1 # $NetBSD: t_option.sh,v 1.1 2016/02/23 16:20:42 christos Exp $
2 #
3 # Copyright (c) 2016 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 # The standard
31 # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
32 # says:
33 # ...[lots]
34
35 test_option_on_off()
36 {
37 atf_require_prog tr
38
39 for opt
40 do
41 # t is needed, as inside $()` $- appears to lose
42 # the 'e' option if it happened to already be
43 # set. Must check if that is what should
44 # happen, but that is a different issue.
45
46 test -z "${opt}" && continue
47
48 # if we are playing with more that one option at a
49 # time, the code below requires that we start with no
50 # options set, or it will mis-diagnose the situation
51 CLEAR=''
52 test "${#opt}" -gt 1 &&
53 CLEAR='xx="$-" && xx=$(echo "$xx" | tr -d cs) && test -n "$xx" && set +"$xx";'
54
55 atf_check -s exit:0 -o empty -e empty "${TEST_SH}" -c \
56 "opt=${opt}"'
57 x() {
58 echo "ERROR: Unable to $1 option $2" >&2
59 exit 1
60 }
61 s() {
62 set -"$1"
63 t="$-"
64 x=$(echo "$t" | tr -d "$1")
65 test "$t" = "$x" && x set "$1"
66 return 0
67 }
68 c() {
69 set +"$1"
70 t="$-"
71 x=$(echo "$t" | tr -d "$1")
72 test "$t" != "$x" && x clear "$1"
73 return 0
74 }
75 '"${CLEAR}"'
76
77 # if we do not do this, -x tracing splatters stderr
78 # for some shells, -v does as well (is that correct?)
79 case "${opt}" in
80 (*[xv]*) exec 2>/dev/null;;
81 esac
82
83 o="$-"
84 x=$(echo "$o" | tr -d "$opt")
85
86 if [ "$o" = "$x" ]; then # option was off
87 s "${opt}"
88 c "${opt}"
89 else
90 c "${opt}"
91 s "${opt}"
92 fi
93 '
94 done
95 }
96
97 test_optional_on_off()
98 {
99 RET=0
100 OPTS=
101 for opt
102 do
103 test "${opt}" = n && continue
104 "${TEST_SH}" -c "set -${opt}" 2>/dev/null &&
105 OPTS="${OPTS} ${opt}" || RET=1
106 done
107
108 test -n "${OPTS}" && test_option_on_off ${OPTS}
109
110 return "${RET}"
111 }
112
113 atf_test_case set_a
114 set_a_head() {
115 atf_set "descr" "Tests that 'set -a' turns on all var export " \
116 "and that it behaves as defined by the standard"
117 }
118 set_a_body() {
119 atf_require_prog env
120 atf_require_prog grep
121
122 test_option_on_off a
123
124 # without -a, new variables should not be exported (so grep "fails")
125 atf_check -s exit:1 -o empty -e empty "${TEST_SH}" -ce \
126 'unset VAR; set +a; VAR=value; env | grep "^VAR="'
127
128 # with -a, they should be
129 atf_check -s exit:0 -o match:VAR=value -e empty "${TEST_SH}" -ce \
130 'unset VAR; set -a; VAR=value; env | grep "^VAR="'
131 }
132
133 atf_test_case set_C
134 set_C_head() {
135 atf_set "descr" "Tests that 'set -C' turns on no clobber mode " \
136 "and that it behaves as defined by the standard"
137 }
138 set_C_body() {
139 atf_require_prog ls
140
141 test_option_on_off C
142
143 # Check that the environment to use for the tests is sane ...
144 # we assume current dir is a new tempory directory & is empty
145
146 test -z "$(ls)" || atf_skip "Test execution directory not clean"
147 test -c "/dev/null" || atf_skip "Problem with /dev/null"
148
149 echo Dummy_Content > Junk_File
150 echo Precious_Content > Important_File
151
152 # Check that we can redirect onto file when -C is not set
153 atf_check -s exit:0 -o empty -e empty "${TEST_SH}" -c \
154 '
155 D=$(ls -l Junk_File) || exit 1
156 set +C
157 echo "Overwrite it now" > Junk_File
158 A=$(ls -l Junk_File) || exit 1
159 test "${A}" != "${D}"
160 '
161
162 # Check that we cannot redirect onto file when -C is set
163 atf_check -s exit:0 -o empty -e not-empty "${TEST_SH}" -c \
164 '
165 D=$(ls -l Important_File) || exit 1
166 set -C
167 echo "Fail to Overwrite it now" > Important_File
168 A=$(ls -l Important_File) || exit 1
169 test "${A}" = "${D}"
170 '
171
172 # Check that we can append to file, even when -C is set
173 atf_check -s exit:0 -o empty -e empty "${TEST_SH}" -c \
174 '
175 D=$(ls -l Junk_File) || exit 1
176 set -C
177 echo "Append to it now" >> Junk_File
178 A=$(ls -l Junk_File) || exit 1
179 test "${A}" != "${D}"
180 '
181
182 # Check that we abort on attempt to redirect onto file when -Ce is set
183 atf_check -s not-exit:0 -o empty -e not-empty "${TEST_SH}" -c \
184 '
185 set -Ce
186 echo "Fail to Overwrite it now" > Important_File
187 echo "Should not reach this point"
188 '
189
190 # Last check that we can override -C for when we really need to
191 atf_check -s exit:0 -o empty -e empty "${TEST_SH}" -c \
192 '
193 D=$(ls -l Junk_File) || exit 1
194 set -C
195 echo "Change the poor bugger again" >| Junk_File
196 A=$(ls -l Junk_File) || exit 1
197 test "${A}" != "${D}"
198 '
199 }
200
201 atf_test_case set_e
202 set_e_head() {
203 atf_set "descr" "Tests that 'set -e' turns on error detection " \
204 "and that a simple case behaves as defined by the standard"
205 }
206 set_e_body() {
207 test_option_on_off e
208
209 # Check that -e does nothing if no commands fail
210 atf_check -s exit:0 -o match:I_am_OK -e empty \
211 "${TEST_SH}" -c \
212 'false; printf "%s" I_am; set -e; true; printf "%s\n" _OK'
213
214 # and that it (silently, but with exit status) aborts if cmd fails
215 atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \
216 "${TEST_SH}" -c \
217 'false; printf "%s" I_am; set -e; false; printf "%s\n" _Broken'
218
219 # same, except -e this time is on from the beginning
220 atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \
221 "${TEST_SH}" -ec 'printf "%s" I_am; false; printf "%s\n" _Broken'
222
223 # More checking of -e in other places, there is lots to deal with.
224 }
225
226 atf_test_case set_f
227 set_f_head() {
228 atf_set "descr" "Tests that 'set -f' turns off pathname expansion " \
229 "and that it behaves as defined by the standard"
230 }
231 set_f_body() {
232 atf_require_prog ls
233
234 test_option_on_off f
235
236 # Check that the environment to use for the tests is sane ...
237 # we assume current dir is a new tempory directory & is empty
238
239 test -z "$(ls)" || atf_skip "Test execution directory not clean"
240
241 # we will assume that atf will clean up this junk directory
242 # when we are done. But for testing pathname expansion
243 # we need files
244
245 for f in a b c d e f aa ab ac ad ae aaa aab aac aad aba abc bbb ccc
246 do
247 echo "$f" > "$f"
248 done
249
250 atf_check -s exit:0 -o empty -e empty "${TEST_SH}" -ec \
251 'X=$(echo b*); Y=$(echo b*); test "${X}" != "a*";
252 test "${X}" = "${Y}"'
253
254 # now test expansion is different when -f is set
255 atf_check -s exit:0 -o empty -e empty "${TEST_SH}" -ec \
256 'X=$(echo b*); Y=$(set -f; echo b*); test "${X}" != "${Y}"'
257 }
258
259 atf_test_case set_n
260 set_n_head() {
261 atf_set "descr" "Tests that 'set -n' supresses command execution " \
262 "and that it behaves as defined by the standard"
263 }
264 set_n_body() {
265 # pointless to test this, if it turns on, it stays on...
266 # test_option_on_off n
267 # so just allow the tests below to verify it can be turned on
268
269 # nothing should be executed, hence no output...
270 atf_check -s exit:0 -o empty -e empty \
271 "${TEST_SH}" -enc 'echo ABANDON HOPE; echo ALL YE; echo ...'
272
273 # this is true even when the "commands" do not exist
274 atf_check -s exit:0 -o empty -e empty \
275 "${TEST_SH}" -enc 'ERR; FAIL; ABANDON HOPE'
276
277 # but if there is a syntax error, it should be detected
278 atf_check -s not-exit:0 -o empty -e not-empty \
279 "${TEST_SH}" -enc 'echo JUMP; for frogs swim; echo in puddles'
280
281 # eventually add tests that enable -n during the script
282 # but the standard NetBSD sh doesn't handle that properly yet, so ...
283 }
284
285 atf_test_case set_u
286 set_u_head() {
287 atf_set "descr" "Tests that 'set -u' turns on unset var detection " \
288 "and that it behaves as defined by the standard"
289 }
290 set_u_body() {
291 test_option_on_off u
292
293 # first make sure it is OK to unset an unset variable
294 atf_check -s exit:0 -o match:OK -e empty "${TEST_SH}" -ce \
295 'unset _UNSET_VARIABLE_; echo OK'
296 # even if -u is set
297 atf_check -s exit:0 -o match:OK -e empty "${TEST_SH}" -cue \
298 'unset _UNSET_VARIABLE_; echo OK'
299
300 # and that without -u accessing an unset variable is harmless
301 atf_check -s exit:0 -o match:OK -e empty "${TEST_SH}" -ce \
302 'unset X; echo ${X}; echo OK'
303 # and that the unset variable test expansion works properly
304 atf_check -s exit:0 -o match:OKOK -e empty "${TEST_SH}" -ce \
305 'unset X; printf "%s" ${X-OK}; echo OK'
306
307 # Next test that with -u set, the shell aborts on access to unset var
308 # do not use -e, want to make sure it is -u that causes abort
309 atf_check -s not-exit:0 -o not-match:ERR -e not-empty "${TEST_SH}" -c \
310 'unset X; set -u; echo ${X}; echo ERR'
311 # quoting should make no difference...
312 atf_check -s not-exit:0 -o not-match:ERR -e not-empty "${TEST_SH}" -c \
313 'unset X; set -u; echo "${X}"; echo ERR'
314
315 # Now a bunch of accesses to unset vars, with -u, in ways that are OK
316 atf_check -s exit:0 -o match:OK -e empty "${TEST_SH}" -ce \
317 'unset X; set -u; echo ${X-GOOD}; echo OK'
318 atf_check -s exit:0 -o match:OK -e empty "${TEST_SH}" -ce \
319 'unset X; set -u; echo ${X-OK}'
320 atf_check -s exit:0 -o not-match:ERR -o match:OK -e empty \
321 "${TEST_SH}" -ce 'unset X; set -u; echo ${X+ERR}; echo OK'
322
323 # and some more ways that are not OK
324 atf_check -s not-exit:0 -o not-match:ERR -e not-empty "${TEST_SH}" -c \
325 'unset X; set -u; echo ${X#foo}; echo ERR'
326 atf_check -s not-exit:0 -o not-match:ERR -e not-empty "${TEST_SH}" -c \
327 'unset X; set -u; echo ${X%%bar}; echo ERR'
328
329 # lastly, just while we are checking unset vars, test aborts w/o -u
330 atf_check -s not-exit:0 -o not-match:ERR -e not-empty "${TEST_SH}" -c \
331 'unset X; echo ${X?}; echo ERR'
332 atf_check -s not-exit:0 -o not-match:ERR -e match:X_NOT_SET \
333 "${TEST_SH}" -c 'unset X; echo ${X?X_NOT_SET}; echo ERR'
334 }
335
336 atf_test_case set_v
337 set_v_head() {
338 atf_set "descr" "Tests that 'set -v' turns on input read echoing " \
339 "and that it behaves as defined by the standard"
340 }
341 set_v_body() {
342 test_option_on_off v
343
344 # check that -v does nothing if no later input line is read
345 atf_check -s exit:0 \
346 -o match:OKOK -o not-match:echo -o not-match:printf \
347 -e empty \
348 "${TEST_SH}" -ec 'printf "%s" OK; set -v; echo OK; exit 0'
349
350 # but that it does when there are multiple lines
351 atf_check -s exit:0 \
352 -o match:OKOK -o not-match:echo -o not-match:printf \
353 -e match:printf -e match:OK -e match:echo \
354 "${TEST_SH}" -ec '{
355 echo "set -v"
356 echo "printf %s OK"
357 echo "echo OK"
358 echo "exit 0"
359 } | '"'${TEST_SH}'"' -e'
360
361 # and that it can be disabled again
362 atf_check -s exit:0 \
363 -o match:OKOK -o not-match:echo -o not-match:printf \
364 -e match:printf -e match:OK -e not-match:echo \
365 "${TEST_SH}" -ec '{
366 echo "set -v"
367 echo "printf %s OK"
368 echo "set +v"
369 echo "echo OK"
370 echo "exit 0"
371 } | '"'${TEST_SH}'"' -e'
372
373 # and lastly, that shell keywords do get output when "read"
374 atf_check -s exit:0 \
375 -o match:111222333 -o not-match:printf \
376 -o not-match:for -o not-match:do -o not-match:done \
377 -e match:printf -e match:111 -e not-match:111222 \
378 -e match:for -e match:do -e match:done \
379 "${TEST_SH}" -ec '{
380 echo "set -v"
381 echo "for i in 111 222 333"
382 echo "do"
383 echo "printf %s \$i"
384 echo "done"
385 echo "exit 0"
386 } | '"'${TEST_SH}'"' -e'
387 }
388
389 atf_test_case set_x
390 set_x_head() {
391 atf_set "descr" "Tests that 'set -x' turns on command exec logging " \
392 "and that it behaves as defined by the standard"
393 }
394 set_x_body() {
395 test_option_on_off x
396
397 # check that cmd output appears after -x is enabled
398 atf_check -s exit:0 \
399 -o match:OKOK -o not-match:echo -o not-match:printf \
400 -e not-match:printf -e match:OK -e match:echo \
401 "${TEST_SH}" -ec 'printf "%s" OK; set -x; echo OK; exit 0'
402
403 # and that it stops again afer -x is disabled
404 atf_check -s exit:0 \
405 -o match:OKOK -o not-match:echo -o not-match:printf \
406 -e match:printf -e match:OK -e not-match:echo \
407 "${TEST_SH}" -ec 'set -x; printf "%s" OK; set +x; echo OK; exit 0'
408
409 # also check that PS4 is output correctly
410 atf_check -s exit:0 \
411 -o match:OK -o not-match:echo \
412 -e match:OK -e match:Run:echo \
413 "${TEST_SH}" -ec 'PS4=Run:; set -x; echo OK; exit 0'
414
415 return 0
416
417 # This one seems controversial... I suspect it is NetBSD's sh
418 # that is wrong to not output "for" "while" "if" ... etc
419
420 # and lastly, that shell keywords do not get output when "executed"
421 atf_check -s exit:0 \
422 -o match:111222333 -o not-match:printf \
423 -o not-match:for \
424 -e match:printf -e match:111 -e not-match:111222 \
425 -e not-match:for -e not-match:do -e not-match:done \
426 "${TEST_SH}" -ec \
427 'set -x; for i in 111 222 333; do printf "%s" $i; done; echo; exit 0'
428 }
429
430 opt_test_setup()
431 {
432 test -n "$1" || { echo >&2 "Internal error"; exit 1; }
433
434 cat > "$1" << 'END_OF_FUNCTIONS'
435 local_opt_check()
436 {
437 local -
438 }
439
440 instr()
441 {
442 expr "$2" : "\(.*$1\)" >/dev/null
443 }
444
445 save_opts()
446 {
447 local -
448
449 set -e
450 set -u
451
452 instr e "$-" && instr u "$-" && return 0
453 echo ERR
454 }
455
456 fiddle_opts()
457 {
458 set -e
459 set -u
460
461 instr e "$-" && instr u "$-" && return 0
462 echo ERR
463 }
464
465 local_test()
466 {
467 set +eu
468
469 save_opts
470 instr '[eu]' "$-" || printf %s "OK"
471
472 fiddle_opts
473 instr e "$-" && instr u "$-" && printf %s "OK"
474
475 set +eu
476 }
477 END_OF_FUNCTIONS
478 }
479
480 atf_test_case restore_local_opts
481 restore_local_opts_head() {
482 atf_set "descr" "Tests that 'local -' saves and restores options. " \
483 "Note that "local" is a local shell addition"
484 }
485 restore_local_opts_body() {
486 atf_require_prog cat
487 atf_require_prog expr
488
489 FN="test-funcs.$$"
490 opt_test_setup "${FN}" || atf_skip "Cannot setup test environment"
491
492 "${TEST_SH}" -ec ". './${FN}'; local_opt_check" 2>/dev/null ||
493 atf_skip "sh extension 'local -' not supported by ${TEST_SH}"
494
495 atf_check -s exit:0 -o match:OKOK -o not-match:ERR -e empty \
496 "${TEST_SH}" -ec ". './${FN}'; local_test"
497 }
498
499 atf_test_case vi_emacs_VE_toggle
500 vi_emacs_VE_toggle_head() {
501 atf_set "descr" "Tests enabling vi disables emacs (and v.v - but why?)"\
502 " Note that -V and -E are local shell additions"
503 }
504 vi_emacs_VE_toggle_body() {
505
506 test_optional_on_off V E ||
507 atf_skip "One or both V & E opts unsupported by ${TEST_SH}"
508
509 atf_check -s exit:0 -o empty -e empty "${TEST_SH}" -c '
510 q() {
511 eval "case \"$-\" in
512 (*${2}*) return 1;;
513 (*${1}*) return 0;;
514 esac"
515 return 1
516 }
517 x() {
518 echo >&2 "Option set or toggle failure:" \
519 " on=$1 off=$2 set=$-"
520 exit 1
521 }
522 set -V; q V E || x V E
523 set -E; q E V || x E V
524 set -V; q V E || x V E
525 set +EV; q "" "[VE]" || x "" VE
526 exit 0
527 '
528 }
529
530 atf_test_case xx_bogus
531 xx_bogus_head() {
532 atf_set "descr" "Tests that attempting to set a nonsense option fails."
533 }
534 xx_bogus_body() {
535 # Biggest problem here is picking a "nonsense option" that is
536 # not implemented by any shell, anywhere. Hopefully this will do.
537 atf_check -s not-exit:0 -o empty -e not-empty \
538 "${TEST_SH}" -c 'set -% ; echo ERR'
539 }
540
541 atf_test_case Option_switching
542 Option_switching_head() {
543 atf_set "descr" "options can be enabled and disabled"
544 }
545 Option_switching_body() {
546
547 # Cannot test -m, setting it causes test shell to fail...
548 # (test shell gets SIGKILL!) Wonder why ... something related to atf
549 # That is, it works if just run as "sh -c 'echo $-; set -m; echo $-'"
550
551 # Don't bother testing toggling -n, once on, it stays on...
552 # (and because the test fn refuses to allow us to try)
553
554 # Cannot test -o or -c here, or the extension -s
555 # they can only be used, not switched
556
557 # these are the posix options, that all shells should implement
558 test_option_on_off a b C e f h u v x # m
559
560 # and these are extensions that might not exist (non-fatal to test)
561 # -i and -s (and -c) are posix options, but are not required to
562 # be accessable via the "set" command, just the command line.
563 # We allow for -i to work with set, as that makes some sense,
564 # -c and -s do not.
565 test_optional_on_off E i I p q V || true
566
567 # Also test (some) option combinations ...
568 # only testing posix options here, because it is easier...
569 test_option_on_off aeu vx Ca aCefux
570 }
571
572 atf_init_test_cases() {
573 # tests are run in order sort of names produces, so choose names wisely
574
575 # this one tests turning on/off all the mandatory. and extra flags
576 atf_add_test_case Option_switching
577 # and this tests the NetBSD "local -" functionality in functions.
578 atf_add_test_case restore_local_opts
579
580 # no tests for -m (no idea how to do that one)
581 # -I (no easy way to generate the EOF it ignores)
582 # -i (not sure how to test that one at the minute)
583 # -p (because we aren't going to run tests setiud)
584 # -V/-E (too much effort, and a real test would be huge)
585 # -c (because almost all the other tests test it anyway)
586 # -q (because, for now, I am lazy)
587 # -s (coming soon, hopefully)
588 # -o (really +o: again, hopefully soon)
589 # -o longname (again, just laziness, don't wait...)
590 # -h/-b (because NetBSD doesn't implement them)
591 atf_add_test_case set_a
592 atf_add_test_case set_C
593 atf_add_test_case set_e
594 atf_add_test_case set_f
595 atf_add_test_case set_n
596 atf_add_test_case set_u
597 atf_add_test_case set_v
598 atf_add_test_case set_x
599
600 atf_add_test_case vi_emacs_VE_toggle
601 atf_add_test_case xx_bogus
602 }
603