aboutsummaryrefslogtreecommitdiff
path: root/lib/ct_formatter.ex
blob: 0c301353b896185460ac09b69596a671295788fe (plain) (blame)
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
defmodule ExUnit.CTFormatter do
  @moduledoc false

  use GenEvent

  import ExUnit.Formatter, only: [format_time: 2, format_test_failure: 5,
                                  format_test_case_failure: 5]

  def init(opts) do
    file = File.open! "exunit.log", [:append]
    # We do not print filter in log file as exclusion of test with tag
    # pending: true is always done
    config = %{
      file: file,
      seed: opts[:seed],
      trace: opts[:trace],
      colors: Keyword.put_new(opts[:colors], :enabled, false),
      width: 80,
      tests_counter: 0,
      failures_counter: 0,
      skipped_counter: 0,
      invalids_counter: 0
    }
    {:ok, config}
  end

  def handle_event({:suite_started, _opts}, config) do
    {:ok, config}
  end

  def handle_event({:suite_finished, run_us, load_us}, config) do
    print_suite(config, run_us, load_us)
    File.close config[:file]
    :remove_handler
  end

  def handle_event({:test_started, %ExUnit.Test{} = test}, config) do
    if config.tests_counter == 0, do: IO.binwrite config[:file], "== Running #{test.case} ==\n\n"
    {:ok, config}
  end

  def handle_event({:test_finished, %ExUnit.Test{state: nil} = _test}, config) do
    IO.binwrite config[:file], "."
    {:ok, %{config | tests_counter: config.tests_counter + 1}}
  end

  def handle_event({:test_finished, %ExUnit.Test{state: {:skip, _}} = _test}, config) do
    {:ok, %{config | tests_counter: config.tests_counter + 1,
                     skipped_counter: config.skipped_counter + 1}}
  end

  def handle_event({:test_finished, %ExUnit.Test{state: {:invalid, _}} = _test}, config) do
    IO.binwrite config[:file], "?"
    {:ok, %{config | tests_counter: config.tests_counter + 1,
                     invalids_counter: config.invalids_counter + 1}}
  end

  def handle_event({:test_finished, %ExUnit.Test{state: {:failed, failures}} = test}, config) do
    formatted = format_test_failure(test, failures, config.failures_counter + 1,
                                    config.width, &formatter(&1, &2, config))
    print_failure(formatted, config)
    print_logs(test.logs)

    {:ok, %{config | tests_counter: config.tests_counter + 1,
                     failures_counter: config.failures_counter + 1}}
  end

  def handle_event({:case_started, %ExUnit.TestCase{}}, config) do
    {:ok, config}
  end

  def handle_event({:case_finished, %ExUnit.TestCase{state: nil}}, config) do
    {:ok, config}
  end

  def handle_event({:case_finished, %ExUnit.TestCase{state: {:failed, failures}} = test_case}, config) do
    formatted = format_test_case_failure(test_case, failures, config.failures_counter + 1,
                                         config.width, &formatter(&1, &2, config))
    print_failure(formatted, config)
    {:ok, %{config | failures_counter: config.failures_counter + 1}}
  end

  ## Printing

  defp print_suite(config, run_us, load_us) do
    IO.binwrite config[:file], "\n\n"
    IO.binwrite config[:file], format_time(run_us, load_us)
    IO.binwrite config[:file], "\n\n"

    # singular/plural
    test_pl = pluralize(config.tests_counter, "test", "tests")
    failure_pl = pluralize(config.failures_counter, "failure", "failures")

    message =
      "#{config.tests_counter} #{test_pl}, #{config.failures_counter} #{failure_pl}"
      |> if_true(config.skipped_counter > 0, & &1 <> ", #{config.skipped_counter} skipped")
      |> if_true(config.invalids_counter > 0, & &1 <> ", #{config.invalids_counter} invalid")

    cond do
      config.failures_counter > 0 -> IO.binwrite config[:file], message
      config.invalids_counter > 0 -> IO.binwrite config[:file], message
      true                        -> IO.binwrite config[:file], message
    end

    IO.binwrite config[:file], "\nRandomized with seed #{config.seed}\n\n\n\n"
  end

  defp if_true(value, false, _fun), do: value
  defp if_true(value, true, fun), do: fun.(value)

  defp print_failure(formatted, config) do
    IO.binwrite config[:file], "\n"
    IO.binwrite config[:file], formatted
    IO.binwrite config[:file], "\n"
  end

  defp formatter(_,  msg, _config),
    do: msg

  defp pluralize(1, singular, _plural), do: singular
  defp pluralize(_, _singular, plural), do: plural

  defp print_logs(""), do: nil

  defp print_logs(output) do
    indent = "\n     "
    output = String.replace(output, "\n", indent)
    IO.puts(["     The following output was logged:", indent | output])
  end
end