;; 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") (er-ffi-test "crypto:hash sha256 -> 32-byte binary" (ffi-ev "byte_size(crypto:hash(sha256, <<97,98,99>>))") 32) (er-ffi-test "crypto:hash sha512 -> 64-byte binary" (ffi-ev "byte_size(crypto:hash(sha512, <<97,98,99>>))") 64) (er-ffi-test "crypto:hash sha3_256 is_binary" (ffi-nm (ffi-ev "is_binary(crypto:hash(sha3_256, <<120>>))")) "true") (er-ffi-test "crypto:hash deterministic" (ffi-nm (ffi-ev "crypto:hash(sha256, <<97>>) =:= crypto:hash(sha256, <<97>>)")) "true") (er-ffi-test "crypto:hash distinct inputs distinct digests" (ffi-nm (ffi-ev "crypto:hash(sha256, <<97>>) =/= crypto:hash(sha256, <<98>>)")) "true") (er-ffi-test "crypto:hash bad type -> error:badarg" (ffi-nm (ffi-ev "try crypto:hash(md5, <<120>>) catch error:badarg -> ok end")) "ok") (er-ffi-test "cid:from_bytes is_binary" (ffi-nm (ffi-ev "is_binary(cid:from_bytes(<<97,98,99>>))")) "true") (er-ffi-test "cid:from_bytes deterministic" (ffi-nm (ffi-ev "cid:from_bytes(<<97,98,99>>) =:= cid:from_bytes(<<97,98,99>>)")) "true") (er-ffi-test "cid:from_bytes distinct inputs distinct CIDs" (ffi-nm (ffi-ev "cid:from_bytes(<<97,98,99>>) =/= cid:from_bytes(<<97,98,100>>)")) "true") (er-ffi-test "cid:from_bytes non-binary -> error:badarg" (ffi-nm (ffi-ev "try cid:from_bytes(42) catch error:badarg -> ok end")) "ok") (er-ffi-test "cid:to_string is_binary" (ffi-nm (ffi-ev "is_binary(cid:to_string({ok, 42}))")) "true") (er-ffi-test "cid:to_string deterministic" (ffi-nm (ffi-ev "cid:to_string(foo) =:= cid:to_string(foo)")) "true") (er-ffi-test "cid:to_string distinct terms distinct CIDs" (ffi-nm (ffi-ev "cid:to_string(foo) =/= cid:to_string(bar)")) "true") (er-ffi-test "file:list_dir ok tag" (ffi-nm (ffi-ev "element(1, file:list_dir(\"lib/erlang\"))")) "ok") (er-ffi-test "file:list_dir non-empty" (ffi-nm (ffi-ev "case file:list_dir(\"lib/erlang\") of {ok, L} -> length(L) > 3 end")) "true") (er-ffi-test "file:list_dir entries are binaries" (ffi-nm (ffi-ev "case file:list_dir(\"lib/erlang\") of {ok, L} -> is_binary(hd(L)) end")) "true") (er-ffi-test "file:list_dir missing enoent" (ffi-nm (ffi-ev "element(2, file:list_dir(\"/no/such/dir/xyz\"))")) "enoent") ;; ── Still deferred (no host primitive): httpc (HTTP client, v2), ;; sqlite-* (v2 indexes). Assert NOT registered so a future iteration ;; that wires them without updating this suite fails fast. (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))