erlang: extract ffi test suite (637/637, ffi 14/14)
This commit is contained in:
@@ -36,6 +36,7 @@ SUITES=(
|
||||
"bank|er-bank-test-pass|er-bank-test-count"
|
||||
"echo|er-echo-test-pass|er-echo-test-count"
|
||||
"fib|er-fib-test-pass|er-fib-test-count"
|
||||
"ffi|er-ffi-test-pass|er-ffi-test-count"
|
||||
)
|
||||
|
||||
cat > "$TMPFILE" << 'EPOCHS'
|
||||
@@ -56,6 +57,7 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(load "lib/erlang/tests/programs/bank.sx")
|
||||
(load "lib/erlang/tests/programs/echo.sx")
|
||||
(load "lib/erlang/tests/programs/fib_server.sx")
|
||||
(load "lib/erlang/tests/ffi.sx")
|
||||
(epoch 100)
|
||||
(eval "(list er-test-pass er-test-count)")
|
||||
(epoch 101)
|
||||
@@ -74,6 +76,8 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(eval "(list er-echo-test-pass er-echo-test-count)")
|
||||
(epoch 108)
|
||||
(eval "(list er-fib-test-pass er-fib-test-count)")
|
||||
(epoch 109)
|
||||
(eval "(list er-ffi-test-pass er-ffi-test-count)")
|
||||
EPOCHS
|
||||
|
||||
timeout 600 "$SX_SERVER" < "$TMPFILE" > "$OUTFILE" 2>&1
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"language": "erlang",
|
||||
"total_pass": 633,
|
||||
"total": 633,
|
||||
"total_pass": 637,
|
||||
"total": 637,
|
||||
"suites": [
|
||||
{"name":"tokenize","pass":62,"total":62,"status":"ok"},
|
||||
{"name":"parse","pass":52,"total":52,"status":"ok"},
|
||||
{"name":"eval","pass":395,"total":395,"status":"ok"},
|
||||
{"name":"eval","pass":385,"total":385,"status":"ok"},
|
||||
{"name":"runtime","pass":93,"total":93,"status":"ok"},
|
||||
{"name":"ring","pass":4,"total":4,"status":"ok"},
|
||||
{"name":"ping-pong","pass":4,"total":4,"status":"ok"},
|
||||
{"name":"bank","pass":8,"total":8,"status":"ok"},
|
||||
{"name":"echo","pass":7,"total":7,"status":"ok"},
|
||||
{"name":"fib","pass":8,"total":8,"status":"ok"}
|
||||
{"name":"fib","pass":8,"total":8,"status":"ok"},
|
||||
{"name":"ffi","pass":14,"total":14,"status":"ok"}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
# Erlang-on-SX Scoreboard
|
||||
|
||||
**Total: 633 / 633 tests passing**
|
||||
**Total: 637 / 637 tests passing**
|
||||
|
||||
| | Suite | Pass | Total |
|
||||
|---|---|---|---|
|
||||
| ✅ | tokenize | 62 | 62 |
|
||||
| ✅ | parse | 52 | 52 |
|
||||
| ✅ | eval | 395 | 395 |
|
||||
| ✅ | eval | 385 | 385 |
|
||||
| ✅ | runtime | 93 | 93 |
|
||||
| ✅ | ring | 4 | 4 |
|
||||
| ✅ | ping-pong | 4 | 4 |
|
||||
| ✅ | bank | 8 | 8 |
|
||||
| ✅ | echo | 7 | 7 |
|
||||
| ✅ | fib | 8 | 8 |
|
||||
| ✅ | ffi | 14 | 14 |
|
||||
|
||||
|
||||
Generated by `lib/erlang/conformance.sh`.
|
||||
|
||||
@@ -1341,54 +1341,6 @@
|
||||
(get (nth (get er-rt-cap-result :elements) 4) :name) "true")
|
||||
|
||||
|
||||
;; ── Phase 8: file module BIFs ───────────────────────────────────
|
||||
(er-modules-reset!)
|
||||
|
||||
;; write + read round-trip
|
||||
(er-eval-test "file:write_file ok"
|
||||
(nm (ev "file:write_file(\"/tmp/er-test-1.txt\", \"hello\")"))
|
||||
"ok")
|
||||
|
||||
(er-eval-test "file:read_file ok tag"
|
||||
(nm (ev "element(1, file:read_file(\"/tmp/er-test-1.txt\"))"))
|
||||
"ok")
|
||||
|
||||
(er-eval-test "file:read_file payload is binary"
|
||||
(ev "case file:read_file(\"/tmp/er-test-1.txt\") of {ok, B} -> is_binary(B) end")
|
||||
(er-mk-atom "true"))
|
||||
|
||||
(er-eval-test "file:read_file content bytes"
|
||||
(ev "case file:read_file(\"/tmp/er-test-1.txt\") of {ok, B} -> byte_size(B) end")
|
||||
5)
|
||||
|
||||
;; missing file → {error, enoent}
|
||||
(er-eval-test "file:read_file missing tag"
|
||||
(nm (ev "element(1, file:read_file(\"/tmp/er-no-such-file-xyz\"))"))
|
||||
"error")
|
||||
|
||||
(er-eval-test "file:read_file missing reason"
|
||||
(nm (ev "element(2, file:read_file(\"/tmp/er-no-such-file-xyz\"))"))
|
||||
"enoent")
|
||||
|
||||
;; delete
|
||||
(er-eval-test "file:delete ok"
|
||||
(nm (ev "file:write_file(\"/tmp/er-test-del.txt\", \"x\"), file:delete(\"/tmp/er-test-del.txt\")"))
|
||||
"ok")
|
||||
|
||||
(er-eval-test "file:read_file after delete"
|
||||
(nm (ev "file:write_file(\"/tmp/er-test-del2.txt\", \"x\"), file:delete(\"/tmp/er-test-del2.txt\"), element(2, file:read_file(\"/tmp/er-test-del2.txt\"))"))
|
||||
"enoent")
|
||||
|
||||
;; write to inaccessible dir → {error, enoent}
|
||||
(er-eval-test "file:write_file bad path"
|
||||
(nm (ev "element(2, file:write_file(\"/tmp/no-such-dir-xyz/x\", \"y\"))"))
|
||||
"enoent")
|
||||
|
||||
;; binary input round-trip (the bytes go through write)
|
||||
(er-eval-test "file:write_file binary payload round-trip"
|
||||
(ev "file:write_file(\"/tmp/er-test-2.bin\", <<1, 2, 3, 4, 5>>), case file:read_file(\"/tmp/er-test-2.bin\") of {ok, B} -> byte_size(B) end")
|
||||
5)
|
||||
|
||||
(define
|
||||
er-eval-test-summary
|
||||
(str "eval " er-eval-test-pass "/" er-eval-test-count))
|
||||
|
||||
113
lib/erlang/tests/ffi.sx
Normal file
113
lib/erlang/tests/ffi.sx
Normal file
@@ -0,0 +1,113 @@
|
||||
;; Phase 8 FFI BIF tests — one round-trip per BIF.
|
||||
;; Each BIF lives in lib/erlang/runtime.sx (registered with
|
||||
;; er-bif-registry) and wraps an SX-host primitive.
|
||||
|
||||
(define er-ffi-test-count 0)
|
||||
(define er-ffi-test-pass 0)
|
||||
(define er-ffi-test-fails (list))
|
||||
|
||||
(define
|
||||
er-ffi-test
|
||||
(fn
|
||||
(name actual expected)
|
||||
(set! er-ffi-test-count (+ er-ffi-test-count 1))
|
||||
(if
|
||||
(= actual expected)
|
||||
(set! er-ffi-test-pass (+ er-ffi-test-pass 1))
|
||||
(append! er-ffi-test-fails {:name name :expected expected :actual actual}))))
|
||||
|
||||
(define ffi-ev erlang-eval-ast)
|
||||
(define ffi-nm (fn (v) (get v :name)))
|
||||
|
||||
;; ── file:read_file/1 + file:write_file/2 ────────────────────────
|
||||
(er-ffi-test
|
||||
"file:write_file ok"
|
||||
(ffi-nm (ffi-ev "file:write_file(\"/tmp/er-ffi-1.txt\", \"hello\")"))
|
||||
"ok")
|
||||
|
||||
(er-ffi-test
|
||||
"file:read_file ok tag"
|
||||
(ffi-nm (ffi-ev "element(1, file:read_file(\"/tmp/er-ffi-1.txt\"))"))
|
||||
"ok")
|
||||
|
||||
(er-ffi-test
|
||||
"file:read_file payload is binary"
|
||||
(ffi-nm
|
||||
(ffi-ev
|
||||
"case file:read_file(\"/tmp/er-ffi-1.txt\") of {ok, B} -> is_binary(B) end"))
|
||||
"true")
|
||||
|
||||
(er-ffi-test
|
||||
"file:read_file content byte_size"
|
||||
(ffi-ev
|
||||
"case file:read_file(\"/tmp/er-ffi-1.txt\") of {ok, B} -> byte_size(B) end")
|
||||
5)
|
||||
|
||||
(er-ffi-test
|
||||
"file:read_file missing enoent"
|
||||
(ffi-nm (ffi-ev "element(2, file:read_file(\"/tmp/er-ffi-no-such-xyz\"))"))
|
||||
"enoent")
|
||||
|
||||
(er-ffi-test
|
||||
"file:write_file bad path enoent"
|
||||
(ffi-nm
|
||||
(ffi-ev "element(2, file:write_file(\"/tmp/er-ffi-no-dir-xyz/x\", \"y\"))"))
|
||||
"enoent")
|
||||
|
||||
(er-ffi-test
|
||||
"file:write_file binary payload"
|
||||
(ffi-ev
|
||||
"file:write_file(\"/tmp/er-ffi-2.bin\", <<1, 2, 3, 4, 5>>), case file:read_file(\"/tmp/er-ffi-2.bin\") of {ok, B} -> byte_size(B) end")
|
||||
5)
|
||||
|
||||
;; ── file:delete/1 ────────────────────────────────────────────────
|
||||
(er-ffi-test
|
||||
"file:delete ok"
|
||||
(ffi-nm
|
||||
(ffi-ev
|
||||
"file:write_file(\"/tmp/er-ffi-del.txt\", \"x\"), file:delete(\"/tmp/er-ffi-del.txt\")"))
|
||||
"ok")
|
||||
|
||||
(er-ffi-test
|
||||
"file:read_file after delete enoent"
|
||||
(ffi-nm
|
||||
(ffi-ev
|
||||
"file:write_file(\"/tmp/er-ffi-del2.txt\", \"x\"), file:delete(\"/tmp/er-ffi-del2.txt\"), element(2, file:read_file(\"/tmp/er-ffi-del2.txt\"))"))
|
||||
"enoent")
|
||||
|
||||
;; ── Blocked BIFs (placeholder asserts so the suite documents intent) ──
|
||||
;; crypto:hash/2, cid:from_bytes/1, cid:to_string/1, file:list_dir/1,
|
||||
;; httpc:request/4, sqlite:* — documented in plans/erlang-on-sx.md
|
||||
;; under Blockers. When the host runtime gains the underlying primitive,
|
||||
;; the wrappers land in runtime.sx and tests appear here. For now we
|
||||
;; assert each is NOT registered, so a future iteration that adds them
|
||||
;; without updating this file fails fast.
|
||||
|
||||
(er-ffi-test
|
||||
"crypto:hash unregistered"
|
||||
(er-lookup-bif "crypto" "hash" 2)
|
||||
nil)
|
||||
|
||||
(er-ffi-test
|
||||
"cid:from_bytes unregistered"
|
||||
(er-lookup-bif "cid" "from_bytes" 1)
|
||||
nil)
|
||||
|
||||
(er-ffi-test
|
||||
"file:list_dir unregistered"
|
||||
(er-lookup-bif "file" "list_dir" 1)
|
||||
nil)
|
||||
|
||||
(er-ffi-test
|
||||
"httpc:request unregistered"
|
||||
(er-lookup-bif "httpc" "request" 4)
|
||||
nil)
|
||||
|
||||
(er-ffi-test
|
||||
"sqlite:exec unregistered"
|
||||
(er-lookup-bif "sqlite" "exec" 2)
|
||||
nil)
|
||||
|
||||
(define
|
||||
er-ffi-test-summary
|
||||
(str "ffi " er-ffi-test-pass "/" er-ffi-test-count))
|
||||
Reference in New Issue
Block a user