Lockstep.Rewriter (Lockstep v0.1.0)

Copy Markdown View Source

Compile-time AST rewriter that converts vanilla OTP calls into their Lockstep.* equivalents inside a ctest body. Lets test bodies read like ordinary Elixir without sacrificing controlled scheduling.

Enabled with use Lockstep.Test, rewrite: true. With rewrite: true the linter is also disabled (since the rewritten body is correct by construction).

Mappings

vanilla                          rewritten
-------                          ---------
GenServer.call(s, m)             Lockstep.GenServer.call(s, m)
GenServer.cast(s, m)             Lockstep.GenServer.cast(s, m)
GenServer.start_link(M, A)       Lockstep.GenServer.start_link(M, A)
Task.async(fn -> ... end)        Lockstep.Task.async(fn -> ... end)
Task.await(t)                    Lockstep.Task.await(t)
Task.await_many(ts)              Lockstep.Task.await_many(ts)
spawn(fun)                       Lockstep.spawn(fun)
spawn_link(fun)                  Lockstep.spawn_link(fun)
send(p, m)                       Lockstep.send(p, m)
Kernel.send(p, m)                Lockstep.send(p, m)
:erlang.send(p, m)               Lockstep.send(p, m)
Process.send_after(p, m, ms)     Lockstep.send_after(p, m, ms)
Process.cancel_timer(ref)        Lockstep.cancel_timer(ref)
Process.monitor(p)               Lockstep.monitor(p)
Process.demonitor(ref[, opts])   Lockstep.demonitor(ref[, opts])
Process.alive?(p)                Lockstep.alive?(p)
Process.sleep(ms)                Lockstep.sleep(ms)
Process.link(p)                  Lockstep.link(p)
Process.unlink(p)                Lockstep.unlink(p)
Process.flag(:trap_exit, b)      Lockstep.flag(:trap_exit, b)
receive do clauses end           rewritten to Lockstep.recv_first/1

Limitations

  • Only the body of ctest is rewritten, not helper functions defined elsewhere. Inline race code into the test body, or call Lockstep wrappers directly in helpers.
  • receive ... after t -> ... end (bare receive with timeout) is not yet rewritten -- bare receives without after are.
  • :erlang.spawn/3 (the 3-arg MFA form) is not rewritten.

Functions defined locally that happen to be named send/2 etc. will be rewritten too. Suppress by qualifying the call (e.g. MyMod.send(...) is left alone).

Summary

Functions

Walk ast and return the rewritten AST.

Functions

rewrite(ast)

Walk ast and return the rewritten AST.