Bootstrap compiler (reader, analyzer, transformer, compiler, Mix plugin), core protocols (16 protocols for Map/List/Tuple/BitString), PersistentVector (bit-partitioned trie), domain tools (clojurify/elixirify), BEAM concurrency (receive, spawn, GenServer), control flow & macros (threading, try/catch, destructuring, defmacro with quasiquote/auto-gensym), and Malli schema adapter (m/=> specs, auto @type, recursive schemas, cross-references). 537 compiler tests + 55 Malli unit tests + 15 integration tests = 607 total. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
134 lines
3.4 KiB
Elixir
134 lines
3.4 KiB
Elixir
defmodule CljElixir.Phase5Test do
|
|
use ExUnit.Case, async: false
|
|
|
|
# Helper to compile and evaluate CljElixir code
|
|
defp eval!(source) do
|
|
case CljElixir.Compiler.eval_string(source) do
|
|
{:ok, result, _bindings} -> result
|
|
{:error, errors} -> raise "CljElixir eval error: #{inspect(errors)}"
|
|
end
|
|
end
|
|
|
|
# ==========================================================================
|
|
# GenServer integration
|
|
# ==========================================================================
|
|
|
|
describe "GenServer - simple counter" do
|
|
test "define and use a counter GenServer" do
|
|
eval!("""
|
|
(defmodule TestCounter
|
|
(use GenServer)
|
|
|
|
(defn init [initial]
|
|
#el[:ok initial])
|
|
|
|
(defn handle-call
|
|
([:get _from state]
|
|
#el[:reply state state])
|
|
([:increment _from state]
|
|
#el[:reply :ok (+ state 1)])))
|
|
""")
|
|
|
|
{:ok, pid} = GenServer.start_link(TestCounter, 0)
|
|
assert GenServer.call(pid, :get) == 0
|
|
assert GenServer.call(pid, :increment) == :ok
|
|
assert GenServer.call(pid, :get) == 1
|
|
GenServer.stop(pid)
|
|
end
|
|
end
|
|
|
|
describe "GenServer - handle_cast" do
|
|
test "cast resets state" do
|
|
eval!("""
|
|
(defmodule TestCaster
|
|
(use GenServer)
|
|
|
|
(defn init [initial]
|
|
#el[:ok initial])
|
|
|
|
(defn handle-call
|
|
([:get _from state]
|
|
#el[:reply state state]))
|
|
|
|
(defn handle-cast
|
|
([:reset _state]
|
|
#el[:noreply 0])))
|
|
""")
|
|
|
|
{:ok, pid} = GenServer.start_link(TestCaster, 42)
|
|
assert GenServer.call(pid, :get) == 42
|
|
GenServer.cast(pid, :reset)
|
|
Process.sleep(50)
|
|
assert GenServer.call(pid, :get) == 0
|
|
GenServer.stop(pid)
|
|
end
|
|
end
|
|
|
|
describe "GenServer - handle_info" do
|
|
test "handle-info receives plain messages" do
|
|
eval!("""
|
|
(defmodule TestInfoHandler
|
|
(use GenServer)
|
|
|
|
(defn init [initial]
|
|
#el[:ok initial])
|
|
|
|
(defn handle-call
|
|
([:get _from state]
|
|
#el[:reply state state]))
|
|
|
|
(defn handle-info
|
|
([:bump state]
|
|
#el[:noreply (+ state 1)])))
|
|
""")
|
|
|
|
{:ok, pid} = GenServer.start_link(TestInfoHandler, 0)
|
|
send(pid, :bump)
|
|
Process.sleep(50)
|
|
assert GenServer.call(pid, :get) == 1
|
|
GenServer.stop(pid)
|
|
end
|
|
end
|
|
|
|
# ==========================================================================
|
|
# ChatRoom pattern: spawn + send + receive loop
|
|
# ==========================================================================
|
|
|
|
describe "ChatRoom pattern" do
|
|
test "spawn + send + receive loop" do
|
|
eval!("""
|
|
(defmodule TestChatLoop
|
|
(defn loop [state]
|
|
(receive
|
|
[:ping pid]
|
|
(do
|
|
(send pid #el[:pong state])
|
|
(TestChatLoop/loop (+ state 1)))
|
|
[:get pid]
|
|
(do
|
|
(send pid #el[:count state])
|
|
(TestChatLoop/loop state))
|
|
:stop
|
|
:stopped
|
|
:after 5000
|
|
:timeout)))
|
|
""")
|
|
|
|
pid = spawn(fn -> TestChatLoop.loop(0) end)
|
|
|
|
send(pid, {:ping, self()})
|
|
assert_receive {:pong, 0}, 1000
|
|
|
|
send(pid, {:ping, self()})
|
|
assert_receive {:pong, 1}, 1000
|
|
|
|
send(pid, {:get, self()})
|
|
assert_receive {:count, 2}, 1000
|
|
|
|
send(pid, :stop)
|
|
Process.sleep(50)
|
|
refute Process.alive?(pid)
|
|
end
|
|
end
|
|
end
|