Bash · 40855 bytes Raw Blame History
1 #!/bin/sh
2 # =====================================
3 # POSIX Compliance Control Flow and Grouping Test Suite for rush
4 # =====================================
5 # Tests control flow, subshells, brace groups per IEEE Std 1003.1-2017
6 # Section: Shell Command Language - Compound Commands
7
8 # Colors (POSIX-compliant way)
9 RED='\033[0;31m'
10 GREEN='\033[0;32m'
11 YELLOW='\033[1;33m'
12 BLUE='\033[0;34m'
13 NC='\033[0m'
14
15 # Test identification
16 TEST_PREFIX="[posix-control]"
17 CURRENT_SECTION=""
18 TEST_NUM=0
19
20 PASSED=0
21 FAILED=0
22 SKIPPED=0
23 FAILED_TESTS_LIST=""
24
25 # Get script directory (POSIX way)
26 SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
27 RUSH_BIN="${RUSH_BIN:-$SCRIPT_DIR/../target/release/rush}"
28
29 # Check if fortsh exists
30 if [ ! -x "$RUSH_BIN" ]; then
31 printf "${RED}ERROR${NC}: rush binary not found at $RUSH_BIN\n"
32 printf "Please run 'make' first or set RUSH_BIN environment variable\n"
33 exit 1
34 fi
35
36 # Test result trackers
37 pass() {
38 TEST_NUM=$((TEST_NUM + 1))
39 printf "${GREEN}✓ PASS${NC} ${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}: %s\n" "$1"
40 PASSED=$((PASSED + 1))
41 }
42
43 fail() {
44 TEST_NUM=$((TEST_NUM + 1))
45 TEST_ID="${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}"
46 printf "${RED}✗ FAIL${NC} ${TEST_ID}: %s\n" "$1"
47 FAILED_TESTS_LIST="${FAILED_TESTS_LIST} ${TEST_ID}: $1\n"
48 if [ -n "$2" ]; then
49 printf " expected: %s\n" "$2"
50 fi
51 if [ -n "$3" ]; then
52 printf " got: %s\n" "$3"
53 fi
54 FAILED=$((FAILED + 1))
55 }
56
57 skip() {
58 TEST_NUM=$((TEST_NUM + 1))
59 printf "${YELLOW}⊘ SKIP${NC} ${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}: %s - %s\n" "$1" "$2"
60 SKIPPED=$((SKIPPED + 1))
61 }
62
63 section() {
64 CURRENT_SECTION=$(echo "$1" | grep -oE '^[0-9]+' || echo "0")
65 TEST_NUM=0
66 printf "\n"
67 printf "${BLUE}==========================================\n"
68 printf "%s\n" "$1"
69 printf "==========================================${NC}\n"
70 }
71
72 # =====================================
73 section "411. SUBSHELL EXECUTION ( )"
74 # =====================================
75
76 result=$("$RUSH_BIN" -c '(echo hello)' 2>&1)
77 if [ "$result" = "hello" ]; then
78 pass "Basic subshell execution"
79 else
80 fail "Basic subshell execution" "hello" "$result"
81 fi
82
83 result=$("$RUSH_BIN" -c 'x=outer; (x=inner; echo $x); echo $x' 2>&1)
84 expected=$(printf "inner\nouter")
85 if [ "$result" = "$expected" ]; then
86 pass "Subshell variable isolation"
87 else
88 fail "Subshell variable isolation" "$expected" "$result"
89 fi
90
91 result=$("$RUSH_BIN" -c '(cd /tmp; pwd); pwd' 2>&1)
92 # Should show /tmp then original dir
93 if echo "$result" | head -1 | grep -q "/tmp"; then
94 pass "Subshell cd isolation"
95 else
96 fail "Subshell cd isolation" "/tmp then original" "$result"
97 fi
98
99 result=$("$RUSH_BIN" -c '(exit 42); echo $?' 2>&1)
100 if [ "$result" = "42" ]; then
101 pass "Subshell exit status propagation"
102 else
103 fail "Subshell exit status propagation" "42" "$result"
104 fi
105
106 result=$("$RUSH_BIN" -c '(echo a; echo b; echo c)' 2>&1)
107 expected=$(printf "a\nb\nc")
108 if [ "$result" = "$expected" ]; then
109 pass "Subshell with multiple commands"
110 else
111 fail "Subshell with multiple commands" "$expected" "$result"
112 fi
113
114 # =====================================
115 section "412. BRACE GROUP { }"
116 # =====================================
117
118 result=$("$RUSH_BIN" -c '{ echo hello; }' 2>&1)
119 if [ "$result" = "hello" ]; then
120 pass "Basic brace group"
121 else
122 fail "Basic brace group" "hello" "$result"
123 fi
124
125 result=$("$RUSH_BIN" -c 'x=outer; { x=inner; echo $x; }; echo $x' 2>&1)
126 expected=$(printf "inner\ninner")
127 if [ "$result" = "$expected" ]; then
128 pass "Brace group shares variable scope"
129 else
130 fail "Brace group shares variable scope" "$expected" "$result"
131 fi
132
133 result=$("$RUSH_BIN" -c '{ echo a; echo b; echo c; }' 2>&1)
134 expected=$(printf "a\nb\nc")
135 if [ "$result" = "$expected" ]; then
136 pass "Brace group with multiple commands"
137 else
138 fail "Brace group with multiple commands" "$expected" "$result"
139 fi
140
141 result=$("$RUSH_BIN" -c '{ false; }; echo $?' 2>&1)
142 if [ "$result" = "1" ]; then
143 pass "Brace group exit status"
144 else
145 fail "Brace group exit status" "1" "$result"
146 fi
147
148 # =====================================
149 section "413. BREAK WITH COUNT"
150 # =====================================
151
152 result=$("$RUSH_BIN" -c '
153 for i in 1 2 3; do
154 for j in a b c; do
155 if [ "$j" = "b" ]; then
156 break
157 fi
158 echo "$i$j"
159 done
160 done' 2>&1)
161 expected=$(printf "1a\n2a\n3a")
162 if [ "$result" = "$expected" ]; then
163 pass "break exits inner loop"
164 else
165 fail "break exits inner loop" "$expected" "$result"
166 fi
167
168 result=$("$RUSH_BIN" -c '
169 for i in 1 2 3; do
170 for j in a b c; do
171 if [ "$j" = "b" ]; then
172 break 2
173 fi
174 echo "$i$j"
175 done
176 done' 2>&1)
177 if [ "$result" = "1a" ]; then
178 pass "break 2 exits both loops"
179 else
180 fail "break 2 exits both loops" "1a" "$result"
181 fi
182
183 result=$("$RUSH_BIN" -c '
184 for i in 1 2 3; do
185 break
186 echo "not reached"
187 done
188 echo "done"' 2>&1)
189 if [ "$result" = "done" ]; then
190 pass "break stops loop immediately"
191 else
192 fail "break stops loop immediately" "done" "$result"
193 fi
194
195 # =====================================
196 section "414. CONTINUE WITH COUNT"
197 # =====================================
198
199 result=$("$RUSH_BIN" -c '
200 for i in 1 2 3; do
201 if [ "$i" = "2" ]; then
202 continue
203 fi
204 echo $i
205 done' 2>&1)
206 expected=$(printf "1\n3")
207 if [ "$result" = "$expected" ]; then
208 pass "continue skips iteration"
209 else
210 fail "continue skips iteration" "$expected" "$result"
211 fi
212
213 result=$("$RUSH_BIN" -c '
214 for i in 1 2; do
215 for j in a b c; do
216 if [ "$j" = "b" ]; then
217 continue 2
218 fi
219 echo "$i$j"
220 done
221 echo "inner done"
222 done' 2>&1)
223 expected=$(printf "1a\n2a")
224 if [ "$result" = "$expected" ]; then
225 pass "continue 2 continues outer loop"
226 else
227 fail "continue 2 continues outer loop" "$expected" "$result"
228 fi
229
230 # =====================================
231 section "415. WHILE LOOP"
232 # =====================================
233
234 result=$("$RUSH_BIN" -c 'i=0; while [ $i -lt 3 ]; do echo $i; i=$((i+1)); done' 2>&1)
235 expected=$(printf "0\n1\n2")
236 if [ "$result" = "$expected" ]; then
237 pass "Basic while loop"
238 else
239 fail "Basic while loop" "$expected" "$result"
240 fi
241
242 result=$("$RUSH_BIN" -c 'while false; do echo no; done; echo done' 2>&1)
243 if [ "$result" = "done" ]; then
244 pass "While with false condition"
245 else
246 fail "While with false condition" "done" "$result"
247 fi
248
249 result=$("$RUSH_BIN" -c '
250 i=0
251 while [ $i -lt 5 ]; do
252 i=$((i+1))
253 if [ $i -eq 3 ]; then continue; fi
254 echo $i
255 done' 2>&1)
256 expected=$(printf "1\n2\n4\n5")
257 if [ "$result" = "$expected" ]; then
258 pass "While with continue"
259 else
260 fail "While with continue" "$expected" "$result"
261 fi
262
263 # =====================================
264 section "416. UNTIL LOOP"
265 # =====================================
266
267 result=$("$RUSH_BIN" -c 'i=0; until [ $i -ge 3 ]; do echo $i; i=$((i+1)); done' 2>&1)
268 expected=$(printf "0\n1\n2")
269 if [ "$result" = "$expected" ]; then
270 pass "Basic until loop"
271 else
272 fail "Basic until loop" "$expected" "$result"
273 fi
274
275 result=$("$RUSH_BIN" -c 'until true; do echo no; done; echo done' 2>&1)
276 if [ "$result" = "done" ]; then
277 pass "Until with true condition"
278 else
279 fail "Until with true condition" "done" "$result"
280 fi
281
282 # =====================================
283 section "417. PIPELINE EXIT STATUS"
284 # =====================================
285
286 result=$("$RUSH_BIN" -c 'true | false; echo $?' 2>&1)
287 if [ "$result" = "1" ]; then
288 pass "Pipeline exit status is last command"
289 else
290 fail "Pipeline exit status is last command" "1" "$result"
291 fi
292
293 result=$("$RUSH_BIN" -c 'false | true; echo $?' 2>&1)
294 if [ "$result" = "0" ]; then
295 pass "Pipeline with false then true"
296 else
297 fail "Pipeline with false then true" "0" "$result"
298 fi
299
300 result=$("$RUSH_BIN" -c 'echo hello | cat | cat; echo $?' 2>&1)
301 expected=$(printf "hello\n0")
302 if [ "$result" = "$expected" ]; then
303 pass "Multi-stage pipeline"
304 else
305 fail "Multi-stage pipeline" "$expected" "$result"
306 fi
307
308 # =====================================
309 section "418. AND-OR LISTS"
310 # =====================================
311
312 result=$("$RUSH_BIN" -c 'true && echo yes' 2>&1)
313 if [ "$result" = "yes" ]; then
314 pass "&& executes on success"
315 else
316 fail "&& executes on success" "yes" "$result"
317 fi
318
319 result=$("$RUSH_BIN" -c 'false && echo yes; echo done' 2>&1)
320 if [ "$result" = "done" ]; then
321 pass "&& skips on failure"
322 else
323 fail "&& skips on failure" "done" "$result"
324 fi
325
326 result=$("$RUSH_BIN" -c 'false || echo fallback' 2>&1)
327 if [ "$result" = "fallback" ]; then
328 pass "|| executes on failure"
329 else
330 fail "|| executes on failure" "fallback" "$result"
331 fi
332
333 result=$("$RUSH_BIN" -c 'true || echo no; echo done' 2>&1)
334 if [ "$result" = "done" ]; then
335 pass "|| skips on success"
336 else
337 fail "|| skips on success" "done" "$result"
338 fi
339
340 result=$("$RUSH_BIN" -c 'true && echo a && echo b' 2>&1)
341 expected=$(printf "a\nb")
342 if [ "$result" = "$expected" ]; then
343 pass "Chained && operators"
344 else
345 fail "Chained && operators" "$expected" "$result"
346 fi
347
348 result=$("$RUSH_BIN" -c 'false || false || echo third' 2>&1)
349 if [ "$result" = "third" ]; then
350 pass "Chained || operators"
351 else
352 fail "Chained || operators" "third" "$result"
353 fi
354
355 result=$("$RUSH_BIN" -c 'false || true && echo mixed' 2>&1)
356 if [ "$result" = "mixed" ]; then
357 pass "Mixed && and ||"
358 else
359 fail "Mixed && and ||" "mixed" "$result"
360 fi
361
362 # =====================================
363 section "419. NEGATION WITH !"
364 # =====================================
365
366 result=$("$RUSH_BIN" -c '! false; echo $?' 2>&1)
367 if [ "$result" = "0" ]; then
368 pass "! false returns 0"
369 else
370 fail "! false returns 0" "0" "$result"
371 fi
372
373 result=$("$RUSH_BIN" -c '! true; echo $?' 2>&1)
374 if [ "$result" = "1" ]; then
375 pass "! true returns 1"
376 else
377 fail "! true returns 1" "1" "$result"
378 fi
379
380 result=$("$RUSH_BIN" -c '! echo hello >/dev/null; echo $?' 2>&1)
381 if [ "$result" = "1" ]; then
382 pass "! negates command exit status"
383 else
384 fail "! negates command exit status" "1" "$result"
385 fi
386
387 # =====================================
388 section "420. DOT (SOURCE) COMMAND"
389 # =====================================
390
391 TEST_DIR="/tmp/fortsh_control_$$"
392 mkdir -p "$TEST_DIR"
393
394 # Create a file to source
395 echo 'SOURCED_VAR=hello' > "$TEST_DIR/sourceme.sh"
396 echo 'sourced_func() { echo "from func"; }' >> "$TEST_DIR/sourceme.sh"
397
398 result=$("$RUSH_BIN" -c '. '"$TEST_DIR"'/sourceme.sh; echo $SOURCED_VAR' 2>&1)
399 if [ "$result" = "hello" ]; then
400 pass ". sources file and sets variable"
401 else
402 fail ". sources file and sets variable" "hello" "$result"
403 fi
404
405 result=$("$RUSH_BIN" -c '. '"$TEST_DIR"'/sourceme.sh; sourced_func' 2>&1)
406 if echo "$result" | grep -q "from func"; then
407 pass ". sources file and defines function"
408 else
409 fail ". sources file and defines function" "from func" "$result"
410 fi
411
412 # Test source with arguments
413 echo 'echo "arg1=$1 arg2=$2"' > "$TEST_DIR/withargs.sh"
414 result=$("$RUSH_BIN" -c '. '"$TEST_DIR"'/withargs.sh foo bar' 2>&1)
415 if [ "$result" = "arg1=foo arg2=bar" ]; then
416 pass ". passes arguments to sourced script"
417 else
418 fail ". passes arguments to sourced script" "arg1=foo arg2=bar" "$result"
419 fi
420
421 rm -rf "$TEST_DIR"
422
423 # =====================================
424 section "421. EXEC BUILTIN"
425 # =====================================
426
427 result=$("$RUSH_BIN" -c 'exec echo replaced' 2>&1)
428 if [ "$result" = "replaced" ]; then
429 pass "exec replaces shell with command"
430 else
431 fail "exec replaces shell with command" "replaced" "$result"
432 fi
433
434 # exec without command just does redirections
435 result=$("$RUSH_BIN" -c 'exec 2>/dev/null; echo hello' 2>&1)
436 if [ "$result" = "hello" ]; then
437 pass "exec without command continues shell"
438 else
439 fail "exec without command continues shell" "hello" "$result"
440 fi
441
442 # =====================================
443 section "422. EVAL BUILTIN"
444 # =====================================
445
446 result=$("$RUSH_BIN" -c 'cmd="echo hello"; eval $cmd' 2>&1)
447 if [ "$result" = "hello" ]; then
448 pass "eval executes string as command"
449 else
450 fail "eval executes string as command" "hello" "$result"
451 fi
452
453 result=$("$RUSH_BIN" -c 'x=world; eval echo "hello $x"' 2>&1)
454 if [ "$result" = "hello world" ]; then
455 pass "eval with variable expansion"
456 else
457 fail "eval with variable expansion" "hello world" "$result"
458 fi
459
460 result=$("$RUSH_BIN" -c 'eval "x=5; echo \$x"' 2>&1)
461 if [ "$result" = "5" ]; then
462 pass "eval with multiple commands"
463 else
464 fail "eval with multiple commands" "5" "$result"
465 fi
466
467 result=$("$RUSH_BIN" -c 'var=x; eval "$var=42"; echo $x' 2>&1)
468 if [ "$result" = "42" ]; then
469 pass "eval for indirect assignment"
470 else
471 fail "eval for indirect assignment" "42" "$result"
472 fi
473
474 # =====================================
475 section "423. RETURN FROM FUNCTION"
476 # =====================================
477
478 result=$("$RUSH_BIN" -c 'f() { return 0; echo no; }; f; echo $?' 2>&1)
479 if [ "$result" = "0" ]; then
480 pass "return 0 from function"
481 else
482 fail "return 0 from function" "0" "$result"
483 fi
484
485 result=$("$RUSH_BIN" -c 'f() { return 42; }; f; echo $?' 2>&1)
486 if [ "$result" = "42" ]; then
487 pass "return with status code"
488 else
489 fail "return with status code" "42" "$result"
490 fi
491
492 result=$("$RUSH_BIN" -c 'f() { echo before; return; echo after; }; f' 2>&1)
493 if [ "$result" = "before" ]; then
494 pass "return without value"
495 else
496 fail "return without value" "before" "$result"
497 fi
498
499 # =====================================
500 section "424. FUNCTION LOCAL VARIABLES"
501 # =====================================
502
503 result=$("$RUSH_BIN" -c 'x=global; f() { local x=local; echo $x; }; f; echo $x' 2>&1)
504 expected=$(printf "local\nglobal")
505 if [ "$result" = "$expected" ]; then
506 pass "local variable in function"
507 else
508 fail "local variable in function" "$expected" "$result"
509 fi
510
511 result=$("$RUSH_BIN" -c 'f() { local x=1; g() { echo $x; }; g; }; f' 2>&1)
512 if [ "$result" = "1" ]; then
513 pass "local visible in nested function"
514 else
515 fail "local visible in nested function" "1" "$result"
516 fi
517
518 result=$("$RUSH_BIN" -c 'f() { local a=1 b=2; echo $a $b; }; f' 2>&1)
519 if [ "$result" = "1 2" ]; then
520 pass "local multiple variables"
521 else
522 fail "local multiple variables" "1 2" "$result"
523 fi
524
525 result=$("$RUSH_BIN" -c 'f() { local x; x=set; echo $x; }; f' 2>&1)
526 if [ "$result" = "set" ]; then
527 pass "local without initial value"
528 else
529 fail "local without initial value" "set" "$result"
530 fi
531
532 # =====================================
533 section "425. FUNCTION RECURSION"
534 # =====================================
535
536 result=$("$RUSH_BIN" -c '
537 factorial() {
538 if [ $1 -le 1 ]; then
539 echo 1
540 else
541 prev=$(factorial $(($1 - 1)))
542 echo $(($1 * prev))
543 fi
544 }
545 factorial 5' 2>&1)
546 if [ "$result" = "120" ]; then
547 pass "Recursive function (factorial)"
548 else
549 fail "Recursive function (factorial)" "120" "$result"
550 fi
551
552 result=$("$RUSH_BIN" -c '
553 count=0
554 recurse() {
555 count=$((count + 1))
556 if [ $count -lt 5 ]; then
557 recurse
558 fi
559 echo $count
560 }
561 recurse | tail -1' 2>&1)
562 if [ "$result" = "5" ]; then
563 pass "Recursive function with global state"
564 else
565 fail "Recursive function with global state" "5" "$result"
566 fi
567
568 # =====================================
569 section "426. FUNCTION ARGUMENTS"
570 # =====================================
571
572 result=$("$RUSH_BIN" -c 'f() { echo $1 $2 $3; }; f a b c' 2>&1)
573 if [ "$result" = "a b c" ]; then
574 pass "Function positional parameters"
575 else
576 fail "Function positional parameters" "a b c" "$result"
577 fi
578
579 result=$("$RUSH_BIN" -c 'f() { echo $#; }; f a b c d e' 2>&1)
580 if [ "$result" = "5" ]; then
581 pass "Function \$# count"
582 else
583 fail "Function \$# count" "5" "$result"
584 fi
585
586 result=$("$RUSH_BIN" -c 'f() { echo "$@"; }; f "a b" c' 2>&1)
587 if [ "$result" = "a b c" ]; then
588 pass "Function \$@ expansion"
589 else
590 fail "Function \$@ expansion" "a b c" "$result"
591 fi
592
593 result=$("$RUSH_BIN" -c 'f() { shift; echo $1; }; f a b c' 2>&1)
594 if [ "$result" = "b" ]; then
595 pass "shift in function"
596 else
597 fail "shift in function" "b" "$result"
598 fi
599
600 result=$("$RUSH_BIN" -c 'f() { set -- x y z; echo $1; }; f a b; echo done' 2>&1)
601 expected=$(printf "x\ndone")
602 if [ "$result" = "$expected" ]; then
603 pass "set -- in function is local"
604 else
605 fail "set -- in function is local" "$expected" "$result"
606 fi
607
608 # =====================================
609 section "427. FUNCTION OVERRIDE"
610 # =====================================
611
612 result=$("$RUSH_BIN" -c 'f() { echo first; }; f() { echo second; }; f' 2>&1)
613 if [ "$result" = "second" ]; then
614 pass "Function redefinition"
615 else
616 fail "Function redefinition" "second" "$result"
617 fi
618
619 result=$("$RUSH_BIN" -c 'f() { echo orig; }; g() { f; }; f() { echo new; }; g' 2>&1)
620 if [ "$result" = "new" ]; then
621 pass "Function sees redefined function"
622 else
623 fail "Function sees redefined function" "new" "$result"
624 fi
625
626 # =====================================
627 section "428. UNSET FUNCTION"
628 # =====================================
629
630 result=$("$RUSH_BIN" -c 'f() { echo hi; }; unset -f f; f 2>/dev/null; echo $?' 2>&1)
631 if echo "$result" | grep -qE "127|1"; then
632 pass "unset -f removes function"
633 else
634 fail "unset -f removes function" "non-zero exit" "$result"
635 fi
636
637 # =====================================
638 section "429. COLON BUILTIN"
639 # =====================================
640
641 result=$("$RUSH_BIN" -c ':; echo $?' 2>&1)
642 if [ "$result" = "0" ]; then
643 pass ": returns 0"
644 else
645 fail ": returns 0" "0" "$result"
646 fi
647
648 result=$("$RUSH_BIN" -c ': this is ignored; echo ok' 2>&1)
649 if [ "$result" = "ok" ]; then
650 pass ": ignores arguments"
651 else
652 fail ": ignores arguments" "ok" "$result"
653 fi
654
655 result=$("$RUSH_BIN" -c ': ${x:=default}; echo $x' 2>&1)
656 if [ "$result" = "default" ]; then
657 pass ": for side effects in expansion"
658 else
659 fail ": for side effects in expansion" "default" "$result"
660 fi
661
662 # =====================================
663 section "426. TRUE AND FALSE BUILTINS"
664 # =====================================
665
666 result=$("$RUSH_BIN" -c 'true; echo $?' 2>&1)
667 if [ "$result" = "0" ]; then
668 pass "true returns 0"
669 else
670 fail "true returns 0" "0" "$result"
671 fi
672
673 result=$("$RUSH_BIN" -c 'false; echo $?' 2>&1)
674 if [ "$result" = "1" ]; then
675 pass "false returns 1"
676 else
677 fail "false returns 1" "1" "$result"
678 fi
679
680 # =====================================
681 section "427. EXIT BUILTIN"
682 # =====================================
683
684 result=$("$RUSH_BIN" -c 'exit 0; echo no' 2>&1)
685 if [ -z "$result" ]; then
686 pass "exit 0 terminates immediately"
687 else
688 fail "exit 0 terminates immediately" "(empty)" "$result"
689 fi
690
691 result=$("$RUSH_BIN" -c 'exit 42' 2>&1)
692 code=$?
693 if [ $code -eq 42 ]; then
694 pass "exit with specific code"
695 else
696 fail "exit with specific code" "42" "$code"
697 fi
698
699 result=$("$RUSH_BIN" -c 'false; exit' 2>&1)
700 code=$?
701 if [ $code -eq 1 ]; then
702 pass "exit without arg uses last status"
703 else
704 fail "exit without arg uses last status" "1" "$code"
705 fi
706
707 # =====================================
708 section "430. CASE STATEMENT PATTERNS"
709 # =====================================
710
711 # Simple exact match
712 result=$("$RUSH_BIN" -c 'case "hello" in hello) echo yes;; esac' 2>&1)
713 if [ "$result" = "yes" ]; then
714 pass "case exact match"
715 else
716 fail "case exact match" "yes" "$result"
717 fi
718
719 # Glob pattern match
720 result=$("$RUSH_BIN" -c 'case "hello" in h*) echo yes;; esac' 2>&1)
721 if [ "$result" = "yes" ]; then
722 pass "case glob pattern"
723 else
724 fail "case glob pattern" "yes" "$result"
725 fi
726
727 # Question mark match
728 result=$("$RUSH_BIN" -c 'case "cat" in c?t) echo yes;; esac' 2>&1)
729 if [ "$result" = "yes" ]; then
730 pass "case ? pattern"
731 else
732 fail "case ? pattern" "yes" "$result"
733 fi
734
735 # Bracket pattern
736 result=$("$RUSH_BIN" -c 'case "b" in [abc]) echo yes;; esac' 2>&1)
737 if [ "$result" = "yes" ]; then
738 pass "case [abc] bracket pattern"
739 else
740 fail "case [abc] bracket pattern" "yes" "$result"
741 fi
742
743 # Multiple patterns with |
744 result=$("$RUSH_BIN" -c 'case "two" in one|two|three) echo yes;; esac' 2>&1)
745 if [ "$result" = "yes" ]; then
746 pass "case multiple patterns with |"
747 else
748 fail "case multiple patterns with |" "yes" "$result"
749 fi
750
751 # Default pattern *
752 result=$("$RUSH_BIN" -c 'case "xyz" in abc) echo no;; *) echo default;; esac' 2>&1)
753 if [ "$result" = "default" ]; then
754 pass "case * default pattern"
755 else
756 fail "case * default pattern" "default" "$result"
757 fi
758
759 # No match produces nothing
760 result=$("$RUSH_BIN" -c 'case "x" in y) echo no;; z) echo also no;; esac; echo done' 2>&1)
761 if [ "$result" = "done" ]; then
762 pass "case no match produces empty"
763 else
764 fail "case no match produces empty" "done" "$result"
765 fi
766
767 # Variable in word
768 result=$("$RUSH_BIN" -c 'x=hello; case "$x" in hello) echo yes;; esac' 2>&1)
769 if [ "$result" = "yes" ]; then
770 pass "case with variable in word"
771 else
772 fail "case with variable in word" "yes" "$result"
773 fi
774
775 # Variable in pattern
776 result=$("$RUSH_BIN" -c 'pat="hel*"; case "hello" in $pat) echo yes;; *) echo no;; esac' 2>&1)
777 if [ "$result" = "yes" ]; then
778 pass "case with variable in pattern"
779 else
780 fail "case with variable in pattern" "yes" "$result"
781 fi
782
783 # Quoted pattern (literal)
784 result=$("$RUSH_BIN" -c 'case "h*" in "h*") echo yes;; esac' 2>&1)
785 if [ "$result" = "yes" ]; then
786 pass "case quoted pattern is literal"
787 else
788 fail "case quoted pattern is literal" "yes" "$result"
789 fi
790
791 # =====================================
792 section "431. NESTED CONTROL STRUCTURES"
793 # =====================================
794
795 # if inside for
796 result=$("$RUSH_BIN" -c 'for i in 1 2 3; do if [ $i -eq 2 ]; then echo found; fi; done' 2>&1)
797 if [ "$result" = "found" ]; then
798 pass "if inside for loop"
799 else
800 fail "if inside for loop" "found" "$result"
801 fi
802
803 # case inside while
804 result=$("$RUSH_BIN" -c 'i=0; while [ $i -lt 3 ]; do case $i in 1) echo one;; esac; i=$((i+1)); done' 2>&1)
805 if [ "$result" = "one" ]; then
806 pass "case inside while loop"
807 else
808 fail "case inside while loop" "one" "$result"
809 fi
810
811 # for inside if
812 result=$("$RUSH_BIN" -c 'if true; then for i in a b; do echo $i; done; fi' 2>&1)
813 expected=$(printf "a\nb")
814 if [ "$result" = "$expected" ]; then
815 pass "for inside if"
816 else
817 fail "for inside if" "$expected" "$result"
818 fi
819
820 # Deeply nested
821 result=$("$RUSH_BIN" -c '
822 for i in 1; do
823 for j in 2; do
824 for k in 3; do
825 echo "$i$j$k"
826 done
827 done
828 done' 2>&1)
829 if [ "$result" = "123" ]; then
830 pass "Triple nested for loops"
831 else
832 fail "Triple nested for loops" "123" "$result"
833 fi
834
835 # =====================================
836 section "432. WHILE AND UNTIL LOOPS"
837 # =====================================
838
839 # while with counter
840 result=$("$RUSH_BIN" -c 'i=0; while [ $i -lt 3 ]; do echo $i; i=$((i+1)); done' 2>&1)
841 expected=$(printf "0\n1\n2")
842 if [ "$result" = "$expected" ]; then
843 pass "while loop with counter"
844 else
845 fail "while loop with counter" "$expected" "$result"
846 fi
847
848 # until loop
849 result=$("$RUSH_BIN" -c 'i=0; until [ $i -ge 3 ]; do echo $i; i=$((i+1)); done' 2>&1)
850 expected=$(printf "0\n1\n2")
851 if [ "$result" = "$expected" ]; then
852 pass "until loop"
853 else
854 fail "until loop" "$expected" "$result"
855 fi
856
857 # while with compound condition
858 result=$("$RUSH_BIN" -c 'i=0; while [ $i -lt 5 ] && [ $i -ne 3 ]; do echo $i; i=$((i+1)); done' 2>&1)
859 expected=$(printf "0\n1\n2")
860 if [ "$result" = "$expected" ]; then
861 pass "while with compound condition"
862 else
863 fail "while with compound condition" "$expected" "$result"
864 fi
865
866 # Empty while body (using :)
867 result=$("$RUSH_BIN" -c 'i=0; while [ $i -lt 3 ]; do : ; i=$((i+1)); done; echo $i' 2>&1)
868 if [ "$result" = "3" ]; then
869 pass "while with empty body (colon)"
870 else
871 fail "while with empty body (colon)" "3" "$result"
872 fi
873
874 # =====================================
875 section "433. BREAK AND CONTINUE"
876 # =====================================
877
878 # break in while
879 result=$("$RUSH_BIN" -c 'i=0; while true; do i=$((i+1)); [ $i -ge 3 ] && break; done; echo $i' 2>&1)
880 if [ "$result" = "3" ]; then
881 pass "break in while loop"
882 else
883 fail "break in while loop" "3" "$result"
884 fi
885
886 # continue in for
887 result=$("$RUSH_BIN" -c 'for i in 1 2 3 4 5; do [ $i -eq 3 ] && continue; echo $i; done' 2>&1)
888 expected=$(printf "1\n2\n4\n5")
889 if [ "$result" = "$expected" ]; then
890 pass "continue in for loop"
891 else
892 fail "continue in for loop" "$expected" "$result"
893 fi
894
895 # break N for nested loops
896 result=$("$RUSH_BIN" -c '
897 for i in 1 2; do
898 for j in a b; do
899 echo "$i$j"
900 [ "$j" = "a" ] && break 2
901 done
902 done
903 echo done' 2>&1)
904 expected=$(printf "1a\ndone")
905 if [ "$result" = "$expected" ]; then
906 pass "break 2 exits outer loop"
907 else
908 fail "break 2 exits outer loop" "$expected" "$result"
909 fi
910
911 # continue N for nested loops
912 result=$("$RUSH_BIN" -c '
913 for i in 1 2; do
914 for j in a b; do
915 [ "$i$j" = "1a" ] && continue 2
916 echo "$i$j"
917 done
918 done' 2>&1)
919 expected=$(printf "2a\n2b")
920 if [ "$result" = "$expected" ]; then
921 pass "continue 2 continues outer loop"
922 else
923 fail "continue 2 continues outer loop" "$expected" "$result"
924 fi
925
926 # =====================================
927 section "434. FOR LOOP VARIATIONS"
928 # =====================================
929
930 # for without in (uses positional params)
931 result=$("$RUSH_BIN" -c 'set -- a b c; for x; do echo $x; done' 2>&1)
932 expected=$(printf "a\nb\nc")
933 if [ "$result" = "$expected" ]; then
934 pass "for without 'in' uses \$@"
935 else
936 fail "for without 'in' uses \$@" "$expected" "$result"
937 fi
938
939 # for with glob pattern
940 # Create temp files for glob test
941 result=$("$RUSH_BIN" -c 'cd /tmp && touch _testglob_a _testglob_b && for f in _testglob_*; do echo $f; done | wc -l && rm -f _testglob_*' 2>&1 | head -1)
942 if [ "$result" = "2" ]; then
943 pass "for with glob expansion"
944 else
945 fail "for with glob expansion" "2" "$result"
946 fi
947
948 # for with command substitution
949 result=$("$RUSH_BIN" -c 'for x in $(echo a b c); do echo $x; done' 2>&1)
950 expected=$(printf "a\nb\nc")
951 if [ "$result" = "$expected" ]; then
952 pass "for with command substitution"
953 else
954 fail "for with command substitution" "$expected" "$result"
955 fi
956
957 # for with quoted string (single iteration)
958 result=$("$RUSH_BIN" -c 'for x in "a b c"; do echo "[$x]"; done' 2>&1)
959 if [ "$result" = "[a b c]" ]; then
960 pass "for with quoted string (single iteration)"
961 else
962 fail "for with quoted string (single iteration)" "[a b c]" "$result"
963 fi
964
965 # =====================================
966 section "435. WHILE LOOP VARIATIONS"
967 # =====================================
968
969 # while with pipeline
970 result=$("$RUSH_BIN" -c 'i=0; while [ $i -lt 3 ]; do echo $i; i=$((i+1)); done | wc -l' 2>&1)
971 if [ "$result" = "3" ]; then
972 pass "while loop with pipeline"
973 else
974 fail "while loop with pipeline" "3" "$result"
975 fi
976
977 # while with command substitution condition
978 result=$("$RUSH_BIN" -c 'X=yes; while [ "$X" = "yes" ]; do echo once; X=no; done' 2>&1)
979 if [ "$result" = "once" ]; then
980 pass "while with variable condition"
981 else
982 fail "while with variable condition" "once" "$result"
983 fi
984
985 # infinite while with break
986 result=$("$RUSH_BIN" -c 'i=0; while true; do i=$((i+1)); [ $i -ge 5 ] && break; done; echo $i' 2>&1)
987 if [ "$result" = "5" ]; then
988 pass "infinite while with break"
989 else
990 fail "infinite while with break" "5" "$result"
991 fi
992
993 # =====================================
994 section "436. UNTIL LOOP VARIATIONS"
995 # =====================================
996
997 # until basic
998 result=$("$RUSH_BIN" -c 'i=0; until [ $i -ge 3 ]; do echo $i; i=$((i+1)); done' 2>&1)
999 expected=$(printf "0\n1\n2")
1000 if [ "$result" = "$expected" ]; then
1001 pass "until loop basic"
1002 else
1003 fail "until loop basic"
1004 fi
1005
1006 # until with break
1007 result=$("$RUSH_BIN" -c 'i=0; until false; do i=$((i+1)); [ $i -ge 3 ] && break; done; echo $i' 2>&1)
1008 if [ "$result" = "3" ]; then
1009 pass "until with break"
1010 else
1011 fail "until with break" "3" "$result"
1012 fi
1013
1014 # =====================================
1015 section "437. IF STATEMENT VARIATIONS"
1016 # =====================================
1017
1018 # if with command
1019 result=$("$RUSH_BIN" -c 'if true; then echo yes; fi' 2>&1)
1020 if [ "$result" = "yes" ]; then
1021 pass "if with true command"
1022 else
1023 fail "if with true command" "yes" "$result"
1024 fi
1025
1026 # if with test
1027 result=$("$RUSH_BIN" -c 'X=5; if [ $X -gt 3 ]; then echo big; fi' 2>&1)
1028 if [ "$result" = "big" ]; then
1029 pass "if with test condition"
1030 else
1031 fail "if with test condition" "big" "$result"
1032 fi
1033
1034 # if-else
1035 result=$("$RUSH_BIN" -c 'if false; then echo no; else echo yes; fi' 2>&1)
1036 if [ "$result" = "yes" ]; then
1037 pass "if-else statement"
1038 else
1039 fail "if-else statement" "yes" "$result"
1040 fi
1041
1042 # if-elif-else
1043 result=$("$RUSH_BIN" -c 'X=2; if [ $X -eq 1 ]; then echo one; elif [ $X -eq 2 ]; then echo two; else echo other; fi' 2>&1)
1044 if [ "$result" = "two" ]; then
1045 pass "if-elif-else statement"
1046 else
1047 fail "if-elif-else statement" "two" "$result"
1048 fi
1049
1050 # nested if
1051 result=$("$RUSH_BIN" -c 'X=1; Y=2; if [ $X -eq 1 ]; then if [ $Y -eq 2 ]; then echo both; fi; fi' 2>&1)
1052 if [ "$result" = "both" ]; then
1053 pass "nested if statement"
1054 else
1055 fail "nested if statement" "both" "$result"
1056 fi
1057
1058 # =====================================
1059 section "438. CASE STATEMENT VARIATIONS"
1060 # =====================================
1061
1062 # case with character class
1063 result=$("$RUSH_BIN" -c 'x=5; case $x in [0-9]) echo digit;; esac' 2>&1)
1064 if [ "$result" = "digit" ]; then
1065 pass "case with character class"
1066 else
1067 fail "case with character class" "digit" "$result"
1068 fi
1069
1070 # case with negation
1071 result=$("$RUSH_BIN" -c 'x=x; case $x in [!0-9]) echo not_digit;; esac' 2>&1)
1072 if [ "$result" = "not_digit" ]; then
1073 pass "case with negation"
1074 else
1075 fail "case with negation" "not_digit" "$result"
1076 fi
1077
1078 # case with question mark
1079 result=$("$RUSH_BIN" -c 'x=ab; case $x in ??) echo two_chars;; esac' 2>&1)
1080 if [ "$result" = "two_chars" ]; then
1081 pass "case with question mark pattern"
1082 else
1083 fail "case with question mark pattern" "two_chars" "$result"
1084 fi
1085
1086 # case fall-through (first match wins)
1087 result=$("$RUSH_BIN" -c 'x=a; case $x in a) echo first;; a) echo second;; esac' 2>&1)
1088 if [ "$result" = "first" ]; then
1089 pass "case first match wins"
1090 else
1091 fail "case first match wins" "first" "$result"
1092 fi
1093
1094 # =====================================
1095 section "439. BRACE GROUP VARIATIONS"
1096 # =====================================
1097
1098 # brace group in pipeline
1099 result=$("$RUSH_BIN" -c '{ echo a; echo b; } | wc -l' 2>&1)
1100 if [ "$result" = "2" ]; then
1101 pass "brace group in pipeline"
1102 else
1103 fail "brace group in pipeline" "2" "$result"
1104 fi
1105
1106 # brace group with redirection
1107 result=$("$RUSH_BIN" -c '{ echo test; } > /tmp/brace_test_$$; cat /tmp/brace_test_$$; rm /tmp/brace_test_$$' 2>&1)
1108 if [ "$result" = "test" ]; then
1109 pass "brace group with redirection"
1110 else
1111 fail "brace group with redirection" "test" "$result"
1112 fi
1113
1114 # brace group preserves variables
1115 result=$("$RUSH_BIN" -c 'X=1; { X=2; }; echo $X' 2>&1)
1116 if [ "$result" = "2" ]; then
1117 pass "brace group preserves variable changes"
1118 else
1119 fail "brace group preserves variable changes" "2" "$result"
1120 fi
1121
1122 # =====================================
1123 section "440. SUBSHELL VARIATIONS"
1124 # =====================================
1125
1126 # subshell in pipeline
1127 result=$("$RUSH_BIN" -c '(echo a; echo b) | wc -l' 2>&1)
1128 if [ "$result" = "2" ]; then
1129 pass "subshell in pipeline"
1130 else
1131 fail "subshell in pipeline" "2" "$result"
1132 fi
1133
1134 # subshell isolates variables
1135 result=$("$RUSH_BIN" -c 'X=1; (X=2); echo $X' 2>&1)
1136 if [ "$result" = "1" ]; then
1137 pass "subshell isolates variable changes"
1138 else
1139 fail "subshell isolates variable changes" "1" "$result"
1140 fi
1141
1142 # nested subshells
1143 result=$("$RUSH_BIN" -c '((echo deep))' 2>&1)
1144 if [ "$result" = "deep" ]; then
1145 pass "nested subshells"
1146 else
1147 fail "nested subshells" "deep" "$result"
1148 fi
1149
1150 # =====================================
1151 section "441. COMPOUND LIST VARIATIONS"
1152 # =====================================
1153
1154 # semicolon separated
1155 result=$("$RUSH_BIN" -c 'echo a; echo b; echo c' 2>&1)
1156 expected=$(printf "a\nb\nc")
1157 if [ "$result" = "$expected" ]; then
1158 pass "semicolon separated commands"
1159 else
1160 fail "semicolon separated commands"
1161 fi
1162
1163 # newline separated
1164 result=$("$RUSH_BIN" -c 'echo a
1165 echo b
1166 echo c' 2>&1)
1167 expected=$(printf "a\nb\nc")
1168 if [ "$result" = "$expected" ]; then
1169 pass "newline separated commands"
1170 else
1171 fail "newline separated commands"
1172 fi
1173
1174 # =====================================
1175 section "442. LOGICAL OPERATORS VARIATIONS"
1176 # =====================================
1177
1178 # && chain
1179 result=$("$RUSH_BIN" -c 'true && true && echo success' 2>&1)
1180 if [ "$result" = "success" ]; then
1181 pass "&& chain all true"
1182 else
1183 fail "&& chain all true" "success" "$result"
1184 fi
1185
1186 # && short-circuit
1187 result=$("$RUSH_BIN" -c 'false && echo never' 2>&1)
1188 if [ -z "$result" ]; then
1189 pass "&& short-circuits on false"
1190 else
1191 fail "&& short-circuits on false" "empty" "$result"
1192 fi
1193
1194 # || chain
1195 result=$("$RUSH_BIN" -c 'false || false || echo fallback' 2>&1)
1196 if [ "$result" = "fallback" ]; then
1197 pass "|| chain with fallback"
1198 else
1199 fail "|| chain with fallback" "fallback" "$result"
1200 fi
1201
1202 # || short-circuit
1203 result=$("$RUSH_BIN" -c 'true || echo never' 2>&1)
1204 if [ -z "$result" ]; then
1205 pass "|| short-circuits on true"
1206 else
1207 fail "|| short-circuits on true" "empty" "$result"
1208 fi
1209
1210 # mixed && and ||
1211 result=$("$RUSH_BIN" -c 'false && echo no || echo yes' 2>&1)
1212 if [ "$result" = "yes" ]; then
1213 pass "mixed && and ||"
1214 else
1215 fail "mixed && and ||" "yes" "$result"
1216 fi
1217
1218 # =====================================
1219 section "443. NEGATION WITH !"
1220 # =====================================
1221
1222 # ! negates exit status
1223 result=$("$RUSH_BIN" -c '! false && echo success' 2>&1)
1224 if [ "$result" = "success" ]; then
1225 pass "! negates false to true"
1226 else
1227 fail "! negates false to true" "success" "$result"
1228 fi
1229
1230 result=$("$RUSH_BIN" -c '! true || echo failed' 2>&1)
1231 if [ "$result" = "failed" ]; then
1232 pass "! negates true to false"
1233 else
1234 fail "! negates true to false" "failed" "$result"
1235 fi
1236
1237 # ! with pipeline
1238 result=$("$RUSH_BIN" -c '! echo test | grep -q nomatch && echo ok' 2>&1)
1239 if [ "$result" = "ok" ]; then
1240 pass "! with pipeline"
1241 else
1242 fail "! with pipeline" "ok" "$result"
1243 fi
1244
1245 # =====================================
1246 section "444. FUNCTION DEFINITIONS"
1247 # =====================================
1248
1249 # function with return
1250 result=$("$RUSH_BIN" -c 'f() { return 42; }; f; echo $?' 2>&1)
1251 if [ "$result" = "42" ]; then
1252 pass "function with return value"
1253 else
1254 fail "function with return value" "42" "$result"
1255 fi
1256
1257 # function with arguments
1258 result=$("$RUSH_BIN" -c 'greet() { echo "Hello, $1"; }; greet World' 2>&1)
1259 if [ "$result" = "Hello, World" ]; then
1260 pass "function with arguments"
1261 else
1262 fail "function with arguments" "Hello, World" "$result"
1263 fi
1264
1265 # function calling function
1266 result=$("$RUSH_BIN" -c 'a() { b; }; b() { echo called; }; a' 2>&1)
1267 if [ "$result" = "called" ]; then
1268 pass "function calling function"
1269 else
1270 fail "function calling function" "called" "$result"
1271 fi
1272
1273 # =====================================
1274 section "445. EXIT AND RETURN"
1275 # =====================================
1276
1277 # exit from subshell
1278 result=$("$RUSH_BIN" -c '(exit 7); echo $?' 2>&1)
1279 if [ "$result" = "7" ]; then
1280 pass "exit from subshell"
1281 else
1282 fail "exit from subshell" "7" "$result"
1283 fi
1284
1285 # return from function
1286 result=$("$RUSH_BIN" -c 'f() { echo before; return; echo after; }; f' 2>&1)
1287 if [ "$result" = "before" ]; then
1288 pass "return stops function execution"
1289 else
1290 fail "return stops function execution" "before" "$result"
1291 fi
1292
1293 # return default value
1294 result=$("$RUSH_BIN" -c 'f() { true; return; }; f; echo $?' 2>&1)
1295 if [ "$result" = "0" ]; then
1296 pass "return with no value uses last exit status"
1297 else
1298 fail "return with no value uses last exit status" "0" "$result"
1299 fi
1300
1301 # =====================================
1302 section "446. COMPLEX CONTROL FLOW"
1303 # =====================================
1304
1305 # Nested if-elif-else
1306 result=$("$RUSH_BIN" -c 'X=5; if [ $X -lt 3 ]; then echo low; elif [ $X -lt 7 ]; then echo mid; else echo high; fi' 2>&1)
1307 if [ "$result" = "mid" ]; then
1308 pass "nested if-elif-else"
1309 else
1310 fail "nested if-elif-else" "mid" "$result"
1311 fi
1312
1313 # Multiple elif
1314 result=$("$RUSH_BIN" -c 'X=4; if [ $X -eq 1 ]; then echo 1; elif [ $X -eq 2 ]; then echo 2; elif [ $X -eq 3 ]; then echo 3; elif [ $X -eq 4 ]; then echo 4; else echo other; fi' 2>&1)
1315 if [ "$result" = "4" ]; then
1316 pass "multiple elif branches"
1317 else
1318 fail "multiple elif branches" "4" "$result"
1319 fi
1320
1321 # Deeply nested if
1322 result=$("$RUSH_BIN" -c 'if true; then if true; then if true; then echo deep; fi; fi; fi' 2>&1)
1323 if [ "$result" = "deep" ]; then
1324 pass "deeply nested if"
1325 else
1326 fail "deeply nested if" "deep" "$result"
1327 fi
1328
1329 # =====================================
1330 section "447. LOOP CONTROL VARIATIONS"
1331 # =====================================
1332
1333 # break with value
1334 result=$("$RUSH_BIN" -c 'for i in 1 2 3; do for j in a b c; do [ "$j" = "b" ] && break; echo "$i$j"; done; done' 2>&1)
1335 expected=$(printf "1a\n2a\n3a")
1336 if [ "$result" = "$expected" ]; then
1337 pass "break in inner loop"
1338 else
1339 fail "break in inner loop"
1340 fi
1341
1342 # continue in while
1343 result=$("$RUSH_BIN" -c 'i=0; while [ $i -lt 5 ]; do i=$((i+1)); [ $i -eq 3 ] && continue; echo $i; done' 2>&1)
1344 if echo "$result" | grep -q "1" && ! echo "$result" | grep -q "^3$"; then
1345 pass "continue in while loop"
1346 else
1347 fail "continue in while loop"
1348 fi
1349
1350 # nested break 2
1351 result=$("$RUSH_BIN" -c 'for i in 1 2 3; do for j in a b c; do [ "$i" = "2" ] && [ "$j" = "b" ] && break 2; echo "$i$j"; done; done; echo done' 2>&1)
1352 if echo "$result" | grep -q "done" && echo "$result" | grep -q "1a"; then
1353 pass "break 2 exits both loops"
1354 else
1355 fail "break 2 exits both loops"
1356 fi
1357
1358 # =====================================
1359 section "448. CASE PATTERN VARIATIONS"
1360 # =====================================
1361
1362 # case with question mark
1363 result=$("$RUSH_BIN" -c 'x=ab; case $x in ??) echo two;; esac' 2>&1)
1364 if [ "$result" = "two" ]; then
1365 pass "case question mark pattern"
1366 else
1367 fail "case question mark pattern" "two" "$result"
1368 fi
1369
1370 # case with bracket negation
1371 result=$("$RUSH_BIN" -c 'x=x; case $x in [!0-9]) echo letter;; esac' 2>&1)
1372 if [ "$result" = "letter" ]; then
1373 pass "case bracket negation"
1374 else
1375 fail "case bracket negation" "letter" "$result"
1376 fi
1377
1378 # case empty string
1379 result=$("$RUSH_BIN" -c 'x=""; case "$x" in "") echo empty;; *) echo other;; esac' 2>&1)
1380 if [ "$result" = "empty" ]; then
1381 pass "case empty string"
1382 else
1383 fail "case empty string" "empty" "$result"
1384 fi
1385
1386 # case with space
1387 result=$("$RUSH_BIN" -c 'x="a b"; case "$x" in "a b") echo space;; esac' 2>&1)
1388 if [ "$result" = "space" ]; then
1389 pass "case with embedded space"
1390 else
1391 fail "case with embedded space" "space" "$result"
1392 fi
1393
1394 # =====================================
1395 section "449. CONDITIONAL EXPRESSION VARIATIONS"
1396 # =====================================
1397
1398 # if with compound command
1399 result=$("$RUSH_BIN" -c 'if { true; true; }; then echo yes; fi' 2>&1)
1400 if [ "$result" = "yes" ]; then
1401 pass "if with brace group"
1402 else
1403 fail "if with brace group" "yes" "$result"
1404 fi
1405
1406 # if with subshell
1407 result=$("$RUSH_BIN" -c 'if (true); then echo yes; fi' 2>&1)
1408 if [ "$result" = "yes" ]; then
1409 pass "if with subshell"
1410 else
1411 fail "if with subshell" "yes" "$result"
1412 fi
1413
1414 # if with pipeline
1415 result=$("$RUSH_BIN" -c 'if echo test | grep -q test; then echo found; fi' 2>&1)
1416 if [ "$result" = "found" ]; then
1417 pass "if with pipeline condition"
1418 else
1419 fail "if with pipeline condition" "found" "$result"
1420 fi
1421
1422 # =====================================
1423 section "450. FUNCTION CONTROL FLOW"
1424 # =====================================
1425
1426 # function with early return
1427 result=$("$RUSH_BIN" -c 'f() { [ $1 -lt 0 ] && return 1; echo positive; return 0; }; f 5; echo $?' 2>&1)
1428 expected=$(printf "positive\n0")
1429 if [ "$result" = "$expected" ]; then
1430 pass "function early return on condition"
1431 else
1432 fail "function early return on condition"
1433 fi
1434
1435 # function with loop
1436 result=$("$RUSH_BIN" -c 'sum() { s=0; for n in "$@"; do s=$((s + n)); done; echo $s; }; sum 1 2 3 4 5' 2>&1)
1437 if [ "$result" = "15" ]; then
1438 pass "function with loop"
1439 else
1440 fail "function with loop" "15" "$result"
1441 fi
1442
1443 # function calling another function
1444 result=$("$RUSH_BIN" -c 'double() { echo $(($1 * 2)); }; triple() { echo $(($1 * 3)); }; echo $(double 5) $(triple 5)' 2>&1)
1445 if [ "$result" = "10 15" ]; then
1446 pass "function calling function"
1447 else
1448 fail "function calling function" "10 15" "$result"
1449 fi
1450
1451 # =====================================
1452 # Summary
1453 # =====================================
1454 printf "\n"
1455 printf "${BLUE}==========================================\n"
1456 printf "POSIX Control Flow and Grouping Summary\n"
1457 printf "==========================================${NC}\n"
1458 printf "Passed: ${GREEN}%d${NC}\n" "$PASSED"
1459 printf "Failed: ${RED}%d${NC}\n" "$FAILED"
1460 printf "Skipped: ${YELLOW}%d${NC}\n" "$SKIPPED"
1461 printf "Total: %d\n" "$((PASSED + FAILED + SKIPPED))"
1462
1463 if [ -n "$FAILED_TESTS_LIST" ]; then
1464 printf "\n${RED}Failed tests:${NC}\n"
1465 printf "%b" "$FAILED_TESTS_LIST"
1466 fi
1467
1468 if [ "$FAILED" -gt 0 ]; then
1469 exit 1
1470 fi
1471 exit 0