<commit_before>Code.require_file "../test_helper.exs", __DIR__

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

  import ExUnit.Formatter
  doctest ExUnit.Formatter

  defmacrop catch_assertion(expr) do
    quote do
      try do
        unquote(expr)
      rescue
        ex -> ex
      end
    end
  end

  defp test_case do
    %ExUnit.TestCase{name: Hello}
  end

  defp test do
    %ExUnit.Test{name: :world, case: Hello, tags: %{file: __ENV__.file, line: 1}}
  end

  def falsy() do
    false
  end

  defp formatter(_kind, message) do
    message
  end

  defp trim_multiline_whitespace(string) do
    String.replace(string, ~r"\n\s+\n", "\n\n")
  end

  test "formats test case filters" do
    filters = [run: true, slow: false]
    assert format_filters(filters, :include) =~ "Including tags: [run: true, slow: false]"
    assert format_filters(filters, :exclude) =~ "Excluding tags: [run: true, slow: false]"
  end

  test "formats test errors" do
    failure = [{:error, catch_error(raise "oops"), []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) =~ """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (RuntimeError) oops
    """
  end

  test "formats test exits" do
    failure = [{:exit, 1, []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (exit) 1
    """
  end

  test "formats test exits with mfa" do
    failure = [{:exit, {:bye, {:mod, :fun, []}}, []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (exit) exited in: :mod.fun()
             ** (EXIT) :bye
    """
  end

  test "formats test throws" do
    failure = [{:throw, 1, []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (throw) 1
    """
  end

  test "formats test EXITs" do
    failure = [{{:EXIT, self()}, 1, []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (EXIT from #{inspect self()}) 1
    """
  end

  test "formats test errors with code snippets" do
    stack = {Hello, :world, 1, [file: __ENV__.file, line: 3]}
    failure = [{:error, catch_error(raise "oops"), [stack]}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) =~ """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (RuntimeError) oops
         code: defmodule ExUnit.FormatterTest do
    """
  end

  test "formats reports" do
    test = test()
    failure = [{:error, catch_error(raise "oops"), []}]

    test = update_in test.tags,
           &Map.merge(&1, %{user_id: 1, report: :user_id})
    assert format_test_failure(test, failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (RuntimeError) oops
         tags:
           user_id: 1
    """

    test = update_in test.tags,
           &Map.merge(&1, %{many_ids: Enum.to_list(1..30), report: [:user_id, :many_ids]})
    assert format_test_failure(test, failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (RuntimeError) oops
         tags:
           many_ids: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
                      19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
           user_id: 1
    """
  end

  test "formats stacktraces" do
    failure = [{:error, catch_error(raise "oops"), [{Oops, :wrong, 1, [file: "formatter_test.exs", line: 1]}]}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) =~ """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (RuntimeError) oops
         stacktrace:
           formatter_test.exs:1: Oops.wrong/1
    """
  end

  test "formats assertions" do
    failure = [{:error, catch_assertion(assert ExUnit.FormatterTest.falsy), []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) =~ """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         Expected truthy, got false
         code: ExUnit.FormatterTest.falsy()
    """
  end

  test "formats multiple assertions" do
    failure = [{:error, catch_assertion(assert ExUnit.FormatterTest.falsy), []},
               {:error, catch_assertion(assert 1 == 2), []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) =~ """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1

         Failure #1
         Expected truthy, got false
         code: ExUnit.FormatterTest.falsy()

         Failure #2
         Assertion with == failed
         code:  1 == 2
         left:  1
         right: 2
    """
  end

  # TODO: Remove this check once we depend only on 20
  if :erlang.system_info(:otp_release) >= '20' do
    test "blames function clause error" do
      {error, stack} =
        try do
          Access.fetch(:foo, :bar)
        rescue<commit_msg>Move a private function around so that it's always used in ExUnit tests (#6176)<commit_after>Code.require_file "../test_helper.exs", __DIR__

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

  import ExUnit.Formatter
  doctest ExUnit.Formatter

  defmacrop catch_assertion(expr) do
    quote do
      try do
        unquote(expr)
      rescue
        ex -> ex
      end
    end
  end

  defp test_case do
    %ExUnit.TestCase{name: Hello}
  end

  defp test do
    %ExUnit.Test{name: :world, case: Hello, tags: %{file: __ENV__.file, line: 1}}
  end

  def falsy() do
    false
  end

  defp formatter(_kind, message) do
    message
  end

  test "formats test case filters" do
    filters = [run: true, slow: false]
    assert format_filters(filters, :include) =~ "Including tags: [run: true, slow: false]"
    assert format_filters(filters, :exclude) =~ "Excluding tags: [run: true, slow: false]"
  end

  test "formats test errors" do
    failure = [{:error, catch_error(raise "oops"), []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) =~ """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (RuntimeError) oops
    """
  end

  test "formats test exits" do
    failure = [{:exit, 1, []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (exit) 1
    """
  end

  test "formats test exits with mfa" do
    failure = [{:exit, {:bye, {:mod, :fun, []}}, []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (exit) exited in: :mod.fun()
             ** (EXIT) :bye
    """
  end

  test "formats test throws" do
    failure = [{:throw, 1, []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (throw) 1
    """
  end

  test "formats test EXITs" do
    failure = [{{:EXIT, self()}, 1, []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (EXIT from #{inspect self()}) 1
    """
  end

  test "formats test errors with code snippets" do
    stack = {Hello, :world, 1, [file: __ENV__.file, line: 3]}
    failure = [{:error, catch_error(raise "oops"), [stack]}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) =~ """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (RuntimeError) oops
         code: defmodule ExUnit.FormatterTest do
    """
  end

  test "formats reports" do
    test = test()
    failure = [{:error, catch_error(raise "oops"), []}]

    test = update_in test.tags,
           &Map.merge(&1, %{user_id: 1, report: :user_id})
    assert format_test_failure(test, failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (RuntimeError) oops
         tags:
           user_id: 1
    """

    test = update_in test.tags,
           &Map.merge(&1, %{many_ids: Enum.to_list(1..30), report: [:user_id, :many_ids]})
    assert format_test_failure(test, failure, 1, 80, &formatter/2) == """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (RuntimeError) oops
         tags:
           many_ids: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
                      19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
           user_id: 1
    """
  end

  test "formats stacktraces" do
    failure = [{:error, catch_error(raise "oops"), [{Oops, :wrong, 1, [file: "formatter_test.exs", line: 1]}]}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) =~ """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         ** (RuntimeError) oops
         stacktrace:
           formatter_test.exs:1: Oops.wrong/1
    """
  end

  test "formats assertions" do
    failure = [{:error, catch_assertion(assert ExUnit.FormatterTest.falsy), []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) =~ """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1
         Expected truthy, got false
         code: ExUnit.FormatterTest.falsy()
    """
  end

  test "formats multiple assertions" do
    failure = [{:error, catch_assertion(assert ExUnit.FormatterTest.falsy), []},
               {:error, catch_assertion(assert 1 == 2), []}]
    assert format_test_failure(test(), failure, 1, 80, &formatter/2) =~ """
      1) world (Hello)
         test/ex_unit/formatter_test.exs:1

         Failure #1
         Expected truthy, got false
         code: ExUnit.FormatterTest.falsy()

         Failure #2
         Assertion with == failed
         code:  1 == 2
         left:  1
         right: 2
    """
  end

  # TODO: Remove this check once we depend only on 20
  if :erlang.system_info(:otp_release) >= '20' do
    defp trim_multiline_whitespace(string) do
      String.replace(string, ~r"\n\s+\n", "\n\n")
    end

    test "blames function clause error" do
      {error, stack} =
        try do
          Access.fetch(:foo, :bar)
        rescue
{'fraction_non_alphanumeric': 0.14595736508546187, 'fraction_numerical': 0.029191473017092375, 'mean_word_length': 2.5043741588156125, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5820620', 'n_tokens_mistral': 3863, 'n_tokens_neox': 3521, 'n_words': 946}