# `ExUnit.Formatter`
[🔗](https://github.com/elixir-lang/elixir/blob/v1.20.5/lib/ex_unit/lib/ex_unit/formatter.ex#L5)

Helper functions for formatting and the formatting protocols.

Formatters are `GenServer`s specified during ExUnit configuration
that receive a series of events as casts.

The following events are possible:

  * `{:suite_started, opts}` -
    the suite has started with the specified options to the runner.

  * `{:suite_finished, times_us}` -
    the suite has finished. Returns several measurements in microseconds
    for running the suite. See `t:times_us/0` for more information.

  * `{:module_started, test_module}` -
    a test module has started. See `ExUnit.TestModule` for details.

  * `{:module_finished, test_module}` -
    a test module has finished. See `ExUnit.TestModule` for details.

  * `{:test_started, test}` -
    a test has started. See `ExUnit.Test` for details.

  * `{:test_finished, test}` -
    a test has finished. See `ExUnit.Test` for details.

  * `{:sigquit, [test | test_module]}` -
    the VM is going to shutdown. It receives the test cases (or test
    module in case of `setup_all`) still running.

  * `:max_failures_reached` -
    the test run has been aborted due to reaching max failures limit set
    with `:max_failures` option.

The formatter will also receive the following events but they are deprecated
and should be ignored:

  * `{:case_started, test_module}` -
    a test module has started. See `ExUnit.TestModule` for details.

  * `{:case_finished, test_module}` -
    a test module has finished. See `ExUnit.TestModule` for details.

The full ExUnit configuration is passed as the argument to `c:GenServer.init/1`
callback when the formatters are started. If you need to do runtime configuration
of a formatter, you can add any configuration needed by using `ExUnit.configure/1`
or `ExUnit.start/1`, and this will then be included in the options passed to
the `c:GenServer.init/1` callback.

# `formatter_callback`
*since 1.16.0* 

```elixir
@type formatter_callback() ::
  (:diff_enabled?, boolean() -&gt; boolean())
  | (formatter_callback_diff_key(), Inspect.Algebra.t() -&gt; Inspect.Algebra.t())
  | (formatter_callback_info_key(), String.t() -&gt; String.t())
```

A function that this module calls to format various things.

You can pass this functions to various functions in this module, and use it
to customize the formatting of the output. For example, ExUnit's CLI formatter
uses this callback to colorize output.

## Keys

The possible keys are:

  * `:diff_enabled?` - whether diffing is enabled. It receives a boolean
    indicating whether diffing is enabled by default and returns a boolean
    indicating whether diffing should be enabled for the current test.

  * `:diff_delete` and `:diff_delete_whitespace` - Should format a diff deletion,
    with or without whitespace respectively.

  * `:diff_insert` and `:diff_insert_whitespace` - Should format a diff insertion,
    with or without whitespace respectively.

  * `:extra_info` - Should format optional extra labels, such as the `"code: "` label
    that precedes code to show.

  * `:error_info` - Should format error information.

  * `:test_module_info` - Should format test module information. The message returned
  when this key is passed precedes messages such as `"failure on setup_all callback [...]"`.

  * `:test_info` - Should format test information.

  * `:parameters_info` - Should format test parameters.

  * `:location_info` - Should format test location information.

  * `:stacktrace_info` - Should format stacktrace information.

  * `:blame_diff` - Should format a string of code.

## Examples

For example, to format errors as *red strings* and everything else as is, you could define
a formatter callback function like this:

    formatter_callback = fn
      :error_info, msg -> [:red, msg, :reset] |> IO.ANSI.format() |> IO.iodata_to_binary()
      _key, value -> value
    end

# `formatter_callback_diff_key`
*since 1.16.0* 

```elixir
@type formatter_callback_diff_key() ::
  :diff_delete
  | :diff_delete_whitespace
  | :diff_insert
  | :diff_insert_whitespace
```

Key passed to a formatter callback to format a diff.

See `t:formatter_callback/0`.

# `formatter_callback_info_key`
*since 1.16.0* 

```elixir
@type formatter_callback_info_key() ::
  :extra_info
  | :error_info
  | :test_module_info
  | :test_info
  | :parameters_info
  | :location_info
  | :stacktrace_info
  | :blame_diff
```

Key passed to a formatter callback to format information.

See `t:formatter_callback/0`.

# `id`

```elixir
@type id() :: term()
```

# `test`

```elixir
@type test() :: ExUnit.Test.t()
```

# `times_us`

```elixir
@type times_us() :: %{
  run: pos_integer(),
  async: pos_integer() | nil,
  load: pos_integer() | nil
}
```

The times spent on several parts of the test suite.

The following properties can be computed:

    sync = run - (async || 0)
    total = run + (load || 0)

`async` is nil when there are no async tests.
`load` is nil when the test suite is running and loading
tests concurrently.

# `width`
*since 1.16.0* 

```elixir
@type width() :: non_neg_integer() | :infinity
```

Width for formatting.

For example, see `format_assertion_diff/4`.

# `format_assertion_diff`

```elixir
@spec format_assertion_diff(
  ExUnit.AssertionError.t(),
  non_neg_integer(),
  width(),
  formatter_callback()
) ::
  keyword()
```

Formats `ExUnit.AssertionError` diff.

It returns a keyword list with diffing information
from the left and right side of the assertion, if
any exists.

It expects the assertion error, the `padding_size`
for formatted content, the width (may be `:infinity`),
and the formatter callback function.

## Examples

    iex> error = assert_raise ExUnit.AssertionError, fn -> assert [1, 2] == [1, 3] end
    iex> formatter_cb = fn
    ...>   :diff_enabled?, _ -> true
    ...>   _key, value -> value
    ...> end
    iex> keyword = format_assertion_diff(error, 5, 80, formatter_cb)
    iex> for {key, val} <- keyword, do: {key, IO.iodata_to_binary(val)}
    [left: "[1, 2]", right: "[1, 3]"]

# `format_filters`

```elixir
@spec format_filters(keyword(), atom()) :: String.t()
```

Formats filters used to constrain cases to be run.

## Examples

    iex> format_filters([run: true, slow: false], :include)
    "Including tags: [run: true, slow: false]"

    iex> format_filters([list: [61, 62, 63]], :exclude)
    "Excluding tags: [list: [61, 62, 63]]"

# `format_test_all_failure`

```elixir
@spec format_test_all_failure(
  ExUnit.TestModule.t(),
  [failure],
  non_neg_integer(),
  width(),
  formatter_callback()
) :: String.t()
when failure: {atom(), term(), Exception.stacktrace()}
```

Receives a test module and formats its failure.

## Examples

    iex> failure = {:error, catch_error(raise "oops"), _stacktrace = []}
    iex> formatter_cb = fn _key, value -> value end
    iex> test_module = %ExUnit.TestModule{name: Hello}
    iex> format_test_all_failure(test_module, [failure], 1, 80, formatter_cb)
    "  1) Hello: failure on setup_all callback, all tests have been invalidated\n     ** (RuntimeError) oops\n"

# `format_test_failure`

```elixir
@spec format_test_failure(
  test(),
  [failure],
  non_neg_integer(),
  width(),
  formatter_callback()
) ::
  String.t()
when failure: {atom(), term(), Exception.stacktrace()}
```

Receives a test and formats its failures.

## Examples

    iex> failure = {:error, catch_error(raise "oops"), _stacktrace = []}
    iex> formatter_cb = fn _key, value -> value end
    iex> test = %ExUnit.Test{name: :"it works", description: "it works", module: MyTest, tags: %{file: "file.ex", line: 7}}
    iex> format_test_failure(test, [failure], 1, 80, formatter_cb)
    "  1) it works (MyTest)\n     file.ex:7\n     ** (RuntimeError) oops\n"

# `format_times`

```elixir
@spec format_times(times_us()) :: String.t()
```

Formats time taken running the test suite.

## Examples

    iex> format_times(%{run: 10_000, async: nil, load: nil})
    "Finished in 0.01 seconds (0.00s async, 0.01s sync)"

    iex> format_times(%{run: 10_000, async: nil, load: 20_000})
    "Finished in 0.03 seconds (0.02s on load, 0.00s async, 0.01s sync)"

    iex> format_times(%{run: 10_000, async: nil, load: 200_000})
    "Finished in 0.2 seconds (0.2s on load, 0.00s async, 0.01s sync)"

    iex> format_times(%{run: 100_000, async: 50_000, load: 200_000})
    "Finished in 0.3 seconds (0.2s on load, 0.05s async, 0.05s sync)"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
