ExUnit CaptureIO and c.flush

Hey everyone,

I have test that check if messages have been sent to a process and to make sure I test against the one I want, I clear out the others. Previously I was using lib.flush_receive/0 but that has since been removed. It looks like c.flush/0 is the replacement. One thing that I dislike about c.flush/0 is that it outputs the messages which makes looking at test failures very tedious. I tried combining the call with ExUnit.CaptureIO.capture_io/1 but I’m getting ArgumentErrors, from seemingly c.flush/0 itself?

Here’s the code I’m using:

   try do
      capture_io(&:c.flush/0)
    rescue
      ArgumentError ->
        IO.puts "Error flushing mailbox!"
        IO.inspect __STACKTRACE__
    end

and the output looks like

Error flushing mailbox!
[
{:lists, :keyfind, [:encoding, 1, {:ok, [binary: true, encoding: :unicode]}],
},
{:c, :flush, 0, [file: ‘c.erl’, line: 819]},
{ExUnit.CaptureIO, :do_capture_io, 2,
[file: ‘lib/ex_unit/capture_io.ex’, line: 151]},
{ExUnit.CaptureIO, :do_capture_io, 3,
[file: ‘lib/ex_unit/capture_io.ex’, line: 121]},
{PlexServer.EmpireChannelUtils, :flush_mailbox, 1,
[file: ‘test/channels/empire_channel_utils.exs’, line: 54]},
{PlexServer.EmpireChannelTest, :“test succesful action: spend invade moves”,
1, [file: ‘test/channels/empire_channel_test.exs’, line: 567]},
{ExUnit.Runner, :exec_test, 1, [file: ‘lib/ex_unit/runner.ex’, line: 355]},
{:timer, :tc, 1, [file: ‘timer.erl’, line: 166]},
{ExUnit.Runner, :“-spawn_test_monitor/4-fun-1-”, 4,
[file: ‘lib/ex_unit/runner.ex’, line: 306]}
]

Digging into the c.flush/0 source, it seems that it calls io.getopts/0 and that’s returning a tuple rather than just an array? Why would this work without the capture_io/1?

Not really sure how to proceed. Any ideas?

Probably not, look at this remark from the modules documentation:

These functions are intended for interactive use in the Erlang shell only. The module prefix can be omitted.

Also :c.flush/0 shouldn’t return a tuple or a list (I assume thats what you are talking about when saying “array”), it should just return the atom :ok.

If you really want to flush the mailbox, you can implement this really easily:

def flush() do
  receive do
    _ -> flush()
  after
    0 -> :ok
  end
end

You can easily extend it to actually print or return the “flushed” messages if you need to.

1 Like

Sorry, I was referring to io.getopts/0 which :c.flush/0 calls. Here’s what that looks like

flush() ->
    receive
	X ->
            case lists:keyfind(encoding, 1, io:getopts()) of
                {encoding,unicode} ->
                    format("Shell got ~tp~n",[X]);
                _ ->
                    format("Shell got ~p~n",[X])
            end,
	    flush()
    after 0 ->
	    ok
    end.

I think I’ll give what you suggested a try and see how that plays out. I’m still curious why :c.flush/0 seems to not work within capture_io, but anyways. Thanks!