| 1 | #!/bin/sh |
| 2 | # ===================================== |
| 3 | # POSIX Quoting Gap Tests |
| 4 | # ===================================== |
| 5 | # Tests for POSIX quoting mechanisms |
| 6 | # Split from posix_compliance_gaps.sh for better organization |
| 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="[gaps-quoting]" |
| 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 | 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 printf " posix: %s\n" "$2"; fi |
| 49 | if [ -n "$3" ]; then printf " fortsh: %s\n" "$3"; fi |
| 50 | FAILED=$((FAILED + 1)) |
| 51 | } |
| 52 | |
| 53 | section() { |
| 54 | CURRENT_SECTION=$(echo "$1" | grep -oE '^[0-9]+' || echo "0") |
| 55 | TEST_NUM=0 |
| 56 | printf "\n${BLUE}==========================================\n%s\n==========================================${NC}\n" "$1" |
| 57 | } |
| 58 | |
| 59 | normalize_output() { sed -e 's/^bash: /sh: /' -e 's/line [0-9]*: //'; } |
| 60 | |
| 61 | compare_posix_output() { |
| 62 | test_name="$1"; command="$2" |
| 63 | posix_out=$("$BASH_REF" -c "$command" 2>&1 | normalize_output) |
| 64 | fortsh_out=$("$FORTSH_BIN" -c "$command" 2>&1 | normalize_output) |
| 65 | if [ "$posix_out" = "$fortsh_out" ]; then pass "$test_name" |
| 66 | else fail "$test_name" "$posix_out" "$fortsh_out"; fi |
| 67 | } |
| 68 | |
| 69 | # ============================================================================ |
| 70 | # QUOTING TESTS |
| 71 | # ============================================================================ |
| 72 | |
| 73 | section "1. SINGLE QUOTING" |
| 74 | compare_posix_output "simple" "echo 'hello'" |
| 75 | compare_posix_output "with space" "echo 'hello world'" |
| 76 | compare_posix_output "dollar literal" "echo '\$x'" |
| 77 | compare_posix_output "backtick literal" "echo '\`cmd\`'" |
| 78 | compare_posix_output "backslash literal" "echo '\\'" |
| 79 | |
| 80 | section "2. DOUBLE QUOTING" |
| 81 | compare_posix_output "simple" 'echo "hello"' |
| 82 | compare_posix_output "with space" 'echo "hello world"' |
| 83 | compare_posix_output "var expand" 'x=val; echo "$x"' |
| 84 | compare_posix_output "escaped dollar" 'echo "\$x"' |
| 85 | compare_posix_output "escaped quote" 'echo "hello\"world"' |
| 86 | |
| 87 | section "3. BACKSLASH ESCAPING" |
| 88 | compare_posix_output "escape space" 'echo hello\ world' |
| 89 | compare_posix_output "escape dollar" 'echo \$x' |
| 90 | compare_posix_output "escape newline" 'echo hello\ |
| 91 | world' |
| 92 | compare_posix_output "escape backslash" 'echo \\\\' |
| 93 | |
| 94 | section "4. MIXED QUOTING" |
| 95 | compare_posix_output "single in double" 'echo "'"'"'"' |
| 96 | compare_posix_output "double in single" "echo '\"'" |
| 97 | compare_posix_output "concat quotes" "echo 'a'b'c'" |
| 98 | compare_posix_output "quote switching" 'echo "a'"'"'b"' |
| 99 | |
| 100 | section "5. EMPTY QUOTES" |
| 101 | compare_posix_output "empty single" "echo ''" |
| 102 | compare_posix_output "empty double" 'echo ""' |
| 103 | compare_posix_output "empty in string" 'echo a""b' |
| 104 | compare_posix_output "empty as arg" 'set -- ""; echo $#' |
| 105 | |
| 106 | section "6. COMPLEX ESCAPING" |
| 107 | compare_posix_output "backslash in dquotes" 'echo "test\\nword"' |
| 108 | compare_posix_output "dollar in dquotes" 'echo "cost: \$5"' |
| 109 | compare_posix_output "backtick in dquotes" 'echo "date: \`date +%Y\`" | grep -c date' |
| 110 | compare_posix_output "mixed quoting" "echo 'single'\"double\"'single'" |
| 111 | compare_posix_output "empty concat" "echo ''test''" |
| 112 | compare_posix_output "quote removal" 'VAR="\"test\""; echo $VAR' |
| 113 | compare_posix_output "backslash newline" "echo 'line1\ |
| 114 | line2' | wc -l" |
| 115 | |
| 116 | # Summary |
| 117 | printf "\n==========================================\n" |
| 118 | printf "QUOTING GAP TEST RESULTS\n" |
| 119 | printf "==========================================\n" |
| 120 | printf "${GREEN}Passed:${NC} %d\n" "$PASSED" |
| 121 | printf "${RED}Failed:${NC} %d\n" "$FAILED" |
| 122 | printf "Total: %d\n" "$((PASSED + FAILED))" |
| 123 | if [ "$FAILED" -gt 0 ]; then |
| 124 | printf "\n${RED}Failed tests:${NC}\n%b" "$FAILED_TESTS_LIST" |
| 125 | exit 1 |
| 126 | fi |
| 127 | exit 0 |