#!/usr/bin/env bash # lib/erlang/test.sh — smoke-test the Erlang runtime layer. # Uses sx_server.exe epoch protocol. # # Usage: # bash lib/erlang/test.sh # bash lib/erlang/test.sh -v set -uo pipefail cd "$(git rev-parse --show-toplevel)" SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}" if [ ! -x "$SX_SERVER" ]; then SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe" fi if [ ! -x "$SX_SERVER" ]; then echo "ERROR: sx_server.exe not found. Run: cd hosts/ocaml && dune build" exit 1 fi VERBOSE="${1:-}" PASS=0; FAIL=0; ERRORS="" TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT cat > "$TMPFILE" << 'EPOCHS' (epoch 1) (load "lib/erlang/runtime.sx") ;; --- Numeric tower --- (epoch 10) (eval "(er-is-integer? 42)") (epoch 11) (eval "(er-is-integer? 3.14)") (epoch 12) (eval "(er-is-float? 3.14)") (epoch 13) (eval "(er-is-float? 42)") (epoch 14) (eval "(er-is-number? 42)") (epoch 15) (eval "(er-is-number? 3.14)") (epoch 16) (eval "(er-float 5)") (epoch 17) (eval "(er-trunc 3.9)") (epoch 18) (eval "(er-round 3.5)") (epoch 19) (eval "(er-abs -7)") (epoch 20) (eval "(er-max 3 7)") (epoch 21) (eval "(er-min 3 7)") ;; --- div + rem --- (epoch 30) (eval "(er-div 10 3)") (epoch 31) (eval "(er-div -10 3)") (epoch 32) (eval "(er-rem 10 3)") (epoch 33) (eval "(er-rem -10 3)") (epoch 34) (eval "(er-gcd 12 8)") ;; --- Bitwise --- (epoch 40) (eval "(er-band 12 10)") (epoch 41) (eval "(er-bor 12 10)") (epoch 42) (eval "(er-bxor 12 10)") (epoch 43) (eval "(er-bnot 0)") (epoch 44) (eval "(er-bsl 1 4)") (epoch 45) (eval "(er-bsr 16 2)") ;; --- Sets --- (epoch 50) (eval "(er-sets-is-set? (er-sets-new))") (epoch 51) (eval "(let ((s (er-sets-new))) (do (er-sets-add-element s 1) (er-sets-is-element s 1)))") (epoch 52) (eval "(er-sets-is-element (er-sets-new) 42)") (epoch 53) (eval "(er-sets-is-element (er-sets-from-list (list 1 2 3)) 2)") (epoch 54) (eval "(er-sets-size (er-sets-from-list (list 1 2 3)))") (epoch 55) (eval "(len (er-sets-to-list (er-sets-from-list (list 1 2 3))))") ;; --- Regexp --- (epoch 60) (eval "(not (= (er-re-run \"hello\" \"ll\") nil))") (epoch 61) (eval "(= (er-re-run \"hello\" \"xyz\") nil)") (epoch 62) (eval "(get (er-re-run \"hello\" \"ll\") :match)") (epoch 63) (eval "(er-re-replace \"hello\" \"l\" \"r\")") (epoch 64) (eval "(er-re-replace-all \"hello\" \"l\" \"r\")") (epoch 65) (eval "(er-re-match-groups (er-re-run \"hello world\" \"(\\w+)\\s+(\\w+)\"))") (epoch 66) (eval "(len (er-re-split \"a,b,c\" \",\"))") ;; --- List BIFs --- (epoch 70) (eval "(er-hd (list 1 2 3))") (epoch 71) (eval "(er-tl (list 1 2 3))") (epoch 72) (eval "(er-length (list 1 2 3))") (epoch 73) (eval "(er-lists-member 2 (list 1 2 3))") (epoch 74) (eval "(er-lists-member 9 (list 1 2 3))") (epoch 75) (eval "(er-lists-reverse (list 1 2 3))") (epoch 76) (eval "(er-lists-nth 2 (list 10 20 30))") (epoch 77) (eval "(er-lists-foldl + 0 (list 1 2 3 4 5))") (epoch 78) (eval "(er-lists-seq 1 5)") (epoch 79) (eval "(er-lists-flatten (list 1 (list 2 3) (list 4 (list 5))))") ;; --- Type conversions --- (epoch 80) (eval "(er-integer-to-list 42)") (epoch 81) (eval "(er-list-to-integer \"42\")") (epoch 82) (eval "(er-integer-to-list-radix 255 16)") (epoch 83) (eval "(er-atom-to-list (make-symbol \"hello\"))") (epoch 84) (eval "(= (type-of (er-list-to-atom \"foo\")) \"symbol\")") ;; --- ok/error tuples --- (epoch 90) (eval "(er-is-ok? (er-ok 42))") (epoch 91) (eval "(er-is-error? (er-error \"reason\"))") (epoch 92) (eval "(er-unwrap (er-ok 42))") (epoch 93) (eval "(er-is-ok? (er-error \"bad\"))") EPOCHS OUTPUT=$(timeout 30 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) check() { local epoch="$1" desc="$2" expected="$3" local actual actual=$(echo "$OUTPUT" | grep -A1 "^(ok-len $epoch " | tail -1 || true) if echo "$actual" | grep -q "^(ok-len"; then actual=""; fi if [ -z "$actual" ]; then actual=$(echo "$OUTPUT" | grep "^(ok $epoch " | head -1 || true) fi if [ -z "$actual" ]; then actual=$(echo "$OUTPUT" | grep "^(error $epoch " | head -1 || true) fi [ -z "$actual" ] && actual="" if echo "$actual" | grep -qF -- "$expected"; then PASS=$((PASS+1)) [ "$VERBOSE" = "-v" ] && echo " ok $desc" else FAIL=$((FAIL+1)) ERRORS+=" FAIL [$desc] (epoch $epoch) expected: $expected | actual: $actual " fi } # Numeric tower check 10 "is-integer? 42" "true" check 11 "is-integer? float" "false" check 12 "is-float? 3.14" "true" check 13 "is-float? int" "false" check 14 "is-number? int" "true" check 15 "is-number? float" "true" check 16 "float 5" "5" check 17 "trunc 3.9" "3" check 18 "round 3.5" "4" check 19 "abs -7" "7" check 20 "max 3 7" "7" check 21 "min 3 7" "3" # div + rem check 30 "div 10 3" "3" check 31 "div -10 3" "-3" check 32 "rem 10 3" "1" check 33 "rem -10 3" "-1" check 34 "gcd 12 8" "4" # Bitwise check 40 "band 12 10" "8" check 41 "bor 12 10" "14" check 42 "bxor 12 10" "6" check 43 "bnot 0" "-1" check 44 "bsl 1 4" "16" check 45 "bsr 16 2" "4" # Sets check 50 "sets-new is-set?" "true" check 51 "sets add+member" "true" check 52 "member empty" "false" check 53 "from-list member" "true" check 54 "sets-size" "3" check 55 "sets-to-list len" "3" # Regexp check 60 "re-run match" "true" check 61 "re-run no match" "true" check 62 "re-run match text" '"ll"' check 63 "re-replace first" '"herlo"' check 64 "re-replace-all" '"herro"' check 65 "re-match-groups" '"hello"' check 66 "re-split count" "3" # List BIFs check 70 "hd" "1" check 71 "tl" "(2 3)" check 72 "length" "3" check 73 "member hit" "true" check 74 "member miss" "false" check 75 "reverse" "(3 2 1)" check 76 "nth 2" "20" check 77 "foldl sum" "15" check 78 "seq 1..5" "(1 2 3 4 5)" check 79 "flatten" "(1 2 3 4 5)" # Type conversions check 80 "integer-to-list" '"42"' check 81 "list-to-integer" "42" check 82 "integer-to-list hex" '"ff"' check 83 "atom-to-list" '"hello"' check 84 "list-to-atom" "true" # ok/error check 90 "ok? ok-tuple" "true" check 91 "error? error-tuple" "true" check 92 "unwrap ok" "42" check 93 "ok? error-tuple" "false" TOTAL=$((PASS+FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL lib/erlang tests passed" else echo "FAIL $PASS/$TOTAL passed, $FAIL failed:" echo "$ERRORS" fi [ $FAIL -eq 0 ]