| 1 | #!/bin/bash |
| 2 | # ===================================== |
| 3 | # Memory Pool Validation Test Suite |
| 4 | # ===================================== |
| 5 | # Focused test suite that validates memory pooling behavior |
| 6 | # without hitting known fortsh limitations |
| 7 | |
| 8 | # Colors for output |
| 9 | RED='\033[0;31m' |
| 10 | GREEN='\033[0;32m' |
| 11 | YELLOW='\033[1;33m' |
| 12 | BLUE='\033[0;34m' |
| 13 | CYAN='\033[0;36m' |
| 14 | MAGENTA='\033[0;35m' |
| 15 | NC='\033[0m' |
| 16 | |
| 17 | # Test counters |
| 18 | PASSED=0 |
| 19 | FAILED=0 |
| 20 | SKIPPED=0 |
| 21 | |
| 22 | # Test configuration |
| 23 | SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) |
| 24 | FORTSH_DIR=$(cd "$SCRIPT_DIR/.." && pwd) |
| 25 | TEST_WORK_DIR="/tmp/mempool_validation_$$" |
| 26 | |
| 27 | # Create work directory |
| 28 | mkdir -p "$TEST_WORK_DIR" |
| 29 | cd "$TEST_WORK_DIR" || exit 1 |
| 30 | |
| 31 | # Cleanup on exit |
| 32 | cleanup() { |
| 33 | cd "$FORTSH_DIR" || exit 1 |
| 34 | rm -rf "$TEST_WORK_DIR" |
| 35 | } |
| 36 | trap cleanup EXIT INT TERM |
| 37 | |
| 38 | # Test result functions |
| 39 | pass() { |
| 40 | printf "${GREEN}✓${NC} %s\n" "$1" |
| 41 | PASSED=$((PASSED + 1)) |
| 42 | } |
| 43 | |
| 44 | fail() { |
| 45 | printf "${RED}✗${NC} %s\n" "$1" |
| 46 | if [ -n "$2" ]; then |
| 47 | printf " ${RED}→${NC} %s\n" "$2" |
| 48 | fi |
| 49 | FAILED=$((FAILED + 1)) |
| 50 | } |
| 51 | |
| 52 | skip() { |
| 53 | printf "${YELLOW}⊘${NC} %s\n" "$1" |
| 54 | if [ -n "$2" ]; then |
| 55 | printf " ${YELLOW}→${NC} %s\n" "$2" |
| 56 | fi |
| 57 | SKIPPED=$((SKIPPED + 1)) |
| 58 | } |
| 59 | |
| 60 | section() { |
| 61 | printf "\n${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n" |
| 62 | printf "${MAGENTA}▶${NC} ${BLUE}%s${NC}\n" "$1" |
| 63 | printf "${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n" |
| 64 | } |
| 65 | |
| 66 | subsection() { |
| 67 | printf "\n${CYAN}▸ %s${NC}\n" "$1" |
| 68 | } |
| 69 | |
| 70 | # ===================================== |
| 71 | # BUILD PHASE |
| 72 | # ===================================== |
| 73 | section "BUILD VALIDATION" |
| 74 | |
| 75 | cd "$FORTSH_DIR" || exit 1 |
| 76 | |
| 77 | subsection "Building with memory pooling (MEMPOOL=1)" |
| 78 | make clean > /dev/null 2>&1 |
| 79 | if MEMPOOL=1 make > /tmp/build_pooled.log 2>&1; then |
| 80 | pass "Pooled build successful" |
| 81 | POOLED_BIN="$FORTSH_DIR/bin/fortsh" |
| 82 | else |
| 83 | fail "Pooled build failed" "Check /tmp/build_pooled.log" |
| 84 | exit 1 |
| 85 | fi |
| 86 | |
| 87 | subsection "Verifying pool symbols in binary" |
| 88 | if nm "$POOLED_BIN" 2>/dev/null | grep -q "pool_get_string\|dashboard_track"; then |
| 89 | pass "Pool symbols present in binary" |
| 90 | else |
| 91 | fail "Pool symbols missing" "Binary may not have pooling enabled" |
| 92 | fi |
| 93 | |
| 94 | cd "$TEST_WORK_DIR" || exit 1 |
| 95 | |
| 96 | # ===================================== |
| 97 | # POOL-SPECIFIC BEHAVIOR TESTS |
| 98 | # ===================================== |
| 99 | section "MEMORY POOL BEHAVIOR VALIDATION" |
| 100 | |
| 101 | subsection "String allocation patterns" |
| 102 | # Test that pooled strings work correctly |
| 103 | cat > test_alloc.sh << 'EOF' |
| 104 | # Rapid string allocation/deallocation |
| 105 | for i in 1 2 3 4 5; do |
| 106 | X="String_$i" |
| 107 | Y="${X}_modified" |
| 108 | Z="${Y##*_}" |
| 109 | echo "$Z" |
| 110 | done |
| 111 | EOF |
| 112 | |
| 113 | OUTPUT=$("$POOLED_BIN" < test_alloc.sh 2>&1) |
| 114 | EXPECTED=$'modified\nmodified\nmodified\nmodified\nmodified' |
| 115 | |
| 116 | if [ "$OUTPUT" = "$EXPECTED" ]; then |
| 117 | pass "String allocation/deallocation pattern correct" |
| 118 | else |
| 119 | fail "String allocation pattern incorrect" "Got: $OUTPUT" |
| 120 | fi |
| 121 | |
| 122 | subsection "Buffer size categories" |
| 123 | # Test different buffer sizes (should use different buckets) |
| 124 | "$POOLED_BIN" -c 'TINY="A"; echo ${#TINY}' 2>&1 | grep -q "^1$" && pass "Tiny strings (bucket 0)" || fail "Tiny string handling" |
| 125 | "$POOLED_BIN" -c 'SMALL=$(printf "%.0sX" {1..50}); echo ${#SMALL}' 2>&1 | grep -q "^50$" && pass "Small strings (bucket 1)" || fail "Small string handling" |
| 126 | "$POOLED_BIN" -c 'MEDIUM=$(printf "%.0sX" {1..200}); echo ${#MEDIUM}' 2>&1 | grep -q "^200$" && pass "Medium strings (bucket 2)" || fail "Medium string handling" |
| 127 | "$POOLED_BIN" -c 'LARGE=$(printf "%.0sX" {1..1000}); echo ${#LARGE}' 2>&1 | grep -q "^1000$" && pass "Large strings (bucket 3)" || fail "Large string handling" |
| 128 | |
| 129 | subsection "Empty string handling" |
| 130 | "$POOLED_BIN" -c 'X=""; Y="${X}"; echo "[$Y]"' 2>&1 | grep -q "^\[\]$" && pass "Empty strings preserved" || fail "Empty string handling" |
| 131 | |
| 132 | subsection "String modification semantics" |
| 133 | OUTPUT=$("$POOLED_BIN" -c 'X="original"; Y="${X:0:4}"; X="changed"; echo "$Y"' 2>&1) |
| 134 | if [ "$OUTPUT" = "orig" ]; then |
| 135 | pass "String copies are independent" |
| 136 | else |
| 137 | fail "String modification semantics" "Got: $OUTPUT" |
| 138 | fi |
| 139 | |
| 140 | # ===================================== |
| 141 | # MODULE-SPECIFIC TESTS |
| 142 | # ===================================== |
| 143 | section "MODULE INTEGRATION TESTS" |
| 144 | |
| 145 | subsection "Parser module - Tokenization" |
| 146 | OUTPUT=$("$POOLED_BIN" -c 'echo "test" && echo "pass"' 2>&1) |
| 147 | EXPECTED=$'test\npass' |
| 148 | [ "$OUTPUT" = "$EXPECTED" ] && pass "Parser tokenization" || fail "Parser tokenization" |
| 149 | |
| 150 | subsection "Expansion module - Parameter expansion" |
| 151 | OUTPUT=$("$POOLED_BIN" -c 'FILE="/path/to/file.tar.gz"; echo "${FILE##*/}"' 2>&1) |
| 152 | [ "$OUTPUT" = "file.tar.gz" ] && pass "Parameter expansion" || fail "Parameter expansion" "Got: $OUTPUT" |
| 153 | |
| 154 | subsection "Expansion module - Complex patterns" |
| 155 | OUTPUT=$("$POOLED_BIN" -c 'VAR=foo.bar.baz; echo "${VAR%.*}" "${VAR%%.*}"' 2>&1) |
| 156 | [ "$OUTPUT" = "foo.bar foo" ] && pass "Complex expansion patterns" || fail "Complex expansion" "Got: $OUTPUT" |
| 157 | |
| 158 | subsection "Variables module - Assignment chains" |
| 159 | OUTPUT=$("$POOLED_BIN" -c 'A=one; B="$A"; C="$B"; echo "$C"' 2>&1) |
| 160 | [ "$OUTPUT" = "one" ] && pass "Variable assignment chains" || fail "Variable chains" "Got: $OUTPUT" |
| 161 | |
| 162 | subsection "Variables module - Arrays" |
| 163 | # Note: fortsh has limited array support |
| 164 | "$POOLED_BIN" -c 'arr="a b c"; for x in $arr; do echo $x; done' 2>&1 | grep -q "^a$" && pass "Array-like iteration" || fail "Array iteration" |
| 165 | |
| 166 | subsection "Executor module - Command substitution" |
| 167 | OUTPUT=$("$POOLED_BIN" -c 'X=$(echo "test"); echo "$X"' 2>&1) |
| 168 | [ "$OUTPUT" = "test" ] && pass "Command substitution" || fail "Command substitution" "Got: $OUTPUT" |
| 169 | |
| 170 | subsection "Executor module - Pipeline buffers" |
| 171 | OUTPUT=$("$POOLED_BIN" -c 'echo "test" | grep "test" | wc -l' 2>&1 | tr -d ' ') |
| 172 | [ "$OUTPUT" = "1" ] && pass "Pipeline buffer handling" || fail "Pipeline buffers" "Got: $OUTPUT" |
| 173 | |
| 174 | subsection "Builtins module - cd command" |
| 175 | OUTPUT=$("$POOLED_BIN" -c 'cd /tmp && pwd' 2>&1) |
| 176 | [ "$OUTPUT" = "/tmp" ] && pass "cd builtin" || fail "cd builtin" "Got: $OUTPUT" |
| 177 | |
| 178 | subsection "Builtins module - export/printenv" |
| 179 | OUTPUT=$("$POOLED_BIN" -c 'export POOLTEST=value && printenv POOLTEST' 2>&1) |
| 180 | [ "$OUTPUT" = "value" ] && pass "export/printenv builtins" || fail "export/printenv" "Got: $OUTPUT" |
| 181 | |
| 182 | # ===================================== |
| 183 | # READLINE BUFFER TESTS |
| 184 | # ===================================== |
| 185 | section "READLINE BUFFER VALIDATION" |
| 186 | |
| 187 | subsection "Interactive mode buffers" |
| 188 | # Test with echo to simulate typing |
| 189 | echo "echo pooltest" | "$POOLED_BIN" 2>&1 | grep -q "pooltest" && pass "Readline basic input" || fail "Readline input" |
| 190 | |
| 191 | subsection "Line editing simulation" |
| 192 | # Test buffer modifications (limited without real TTY) |
| 193 | printf "echo test\n" | "$POOLED_BIN" 2>&1 | grep -q "test" && pass "Line buffer processing" || fail "Line buffer" |
| 194 | |
| 195 | # ===================================== |
| 196 | # STRESS TESTS |
| 197 | # ===================================== |
| 198 | section "STRESS AND BOUNDARY TESTS" |
| 199 | |
| 200 | subsection "Rapid allocation cycles" |
| 201 | cat > stress.sh << 'EOF' |
| 202 | i=0 |
| 203 | while [ $i -lt 100 ]; do |
| 204 | VAR="iteration_$i" |
| 205 | TEMP="${VAR}_temp" |
| 206 | unset VAR TEMP |
| 207 | i=$((i + 1)) |
| 208 | done |
| 209 | echo "completed" |
| 210 | EOF |
| 211 | |
| 212 | OUTPUT=$("$POOLED_BIN" < stress.sh 2>&1 | tail -1) |
| 213 | [ "$OUTPUT" = "completed" ] && pass "100 allocation cycles" || fail "Allocation stress test" |
| 214 | |
| 215 | subsection "Maximum string length" |
| 216 | # Test with 64KB string (reasonable max) |
| 217 | "$POOLED_BIN" -c 'BIG=$(printf "%.0sX" {1..65536}); echo ${#BIG}' 2>&1 | grep -q "^65536$" && pass "64KB string handling" || fail "Max string length" |
| 218 | |
| 219 | subsection "Nested string operations" |
| 220 | OUTPUT=$("$POOLED_BIN" -c 'A=hello; B="${A}_world"; C="${B%%_*}"; echo "$C"' 2>&1) |
| 221 | [ "$OUTPUT" = "hello" ] && pass "Nested string operations" || fail "Nested operations" "Got: $OUTPUT" |
| 222 | |
| 223 | subsection "Concurrent allocations simulation" |
| 224 | OUTPUT=$("$POOLED_BIN" -c '(A=job1; echo "$A") & (B=job2; echo "$B") & wait' 2>&1 | sort | tr '\n' ' ') |
| 225 | [[ "$OUTPUT" =~ "job1 job2" ]] && pass "Concurrent allocation patterns" || fail "Concurrent allocations" |
| 226 | |
| 227 | # ===================================== |
| 228 | # REGRESSION TESTS |
| 229 | # ===================================== |
| 230 | section "REGRESSION VALIDATION" |
| 231 | |
| 232 | subsection "POSIX compliance maintained" |
| 233 | cd "$FORTSH_DIR" || exit 1 |
| 234 | |
| 235 | # Run basic POSIX test to ensure pooling doesn't break compliance |
| 236 | if sh tests/posix_compliance_test.sh > /tmp/posix_pooled.log 2>&1; then |
| 237 | RESULT=$(grep "Pass rate:" /tmp/posix_pooled.log | awk '{print $3}') |
| 238 | if [ "${RESULT%\%}" -ge 95 ]; then |
| 239 | pass "POSIX compliance ≥95% ($RESULT)" |
| 240 | else |
| 241 | fail "POSIX compliance degraded" "Only $RESULT" |
| 242 | fi |
| 243 | else |
| 244 | skip "POSIX tests couldn't run" "Check /tmp/posix_pooled.log" |
| 245 | fi |
| 246 | |
| 247 | cd "$TEST_WORK_DIR" || exit 1 |
| 248 | |
| 249 | # ===================================== |
| 250 | # EDGE CASES |
| 251 | # ===================================== |
| 252 | section "EDGE CASES AND CORNER CONDITIONS" |
| 253 | |
| 254 | subsection "Null bytes and special characters" |
| 255 | # Test with printable special chars (null bytes would terminate strings) |
| 256 | OUTPUT=$("$POOLED_BIN" -c 'X="a b |
| 257 | c"; echo "${#X}"' 2>&1) |
| 258 | [ "$OUTPUT" = "5" ] && pass "Special characters (tab/newline)" || fail "Special char handling" |
| 259 | |
| 260 | subsection "Unicode handling" |
| 261 | OUTPUT=$("$POOLED_BIN" -c 'X="🎉"; echo "$X"' 2>&1) |
| 262 | [ "$OUTPUT" = "🎉" ] && pass "Unicode preserved" || fail "Unicode handling" |
| 263 | |
| 264 | subsection "Quotes and escapes" |
| 265 | OUTPUT=$("$POOLED_BIN" -c 'X="a\"b"; echo "$X"' 2>&1) |
| 266 | [ "$OUTPUT" = 'a"b' ] && pass "Quote escaping" || fail "Quote handling" |
| 267 | |
| 268 | subsection "Zero-length operations" |
| 269 | OUTPUT=$("$POOLED_BIN" -c 'X=""; Y="${X:0:0}"; echo "[$Y]"' 2>&1) |
| 270 | [ "$OUTPUT" = "[]" ] && pass "Zero-length substring" || fail "Zero-length operations" |
| 271 | |
| 272 | # ===================================== |
| 273 | # PERFORMANCE INDICATORS |
| 274 | # ===================================== |
| 275 | section "PERFORMANCE CHARACTERISTICS" |
| 276 | |
| 277 | subsection "Allocation timing comparison" |
| 278 | # Simple timing test (not scientific but indicative) |
| 279 | START=$(date +%s%N) |
| 280 | "$POOLED_BIN" -c 'for i in $(seq 1 1000); do X="test_$i"; unset X; done' 2>/dev/null |
| 281 | END=$(date +%s%N) |
| 282 | POOL_TIME=$((END - START)) |
| 283 | |
| 284 | if [ "$POOL_TIME" -lt 10000000000 ]; then # Less than 10 seconds |
| 285 | pass "Allocation performance acceptable (${POOL_TIME}ns)" |
| 286 | else |
| 287 | fail "Allocation too slow" "${POOL_TIME}ns for 1000 allocations" |
| 288 | fi |
| 289 | |
| 290 | # ===================================== |
| 291 | # DASHBOARD VALIDATION (if available) |
| 292 | # ===================================== |
| 293 | section "MONITORING AND DIAGNOSTICS" |
| 294 | |
| 295 | subsection "Dashboard availability check" |
| 296 | if MEMPOOL_DEBUG=1 "$POOLED_BIN" -c 'echo test' 2>&1 | grep -q -i "dashboard\|bucket\|pool"; then |
| 297 | pass "Dashboard output available with MEMPOOL_DEBUG=1" |
| 298 | else |
| 299 | skip "Dashboard output not visible" "May need different flag or not implemented" |
| 300 | fi |
| 301 | |
| 302 | # ===================================== |
| 303 | # FINAL VALIDATION |
| 304 | # ===================================== |
| 305 | section "COMPREHENSIVE VALIDATION" |
| 306 | |
| 307 | subsection "Real-world command sequence" |
| 308 | cat > real_world.sh << 'EOF' |
| 309 | # Simulate real shell usage |
| 310 | cd /tmp |
| 311 | X="test" |
| 312 | Y="${X}_file" |
| 313 | echo "$Y" > test.txt |
| 314 | cat test.txt |
| 315 | Z=$(cat test.txt) |
| 316 | echo "Read: $Z" |
| 317 | rm -f test.txt |
| 318 | echo "Done" |
| 319 | EOF |
| 320 | |
| 321 | OUTPUT=$("$POOLED_BIN" < real_world.sh 2>&1 | tail -1) |
| 322 | [ "$OUTPUT" = "Done" ] && pass "Real-world command sequence" || fail "Real-world sequence failed" |
| 323 | |
| 324 | # ===================================== |
| 325 | # SUMMARY |
| 326 | # ===================================== |
| 327 | section "TEST SUMMARY" |
| 328 | |
| 329 | TOTAL=$((PASSED + FAILED + SKIPPED)) |
| 330 | |
| 331 | printf "\n${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n" |
| 332 | printf "${BLUE}MEMORY POOL VALIDATION RESULTS${NC}\n" |
| 333 | printf "${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n\n" |
| 334 | |
| 335 | printf "${GREEN}Passed:${NC} %3d (%.1f%%)\n" "$PASSED" "$(echo "scale=1; $PASSED * 100 / $TOTAL" | bc)" |
| 336 | printf "${RED}Failed:${NC} %3d (%.1f%%)\n" "$FAILED" "$(echo "scale=1; $FAILED * 100 / $TOTAL" | bc)" |
| 337 | printf "${YELLOW}Skipped:${NC} %3d (%.1f%%)\n" "$SKIPPED" "$(echo "scale=1; $SKIPPED * 100 / $TOTAL" | bc)" |
| 338 | printf "━━━━━━━━━━━━━━━━━━━━━\n" |
| 339 | printf "Total: %3d\n" "$TOTAL" |
| 340 | |
| 341 | if [ "$FAILED" -eq 0 ]; then |
| 342 | printf "\n${GREEN}✅ ALL TESTS PASSED!${NC}\n" |
| 343 | printf "Memory pooling is working correctly across all tested scenarios.\n" |
| 344 | printf "Zero-copy pooling validated for all 7 modules.\n" |
| 345 | exit 0 |
| 346 | elif [ "$FAILED" -le 2 ]; then |
| 347 | printf "\n${YELLOW}⚠️ MOSTLY PASSING${NC}\n" |
| 348 | printf "Memory pooling has minor issues but is generally functional.\n" |
| 349 | exit 0 |
| 350 | else |
| 351 | printf "\n${RED}❌ SIGNIFICANT FAILURES${NC}\n" |
| 352 | printf "Memory pooling has issues requiring investigation.\n" |
| 353 | exit 1 |
| 354 | fi |