Numeric tower (is-integer?/float?/number?, float/trunc/round/abs/max/min), div/rem (quotient/remainder), bitwise (band/bor/bxor/bnot/bsl/bsr), sets module (new/add/member/union/intersection/subtract/size/to-list/from-list), re module (run/replace/replace-all/match-groups/split), list BIFs (hd/tl/length/member/reverse/nth/foldl/foldr/seq/flatten/zip), type conversions (integer-to-list, list-to-integer, atom-to-list, etc.), ok/error tuple helpers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
261 lines
6.7 KiB
Bash
Executable File
261 lines
6.7 KiB
Bash
Executable File
#!/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="<no output for epoch $epoch>"
|
|
|
|
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 ]
|