Add an assert_receive to ExUnit that allows specifying which PID received the message

33 views
Skip to first unread message

Adam Lancaster

unread,
Jun 2, 2020, 4:26:40 PM6/2/20
to elixir-lang-core
It would be great if we could expose a function that allowed us to say something like this:

defmodule MyGen do
 
use GenServer

  @impl true
 
def init(data) do
   
{:ok, data}
 
end
end

{:ok, pid} = MyGen.start_link(MyGen, :hi)

assert_receive
(pid, :hello)




I recently learned you can do something like this:

:erlang.trace(pid, true, [:receive])

So I wondered if that or :erlang.trace_delivered may be helpful here.

Best

Adam

Aaron Ross

unread,
Jun 2, 2020, 5:26:12 PM6/2/20
to elixir-lang-core
`:erlang.trace/3` allows a process to trace _all_ other process message send/receives by passing `:processes` as the first argument. Depending on how much overhead that adds, this could be called when any test process that includes a `assert_pid_receive` is started. I'm not familiar with ExUnit's internals, but the implementation could look something like

```elixir
# somewhere during setup
Module.register_attribute(__MODULE__, :needs_trace, accumulate: true)

defmacro assert_pid_receive(pid, message, timeout \\ 5000) do
  quote do
    Module.put_attribute(__MODULE__, :needs_trace, __EX_UNIT_TEST_NAME__)
    receive do
      {:trace, unquote(pid), :receive, unquote(message)} ->
        :ok
    after
      unquote(timeout) ->
        {:error, :timeout}
end

# somewhere in test process bootstrap
if (__EX_UNIT_TEST_NAME__ in @needs_trace) do
  :erlang.trace(:processes, true, [:receive])
end
```

Aaron Ross

unread,
Jun 2, 2020, 5:28:40 PM6/2/20
to elixir-lang-core
# somewhere during setup
Module.register_attribute(__MODULE__, :needs_trace, accumulate: true)

defmacro assert_pid_receive
(pid, message, timeout \\ 5000) do
  quote
do
   
Module.put_attribute(__MODULE__, :needs_trace, __EX_UNIT_TEST_NAME__)
    receive
do
     
{:trace, unquote(pid), :receive, unquote(message)} ->
       
:ok
    after
      unquote
(timeout) ->
       
{:error, :timeout}
 
end
end

# somewhere in test process bootstrap
if (__EX_UNIT_TEST_NAME__ in @needs_trace) do
 
:erlang.trace(:processes, true, [:receive])
end

fixed the formatting

On Tuesday, June 2, 2020 at 1:26:40 PM UTC-7, Adam Lancaster wrote:
Reply all
Reply to author
Forward
0 new messages