YAML · 20200 bytes Raw Blame History
1 # POSIX Shell Feature Tests
2 # Comprehensive tests for core POSIX shell functionality
3
4 metadata:
5 category: "POSIX Shell Features"
6 description: "Tests for POSIX-compliant shell operations"
7 phase: 1
8
9 tests:
10 # =============================================================
11 # BASIC OPERATIONS
12 # =============================================================
13 - name: "Simple echo"
14 steps:
15 - send_line: "echo hello"
16 expect_output: "hello"
17 match_type: "contains"
18
19 - name: "Exit status 0 for success"
20 steps:
21 - send_line: "true; echo $?"
22 expect_output: "0"
23 match_type: "contains"
24
25 - name: "Exit status 1 for failure"
26 steps:
27 - send_line: "false; echo $?"
28 expect_output: "1"
29 match_type: "contains"
30
31 - name: "Multiple commands with semicolon"
32 steps:
33 - send_line: "echo one; echo two; echo three"
34 expect_output: "three"
35 match_type: "contains"
36
37 - name: "Comment ignored"
38 steps:
39 - send_line: "echo visible # invisible"
40 expect_output: "visible"
41 match_type: "contains"
42
43 - name: "Command not found error"
44 steps:
45 - send_line: "nonexistentcmd123"
46 expect_output: "not found"
47 match_type: "contains"
48
49 # =============================================================
50 # QUOTING AND ESCAPING
51 # =============================================================
52 - name: "Single quotes preserve literal"
53 steps:
54 - send_line: "echo '$HOME'"
55 expect_output: "\\$HOME"
56 match_type: "contains"
57
58 - name: "Double quotes allow expansion"
59 steps:
60 - send_line: "echo \"$HOME\""
61 expect_output: "/"
62 match_type: "contains"
63
64 - name: "Backslash escapes special char"
65 steps:
66 - send_line: "echo \\$HOME"
67 expect_output: "\\$HOME"
68 match_type: "contains"
69
70 - name: "Backslash in double quotes"
71 steps:
72 - send_line: "echo \"\\$HOME\""
73 expect_output: "\\$HOME"
74 match_type: "contains"
75
76 - name: "Single quotes in double quotes"
77 steps:
78 - send_line: "echo \"it's\""
79 expect_output: "it's"
80 match_type: "contains"
81
82 - name: "Double quotes in single quotes"
83 steps:
84 - send_line: "echo '\"quoted\"'"
85 expect_output: "\"quoted\""
86 match_type: "contains"
87
88 - name: "Empty string"
89 steps:
90 - send_line: "echo ''"
91 expect_output: ""
92 match_type: "contains"
93
94 - name: "Concatenated quotes"
95 steps:
96 - send_line: "echo 'hello'\"world\""
97 expect_output: "helloworld"
98 match_type: "contains"
99
100 - name: "Escaped newline continues line"
101 steps:
102 - send: "echo hello \\"
103 - send_key: "Enter"
104 - send_line: "world"
105 expect_output: "hello world"
106 match_type: "contains"
107
108 - name: "Tab character with $''"
109 steps:
110 - send_line: "echo $'a\\tb'"
111 expect_output: "a"
112 match_type: "contains"
113
114 # =============================================================
115 # VARIABLES
116 # =============================================================
117 - name: "Variable assignment and use"
118 steps:
119 - send_line: "VAR=hello; echo $VAR"
120 expect_output: "hello"
121 match_type: "contains"
122
123 - name: "Variable with spaces in value"
124 steps:
125 - send_line: "VAR='hello world'; echo \"$VAR\""
126 expect_output: "hello world"
127 match_type: "contains"
128
129 - name: "Unset variable is empty"
130 steps:
131 - send_line: "echo \"x${UNSET_VAR}x\""
132 expect_output: "xx"
133 match_type: "contains"
134
135 - name: "Export variable"
136 steps:
137 - send_line: "export MYVAR=exported; echo $MYVAR"
138 expect_output: "exported"
139 match_type: "contains"
140
141 - name: "Variable in command"
142 steps:
143 - send_line: "CMD=echo; $CMD test"
144 expect_output: "test"
145 match_type: "contains"
146
147 - name: "Curly brace expansion"
148 steps:
149 - send_line: "VAR=test; echo ${VAR}ing"
150 expect_output: "testing"
151 match_type: "contains"
152
153 - name: "Default value ${var:-default}"
154 steps:
155 - send_line: "echo ${UNSET:-default}"
156 expect_output: "default"
157 match_type: "contains"
158
159 - name: "Assign default ${var:=default}"
160 steps:
161 - send_line: "echo ${NEWVAR:=assigned}; echo $NEWVAR"
162 expect_output: "assigned"
163 match_type: "contains"
164
165 - name: "Error if unset ${var:?msg}"
166 steps:
167 - send_line: "echo ${UNSET:?error message}"
168 expect_output: "error"
169 match_type: "contains"
170
171 - name: "Use alternate ${var:+alt}"
172 steps:
173 - send_line: "SET=yes; echo ${SET:+alternate}"
174 expect_output: "alternate"
175 match_type: "contains"
176
177 - name: "String length ${#var}"
178 fresh_session: true
179 steps:
180 - send_line: "VAR=hello; echo ${#VAR}"
181 - wait: 1.0
182 expect_output: "5"
183 match_type: "contains"
184
185 - name: "Remove prefix ${var#pattern}"
186 steps:
187 - send_line: "VAR=hello; echo ${VAR#hel}"
188 expect_output: "lo"
189 match_type: "contains"
190
191 - name: "Remove suffix ${var%pattern}"
192 steps:
193 - send_line: "VAR=hello; echo ${VAR%lo}"
194 expect_output: "hel"
195 match_type: "contains"
196
197 # =============================================================
198 # SPECIAL VARIABLES
199 # =============================================================
200 - name: "$? exit status"
201 steps:
202 - send_line: "true; echo $?"
203 expect_output: "0"
204 match_type: "contains"
205
206 - name: "$$ process ID"
207 steps:
208 - send_line: "echo $$"
209 expect_output: ""
210 match_type: "contains"
211
212 - name: "$0 shell name"
213 steps:
214 - send_line: "echo $0"
215 expect_output: "" # shell name — skip or parameterize
216 match_type: "contains"
217
218 - name: "$HOME environment"
219 steps:
220 - send_line: "echo $HOME"
221 expect_output: "/"
222 match_type: "contains"
223
224 - name: "$PWD current directory"
225 steps:
226 - send_line: "cd /tmp; echo $PWD"
227 expect_output: "/tmp"
228 match_type: "contains"
229
230 - name: "$OLDPWD previous directory"
231 steps:
232 - send_line: "cd /tmp; cd /; echo $OLDPWD"
233 expect_output: "/tmp"
234 match_type: "contains"
235
236 # =============================================================
237 # PIPELINES
238 # =============================================================
239 - name: "Simple pipe"
240 steps:
241 - send_line: "echo hello | cat"
242 expect_output: "hello"
243 match_type: "contains"
244
245 - name: "Multiple pipes"
246 steps:
247 - send_line: "echo hello | cat | cat"
248 expect_output: "hello"
249 match_type: "contains"
250
251 - name: "Pipe with grep"
252 steps:
253 - send_line: "echo -e 'one\\ntwo\\nthree' | grep two"
254 expect_output: "two"
255 match_type: "contains"
256
257 - name: "Pipe with wc"
258 steps:
259 - send_line: "echo hello | wc -c"
260 expect_output: "6"
261 match_type: "contains"
262
263 - name: "Pipe exit status is last command"
264 steps:
265 - send_line: "true | false; echo $?"
266 expect_output: "1"
267 match_type: "contains"
268
269 - name: "Negated pipeline"
270 steps:
271 - send_line: "! false; echo $?"
272 expect_output: "0"
273 match_type: "contains"
274
275 # =============================================================
276 # REDIRECTIONS
277 # =============================================================
278 - name: "Redirect stdout to file"
279 steps:
280 - send_line: "echo hello > /tmp/test_out.txt; cat /tmp/test_out.txt"
281 expect_output: "hello"
282 match_type: "contains"
283
284 - name: "Append to file"
285 steps:
286 - send_line: "echo one > /tmp/append.txt; echo two >> /tmp/append.txt; cat /tmp/append.txt"
287 expect_output: "two"
288 match_type: "contains"
289
290 - name: "Redirect stdin from file"
291 steps:
292 - send_line: "echo hello > /tmp/in.txt; cat < /tmp/in.txt"
293 expect_output: "hello"
294 match_type: "contains"
295
296 - name: "Redirect stderr"
297 steps:
298 - send_line: "ls /nonexistent 2> /tmp/err.txt; cat /tmp/err.txt"
299 expect_output: "No such file"
300 match_type: "contains"
301
302 - name: "Redirect stdout and stderr"
303 steps:
304 - send_line: "ls /nonexistent > /tmp/both.txt 2>&1; cat /tmp/both.txt"
305 expect_output: "No such file"
306 match_type: "contains"
307
308 - name: "Here document"
309 steps:
310 - send: "cat <<EOF"
311 - send_key: "Enter"
312 - send: "hello"
313 - send_key: "Enter"
314 - send: "EOF"
315 - send_key: "Enter"
316 expect_output: "hello"
317 match_type: "contains"
318
319 - name: "Here string"
320 steps:
321 - send_line: "cat <<< 'hello'"
322 expect_output: "hello"
323 match_type: "contains"
324
325 - name: "Redirect to /dev/null"
326 steps:
327 - send_line: "echo hidden > /dev/null; echo visible"
328 expect_output: "visible"
329 match_type: "contains"
330
331 # =============================================================
332 # COMMAND LISTS
333 # =============================================================
334 - name: "AND list - both succeed"
335 steps:
336 - send_line: "true && echo success"
337 expect_output: "success"
338 match_type: "contains"
339
340 - name: "AND list - first fails"
341 steps:
342 - send_line: "false && echo fail; echo done"
343 expect_output: "done"
344 match_type: "contains"
345
346 - name: "OR list - first succeeds"
347 steps:
348 - send_line: "true || echo fail; echo done"
349 expect_output: "done"
350 match_type: "contains"
351
352 - name: "OR list - first fails"
353 steps:
354 - send_line: "false || echo success"
355 expect_output: "success"
356 match_type: "contains"
357
358 - name: "Mixed AND/OR"
359 steps:
360 - send_line: "false || true && echo success"
361 expect_output: "success"
362 match_type: "contains"
363
364 # =============================================================
365 # CONTROL STRUCTURES
366 # =============================================================
367 - name: "If statement - true branch"
368 steps:
369 - send_line: "if true; then echo yes; fi"
370 expect_output: "yes"
371 match_type: "contains"
372
373 - name: "If statement - false branch"
374 steps:
375 - send_line: "if false; then echo no; else echo yes; fi"
376 expect_output: "yes"
377 match_type: "contains"
378
379 - name: "If with test command"
380 steps:
381 - send_line: "if [ 1 -eq 1 ]; then echo equal; fi"
382 expect_output: "equal"
383 match_type: "contains"
384
385 - name: "If with string test"
386 steps:
387 - send_line: "if [ 'a' = 'a' ]; then echo match; fi"
388 expect_output: "match"
389 match_type: "contains"
390
391 - name: "Elif chain"
392 steps:
393 - send_line: "if false; then echo 1; elif true; then echo 2; else echo 3; fi"
394 expect_output: "2"
395 match_type: "contains"
396
397 - name: "For loop with list"
398 steps:
399 - send_line: "for i in 1 2 3; do echo $i; done"
400 expect_output: "3"
401 match_type: "contains"
402
403 - name: "For loop with variable"
404 steps:
405 - send_line: "for i in a b c; do echo -n $i; done; echo"
406 expect_output: "abc"
407 match_type: "contains"
408
409 - name: "While loop"
410 steps:
411 - send_line: "i=0; while [ $i -lt 3 ]; do echo $i; i=$((i+1)); done"
412 expect_output: "2"
413 match_type: "contains"
414
415 - name: "Until loop"
416 steps:
417 - send_line: "i=0; until [ $i -ge 3 ]; do echo $i; i=$((i+1)); done"
418 expect_output: "2"
419 match_type: "contains"
420
421 - name: "Case statement"
422 steps:
423 - send_line: "case abc in a*) echo match;; esac"
424 expect_output: "match"
425 match_type: "contains"
426
427 - name: "Case with multiple patterns"
428 steps:
429 - send_line: "case foo in bar|foo) echo found;; esac"
430 expect_output: "found"
431 match_type: "contains"
432
433 - name: "Case default"
434 steps:
435 - send_line: "case xyz in *) echo default;; esac"
436 expect_output: "default"
437 match_type: "contains"
438
439 - name: "Break in loop"
440 steps:
441 - send_line: "for i in 1 2 3 4 5; do if [ $i -eq 3 ]; then break; fi; echo $i; done"
442 expect_output: "2"
443 match_type: "contains"
444
445 - name: "Continue in loop"
446 steps:
447 - send_line: "for i in 1 2 3; do if [ $i -eq 2 ]; then continue; fi; echo $i; done"
448 expect_output: "3"
449 match_type: "contains"
450
451 # =============================================================
452 # FUNCTIONS
453 # =============================================================
454 - name: "Function definition and call"
455 steps:
456 - send_line: "myfunc() { echo hello; }; myfunc"
457 expect_output: "hello"
458 match_type: "contains"
459
460 - name: "Function with arguments"
461 steps:
462 - send_line: "greet() { echo \"Hello $1\"; }; greet World"
463 expect_output: "Hello World"
464 match_type: "contains"
465
466 - name: "Function return value"
467 steps:
468 - send_line: "ret5() { return 5; }; ret5; echo $?"
469 expect_output: "5"
470 match_type: "contains"
471
472 - name: "Function local variable"
473 steps:
474 - send_line: "f() { local x=inside; echo $x; }; x=outside; f; echo $x"
475 expect_output: "outside"
476 match_type: "contains"
477
478 - name: "Function with $# argc"
479 steps:
480 - send_line: "argc() { echo $#; }; argc a b c"
481 expect_output: "3"
482 match_type: "contains"
483
484 - name: "Function with $@"
485 steps:
486 - send_line: "all() { echo \"$@\"; }; all one two three"
487 expect_output: "one two three"
488 match_type: "contains"
489
490 # =============================================================
491 # ARITHMETIC
492 # =============================================================
493 - name: "Arithmetic expansion"
494 steps:
495 - send_line: "echo $((2 + 3))"
496 expect_output: "5"
497 match_type: "contains"
498
499 - name: "Arithmetic subtraction"
500 steps:
501 - send_line: "echo $((10 - 4))"
502 expect_output: "6"
503 match_type: "contains"
504
505 - name: "Arithmetic multiplication"
506 steps:
507 - send_line: "echo $((3 * 4))"
508 expect_output: "12"
509 match_type: "contains"
510
511 - name: "Arithmetic division"
512 steps:
513 - send_line: "echo $((15 / 3))"
514 expect_output: "5"
515 match_type: "contains"
516
517 - name: "Arithmetic modulo"
518 steps:
519 - send_line: "echo $((17 % 5))"
520 expect_output: "2"
521 match_type: "contains"
522
523 - name: "Arithmetic with variable"
524 steps:
525 - send_line: "x=10; echo $((x * 2))"
526 expect_output: "20"
527 match_type: "contains"
528
529 - name: "Arithmetic comparison"
530 steps:
531 - send_line: "echo $((5 > 3))"
532 expect_output: "1"
533 match_type: "contains"
534
535 - name: "Nested arithmetic"
536 steps:
537 - send_line: "echo $(((2 + 3) * 4))"
538 expect_output: "20"
539 match_type: "contains"
540
541 # =============================================================
542 # COMMAND SUBSTITUTION
543 # =============================================================
544 - name: "Command substitution $()"
545 steps:
546 - send_line: "echo $(echo hello)"
547 expect_output: "hello"
548 match_type: "contains"
549
550 - name: "Command substitution backticks"
551 steps:
552 - send_line: "echo `echo hello`"
553 expect_output: "hello"
554 match_type: "contains"
555
556 - name: "Nested command substitution"
557 steps:
558 - send_line: "echo $(echo $(echo deep))"
559 expect_output: "deep"
560 match_type: "contains"
561
562 - name: "Command substitution in variable"
563 steps:
564 - send_line: "VAR=$(echo value); echo $VAR"
565 expect_output: "value"
566 match_type: "contains"
567
568 - name: "Command substitution in string"
569 steps:
570 - send_line: "echo \"Today is $(date +%A)\""
571 expect_output: "Today is"
572 match_type: "contains"
573
574 # =============================================================
575 # SUBSHELLS
576 # =============================================================
577 - name: "Subshell execution"
578 steps:
579 - send_line: "(echo subshell)"
580 expect_output: "subshell"
581 match_type: "contains"
582
583 - name: "Subshell variable isolation"
584 steps:
585 - send_line: "X=outer; (X=inner; echo $X); echo $X"
586 expect_output: "outer"
587 match_type: "contains"
588
589 - name: "Subshell cd isolation"
590 steps:
591 - send_line: "cd /tmp; (cd /); pwd"
592 expect_output: "/tmp"
593 match_type: "contains"
594
595 # =============================================================
596 # GLOBBING
597 # =============================================================
598 - name: "Star glob"
599 steps:
600 - send_line: "cd /tmp; touch glob1.txt glob2.txt; ls glob*.txt"
601 expect_output: "glob"
602 match_type: "contains"
603
604 - name: "Question mark glob"
605 steps:
606 - send_line: "cd /tmp; touch a1 a2 a3; ls a?"
607 expect_output: "a1"
608 match_type: "contains"
609
610 - name: "Bracket glob"
611 steps:
612 - send_line: "cd /tmp; touch b1 b2 b3; ls b[12]"
613 expect_output: "b1"
614 match_type: "contains"
615
616 - name: "No match returns literal"
617 steps:
618 - send_line: "echo /nonexistent/*.xyz"
619 expect_output: "/nonexistent/\\*\\.xyz"
620 match_type: "contains"
621
622 # =============================================================
623 # BUILTINS
624 # =============================================================
625 - name: "cd to directory"
626 steps:
627 - send_line: "cd /tmp; pwd"
628 expect_output: "/tmp"
629 match_type: "contains"
630
631 - name: "cd to home"
632 steps:
633 - send_line: "cd; pwd"
634 expect_output: "/"
635 match_type: "contains"
636
637 - name: "cd to previous"
638 steps:
639 - send_line: "cd /tmp; cd /; cd -; pwd"
640 expect_output: "/tmp"
641 match_type: "contains"
642
643 - name: "pwd builtin"
644 steps:
645 - send_line: "pwd"
646 expect_output: "/"
647 match_type: "contains"
648
649 - name: "echo with -n"
650 steps:
651 - send_line: "echo -n hello; echo world"
652 expect_output: "helloworld"
653 match_type: "contains"
654
655 - name: "echo with -e"
656 steps:
657 - send_line: "echo -e 'a\\tb'"
658 expect_output: "a"
659 match_type: "contains"
660
661 - name: "printf basic"
662 steps:
663 - send_line: "printf '%s\\n' hello"
664 expect_output: "hello"
665 match_type: "contains"
666
667 - name: "printf with format"
668 steps:
669 - send_line: "printf '%d + %d = %d\\n' 2 3 5"
670 expect_output: "2 \\+ 3 = 5"
671 match_type: "contains"
672
673 - name: "test numeric equality"
674 steps:
675 - send_line: "test 5 -eq 5 && echo equal"
676 expect_output: "equal"
677 match_type: "contains"
678
679 - name: "test string equality"
680 steps:
681 - send_line: "test 'a' = 'a' && echo equal"
682 expect_output: "equal"
683 match_type: "contains"
684
685 - name: "test file exists"
686 steps:
687 - send_line: "touch /tmp/exists.txt; test -e /tmp/exists.txt && echo exists"
688 expect_output: "exists"
689 match_type: "contains"
690
691 - name: "test file is regular"
692 steps:
693 - send_line: "touch /tmp/regular.txt; test -f /tmp/regular.txt && echo regular"
694 expect_output: "regular"
695 match_type: "contains"
696
697 - name: "test directory"
698 steps:
699 - send_line: "test -d /tmp && echo isdir"
700 expect_output: "isdir"
701 match_type: "contains"
702
703 - name: "read builtin"
704 steps:
705 - send_line: "echo hello | { read x; echo $x; }"
706 expect_output: "hello"
707 match_type: "contains"
708
709 - name: "type builtin"
710 steps:
711 - send_line: "type echo"
712 expect_output: "builtin"
713 match_type: "contains"
714
715 - name: "unset variable"
716 steps:
717 - send_line: "VAR=set; unset VAR; echo \"x${VAR}x\""
718 expect_output: "xx"
719 match_type: "contains"
720
721 - name: "readonly variable"
722 steps:
723 - send_line: "readonly RO=value; echo $RO"
724 expect_output: "value"
725 match_type: "contains"
726
727 - name: "eval command"
728 steps:
729 - send_line: "CMD='echo hello'; eval $CMD"
730 expect_output: "hello"
731 match_type: "contains"
732
733 - name: "exec replaces shell (test with subshell)"
734 steps:
735 - send_line: "(exec echo replaced)"
736 expect_output: "replaced"
737 match_type: "contains"
738
739 - name: "source/dot command"
740 steps:
741 - send_line: "echo 'VAR=sourced' > /tmp/src.sh; . /tmp/src.sh; echo $VAR"
742 expect_output: "sourced"
743 match_type: "contains"
744
745 - name: "shift arguments"
746 steps:
747 - send_line: "set -- a b c; shift; echo $1"
748 expect_output: "b"
749 match_type: "contains"
750
751 - name: "set positional parameters"
752 steps:
753 - send_line: "set -- x y z; echo $2"
754 expect_output: "y"
755 match_type: "contains"
756
757 - name: "trap command"
758 steps:
759 - send_line: "trap 'echo trapped' EXIT; exit"
760 expect_output: "trapped"
761 match_type: "contains"
762
763 - name: "alias creation and use"
764 steps:
765 - send_line: "alias ll='ls -l'; type ll"
766 expect_output: "alias"
767 match_type: "contains"
768
769 - name: "unalias removes alias"
770 steps:
771 - send_line: "alias foo=bar; unalias foo; type foo"
772 expect_output: "not found"
773 match_type: "contains"
774
775