| 1 | #!/bin/sh |
| 2 | # ===================================== |
| 3 | # POSIX Compliance Shell Options and Read Builtin Test Suite for fortsh |
| 4 | # ===================================== |
| 5 | # Tests set options and read builtin per IEEE Std 1003.1-2017 |
| 6 | # Section: Shell Command Language - set, read |
| 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-options]" |
| 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 | FORTSH_BIN="${FORTSH_BIN:-$SCRIPT_DIR/../bin/fortsh}" |
| 28 | BASH_REF="${BASH_REF:-bash}" |
| 29 | |
| 30 | # Check if fortsh exists |
| 31 | if [ ! -x "$FORTSH_BIN" ]; then |
| 32 | printf "${RED}ERROR${NC}: fortsh binary not found at $FORTSH_BIN\n" |
| 33 | printf "Please run 'make' first or set FORTSH_BIN environment variable\n" |
| 34 | exit 1 |
| 35 | fi |
| 36 | |
| 37 | # Test result trackers |
| 38 | pass() { |
| 39 | TEST_NUM=$((TEST_NUM + 1)) |
| 40 | printf "${GREEN}✓ PASS${NC} ${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}: %s\n" "$1" |
| 41 | PASSED=$((PASSED + 1)) |
| 42 | } |
| 43 | |
| 44 | fail() { |
| 45 | TEST_NUM=$((TEST_NUM + 1)) |
| 46 | TEST_ID="${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}" |
| 47 | printf "${RED}✗ FAIL${NC} ${TEST_ID}: %s\n" "$1" |
| 48 | FAILED_TESTS_LIST="${FAILED_TESTS_LIST} ${TEST_ID}: $1\n" |
| 49 | if [ -n "$2" ]; then |
| 50 | printf " expected: %s\n" "$2" |
| 51 | fi |
| 52 | if [ -n "$3" ]; then |
| 53 | printf " got: %s\n" "$3" |
| 54 | fi |
| 55 | FAILED=$((FAILED + 1)) |
| 56 | } |
| 57 | |
| 58 | skip() { |
| 59 | TEST_NUM=$((TEST_NUM + 1)) |
| 60 | printf "${YELLOW}⊘ SKIP${NC} ${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}: %s - %s\n" "$1" "$2" |
| 61 | SKIPPED=$((SKIPPED + 1)) |
| 62 | } |
| 63 | |
| 64 | section() { |
| 65 | CURRENT_SECTION=$(echo "$1" | grep -oE '^[0-9]+' || echo "0") |
| 66 | TEST_NUM=0 |
| 67 | printf "\n" |
| 68 | printf "${BLUE}==========================================\n" |
| 69 | printf "%s\n" "$1" |
| 70 | printf "==========================================${NC}\n" |
| 71 | } |
| 72 | |
| 73 | # ===================================== |
| 74 | section "381. SET -e (ERREXIT)" |
| 75 | # ===================================== |
| 76 | |
| 77 | result=$("$FORTSH_BIN" -c 'set -e; true; echo reached' 2>&1) |
| 78 | if [ "$result" = "reached" ]; then |
| 79 | pass "set -e allows true command" |
| 80 | else |
| 81 | fail "set -e allows true command" "reached" "$result" |
| 82 | fi |
| 83 | |
| 84 | result=$("$FORTSH_BIN" -c 'set -e; false; echo "should not reach"' 2>&1) |
| 85 | if [ -z "$result" ] || ! echo "$result" | grep -q "should not reach"; then |
| 86 | pass "set -e exits on false" |
| 87 | else |
| 88 | fail "set -e exits on false" "(no output)" "$result" |
| 89 | fi |
| 90 | |
| 91 | result=$("$FORTSH_BIN" -c 'set -e; if false; then echo no; fi; echo reached' 2>&1) |
| 92 | if [ "$result" = "reached" ]; then |
| 93 | pass "set -e ignores false in if condition" |
| 94 | else |
| 95 | fail "set -e ignores false in if condition" "reached" "$result" |
| 96 | fi |
| 97 | |
| 98 | result=$("$FORTSH_BIN" -c 'set -e; false || echo fallback' 2>&1) |
| 99 | if [ "$result" = "fallback" ]; then |
| 100 | pass "set -e ignores false with || continuation" |
| 101 | else |
| 102 | fail "set -e ignores false with || continuation" "fallback" "$result" |
| 103 | fi |
| 104 | |
| 105 | result=$("$FORTSH_BIN" -c 'set -e; ! false; echo reached' 2>&1) |
| 106 | if [ "$result" = "reached" ]; then |
| 107 | pass "set -e ignores negated false" |
| 108 | else |
| 109 | fail "set -e ignores negated false" "reached" "$result" |
| 110 | fi |
| 111 | |
| 112 | # ===================================== |
| 113 | section "382. SET -u (NOUNSET)" |
| 114 | # ===================================== |
| 115 | |
| 116 | result=$("$FORTSH_BIN" -c 'set -u; x=hello; echo $x' 2>&1) |
| 117 | if [ "$result" = "hello" ]; then |
| 118 | pass "set -u allows set variable" |
| 119 | else |
| 120 | fail "set -u allows set variable" "hello" "$result" |
| 121 | fi |
| 122 | |
| 123 | result=$("$FORTSH_BIN" -c 'set -u; echo $UNSET_VAR_12345' 2>&1) |
| 124 | exit_code=$? |
| 125 | if echo "$result" | grep -qi "unbound\|unset\|error" || [ $exit_code -ne 0 ]; then |
| 126 | pass "set -u errors on unset variable" |
| 127 | else |
| 128 | fail "set -u errors on unset variable" "error message" "$result" |
| 129 | fi |
| 130 | |
| 131 | result=$("$FORTSH_BIN" -c 'set -u; echo ${UNSET_VAR_12345:-default}' 2>&1) |
| 132 | if [ "$result" = "default" ]; then |
| 133 | pass "set -u allows default expansion" |
| 134 | else |
| 135 | fail "set -u allows default expansion" "default" "$result" |
| 136 | fi |
| 137 | |
| 138 | # ===================================== |
| 139 | section "383. SET -f (NOGLOB)" |
| 140 | # ===================================== |
| 141 | |
| 142 | result=$("$FORTSH_BIN" -c 'set -f; echo *' 2>&1) |
| 143 | if [ "$result" = "*" ]; then |
| 144 | pass "set -f disables globbing" |
| 145 | else |
| 146 | fail "set -f disables globbing" "*" "$result" |
| 147 | fi |
| 148 | |
| 149 | result=$("$FORTSH_BIN" -c 'set -f; echo [a-z]*' 2>&1) |
| 150 | if [ "$result" = "[a-z]*" ]; then |
| 151 | pass "set -f disables bracket patterns" |
| 152 | else |
| 153 | fail "set -f disables bracket patterns" "[a-z]*" "$result" |
| 154 | fi |
| 155 | |
| 156 | result=$("$FORTSH_BIN" -c 'set -f; set +f; cd /tmp && echo [a-z]* | head -c 20' 2>&1) |
| 157 | if [ "$result" != "[a-z]*" ] && [ -n "$result" ]; then |
| 158 | pass "set +f re-enables globbing" |
| 159 | else |
| 160 | fail "set +f re-enables globbing" "(expanded)" "$result" |
| 161 | fi |
| 162 | |
| 163 | # ===================================== |
| 164 | section "384. SET -n (NOEXEC)" |
| 165 | # ===================================== |
| 166 | |
| 167 | result=$("$FORTSH_BIN" -c 'set -n; echo hello' 2>&1) |
| 168 | if [ -z "$result" ]; then |
| 169 | pass "set -n prevents execution" |
| 170 | else |
| 171 | fail "set -n prevents execution" "(empty)" "$result" |
| 172 | fi |
| 173 | |
| 174 | # ===================================== |
| 175 | section "385. SET -x (XTRACE)" |
| 176 | # ===================================== |
| 177 | |
| 178 | result=$("$FORTSH_BIN" -c 'set -x; echo hello' 2>&1) |
| 179 | if echo "$result" | grep -q "echo hello\|+ echo"; then |
| 180 | pass "set -x shows trace output" |
| 181 | else |
| 182 | fail "set -x shows trace output" "trace line" "$result" |
| 183 | fi |
| 184 | |
| 185 | result=$("$FORTSH_BIN" -c 'set -x; x=5; echo $x' 2>&1) |
| 186 | if echo "$result" | grep -qE "x=5|echo 5|\+ echo"; then |
| 187 | pass "set -x traces variable assignment" |
| 188 | else |
| 189 | fail "set -x traces variable assignment" "trace lines" "$result" |
| 190 | fi |
| 191 | |
| 192 | # ===================================== |
| 193 | section "386. SET -v (VERBOSE)" |
| 194 | # ===================================== |
| 195 | |
| 196 | # Note: set -v only echoes lines as they're READ, not from -c argument |
| 197 | # Test that -v option is accepted and runs without error |
| 198 | "$FORTSH_BIN" -c 'set -v; echo hello' >/dev/null 2>&1 |
| 199 | fortsh_exit=$? |
| 200 | "$BASH_REF" -c 'set -v; echo hello' >/dev/null 2>&1 |
| 201 | bash_exit=$? |
| 202 | if [ "$fortsh_exit" -eq "$bash_exit" ]; then |
| 203 | pass "set -v runs without error" |
| 204 | else |
| 205 | fail "set -v runs without error" "exit=$bash_exit" "exit=$fortsh_exit" |
| 206 | fi |
| 207 | |
| 208 | # ===================================== |
| 209 | section "387. SET -C (NOCLOBBER)" |
| 210 | # ===================================== |
| 211 | |
| 212 | TEST_DIR="/tmp/fortsh_options_$$" |
| 213 | mkdir -p "$TEST_DIR" |
| 214 | |
| 215 | result=$("$FORTSH_BIN" -c 'set -C; echo test > '"$TEST_DIR"'/clobber1; echo test2 > '"$TEST_DIR"'/clobber1' 2>&1) |
| 216 | if echo "$result" | grep -qi "exist\|cannot\|error\|clobber"; then |
| 217 | pass "set -C prevents clobbering existing file" |
| 218 | else |
| 219 | # Check if file still has original content |
| 220 | if [ -f "$TEST_DIR/clobber1" ]; then |
| 221 | content=$(cat "$TEST_DIR/clobber1") |
| 222 | if [ "$content" = "test" ]; then |
| 223 | pass "set -C prevents clobbering existing file" |
| 224 | else |
| 225 | fail "set -C prevents clobbering existing file" "error or original content" "$result" |
| 226 | fi |
| 227 | else |
| 228 | fail "set -C prevents clobbering existing file" "error message" "$result" |
| 229 | fi |
| 230 | fi |
| 231 | |
| 232 | rm -rf "$TEST_DIR" |
| 233 | |
| 234 | # ===================================== |
| 235 | section "388. SET -o OPTIONS" |
| 236 | # ===================================== |
| 237 | |
| 238 | result=$("$FORTSH_BIN" -c 'set -o errexit; true; echo ok' 2>&1) |
| 239 | if [ "$result" = "ok" ]; then |
| 240 | pass "set -o errexit (same as -e)" |
| 241 | else |
| 242 | fail "set -o errexit (same as -e)" "ok" "$result" |
| 243 | fi |
| 244 | |
| 245 | result=$("$FORTSH_BIN" -c 'set -o noglob; echo *' 2>&1) |
| 246 | if [ "$result" = "*" ]; then |
| 247 | pass "set -o noglob (same as -f)" |
| 248 | else |
| 249 | fail "set -o noglob (same as -f)" "*" "$result" |
| 250 | fi |
| 251 | |
| 252 | result=$("$FORTSH_BIN" -c 'set -o nounset; x=val; echo $x' 2>&1) |
| 253 | if [ "$result" = "val" ]; then |
| 254 | pass "set -o nounset (same as -u)" |
| 255 | else |
| 256 | fail "set -o nounset (same as -u)" "val" "$result" |
| 257 | fi |
| 258 | |
| 259 | # ===================================== |
| 260 | section "389. SET -- POSITIONAL PARAMETERS" |
| 261 | # ===================================== |
| 262 | |
| 263 | result=$("$FORTSH_BIN" -c 'set -- a b c; echo $1 $2 $3' 2>&1) |
| 264 | if [ "$result" = "a b c" ]; then |
| 265 | pass "set -- sets positional parameters" |
| 266 | else |
| 267 | fail "set -- sets positional parameters" "a b c" "$result" |
| 268 | fi |
| 269 | |
| 270 | result=$("$FORTSH_BIN" -c 'set -- a b c d e; echo $#' 2>&1) |
| 271 | if [ "$result" = "5" ]; then |
| 272 | pass "set -- updates \$#" |
| 273 | else |
| 274 | fail "set -- updates \$#" "5" "$result" |
| 275 | fi |
| 276 | |
| 277 | result=$("$FORTSH_BIN" -c 'set -- a b c; set --; echo $#' 2>&1) |
| 278 | if [ "$result" = "0" ]; then |
| 279 | pass "set -- clears positional parameters" |
| 280 | else |
| 281 | fail "set -- clears positional parameters" "0" "$result" |
| 282 | fi |
| 283 | |
| 284 | result=$("$FORTSH_BIN" -c 'set -- "a b" c; echo $1' 2>&1) |
| 285 | if [ "$result" = "a b" ]; then |
| 286 | pass "set -- preserves quoted args" |
| 287 | else |
| 288 | fail "set -- preserves quoted args" "a b" "$result" |
| 289 | fi |
| 290 | |
| 291 | # ===================================== |
| 292 | section "390. READ BUILTIN BASIC" |
| 293 | # ===================================== |
| 294 | |
| 295 | result=$("$FORTSH_BIN" -c 'echo "hello" | { read x; echo $x; }' 2>&1) |
| 296 | if [ "$result" = "hello" ]; then |
| 297 | pass "read basic input" |
| 298 | else |
| 299 | fail "read basic input" "hello" "$result" |
| 300 | fi |
| 301 | |
| 302 | result=$("$FORTSH_BIN" -c 'echo "one two three" | { read a b c; echo "$a|$b|$c"; }' 2>&1) |
| 303 | if [ "$result" = "one|two|three" ]; then |
| 304 | pass "read multiple variables" |
| 305 | else |
| 306 | fail "read multiple variables" "one|two|three" "$result" |
| 307 | fi |
| 308 | |
| 309 | result=$("$FORTSH_BIN" -c 'echo "one two three four" | { read a b; echo "$a|$b"; }' 2>&1) |
| 310 | if [ "$result" = "one|two three four" ]; then |
| 311 | pass "read remaining into last variable" |
| 312 | else |
| 313 | fail "read remaining into last variable" "one|two three four" "$result" |
| 314 | fi |
| 315 | |
| 316 | # ===================================== |
| 317 | section "391. READ WITH IFS" |
| 318 | # ===================================== |
| 319 | |
| 320 | result=$("$FORTSH_BIN" -c 'echo "a:b:c" | { IFS=: read x y z; echo "$x|$y|$z"; }' 2>&1) |
| 321 | if [ "$result" = "a|b|c" ]; then |
| 322 | pass "read with custom IFS" |
| 323 | else |
| 324 | fail "read with custom IFS" "a|b|c" "$result" |
| 325 | fi |
| 326 | |
| 327 | result=$("$FORTSH_BIN" -c 'echo "a::c" | { IFS=: read x y z; echo "$x|$y|$z"; }' 2>&1) |
| 328 | if [ "$result" = "a||c" ]; then |
| 329 | pass "read with empty field" |
| 330 | else |
| 331 | fail "read with empty field" "a||c" "$result" |
| 332 | fi |
| 333 | |
| 334 | result=$("$FORTSH_BIN" -c 'echo " a b " | { read x y; echo ">$x|$y<"; }' 2>&1) |
| 335 | if [ "$result" = ">a|b<" ]; then |
| 336 | pass "read strips leading/trailing whitespace" |
| 337 | else |
| 338 | fail "read strips leading/trailing whitespace" ">a|b<" "$result" |
| 339 | fi |
| 340 | |
| 341 | # ===================================== |
| 342 | section "392. READ -r (RAW MODE)" |
| 343 | # ===================================== |
| 344 | |
| 345 | result=$("$FORTSH_BIN" -c 'echo "hello\\nworld" | { read -r x; echo "$x"; }' 2>&1) |
| 346 | if [ "$result" = 'hello\nworld' ]; then |
| 347 | pass "read -r preserves backslashes" |
| 348 | else |
| 349 | fail "read -r preserves backslashes" 'hello\nworld' "$result" |
| 350 | fi |
| 351 | |
| 352 | # POSIX: read without -r joins lines ending with backslash (line continuation) |
| 353 | # printf with single quotes produces: line<backslash><newline>continued<newline> |
| 354 | # read joins: line + continued = linecontinued |
| 355 | result=$("$FORTSH_BIN" -c 'printf '"'"'line\\\ncontinued\n'"'"' | { read x; echo "$x"; }' 2>&1) |
| 356 | if [ "$result" = "linecontinued" ]; then |
| 357 | pass "read without -r joins continued lines" |
| 358 | else |
| 359 | fail "read without -r joins continued lines" "linecontinued" "$result" |
| 360 | fi |
| 361 | |
| 362 | # ===================================== |
| 363 | section "393. READ EXIT STATUS" |
| 364 | # ===================================== |
| 365 | |
| 366 | result=$("$FORTSH_BIN" -c 'echo "test" | { read x; echo $?; }' 2>&1) |
| 367 | if [ "$result" = "0" ]; then |
| 368 | pass "read returns 0 on success" |
| 369 | else |
| 370 | fail "read returns 0 on success" "0" "$result" |
| 371 | fi |
| 372 | |
| 373 | result=$("$FORTSH_BIN" -c 'echo -n "" | { read x; echo $?; }' 2>&1) |
| 374 | if [ "$result" = "1" ]; then |
| 375 | pass "read returns non-zero on EOF" |
| 376 | else |
| 377 | fail "read returns non-zero on EOF" "1" "$result" |
| 378 | fi |
| 379 | |
| 380 | # ===================================== |
| 381 | section "394. SHIFT BUILTIN" |
| 382 | # ===================================== |
| 383 | |
| 384 | result=$("$FORTSH_BIN" -c 'set -- a b c d; shift; echo $1 $2 $3' 2>&1) |
| 385 | if [ "$result" = "b c d" ]; then |
| 386 | pass "shift removes first parameter" |
| 387 | else |
| 388 | fail "shift removes first parameter" "b c d" "$result" |
| 389 | fi |
| 390 | |
| 391 | result=$("$FORTSH_BIN" -c 'set -- a b c d e; shift 2; echo $1 $2 $3' 2>&1) |
| 392 | if [ "$result" = "c d e" ]; then |
| 393 | pass "shift n removes n parameters" |
| 394 | else |
| 395 | fail "shift n removes n parameters" "c d e" "$result" |
| 396 | fi |
| 397 | |
| 398 | result=$("$FORTSH_BIN" -c 'set -- a b c; shift; echo $#' 2>&1) |
| 399 | if [ "$result" = "2" ]; then |
| 400 | pass "shift updates \$#" |
| 401 | else |
| 402 | fail "shift updates \$#" "2" "$result" |
| 403 | fi |
| 404 | |
| 405 | result=$("$FORTSH_BIN" -c 'set -- a; shift 5 2>/dev/null; echo $?' 2>&1) |
| 406 | if [ "$result" = "1" ]; then |
| 407 | pass "shift beyond count returns error" |
| 408 | else |
| 409 | fail "shift beyond count returns error" "1" "$result" |
| 410 | fi |
| 411 | |
| 412 | # ===================================== |
| 413 | section "395. SPECIAL PARAMETERS" |
| 414 | # ===================================== |
| 415 | |
| 416 | result=$("$FORTSH_BIN" -c 'set -- a b c; echo "$@"' 2>&1) |
| 417 | if [ "$result" = "a b c" ]; then |
| 418 | pass "\$@ expands all positional parameters" |
| 419 | else |
| 420 | fail "\$@ expands all positional parameters" "a b c" "$result" |
| 421 | fi |
| 422 | |
| 423 | result=$("$FORTSH_BIN" -c 'set -- a b c; echo "$*"' 2>&1) |
| 424 | if [ "$result" = "a b c" ]; then |
| 425 | pass "\$* expands all positional parameters" |
| 426 | else |
| 427 | fail "\$* expands all positional parameters" "a b c" "$result" |
| 428 | fi |
| 429 | |
| 430 | result=$("$FORTSH_BIN" -c 'set -- a b c; for x in "$@"; do echo "[$x]"; done' 2>&1) |
| 431 | expected=$(printf "[a]\n[b]\n[c]") |
| 432 | if [ "$result" = "$expected" ]; then |
| 433 | pass "\"\$@\" preserves separate arguments" |
| 434 | else |
| 435 | fail "\"\$@\" preserves separate arguments" "$expected" "$result" |
| 436 | fi |
| 437 | |
| 438 | result=$("$FORTSH_BIN" -c 'set -- "a b" c; for x in "$@"; do echo "[$x]"; done' 2>&1) |
| 439 | expected=$(printf "[a b]\n[c]") |
| 440 | if [ "$result" = "$expected" ]; then |
| 441 | pass "\"\$@\" preserves quoted arguments" |
| 442 | else |
| 443 | fail "\"\$@\" preserves quoted arguments" "$expected" "$result" |
| 444 | fi |
| 445 | |
| 446 | result=$("$FORTSH_BIN" -c 'echo $$' 2>&1) |
| 447 | if echo "$result" | grep -qE '^[0-9]+$' && [ "$result" -gt 0 ]; then |
| 448 | pass "\$\$ returns PID" |
| 449 | else |
| 450 | fail "\$\$ returns PID" "numeric PID" "$result" |
| 451 | fi |
| 452 | |
| 453 | result=$("$FORTSH_BIN" -c 'echo $?' 2>&1) |
| 454 | if [ "$result" = "0" ]; then |
| 455 | pass "\$? returns exit status" |
| 456 | else |
| 457 | fail "\$? returns exit status" "0" "$result" |
| 458 | fi |
| 459 | |
| 460 | # ===================================== |
| 461 | section "396. IFS WORD SPLITTING" |
| 462 | # ===================================== |
| 463 | |
| 464 | result=$("$FORTSH_BIN" -c 'x="a:b:c"; IFS=:; for w in $x; do echo "[$w]"; done' 2>&1) |
| 465 | expected=$(printf "[a]\n[b]\n[c]") |
| 466 | if [ "$result" = "$expected" ]; then |
| 467 | pass "IFS changes word splitting" |
| 468 | else |
| 469 | fail "IFS changes word splitting" "$expected" "$result" |
| 470 | fi |
| 471 | |
| 472 | result=$("$FORTSH_BIN" -c 'x="a::c"; IFS=:; set -- $x; echo $#' 2>&1) |
| 473 | if [ "$result" = "3" ]; then |
| 474 | pass "IFS empty fields create arguments" |
| 475 | else |
| 476 | fail "IFS empty fields create arguments" "3" "$result" |
| 477 | fi |
| 478 | |
| 479 | result=$("$FORTSH_BIN" -c 'IFS=:; x="a:b:c"; echo $x' 2>&1) |
| 480 | if [ "$result" = "a b c" ]; then |
| 481 | pass "IFS affects echo output" |
| 482 | else |
| 483 | fail "IFS affects echo output" "a b c" "$result" |
| 484 | fi |
| 485 | |
| 486 | result=$("$FORTSH_BIN" -c 'IFS=""; x="a b c"; for w in $x; do echo "[$w]"; done' 2>&1) |
| 487 | if [ "$result" = "[a b c]" ]; then |
| 488 | pass "Empty IFS prevents splitting" |
| 489 | else |
| 490 | fail "Empty IFS prevents splitting" "[a b c]" "$result" |
| 491 | fi |
| 492 | |
| 493 | result=$("$FORTSH_BIN" -c 'unset IFS; x="a b"; echo $x' 2>&1) |
| 494 | if [ "$result" = "a b" ]; then |
| 495 | pass "unset IFS uses default" |
| 496 | else |
| 497 | fail "unset IFS uses default" "a b" "$result" |
| 498 | fi |
| 499 | |
| 500 | result=$("$FORTSH_BIN" -c 'IFS=",;"; x="a,b;c"; set -- $x; echo $1 $2 $3' 2>&1) |
| 501 | if [ "$result" = "a b c" ]; then |
| 502 | pass "IFS with multiple characters" |
| 503 | else |
| 504 | fail "IFS with multiple characters" "a b c" "$result" |
| 505 | fi |
| 506 | |
| 507 | # ===================================== |
| 508 | section "397. ALIAS BUILTIN" |
| 509 | # ===================================== |
| 510 | |
| 511 | result=$("$FORTSH_BIN" -c 'alias ll="ls -la"; alias ll' 2>&1) |
| 512 | if echo "$result" | grep -q "ls -la\|ll="; then |
| 513 | pass "alias defines and shows alias" |
| 514 | else |
| 515 | fail "alias defines and shows alias" "ls -la" "$result" |
| 516 | fi |
| 517 | |
| 518 | # Note: Aliases defined in -c mode aren't expanded in the same command line |
| 519 | # This matches bash behavior - aliases are expanded at parse time |
| 520 | result=$("$FORTSH_BIN" -c 'alias greeting="echo hello"; alias greeting' 2>&1) |
| 521 | if echo "$result" | grep -q "echo hello"; then |
| 522 | pass "alias defines correctly (matches bash)" |
| 523 | else |
| 524 | fail "alias defines correctly (matches bash)" "echo hello" "$result" |
| 525 | fi |
| 526 | |
| 527 | result=$("$FORTSH_BIN" -c 'alias x="echo a"; alias y="echo b"; alias | wc -l' 2>&1) |
| 528 | if [ "$result" -ge 2 ] 2>/dev/null; then |
| 529 | pass "alias lists all aliases" |
| 530 | else |
| 531 | fail "alias lists all aliases" "at least 2" "$result" |
| 532 | fi |
| 533 | |
| 534 | result=$("$FORTSH_BIN" -c 'alias greet="echo hi"; unalias greet; greet 2>/dev/null; echo $?' 2>&1) |
| 535 | if echo "$result" | grep -qE "127|1"; then |
| 536 | pass "unalias removes alias" |
| 537 | else |
| 538 | fail "unalias removes alias" "non-zero exit" "$result" |
| 539 | fi |
| 540 | |
| 541 | # Note: Test redefinition by checking alias output (execution won't work in same line) |
| 542 | result=$("$FORTSH_BIN" -c 'alias foo="echo one"; alias foo="echo two"; alias foo' 2>&1) |
| 543 | if echo "$result" | grep -q "echo two"; then |
| 544 | pass "alias can be redefined" |
| 545 | else |
| 546 | fail "alias can be redefined" "echo two" "$result" |
| 547 | fi |
| 548 | |
| 549 | # ===================================== |
| 550 | section "398. EXPORT BUILTIN" |
| 551 | # ===================================== |
| 552 | |
| 553 | result=$("$FORTSH_BIN" -c 'export TESTVAR=hello; echo $TESTVAR' 2>&1) |
| 554 | if [ "$result" = "hello" ]; then |
| 555 | pass "export sets and exports variable" |
| 556 | else |
| 557 | fail "export sets and exports variable" "hello" "$result" |
| 558 | fi |
| 559 | |
| 560 | result=$("$FORTSH_BIN" -c 'MYVAR=world; export MYVAR; echo $MYVAR' 2>&1) |
| 561 | if [ "$result" = "world" ]; then |
| 562 | pass "export existing variable" |
| 563 | else |
| 564 | fail "export existing variable" "world" "$result" |
| 565 | fi |
| 566 | |
| 567 | result=$("$FORTSH_BIN" -c 'export X=1 Y=2 Z=3; echo $X $Y $Z' 2>&1) |
| 568 | if [ "$result" = "1 2 3" ]; then |
| 569 | pass "export multiple variables" |
| 570 | else |
| 571 | fail "export multiple variables" "1 2 3" "$result" |
| 572 | fi |
| 573 | |
| 574 | result=$("$FORTSH_BIN" -c 'export SUBTEST=value; sh -c "echo \$SUBTEST"' 2>&1) |
| 575 | if [ "$result" = "value" ]; then |
| 576 | pass "exported var visible in subshell" |
| 577 | else |
| 578 | fail "exported var visible in subshell" "value" "$result" |
| 579 | fi |
| 580 | |
| 581 | # ===================================== |
| 582 | section "399. READONLY BUILTIN" |
| 583 | # ===================================== |
| 584 | |
| 585 | result=$("$FORTSH_BIN" -c 'readonly CONST=42; echo $CONST' 2>&1) |
| 586 | if [ "$result" = "42" ]; then |
| 587 | pass "readonly sets variable" |
| 588 | else |
| 589 | fail "readonly sets variable" "42" "$result" |
| 590 | fi |
| 591 | |
| 592 | result=$("$FORTSH_BIN" -c 'readonly RO=1; RO=2 2>&1; echo $RO' 2>&1) |
| 593 | if echo "$result" | grep -q "1"; then |
| 594 | pass "readonly prevents modification" |
| 595 | else |
| 596 | fail "readonly prevents modification" "1" "$result" |
| 597 | fi |
| 598 | |
| 599 | result=$("$FORTSH_BIN" -c 'readonly A=1 B=2; echo $A $B' 2>&1) |
| 600 | if [ "$result" = "1 2" ]; then |
| 601 | pass "readonly multiple variables" |
| 602 | else |
| 603 | fail "readonly multiple variables" "1 2" "$result" |
| 604 | fi |
| 605 | |
| 606 | # ===================================== |
| 607 | section "400. UNSET BUILTIN" |
| 608 | # ===================================== |
| 609 | |
| 610 | result=$("$FORTSH_BIN" -c 'x=hello; unset x; echo "${x:-unset}"' 2>&1) |
| 611 | if [ "$result" = "unset" ]; then |
| 612 | pass "unset removes variable" |
| 613 | else |
| 614 | fail "unset removes variable" "unset" "$result" |
| 615 | fi |
| 616 | |
| 617 | result=$("$FORTSH_BIN" -c 'x=1; y=2; unset x y; echo "${x:-a} ${y:-b}"' 2>&1) |
| 618 | if [ "$result" = "a b" ]; then |
| 619 | pass "unset multiple variables" |
| 620 | else |
| 621 | fail "unset multiple variables" "a b" "$result" |
| 622 | fi |
| 623 | |
| 624 | result=$("$FORTSH_BIN" -c 'f() { echo hi; }; unset -f f; type f 2>/dev/null; echo $?' 2>&1) |
| 625 | if echo "$result" | grep -qE "1|127"; then |
| 626 | pass "unset -f removes function" |
| 627 | else |
| 628 | fail "unset -f removes function" "non-zero exit" "$result" |
| 629 | fi |
| 630 | |
| 631 | # ===================================== |
| 632 | section "401. GETOPTS BUILTIN" |
| 633 | # ===================================== |
| 634 | |
| 635 | # Basic option parsing |
| 636 | result=$("$FORTSH_BIN" -c 'set -- -a; getopts "ab" opt; echo $opt' 2>&1) |
| 637 | if [ "$result" = "a" ]; then |
| 638 | pass "getopts parses simple option" |
| 639 | else |
| 640 | fail "getopts parses simple option" "a" "$result" |
| 641 | fi |
| 642 | |
| 643 | # Option with argument |
| 644 | result=$("$FORTSH_BIN" -c 'set -- -a value; getopts "a:" opt; echo "$OPTARG"' 2>&1) |
| 645 | if [ "$result" = "value" ]; then |
| 646 | pass "getopts sets OPTARG" |
| 647 | else |
| 648 | fail "getopts sets OPTARG" "value" "$result" |
| 649 | fi |
| 650 | |
| 651 | # OPTIND increment |
| 652 | result=$("$FORTSH_BIN" -c 'set -- -a -b; getopts "ab" opt; getopts "ab" opt; echo $OPTIND' 2>&1) |
| 653 | if [ "$result" = "3" ]; then |
| 654 | pass "getopts increments OPTIND" |
| 655 | else |
| 656 | fail "getopts increments OPTIND" "3" "$result" |
| 657 | fi |
| 658 | |
| 659 | # Multiple options in loop |
| 660 | result=$("$FORTSH_BIN" -c 'set -- -a -b -c; while getopts "abc" opt; do echo -n $opt; done' 2>&1) |
| 661 | if [ "$result" = "abc" ]; then |
| 662 | pass "getopts in while loop" |
| 663 | else |
| 664 | fail "getopts in while loop" "abc" "$result" |
| 665 | fi |
| 666 | |
| 667 | # Invalid option handling |
| 668 | result=$("$FORTSH_BIN" -c 'set -- -z; getopts "ab" opt 2>/dev/null; echo $opt' 2>&1) |
| 669 | if [ "$result" = "?" ]; then |
| 670 | pass "getopts returns ? for invalid option" |
| 671 | else |
| 672 | fail "getopts returns ? for invalid option" "?" "$result" |
| 673 | fi |
| 674 | |
| 675 | # Silent mode with leading colon |
| 676 | result=$("$FORTSH_BIN" -c 'set -- -a; getopts ":a:b" opt; echo $opt' 2>&1) |
| 677 | if [ "$result" = ":" ] || [ "$result" = "a" ]; then |
| 678 | pass "getopts silent mode with colon" |
| 679 | else |
| 680 | fail "getopts silent mode with colon" ": or a" "$result" |
| 681 | fi |
| 682 | |
| 683 | # Reset OPTIND |
| 684 | result=$("$FORTSH_BIN" -c 'set -- -a; getopts "a" opt; OPTIND=1; getopts "a" opt; echo $opt' 2>&1) |
| 685 | if [ "$result" = "a" ]; then |
| 686 | pass "OPTIND reset allows reparse" |
| 687 | else |
| 688 | fail "OPTIND reset allows reparse" "a" "$result" |
| 689 | fi |
| 690 | |
| 691 | # ===================================== |
| 692 | section "402. TYPE BUILTIN" |
| 693 | # ===================================== |
| 694 | |
| 695 | # type finds commands |
| 696 | result=$("$FORTSH_BIN" -c 'type echo' 2>&1) |
| 697 | if echo "$result" | grep -qE "echo|builtin|/"; then |
| 698 | pass "type finds echo" |
| 699 | else |
| 700 | fail "type finds echo" "echo info" "$result" |
| 701 | fi |
| 702 | |
| 703 | # type returns error for unknown |
| 704 | result=$("$FORTSH_BIN" -c 'type nonexistent_xyz_cmd 2>/dev/null; echo $?' 2>&1) |
| 705 | if [ "$result" != "0" ]; then |
| 706 | pass "type returns error for unknown command" |
| 707 | else |
| 708 | fail "type returns error for unknown command" "non-zero" "$result" |
| 709 | fi |
| 710 | |
| 711 | # type finds functions |
| 712 | result=$("$FORTSH_BIN" -c 'myfunc() { :; }; type myfunc' 2>&1) |
| 713 | if echo "$result" | grep -qE "myfunc|function"; then |
| 714 | pass "type finds functions" |
| 715 | else |
| 716 | fail "type finds functions" "function info" "$result" |
| 717 | fi |
| 718 | |
| 719 | # type finds aliases |
| 720 | result=$("$FORTSH_BIN" -c 'alias myalias=echo; type myalias' 2>&1) |
| 721 | if echo "$result" | grep -qE "myalias|alias"; then |
| 722 | pass "type finds aliases" |
| 723 | else |
| 724 | fail "type finds aliases" "alias info" "$result" |
| 725 | fi |
| 726 | |
| 727 | # type finds builtins |
| 728 | result=$("$FORTSH_BIN" -c 'type cd' 2>&1) |
| 729 | if echo "$result" | grep -qE "cd|builtin"; then |
| 730 | pass "type identifies builtins" |
| 731 | else |
| 732 | fail "type identifies builtins" "builtin info" "$result" |
| 733 | fi |
| 734 | |
| 735 | # ===================================== |
| 736 | section "403. COMMAND BUILTIN" |
| 737 | # ===================================== |
| 738 | |
| 739 | # command -v finds commands |
| 740 | result=$("$FORTSH_BIN" -c 'command -v echo' 2>&1) |
| 741 | if [ -n "$result" ]; then |
| 742 | pass "command -v finds echo" |
| 743 | else |
| 744 | fail "command -v finds echo" "non-empty" "$result" |
| 745 | fi |
| 746 | |
| 747 | # command -v returns empty for unknown |
| 748 | result=$("$FORTSH_BIN" -c 'command -v nonexistent_xyz_cmd; echo $?' 2>&1) |
| 749 | if [ "$result" != "0" ]; then |
| 750 | pass "command -v returns error for unknown" |
| 751 | else |
| 752 | fail "command -v returns error for unknown" "non-zero exit" "$result" |
| 753 | fi |
| 754 | |
| 755 | # command bypasses functions |
| 756 | result=$("$FORTSH_BIN" -c 'echo() { printf "FUNC"; }; command echo hello' 2>&1) |
| 757 | if [ "$result" = "hello" ]; then |
| 758 | pass "command bypasses function" |
| 759 | else |
| 760 | fail "command bypasses function" "hello" "$result" |
| 761 | fi |
| 762 | |
| 763 | # command bypasses aliases |
| 764 | result=$("$FORTSH_BIN" -c 'alias echo="printf ALIAS"; command echo hello' 2>&1) |
| 765 | if [ "$result" = "hello" ]; then |
| 766 | pass "command bypasses alias" |
| 767 | else |
| 768 | fail "command bypasses alias" "hello" "$result" |
| 769 | fi |
| 770 | |
| 771 | # ===================================== |
| 772 | section "404. TRAP BUILTIN EDGE CASES" |
| 773 | # ===================================== |
| 774 | |
| 775 | # trap with empty action |
| 776 | result=$("$FORTSH_BIN" -c 'trap "" INT; trap' 2>&1) |
| 777 | if echo "$result" | grep -qE "INT|''"; then |
| 778 | pass "trap with empty action (ignore signal)" |
| 779 | else |
| 780 | fail "trap with empty action (ignore signal)" "INT trap" "$result" |
| 781 | fi |
| 782 | |
| 783 | # trap - removes trap |
| 784 | result=$("$FORTSH_BIN" -c 'trap "echo x" EXIT; trap - EXIT; trap' 2>&1) |
| 785 | if ! echo "$result" | grep -q "EXIT"; then |
| 786 | pass "trap - removes trap" |
| 787 | else |
| 788 | fail "trap - removes trap" "no EXIT" "$result" |
| 789 | fi |
| 790 | |
| 791 | # Multiple traps |
| 792 | result=$("$FORTSH_BIN" -c 'trap "echo INT" INT; trap "echo TERM" TERM; trap | wc -l' 2>&1) |
| 793 | if [ "$result" -ge 2 ] 2>/dev/null; then |
| 794 | pass "Multiple traps can be set" |
| 795 | else |
| 796 | fail "Multiple traps can be set" ">=2 lines" "$result" |
| 797 | fi |
| 798 | |
| 799 | # trap with no args lists traps |
| 800 | result=$("$FORTSH_BIN" -c 'trap "echo x" INT; trap' 2>&1) |
| 801 | if [ -n "$result" ]; then |
| 802 | pass "trap with no args lists traps" |
| 803 | else |
| 804 | fail "trap with no args lists traps" "non-empty" "$result" |
| 805 | fi |
| 806 | |
| 807 | # EXIT trap runs on exit |
| 808 | result=$("$FORTSH_BIN" -c 'trap "echo EXITING" EXIT; exit 0' 2>&1) |
| 809 | if [ "$result" = "EXITING" ]; then |
| 810 | pass "EXIT trap executes on exit" |
| 811 | else |
| 812 | fail "EXIT trap executes on exit" "EXITING" "$result" |
| 813 | fi |
| 814 | |
| 815 | # ===================================== |
| 816 | section "405. HASH BUILTIN" |
| 817 | # ===================================== |
| 818 | |
| 819 | # hash without args |
| 820 | result=$("$FORTSH_BIN" -c 'hash 2>&1; echo $?' 2>&1) |
| 821 | # hash returns 0 even if empty |
| 822 | pass "hash builtin exists" |
| 823 | |
| 824 | # hash -r clears cache |
| 825 | result=$("$FORTSH_BIN" -c 'ls >/dev/null 2>&1; hash -r; echo $?' 2>&1) |
| 826 | if [ "$result" = "0" ]; then |
| 827 | pass "hash -r clears cache" |
| 828 | else |
| 829 | fail "hash -r clears cache" "0" "$result" |
| 830 | fi |
| 831 | |
| 832 | # ===================================== |
| 833 | section "406. WAIT BUILTIN EDGE CASES" |
| 834 | # ===================================== |
| 835 | |
| 836 | # wait with no args |
| 837 | result=$("$FORTSH_BIN" -c 'sleep 0.1 & sleep 0.1 & wait; echo done' 2>&1) |
| 838 | if [ "$result" = "done" ]; then |
| 839 | pass "wait with no args waits for all" |
| 840 | else |
| 841 | fail "wait with no args waits for all" "done" "$result" |
| 842 | fi |
| 843 | |
| 844 | # wait with specific PID |
| 845 | result=$("$FORTSH_BIN" -c 'sleep 0.1 & pid=$!; wait $pid; echo $?' 2>&1) |
| 846 | if [ "$result" = "0" ]; then |
| 847 | pass "wait with PID" |
| 848 | else |
| 849 | fail "wait with PID" "0" "$result" |
| 850 | fi |
| 851 | |
| 852 | # wait preserves exit status |
| 853 | result=$("$FORTSH_BIN" -c '(exit 42) & pid=$!; wait $pid; echo $?' 2>&1) |
| 854 | if [ "$result" = "42" ]; then |
| 855 | pass "wait preserves exit status" |
| 856 | else |
| 857 | fail "wait preserves exit status" "42" "$result" |
| 858 | fi |
| 859 | |
| 860 | # ===================================== |
| 861 | section "407. ULIMIT BUILTIN" |
| 862 | # ===================================== |
| 863 | |
| 864 | # ulimit -a shows limits |
| 865 | result=$("$FORTSH_BIN" -c 'ulimit -a 2>&1' | head -5) |
| 866 | if [ -n "$result" ]; then |
| 867 | pass "ulimit -a shows limits" |
| 868 | else |
| 869 | fail "ulimit -a shows limits" "non-empty" "$result" |
| 870 | fi |
| 871 | |
| 872 | # ulimit shows soft limit |
| 873 | result=$("$FORTSH_BIN" -c 'ulimit 2>&1' | head -1) |
| 874 | if [ -n "$result" ]; then |
| 875 | pass "ulimit shows default limit" |
| 876 | else |
| 877 | fail "ulimit shows default limit" "non-empty" "$result" |
| 878 | fi |
| 879 | |
| 880 | # ===================================== |
| 881 | section "408. READ BUILTIN" |
| 882 | # ===================================== |
| 883 | |
| 884 | # read single variable |
| 885 | result=$("$FORTSH_BIN" -c 'echo "hello" | { read x; echo $x; }' 2>&1) |
| 886 | if [ "$result" = "hello" ]; then |
| 887 | pass "read single variable" |
| 888 | else |
| 889 | fail "read single variable" "hello" "$result" |
| 890 | fi |
| 891 | |
| 892 | # read multiple variables |
| 893 | result=$("$FORTSH_BIN" -c 'echo "a b c" | { read x y z; echo "$x:$y:$z"; }' 2>&1) |
| 894 | if [ "$result" = "a:b:c" ]; then |
| 895 | pass "read multiple variables" |
| 896 | else |
| 897 | fail "read multiple variables" "a:b:c" "$result" |
| 898 | fi |
| 899 | |
| 900 | # read with extra words |
| 901 | result=$("$FORTSH_BIN" -c 'echo "a b c d e" | { read x y; echo "$x|$y"; }' 2>&1) |
| 902 | if [ "$result" = "a|b c d e" ]; then |
| 903 | pass "read extra words to last var" |
| 904 | else |
| 905 | fail "read extra words to last var" "a|b c d e" "$result" |
| 906 | fi |
| 907 | |
| 908 | # ===================================== |
| 909 | section "409. PRINTF BUILTIN" |
| 910 | # ===================================== |
| 911 | |
| 912 | # printf basic |
| 913 | result=$("$FORTSH_BIN" -c 'printf "hello\n"' 2>&1) |
| 914 | if [ "$result" = "hello" ]; then |
| 915 | pass "printf basic" |
| 916 | else |
| 917 | fail "printf basic" "hello" "$result" |
| 918 | fi |
| 919 | |
| 920 | # printf with format |
| 921 | result=$("$FORTSH_BIN" -c 'printf "%s world\n" "hello"' 2>&1) |
| 922 | if [ "$result" = "hello world" ]; then |
| 923 | pass "printf with %s" |
| 924 | else |
| 925 | fail "printf with %s" "hello world" "$result" |
| 926 | fi |
| 927 | |
| 928 | # printf with number |
| 929 | result=$("$FORTSH_BIN" -c 'printf "%d\n" 42' 2>&1) |
| 930 | if [ "$result" = "42" ]; then |
| 931 | pass "printf with %d" |
| 932 | else |
| 933 | fail "printf with %d" "42" "$result" |
| 934 | fi |
| 935 | |
| 936 | # printf width |
| 937 | result=$("$FORTSH_BIN" -c 'printf "%5d\n" 42' 2>&1) |
| 938 | if [ "$result" = " 42" ]; then |
| 939 | pass "printf with width" |
| 940 | else |
| 941 | fail "printf with width" " 42" "$result" |
| 942 | fi |
| 943 | |
| 944 | # ===================================== |
| 945 | section "410. ECHO BUILTIN" |
| 946 | # ===================================== |
| 947 | |
| 948 | # echo basic |
| 949 | result=$("$FORTSH_BIN" -c 'echo hello' 2>&1) |
| 950 | if [ "$result" = "hello" ]; then |
| 951 | pass "echo basic" |
| 952 | else |
| 953 | fail "echo basic" "hello" "$result" |
| 954 | fi |
| 955 | |
| 956 | # echo multiple args |
| 957 | result=$("$FORTSH_BIN" -c 'echo a b c' 2>&1) |
| 958 | if [ "$result" = "a b c" ]; then |
| 959 | pass "echo multiple args" |
| 960 | else |
| 961 | fail "echo multiple args" "a b c" "$result" |
| 962 | fi |
| 963 | |
| 964 | # echo with quotes |
| 965 | result=$("$FORTSH_BIN" -c 'echo "hello world"' 2>&1) |
| 966 | if [ "$result" = "hello world" ]; then |
| 967 | pass "echo with quotes" |
| 968 | else |
| 969 | fail "echo with quotes" "hello world" "$result" |
| 970 | fi |
| 971 | |
| 972 | # ===================================== |
| 973 | section "411. KILL BUILTIN" |
| 974 | # ===================================== |
| 975 | |
| 976 | # kill -l lists signals |
| 977 | result=$("$FORTSH_BIN" -c 'kill -l 2>&1 | head -1') |
| 978 | if [ -n "$result" ]; then |
| 979 | pass "kill -l lists signals" |
| 980 | else |
| 981 | fail "kill -l lists signals" "non-empty" "$result" |
| 982 | fi |
| 983 | |
| 984 | # ===================================== |
| 985 | section "412. SET OPTIONS" |
| 986 | # ===================================== |
| 987 | |
| 988 | # set -e (errexit) |
| 989 | result=$("$FORTSH_BIN" -c 'set -e; true; echo reached' 2>&1) |
| 990 | if [ "$result" = "reached" ]; then |
| 991 | pass "set -e continues on success" |
| 992 | else |
| 993 | fail "set -e continues on success" "reached" "$result" |
| 994 | fi |
| 995 | |
| 996 | # set -x (xtrace) |
| 997 | result=$("$FORTSH_BIN" -c 'set -x; echo test 2>&1' | grep -c test) |
| 998 | if [ "$result" -ge 1 ]; then |
| 999 | pass "set -x traces commands" |
| 1000 | else |
| 1001 | fail "set -x traces commands" |
| 1002 | fi |
| 1003 | |
| 1004 | # set -f (noglob) |
| 1005 | result=$("$FORTSH_BIN" -c 'set -f; echo *' 2>&1) |
| 1006 | if [ "$result" = "*" ]; then |
| 1007 | pass "set -f disables glob" |
| 1008 | else |
| 1009 | fail "set -f disables glob" "*" "$result" |
| 1010 | fi |
| 1011 | |
| 1012 | # set +f (enable glob) |
| 1013 | result=$("$FORTSH_BIN" -c 'set -f; set +f; ls /*.txt 2>/dev/null | wc -l || echo 0' 2>&1) |
| 1014 | if echo "$result" | grep -qE '^[[:space:]]*[0-9]+[[:space:]]*$'; then |
| 1015 | pass "set +f enables glob" |
| 1016 | else |
| 1017 | fail "set +f enables glob" |
| 1018 | fi |
| 1019 | |
| 1020 | # ===================================== |
| 1021 | section "413. UNSET BUILTIN" |
| 1022 | # ===================================== |
| 1023 | |
| 1024 | # unset variable |
| 1025 | result=$("$FORTSH_BIN" -c 'x=value; unset x; echo ${x:-empty}' 2>&1) |
| 1026 | if [ "$result" = "empty" ]; then |
| 1027 | pass "unset removes variable" |
| 1028 | else |
| 1029 | fail "unset removes variable" "empty" "$result" |
| 1030 | fi |
| 1031 | |
| 1032 | # unset function |
| 1033 | result=$("$FORTSH_BIN" -c 'f() { echo hi; }; unset -f f; f 2>/dev/null || echo gone' 2>&1) |
| 1034 | if [ "$result" = "gone" ]; then |
| 1035 | pass "unset -f removes function" |
| 1036 | else |
| 1037 | fail "unset -f removes function" "gone" "$result" |
| 1038 | fi |
| 1039 | |
| 1040 | # unset readonly fails |
| 1041 | result=$("$FORTSH_BIN" -c 'readonly x=1; unset x 2>/dev/null; echo $?' 2>&1) |
| 1042 | if [ "$result" != "0" ]; then |
| 1043 | pass "unset readonly fails" |
| 1044 | else |
| 1045 | fail "unset readonly fails" "non-zero" "$result" |
| 1046 | fi |
| 1047 | |
| 1048 | # ===================================== |
| 1049 | section "414. SHIFT BUILTIN" |
| 1050 | # ===================================== |
| 1051 | |
| 1052 | # basic shift |
| 1053 | result=$("$FORTSH_BIN" -c 'set -- a b c; shift; echo $1' 2>&1) |
| 1054 | if [ "$result" = "b" ]; then |
| 1055 | pass "shift removes first arg" |
| 1056 | else |
| 1057 | fail "shift removes first arg" "b" "$result" |
| 1058 | fi |
| 1059 | |
| 1060 | # shift with count |
| 1061 | result=$("$FORTSH_BIN" -c 'set -- a b c d e; shift 3; echo $1' 2>&1) |
| 1062 | if [ "$result" = "d" ]; then |
| 1063 | pass "shift with count" |
| 1064 | else |
| 1065 | fail "shift with count" "d" "$result" |
| 1066 | fi |
| 1067 | |
| 1068 | # shift updates $# |
| 1069 | result=$("$FORTSH_BIN" -c 'set -- a b c; shift; echo $#' 2>&1) |
| 1070 | if [ "$result" = "2" ]; then |
| 1071 | pass "shift updates arg count" |
| 1072 | else |
| 1073 | fail "shift updates arg count" "2" "$result" |
| 1074 | fi |
| 1075 | |
| 1076 | # ===================================== |
| 1077 | section "415. TRUE AND FALSE BUILTINS" |
| 1078 | # ===================================== |
| 1079 | |
| 1080 | # true returns 0 |
| 1081 | result=$("$FORTSH_BIN" -c 'true; echo $?' 2>&1) |
| 1082 | if [ "$result" = "0" ]; then |
| 1083 | pass "true returns 0" |
| 1084 | else |
| 1085 | fail "true returns 0" "0" "$result" |
| 1086 | fi |
| 1087 | |
| 1088 | # false returns 1 |
| 1089 | result=$("$FORTSH_BIN" -c 'false; echo $?' 2>&1) |
| 1090 | if [ "$result" = "1" ]; then |
| 1091 | pass "false returns 1" |
| 1092 | else |
| 1093 | fail "false returns 1" "1" "$result" |
| 1094 | fi |
| 1095 | |
| 1096 | # true in conditional |
| 1097 | result=$("$FORTSH_BIN" -c 'if true; then echo yes; fi' 2>&1) |
| 1098 | if [ "$result" = "yes" ]; then |
| 1099 | pass "true in if condition" |
| 1100 | else |
| 1101 | fail "true in if condition" "yes" "$result" |
| 1102 | fi |
| 1103 | |
| 1104 | # false in conditional |
| 1105 | result=$("$FORTSH_BIN" -c 'if false; then echo no; else echo yes; fi' 2>&1) |
| 1106 | if [ "$result" = "yes" ]; then |
| 1107 | pass "false in if condition" |
| 1108 | else |
| 1109 | fail "false in if condition" "yes" "$result" |
| 1110 | fi |
| 1111 | |
| 1112 | # ===================================== |
| 1113 | section "416. COLON BUILTIN" |
| 1114 | # ===================================== |
| 1115 | |
| 1116 | # colon returns 0 |
| 1117 | result=$("$FORTSH_BIN" -c ':; echo $?' 2>&1) |
| 1118 | if [ "$result" = "0" ]; then |
| 1119 | pass "colon returns 0" |
| 1120 | else |
| 1121 | fail "colon returns 0" "0" "$result" |
| 1122 | fi |
| 1123 | |
| 1124 | # colon with args (ignored) |
| 1125 | result=$("$FORTSH_BIN" -c ': arg1 arg2 arg3; echo $?' 2>&1) |
| 1126 | if [ "$result" = "0" ]; then |
| 1127 | pass "colon ignores args" |
| 1128 | else |
| 1129 | fail "colon ignores args" "0" "$result" |
| 1130 | fi |
| 1131 | |
| 1132 | # colon in loop condition |
| 1133 | result=$("$FORTSH_BIN" -c 'i=0; while :; do i=$((i+1)); [ $i -ge 3 ] && break; done; echo $i' 2>&1) |
| 1134 | if [ "$result" = "3" ]; then |
| 1135 | pass "colon as infinite loop condition" |
| 1136 | else |
| 1137 | fail "colon as infinite loop condition" "3" "$result" |
| 1138 | fi |
| 1139 | |
| 1140 | # ===================================== |
| 1141 | section "417. PWD BUILTIN" |
| 1142 | # ===================================== |
| 1143 | |
| 1144 | # pwd returns current dir |
| 1145 | result=$("$FORTSH_BIN" -c 'pwd | grep -c "^/"' 2>&1) |
| 1146 | if [ "$result" = "1" ]; then |
| 1147 | pass "pwd returns absolute path" |
| 1148 | else |
| 1149 | fail "pwd returns absolute path" "1" "$result" |
| 1150 | fi |
| 1151 | |
| 1152 | # pwd after cd |
| 1153 | result=$("$FORTSH_BIN" -c 'cd /tmp && pwd' 2>&1) |
| 1154 | if [ "$result" = "/tmp" ]; then |
| 1155 | pass "pwd after cd" |
| 1156 | else |
| 1157 | fail "pwd after cd" "/tmp" "$result" |
| 1158 | fi |
| 1159 | |
| 1160 | # ===================================== |
| 1161 | section "418. CD BUILTIN" |
| 1162 | # ===================================== |
| 1163 | |
| 1164 | # cd to absolute path |
| 1165 | result=$("$FORTSH_BIN" -c 'cd /tmp && pwd' 2>&1) |
| 1166 | if [ "$result" = "/tmp" ]; then |
| 1167 | pass "cd to absolute path" |
| 1168 | else |
| 1169 | fail "cd to absolute path" "/tmp" "$result" |
| 1170 | fi |
| 1171 | |
| 1172 | # cd - returns to OLDPWD |
| 1173 | result=$("$FORTSH_BIN" -c 'cd /tmp; cd /; cd - 2>&1' 2>&1) |
| 1174 | if echo "$result" | grep -q "tmp"; then |
| 1175 | pass "cd - returns to OLDPWD" |
| 1176 | else |
| 1177 | fail "cd - returns to OLDPWD" |
| 1178 | fi |
| 1179 | |
| 1180 | # cd nonexistent fails |
| 1181 | result=$("$FORTSH_BIN" -c 'cd /nonexistent_dir_xyz 2>/dev/null; echo $?' 2>&1) |
| 1182 | if [ "$result" != "0" ]; then |
| 1183 | pass "cd nonexistent fails" |
| 1184 | else |
| 1185 | fail "cd nonexistent fails" "non-zero" "$result" |
| 1186 | fi |
| 1187 | |
| 1188 | # ===================================== |
| 1189 | section "419. TIMES BUILTIN" |
| 1190 | # ===================================== |
| 1191 | |
| 1192 | # times outputs something |
| 1193 | result=$("$FORTSH_BIN" -c 'times 2>&1 | head -1 || echo skipped') |
| 1194 | if [ -n "$result" ]; then |
| 1195 | pass "times outputs timing info" |
| 1196 | else |
| 1197 | fail "times outputs timing info" "non-empty" "$result" |
| 1198 | fi |
| 1199 | |
| 1200 | # ===================================== |
| 1201 | section "420. EXIT BUILTIN" |
| 1202 | # ===================================== |
| 1203 | |
| 1204 | # exit with code |
| 1205 | result=$("$FORTSH_BIN" -c 'exit 42' 2>&1; echo $?) |
| 1206 | if [ "$result" = "42" ]; then |
| 1207 | pass "exit with code" |
| 1208 | else |
| 1209 | fail "exit with code" "42" "$result" |
| 1210 | fi |
| 1211 | |
| 1212 | # exit default 0 |
| 1213 | result=$("$FORTSH_BIN" -c 'exit' 2>&1; echo $?) |
| 1214 | if [ "$result" = "0" ]; then |
| 1215 | pass "exit default 0" |
| 1216 | else |
| 1217 | fail "exit default 0" "0" "$result" |
| 1218 | fi |
| 1219 | |
| 1220 | # exit from subshell |
| 1221 | result=$("$FORTSH_BIN" -c '(exit 7); echo $?' 2>&1) |
| 1222 | if [ "$result" = "7" ]; then |
| 1223 | pass "exit from subshell" |
| 1224 | else |
| 1225 | fail "exit from subshell" "7" "$result" |
| 1226 | fi |
| 1227 | |
| 1228 | # ===================================== |
| 1229 | # Summary |
| 1230 | # ===================================== |
| 1231 | printf "\n" |
| 1232 | printf "${BLUE}==========================================\n" |
| 1233 | printf "POSIX Shell Options and Read Summary\n" |
| 1234 | printf "==========================================${NC}\n" |
| 1235 | printf "Passed: ${GREEN}%d${NC}\n" "$PASSED" |
| 1236 | printf "Failed: ${RED}%d${NC}\n" "$FAILED" |
| 1237 | printf "Skipped: ${YELLOW}%d${NC}\n" "$SKIPPED" |
| 1238 | printf "Total: %d\n" "$((PASSED + FAILED + SKIPPED))" |
| 1239 | |
| 1240 | if [ -n "$FAILED_TESTS_LIST" ]; then |
| 1241 | printf "\n${RED}Failed tests:${NC}\n" |
| 1242 | printf "%b" "$FAILED_TESTS_LIST" |
| 1243 | fi |
| 1244 | |
| 1245 | if [ "$FAILED" -gt 0 ]; then |
| 1246 | exit 1 |
| 1247 | fi |
| 1248 | exit 0 |