# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2021 The Elixir Team
# SPDX-FileCopyrightText: 2012 Plataformatec

Code.require_file("../test_helper.exs", __DIR__)

defmodule ExUnit.AssertionsTest.Value do
  def tuple, do: {2, 1}
  def falsy, do: nil
  def truthy, do: :truthy
  def binary, do: <<5, "Frank the Walrus">>
end

defmodule ExUnit.AssertionsTest.BrokenError do
  defexception [:message]

  @impl true
  def message(_) do
    raise "error"
  end
end

defmodule ExUnit.AssertionsTest.OverrideOperator do
  import Kernel, except: [{:=~, 2}]

  defmacro __using__(_) do
    quote do
      import Kernel, except: [{:=~, 2}]
      import ExUnit.AssertionsTest.OverrideOperator
    end
  end

  defmacro left =~ right do
    quote do
      match?(unquote(left), unquote(right))
    end
  end
end

alias ExUnit.AssertionsTest.{BrokenError, Value}

defmodule ExUnit.AssertionsTest do
  use ExUnit.Case, async: true

  defmacro sigil_l({:<<>>, _, [string]}, _), do: Code.string_to_quoted!(string, [])
  defmacro argless_macro(), do: raise("should not be invoked")

  defmacrop assert_ok(arg) do
    quote do
      assert {:ok, val} = ok(unquote(arg))
    end
  end

  defmacrop assert_ok_with_pin_from_quoted_var(arg) do
    quote do
      kind = :ok
      assert {^kind, value} = unquote(arg)
    end
  end

  require Record
  Record.defrecordp(:vec, x: 0, y: 0, z: 0)

  defguardp is_zero(zero) when zero == 0

  test "direct raise" do
    try do
      raise ExUnit.AssertionError, "This is a test"
    rescue
      error in [ExUnit.AssertionError] ->
        "This is a test" = error.message
    end
  end

  test "assert inside macro" do
    assert_ok(42)
  end

  test "assert inside macro with pins" do
    try do
      assert_ok_with_pin_from_quoted_var({:error, :oops})
    rescue
      error in [ExUnit.AssertionError] ->
        "match (=) failed" = error.message
    end
  end

  test "assert with truthy value" do
    :truthy = assert Value.truthy()
  end

  test "assert with message when value is falsy" do
    try do
      assert Value.falsy(), "This should be truthy"
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "This should be truthy" = error.message
    end
  end

  test "assert when value evaluates to falsy" do
    try do
      assert Value.falsy()
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "assert Value.falsy()" = error.expr |> Macro.to_string()
        "Expected truthy, got nil" = error.message
    end
  end

  test "assert arguments in special form" do
    true =
      assert (case :ok do
                :ok -> true
              end)
  end

  test "assert arguments semantics on function call" do
    x = 1
    true = assert not_equal(x = 2, x)
    2 = x
  end

  test "assert arguments are not kept for operators" do
    try do
      assert !Value.truthy()
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        false = is_list(error.args)
    end
  end

  test "assert with equality" do
    try do
      assert 1 + 1 == 1
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        1 = error.right
        2 = error.left
        "assert 1 + 1 == 1" = error.expr |> Macro.to_string()
    end
  end

  test "assert with equality in reverse" do
    try do
      assert 1 == 1 + 1
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        1 = error.left
        2 = error.right
        "assert 1 == 1 + 1" = error.expr |> Macro.to_string()
    end
  end

  test "assert exposes nested macro variables in matches" do
    assert ~l(a) = 1
    assert a == 1

    assert {~l(b), ~l(c)} = {2, 3}
    assert b == 2
    assert c == 3
  end

  test "assert does not expand variables" do
    assert argless_macro = 1
    assert argless_macro == 1
  end

  defmodule OperatorOverrideTest do
    use ExUnit.Case
    use ExUnit.AssertionsTest.OverrideOperator

    test "assert when operator be overrode" do
      assert [_a, _b] =~ [1, 2]
    end
  end

  test "refute when value is falsy" do
    false = refute false
    nil = refute Value.falsy()
  end

  test "refute when value evaluates to truthy" do
    try do
      refute Value.truthy()
      raise "refute was supposed to fail"
    rescue
      error in [ExUnit.AssertionError] ->
        "refute Value.truthy()" = Macro.to_string(error.expr)
        "Expected false or nil, got :truthy" = error.message
    end
  end

  test "assert match when equal" do
    {2, 1} = assert {2, 1} = Value.tuple()

    # With dup vars
    assert {tuple, tuple} = {Value.tuple(), Value.tuple()}
    assert <<name_size::size(8), _::binary-size(name_size), " the ", _::binary>> = Value.binary()
  end

  test "assert match with unused var" do
    assert ExUnit.CaptureIO.capture_io(:stderr, fn ->
             Code.eval_string("""
             defmodule ExSample do
               import ExUnit.Assertions

               def run do
                 {2, 1} = assert {2, var} = ExUnit.AssertionsTest.Value.tuple()
               end
             end
             """)
           end) =~ "variable \"var\" is unused"
  after
    :code.purge(ExSample)
    :code.delete(ExSample)
  end

  test "assert match with quote on left-side" do
    assert quote(do: x in Alias) = quote(do: x in Alias)
  end

  defmacro quote_like(ast), do: Macro.escape(ast, prune_metadata: true)

  test "assert match with quote-like on left-side" do
    assert quote_like(x in Alias) = quote_like(x in Alias)
  end

  test "assert match expands argument in match context" do
    {x, y, z} = {1, 2, 3}
    assert vec(x: ^x, y: ^y) = vec(x: x, y: y, z: z)
  end

  @test_mod_attribute %{key: :value}
  test "assert match with module attribute" do
    try do
      assert {@test_mod_attribute, 1} = Value.tuple()
    rescue
      error in [ExUnit.AssertionError] ->
        assert "{%{key: :value}, 1}" == Macro.to_string(error.left)
    end
  end

  test "assert parallel match" do
    assert %URI{} = uri = apply(URI, :parse, ["/foo"])
    # This should not warn
    assert %URI{uri | path: "/bar"}.path == "/bar"

    assert uri = %URI{} = apply(URI, :parse, ["/foo"])
    # This should not warn
    assert %URI{uri | path: "/bar"}.path == "/bar"
  end

  test "assert match with pinned variable" do
    a = 1
    {2, 1} = assert {2, ^a} = Value.tuple()

    try do
      assert {^a, 1} = Value.tuple()
    rescue
      error in [ExUnit.AssertionError] ->
        "match (=) failed\n" <> "The following variables were pinned:\n" <> "  a = 1" =
          error.message

        "assert {^a, 1} = Value.tuple()" = Macro.to_string(error.expr)
    end
  end

  test "assert match with pinned variable from another context" do
    var!(a, Elixir) = 1
    {2, 1} = assert {2, ^var!(a, Elixir)} = Value.tuple()

    try do
      assert {^var!(a, Elixir), 1} = Value.tuple()
    rescue
      error in [ExUnit.AssertionError] ->
        "match (=) failed" = error.message
        "assert {^var!(a, Elixir), 1} = Value.tuple()" = Macro.to_string(error.expr)
    end
  end

  test "assert match with `when` in the pattern fails" do
    message = """
    invalid pattern in assert\/1:

      x when is_map(x)

    To assert with guards, use match?/2:

      assert match?(x when is_map(x), %{})
    """

    assert_raise ArgumentError, message, fn ->
      Code.eval_string("""
      defmodule AssertGuard do
        import ExUnit.Assertions

        def run do
          assert (x when is_map(x)) = %{}
        end
      end
      """)
    end
  after
    :code.purge(AssertGuard)
    :code.delete(AssertGuard)
  end

  test "assert match with __ENV__ in the pattern" do
    message =
      ExUnit.CaptureIO.capture_io(:stderr, fn ->
        assert_raise CompileError, fn ->
          Code.eval_string("""
          defmodule EnvMatch do
            import ExUnit.Assertions

            def run do
              assert __ENV__ = %{}
            end
          end
          """)
        end
      end)

    assert message =~ "invalid pattern in match, __ENV__ is not allowed in matches"
  after
    :code.purge(EnvMatch)
    :code.delete(EnvMatch)
  end

  test "assert match?" do
    true = assert match?({2, 1}, Value.tuple())

    try do
      assert match?({:ok, _}, Process.get(:unused, :ok))
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "match (match?) failed" = error.message
        "assert match?({:ok, _}, Process.get(:unused, :ok))" = Macro.to_string(error.expr)
        ":ok" = Macro.to_string(error.right)
    end
  end

  test "assert match? with guards" do
    true = assert match?(tuple when is_tuple(tuple), Value.tuple())

    try do
      assert match?(tuple when not is_tuple(tuple), error(true))
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "match (match?) failed" = error.message

        "assert match?(tuple when not is_tuple(tuple), error(true))" = Macro.to_string(error.expr)

        "{:error, true}" = Macro.to_string(error.right)
    end
  end

  test "refute match?" do
    false = refute match?({1, 1}, Value.tuple())

    try do
      refute match?({:error, _}, error(true))
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "match (match?) succeeded, but should have failed" = error.message
        "refute match?({:error, _}, error(true))" = Macro.to_string(error.expr)
        "{:error, true}" = Macro.to_string(error.right)
    end
  end

  test "assert match? with pinned variable" do
    a = 1

    try do
      assert(match?({^a, 1}, Value.tuple()))
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "match (match?) failed\nThe following variables were pinned:\n  a = 1" = error.message

        "assert match?({^a, 1}, Value.tuple())" = Macro.to_string(error.expr)
    end
  end

  test "refute match? with pinned variable" do
    a = 2

    try do
      refute(match?({^a, 1}, Value.tuple()))
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        """
        match (match?) succeeded, but should have failed
        The following variables were pinned:
          a = 2\
        """ = error.message

        "refute match?({^a, 1}, Value.tuple())" = Macro.to_string(error.expr)
    end
  end

  test "assert receive waits" do
    parent = self()
    spawn(fn -> send(parent, :hello) end)
    :hello = assert_receive :hello
  end

  @string "hello"

  test "assert receive with interpolated compile-time string" do
    parent = self()
    spawn(fn -> send(parent, "string: hello") end)
    "string: #{@string}" = assert_receive "string: #{@string}"
  end

  test "assert receive accepts custom failure message" do
    send(self(), :hello)
    assert_receive message, 0, "failure message"
    :hello = message
  end

  test "assert receive with message in mailbox after timeout, but before reading mailbox tells user to increase timeout" do
    parent = self()
    # This is testing a race condition, so it's not
    # guaranteed this works under all loads of the system
    timeout = 100
    spawn(fn -> Process.send_after(parent, :hello, timeout) end)

    try do
      assert_receive :hello, timeout
    rescue
      error in [ExUnit.AssertionError] ->
        true =
          error.message =~ "Found message matching :hello after 100ms" or
            error.message =~ "no matching message after 100ms"
    end
  end

  test "assert_receive exposes nested macro variables" do
    send(self(), {:hello})
    assert_receive {~l(a)}, 0, "failure message"

    assert a == :hello
  end

  test "assert_receive raises on invalid timeout" do
    timeout = ok(1)

    try do
      assert_receive {~l(_a)}, timeout
    rescue
      error in [ArgumentError] ->
        "timeout must be a non-negative integer, got: {:ok, 1}" = error.message
    end
  end

  test "assert_receive expands argument in match context" do
    {x, y, z} = {1, 2, 3}
    send(self(), vec(x: x, y: y, z: z))
    assert_receive vec(x: ^x, y: ^y)
  end

  test "assert_receive expands argument in guard context" do
    send(self(), {:ok, 0, :other})
    assert_receive {:ok, val, atom} when is_zero(val) and is_atom(atom)
  end

  test "assert received does not wait" do
    send(self(), :hello)
    :hello = assert_received :hello
  end

  @received :hello

  test "assert received with module attribute" do
    send(self(), :hello)
    :hello = assert_received @received
  end

  test "assert received with pinned variable" do
    status = :valid
    send(self(), {:status, :invalid})

    try do
      assert_received {:status, ^status}
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        """
        Assertion failed, no matching message after 0ms
        The following variables were pinned:
          status = :valid
        Showing 1 of 1 message in the mailbox\
        """ = error.message

        "assert_received {:status, ^status}" = Macro.to_string(error.expr)
        "{:status, ^status}" = Macro.to_string(error.left)
    end
  end

  test "assert received with multiple identical pinned variables" do
    status = :valid
    send(self(), {:status, :invalid, :invalid})

    try do
      assert_received {:status, ^status, ^status}
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        """
        Assertion failed, no matching message after 0ms
        The following variables were pinned:
          status = :valid
        Showing 1 of 1 message in the mailbox\
        """ = error.message

        "assert_received {:status, ^status, ^status}" = Macro.to_string(error.expr)
        "{:status, ^status, ^status}" = Macro.to_string(error.left)
        "\n\nAssertion failed" <> _ = Exception.message(error)
    end
  end

  test "assert received with multiple unique pinned variables" do
    status = :valid
    other_status = :invalid
    send(self(), {:status, :invalid, :invalid})

    try do
      assert_received {:status, ^status, ^other_status}
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        """
        Assertion failed, no matching message after 0ms
        The following variables were pinned:
          other_status = :invalid
          status = :valid
        Showing 1 of 1 message in the mailbox\
        """ = error.message

        "assert_received {:status, ^status, ^other_status}" = Macro.to_string(error.expr)
        "{:status, ^status, ^other_status}" = Macro.to_string(error.left)
    end
  end

  test "assert received when empty mailbox" do
    try do
      assert_received :hello
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "Assertion failed, no matching message after 0ms\nThe process mailbox is empty." =
          error.message

        "assert_received :hello" = Macro.to_string(error.expr)
    end
  end

  test "assert received when different message" do
    send(self(), {:message, :not_expected, :at_all})

    try do
      assert_received :hello
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        """
        Assertion failed, no matching message after 0ms
        Showing 1 of 1 message in the mailbox\
        """ = error.message

        "assert_received :hello" = Macro.to_string(error.expr)
        ":hello" = Macro.to_string(error.left)
    end
  end

  test "assert received when different message having more than 10 on mailbox" do
    for i <- 1..11, do: send(self(), {:message, i})

    try do
      assert_received x when x == :hello
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        """
        Assertion failed, no matching message after 0ms
        Showing 10 of 11 messages in the mailbox\
        """ = error.message

        "assert_received x when x == :hello" = Macro.to_string(error.expr)
        "x when x == :hello" = Macro.to_string(error.left)
    end
  end

  test "assert received binds variables" do
    send(self(), {:hello, :world})
    assert_received {:hello, world}
    :world = world
  end

  test "assert received does not leak external variables used in guards" do
    send(self(), {:hello, :world})
    guard_world = :world
    assert_received {:hello, world} when world == guard_world
    :world = world
  end

  test "refute received does not wait" do
    false = refute_received :hello
  end

  test "refute receive waits" do
    false = refute_receive :hello, 100
  end

  test "refute received when equal" do
    send(self(), :hello)

    try do
      refute_received :hello
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "Unexpectedly received message :hello (which matched :hello)" = error.message
    end
  end

  test "assert in when member" do
    true = assert ~c"foo" in [~c"foo", ~c"bar"]
  end

  test "assert in when is not member" do
    try do
      assert ~c"foo" in ~c"bar"
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        ~c"foo" = error.left
        ~c"bar" = error.right
        ~S(assert ~c"foo" in ~c"bar") = Macro.to_string(error.expr)
    end
  end

  test "refute in when is not member" do
    false = refute ~c"baz" in [~c"foo", ~c"bar"]
  end

  test "refute in when is member" do
    try do
      refute ~c"foo" in [~c"foo", ~c"bar"]
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        ~c"foo" = error.left
        [~c"foo", ~c"bar"] = error.right
        ~S(refute ~c"foo" in [~c"foo", ~c"bar"]) = Macro.to_string(error.expr)
    end
  end

  test "assert match" do
    {:ok, true} = assert {:ok, _} = ok(true)
  end

  test "assert match with bitstrings" do
    "foobar" = assert "foo" <> bar = "foobar"
    "bar" = bar
  end

  test "assert match when no match" do
    try do
      assert {:ok, _} = error(true)
    rescue
      error in [ExUnit.AssertionError] ->
        "match (=) failed" = error.message
        "assert {:ok, _} = error(true)" = Macro.to_string(error.expr)
        "{:error, true}" = Macro.to_string(error.right)
    end
  end

  test "assert match when falsy but not match" do
    try do
      assert {:ok, _x} = nil
    rescue
      error in [ExUnit.AssertionError] ->
        "match (=) failed" = error.message
        "assert {:ok, _x} = nil" = Macro.to_string(error.expr)
        "nil" = Macro.to_string(error.right)
    end
  end

  test "assert match when falsy" do
    try do
      assert _x = nil
    rescue
      error in [ExUnit.AssertionError] ->
        "Expected truthy, got nil" = error.message
        "assert _x = nil" = Macro.to_string(error.expr)
    end
  end

  test "refute match when no match" do
    try do
      refute _ = ok(true)
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "refute _ = ok(true)" = Macro.to_string(error.expr)
        "Expected false or nil, got {:ok, true}" = error.message
    end
  end

  test "assert regex match" do
    true = assert "foo" =~ ~r/o/
  end

  test "assert regex match when no match" do
    try do
      assert "foo" =~ ~r/a/
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "foo" = error.left
        %Regex{} = error.right
    end
  end

  test "refute regex match" do
    false = refute "foo" =~ ~r/a/
  end

  test "refute regex match when match" do
    try do
      refute "foo" =~ ~r/o/
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "foo" = error.left
        %Regex{} = error.right
    end
  end

  test "assert raise with no error" do
    assert_raise ArgumentError, fn -> nil end
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      "Expected exception ArgumentError but nothing was raised" = error.message
  end

  test "assert raise with error" do
    error = assert_raise ArgumentError, fn -> raise ArgumentError, "test error" end
    "test error" = error.message
  end

  @compile {:no_warn_undefined, Not.Defined}

  test "assert raise with some other error" do
    "This should never be tested" =
      assert_raise ArgumentError, fn -> Not.Defined.function(1, 2, 3) end
  rescue
    error in [ExUnit.AssertionError] ->
      "Expected exception ArgumentError but got UndefinedFunctionError " <>
        "(function Not.Defined.function/3 is undefined (module Not.Defined is not available))" =
        error.message
  end

  test "assert raise with some other error includes stacktrace from original error" do
    "This should never be tested" =
      assert_raise ArgumentError, fn -> Not.Defined.function(1, 2, 3) end
  rescue
    ExUnit.AssertionError ->
      [{Not.Defined, :function, [1, 2, 3], _} | _] = __STACKTRACE__
  end

  test "assert raise with Erlang error" do
    assert_raise SyntaxError, fn ->
      List.flatten(1)
    end
  rescue
    error in [ExUnit.AssertionError] ->
      "Expected exception SyntaxError but got FunctionClauseError (no function clause matching in :lists.flatten/1)" =
        error.message
  end

  test "assert raise comparing messages (for equality)" do
    assert_raise RuntimeError, "foo", fn ->
      raise RuntimeError, "bar"
    end
  rescue
    error in [ExUnit.AssertionError] ->
      """
      Wrong message for RuntimeError
      expected:
        "foo"
      actual:
        "bar"\
      """ = error.message
  end

  test "assert raise comparing messages (with a regex)" do
    assert_raise RuntimeError, ~r/ba[zk]/, fn ->
      raise RuntimeError, "bar"
    end
  rescue
    error in [ExUnit.AssertionError] ->
      """
      Wrong message for RuntimeError
      expected:
        ~r/ba[zk]/
      actual:
        "bar"\
      """ = error.message
  end

  test "assert raise with an exception with bad message/1 implementation" do
    assert_raise BrokenError, fn ->
      raise BrokenError
    end
  rescue
    error in [ExUnit.AssertionError] ->
      """
      Got exception ExUnit.AssertionsTest.BrokenError but it failed to produce a message with:

      ** (RuntimeError) error
      """ <> _ = error.message
  end

  test "assert greater-than operator" do
    true = assert 2 > 1
  end

  test "assert greater-than operator error" do
    assert 1 > 2
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      1 = error.left
      2 = error.right
      "assert 1 > 2" = Macro.to_string(error.expr)
  end

  test "assert less or equal than operator" do
    true = assert 1 <= 2
  end

  test "assert less or equal than operator error" do
    assert 2 <= 1
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      "assert 2 <= 1" = Macro.to_string(error.expr)
      2 = error.left
      1 = error.right
  end

  test "assert operator with expressions" do
    greater = 5
    true = assert 1 + 2 < greater
  end

  test "assert operator with custom message" do
    assert 1 > 2, "assertion"
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      "assertion" = error.message
  end

  test "assert operator with custom options" do
    assert 1 > 2, message: "assertion"
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      "assertion" = error.message
  end

  test "assert operator with invalid options" do
    assert 1 > 2, message: 123
    flunk("This should never be tested")
  rescue
    error in [ArgumentError] ->
      ":message in ExUnit.Assertion must be a binary, got: 123" = error.message
  end

  test "assert lack of equality" do
    try do
      assert "one" != "one"
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "Assertion with != failed, both sides are exactly equal" = error.message
        "one" = error.left
    end

    try do
      assert 2 != 2.0
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "Assertion with != failed" = error.message
        2 = error.left
        2.0 = error.right
    end
  end

  test "refute equality" do
    try do
      refute "one" == "one"
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "Refute with == failed, both sides are exactly equal" = error.message
        "one" = error.left
    end

    try do
      refute 2 == 2.0
      flunk("This should never be tested")
    rescue
      error in [ExUnit.AssertionError] ->
        "Refute with == failed" = error.message
        2 = error.left
        2.0 = error.right
    end
  end

  test "assert in delta" do
    true = assert_in_delta(1.1, 1.2, 0.2)
  end

  test "assert in delta raises when passing a negative delta" do
    assert_raise ArgumentError, fn ->
      assert_in_delta(1.1, 1.2, -0.2)
    end
  end

  test "assert in delta works with equal values and a delta of zero" do
    assert_in_delta(10, 10, 0)
  end

  test "assert in delta error" do
    assert_in_delta(10, 12, 1)
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      "Expected the difference between 10 and 12 (2) to be less than or equal to 1" =
        error.message
  end

  test "assert in delta with message" do
    assert_in_delta(10, 12, 1, "test message")
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      "test message" = error.message
  end

  test "refute in delta" do
    false = refute_in_delta(1.1, 1.5, 0.2)
  end

  test "refute in delta error" do
    refute_in_delta(10, 11, 2)
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      "Expected the difference between 10 and 11 (1) to be more than 2" = error.message
  end

  test "refute in delta with message" do
    refute_in_delta(10, 11, 2, "test message")
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      "test message (difference between 10 and 11 is less than 2)" = error.message
  end

  test "catch_throw with no throw" do
    catch_throw(1)
  rescue
    error in [ExUnit.AssertionError] ->
      "Expected to catch throw, got nothing" = error.message
  end

  test "catch_error with no error" do
    catch_error(1)
  rescue
    error in [ExUnit.AssertionError] ->
      "Expected to catch error, got nothing" = error.message
  end

  test "catch_exit with no exit" do
    catch_exit(1)
  rescue
    error in [ExUnit.AssertionError] ->
      "Expected to catch exit, got nothing" = error.message
  end

  test "catch_throw with throw" do
    1 = catch_throw(throw(1))
  end

  test "catch_exit with exit" do
    1 = catch_exit(exit(1))
  end

  test "catch_error with error" do
    :function_clause = catch_error(List.flatten(1))
  end

  test "flunk" do
    flunk()
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      "Flunked!" = error.message
  end

  test "flunk with message" do
    flunk("This should raise an error")
    flunk("This should never be tested")
  rescue
    error in [ExUnit.AssertionError] ->
      "This should raise an error" = error.message
  end

  test "flunk with wrong argument type" do
    flunk(["flunk takes a binary, not a list"])
    flunk("This should never be tested")
  rescue
    error ->
      "no function clause matching in ExUnit.Assertions.flunk/1" =
        FunctionClauseError.message(error)
  end

  test "AssertionError.message/1 is nicely formatted" do
    assert :a = :b
  rescue
    error in [ExUnit.AssertionError] ->
      """


      match (=) failed
      code:  assert :a = :b
      left:  :a
      right: :b
      """ = Exception.message(error)
  end

  defp ok(val), do: {:ok, val}
  defp error(val), do: {:error, val}
  defp not_equal(left, right), do: left != right
end
