Current section
Files
Jump to
Current section
Files
lib/assert_async.ex
defmodule AssertAsync do
@moduledoc """
Assert results with retries
"""
require Logger
@doc """
This does the same as assert, but when it fails it retries up to 5 times and
waiting for 100 ms. To wait for the javascript to finish
"""
defmacro assert_eventually(assertion, opts \\ []) do
quote do
assert AssertAsync.retry(
fn -> !!unquote(assertion) end,
unquote(opts)
)
end
end
def retry(func, opts \\ [])
def retry(func, %{retries: 1}), do: func.()
def retry(func, %{step: step, evaluator: evaluator} = opts) do
value = safe_call(func)
case evaluator.(value) do
true ->
value
false ->
wait_and_recurse(func, opts)
%RuntimeError{message: message} ->
Logger.info("""
Error on #{step_to_ordinal(step)} try with following message:
#{message}
STACKTRACE:
#{Exception.format_stacktrace()}
""")
wait_and_recurse(func, opts)
end
end
def retry(func, opts) do
# just add default values
retry(
func,
Enum.into(opts, %{
retries: 8,
step_time: 100,
step: 1,
evaluator: & &1
})
)
end
defp wait_and_recurse(func, %{
retries: retries,
step_time: step_time,
step: step,
evaluator: evaluator
}) do
wait_for(step_time) &&
retry(func, %{
retries: retries - 1,
step_time: step_time * step,
step: step + 1,
evaluator: evaluator
})
end
defp safe_call(func) do
func.()
rescue
e in RuntimeError -> e
end
defp wait_for(milliseconds) do
case :timer.sleep(milliseconds) do
:ok ->
true
# this should never happen
_ ->
false
end
end
defp step_to_ordinal(1), do: "1st"
defp step_to_ordinal(2), do: "2nd"
defp step_to_ordinal(3), do: "3rd"
defp step_to_ordinal(n), do: "#{n}th"
end