Skip to content

Commit b0a5197

Browse files
author
José Valim
committed
Convert ExUnit.Test and ExUnit.TestCase to structs
1 parent c5ffa76 commit b0a5197

File tree

11 files changed

+118
-74
lines changed

11 files changed

+118
-74
lines changed

lib/elixir/lib/kernel/cli.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ defmodule Kernel.CLI do
8888
IO.puts :stderr, Exception.format(kind, reason, prune_stacktrace(trace))
8989
end
9090

91-
@elixir_internals [:elixir_compiler, :elixir_module]
91+
@elixir_internals [:elixir_compiler, :elixir_module, :elixir_translator, :elixir_expand]
9292

9393
defp prune_stacktrace([{mod, _, _, _}|t]) when mod in @elixir_internals do
9494
prune_stacktrace(t)

lib/elixir/lib/kernel/parallel_compiler.ex

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,28 @@ defmodule Kernel.ParallelCompiler do
201201

202202
defp print_failure(file, {:failure, kind, reason, stacktrace}) do
203203
IO.puts "\n== Compilation error on file #{Path.relative_to_cwd(file)} =="
204-
IO.puts Exception.format(kind, reason, stacktrace)
204+
IO.puts Exception.format(kind, reason, prune_stacktrace(stacktrace))
205205
end
206206

207207
defp print_failure(file, reason) do
208208
IO.puts "\n== Compilation error on file #{Path.relative_to_cwd(file)} =="
209209
IO.puts Exception.format(:exit, reason, [])
210210
end
211211

212+
@elixir_internals [:elixir_compiler, :elixir_module, :elixir_translator, :elixir_expand]
213+
214+
defp prune_stacktrace([{mod, _, _, _}|t]) when mod in @elixir_internals do
215+
prune_stacktrace(t)
216+
end
217+
218+
defp prune_stacktrace([h|t]) do
219+
[h|prune_stacktrace(t)]
220+
end
221+
222+
defp prune_stacktrace([]) do
223+
[]
224+
end
225+
212226
defp all_missing?(entries, waiting, queued) do
213227
entries == [] and waiting != [] and
214228
length(waiting) == length(queued)

lib/elixir/src/elixir_map.erl

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -89,33 +89,42 @@ translate_struct(Meta, Name, {'%{}', MapMeta, Args}, S) ->
8989

9090
%% Helpers
9191

92-
load_struct(Meta, Name, S) ->
92+
load_struct(Meta, Module, S) ->
9393
Local =
94-
elixir_module:is_open(Name) andalso
94+
elixir_module:is_open(Module) andalso
9595
(case lists:keyfind(struct, 1, Meta) of
9696
{struct, context} -> true;
97-
_ -> wait_for_struct(Name)
97+
_ -> wait_for_struct(Module)
9898
end),
9999

100-
try
101-
case Local of
102-
true ->
103-
try
104-
(elixir_locals:local_for(Name, '__struct__', 0, def))()
105-
catch
106-
error:undef -> Name:'__struct__'();
107-
error:badarg -> Name:'__struct__'()
108-
end;
109-
false ->
110-
Name:'__struct__'()
111-
end
112-
catch
113-
error:undef ->
114-
Inspected = elixir_aliases:inspect(Name),
115-
compile_error(Meta, S#elixir_scope.file, "~ts.__struct__/0 is undefined, "
116-
"cannot expand struct ~ts", [Inspected, Inspected])
100+
case Local of
101+
true ->
102+
try
103+
(elixir_locals:local_for(Module, '__struct__', 0, def))()
104+
catch
105+
error:undef -> get_struct(Meta, Module, S);
106+
error:badarg -> get_struct(Meta, Module, S)
107+
end;
108+
false ->
109+
get_struct(Meta, Module, S)
110+
end.
111+
112+
get_struct(Meta, Module, S) ->
113+
case code:ensure_loaded(Module) of
114+
{module, Module} ->
115+
case erlang:function_exported(Module, '__struct__', 0) of
116+
true -> Module:'__struct__'();
117+
false -> raise_struct(Meta, Module, S)
118+
end;
119+
{error, _} ->
120+
raise_struct(Meta, Module, S)
117121
end.
118122

123+
raise_struct(Meta, Module, S) ->
124+
Inspected = elixir_aliases:inspect(Module),
125+
compile_error(Meta, S#elixir_scope.file, "~ts.__struct__/0 is undefined, "
126+
"cannot expand struct ~ts", [Inspected, Inspected]).
127+
119128
wait_for_struct(Module) ->
120129
case erlang:get(elixir_compiler_pid) of
121130
undefined ->

lib/ex_unit/lib/ex_unit.ex

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,27 +57,44 @@ defmodule ExUnit do
5757
files. See `Mix.Tasks.Test` for more information.
5858
"""
5959

60-
@typedoc "The state returned by ExUnit.Test and ExUnit.TestCase."
60+
@typedoc "The state returned by ExUnit.Test and ExUnit.TestCase"
6161
@type state :: nil | {:failed, failed} | {:skip, binary} | {:invalid, module}
6262
@type failed :: {:error | :exit | :throw | :EXIT, reason :: term, stacktrace :: [tuple]}
6363

64-
defrecord Test, [:name, :case, :state, :time, :tags] do
64+
defmodule Test do
6565
@moduledoc """
66-
A record that keeps information about the test.
67-
It is received by formatters and also accessible
68-
in the metadata under the key `:test`.
66+
A struct that keeps information about the test.
67+
68+
It is received by formatters and contains the following fields:
69+
70+
* `:name` - the test name
71+
* `:case` - the test case
72+
* `:state` - the test state (see ExUnit.state)
73+
* `:time` - the time to run the test
74+
* `:tags` - the test tags
75+
6976
"""
70-
record_type name: atom, case: module, state: ExUnit.state,
71-
time: non_neg_integer, tags: Keyword.t
77+
defstruct name: nil :: atom,
78+
case: nil :: module,
79+
state: nil :: ExUnit.state,
80+
time: 0 :: non_neg_integer,
81+
tags: %{} :: map
7282
end
7383

74-
defrecord TestCase, [:name, :state, :tests] do
84+
defmodule TestCase do
7585
@moduledoc """
76-
A record that keeps information about the test case.
77-
It is received by formatters and also accessible
78-
in the metadata under the key `:case`.
86+
A struct that keeps information about the test case.
87+
88+
It is received by formatters and contains the following fields:
89+
90+
* `:name` - the test case name
91+
* `:state` - the test state (see ExUnit.state)
92+
* `:tests` - all tests for this case
93+
7994
"""
80-
record_type name: module, state: ExUnit.state, tests: [ExUnit.Test.t]
95+
defstruct name: nil :: module,
96+
state: nil :: ExUnit.state,
97+
tests: [] :: [ExUnit.Test.t]
8198
end
8299

83100
use Application

lib/ex_unit/lib/ex_unit/case.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ defmodule ExUnit.Case do
225225
defmacro __before_compile__(_) do
226226
quote do
227227
def __ex_unit__(:case) do
228-
ExUnit.TestCase[name: __MODULE__, tests: @ex_unit_tests]
228+
%ExUnit.TestCase{name: __MODULE__, tests: @ex_unit_tests}
229229
end
230230
end
231231
end
@@ -237,7 +237,7 @@ defmodule ExUnit.Case do
237237
tags = [line: env.line, file: env.file] ++ normalize_tags(tags)
238238

239239
Module.put_attribute(mod, :ex_unit_tests,
240-
ExUnit.Test[name: name, case: mod, tags: tags])
240+
%ExUnit.Test{name: name, case: mod, tags: tags})
241241

242242
Module.delete_attribute(mod, :tag)
243243
end

lib/ex_unit/lib/ex_unit/cli_formatter.ex

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ defmodule ExUnit.CLIFormatter do
2121
:remove_handler
2222
end
2323

24-
def handle_event({:test_started, ExUnit.Test[] = test}, config) do
24+
def handle_event({:test_started, %ExUnit.Test{} = test}, config) do
2525
if config.trace, do: IO.write " * #{trace_test_name test}"
2626
{:ok, config}
2727
end
2828

29-
def handle_event({:test_finished, ExUnit.Test[state: nil] = test}, config) do
29+
def handle_event({:test_finished, %ExUnit.Test{state: nil} = test}, config) do
3030
if config.trace do
3131
IO.puts success(trace_test_result(test), config)
3232
else
@@ -35,12 +35,12 @@ defmodule ExUnit.CLIFormatter do
3535
{:ok, config.update_tests_counter(&(&1 + 1))}
3636
end
3737

38-
def handle_event({:test_finished, ExUnit.Test[state: {:skip, _}] = test}, config) do
38+
def handle_event({:test_finished, %ExUnit.Test{state: {:skip, _}} = test}, config) do
3939
if config.trace, do: IO.puts trace_test_skip(test)
4040
{:ok, config}
4141
end
4242

43-
def handle_event({:test_finished, ExUnit.Test[state: {:invalid, _}] = test}, config) do
43+
def handle_event({:test_finished, %ExUnit.Test{state: {:invalid, _}} = test}, config) do
4444
if config.trace do
4545
IO.puts invalid(trace_test_result(test), config)
4646
else
@@ -51,7 +51,7 @@ defmodule ExUnit.CLIFormatter do
5151
.update_invalids_counter(&(&1 + 1))}
5252
end
5353

54-
def handle_event({:test_finished, ExUnit.Test[state: {:failed, failed}] = test}, config) do
54+
def handle_event({:test_finished, %ExUnit.Test{state: {:failed, failed}} = test}, config) do
5555
if config.trace do
5656
IO.puts failure(trace_test_result(test), config)
5757
end
@@ -64,19 +64,19 @@ defmodule ExUnit.CLIFormatter do
6464
.update_failures_counter(&(&1 + 1))}
6565
end
6666

67-
def handle_event({:case_started, ExUnit.TestCase[] = test_case}, config) do
67+
def handle_event({:case_started, %ExUnit.TestCase{name: name}}, config) do
6868
if config.trace do
69-
IO.puts("\n#{inspect test_case.name}")
69+
IO.puts("\n#{inspect name}")
7070
end
7171

7272
{:ok, config}
7373
end
7474

75-
def handle_event({:case_finished, ExUnit.TestCase[state: nil]}, config) do
75+
def handle_event({:case_finished, %ExUnit.TestCase{state: nil}}, config) do
7676
{:ok, config}
7777
end
7878

79-
def handle_event({:case_finished, ExUnit.TestCase[state: {:failed, failed}] = test_case}, config) do
79+
def handle_event({:case_finished, %ExUnit.TestCase{state: {:failed, failed}} = test_case}, config) do
8080
formatted = format_test_case_failure(test_case, failed, config.failures_counter + 1,
8181
config.width, &formatter(&1, &2, config))
8282
print_failure(formatted, config)
@@ -89,14 +89,14 @@ defmodule ExUnit.CLIFormatter do
8989

9090
## Tracing
9191

92-
defp trace_test_name(ExUnit.Test[name: name]) do
92+
defp trace_test_name(%ExUnit.Test{name: name}) do
9393
case atom_to_binary(name) do
9494
"test " <> rest -> rest
9595
rest -> rest
9696
end
9797
end
9898

99-
defp trace_test_time(ExUnit.Test[time: time]) do
99+
defp trace_test_time(%ExUnit.Test{time: time}) do
100100
"#{format_us(time)}ms"
101101
end
102102

lib/ex_unit/lib/ex_unit/formatter.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ defmodule ExUnit.Formatter do
101101
Receives a test and formats its failure.
102102
"""
103103
def format_test_failure(test, {kind, reason, stack}, counter, width, formatter) do
104-
ExUnit.Test[name: name, case: case, tags: tags] = test
104+
%ExUnit.Test{name: name, case: case, tags: tags} = test
105105
test_info(with_counter(counter, "#{name} (#{inspect case})"), formatter)
106106
<> test_location(with_location(tags), formatter)
107107
<> format_kind_reason(kind, reason, width, formatter)
@@ -112,7 +112,7 @@ defmodule ExUnit.Formatter do
112112
Receives a test case and formats its failure.
113113
"""
114114
def format_test_case_failure(test_case, {kind, reason, stacktrace}, counter, width, formatter) do
115-
ExUnit.TestCase[name: name] = test_case
115+
%ExUnit.TestCase{name: name} = test_case
116116
test_case_info(with_counter(counter, "#{inspect name}: "), formatter)
117117
<> format_kind_reason(kind, reason, width, formatter)
118118
<> format_stacktrace(stacktrace, name, nil, formatter)

lib/ex_unit/lib/ex_unit/runner.ex

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ defmodule ExUnit.Runner do
9595
end
9696

9797
defp run_case(config, pid, case_name) do
98-
ExUnit.TestCase[] = test_case = case_name.__ex_unit__(:case)
98+
test_case = case_name.__ex_unit__(:case)
9999
EM.case_started(config.manager, test_case)
100100

101101
# Prepare tests, selecting which ones should
@@ -124,8 +124,8 @@ defmodule ExUnit.Runner do
124124
for test <- tests do
125125
tags = Keyword.put(test.tags, :test, test.name)
126126
case ExUnit.Filters.eval(include, exclude, tags) do
127-
:ok -> test.tags(tags)
128-
{:error, tag} -> test.state({:skip, "due to #{tag} filter"})
127+
:ok -> %{test | tags: tags}
128+
{:error, tag} -> %{test | state: {:skip, "due to #{tag} filter"}}
129129
end
130130
end
131131
end
@@ -142,7 +142,7 @@ defmodule ExUnit.Runner do
142142
send self_pid, {self, :case_finished, test_case, []}
143143

144144
{:error, test_case} ->
145-
failed_tests = Enum.map(tests, &(&1.state({:invalid, test_case})))
145+
failed_tests = Enum.map(tests, & %{&1 | state: {:invalid, test_case}})
146146
send self_pid, {self, :case_finished, test_case, failed_tests}
147147
end
148148
end)
@@ -151,24 +151,26 @@ defmodule ExUnit.Runner do
151151
{^case_pid, :case_finished, test_case, tests} ->
152152
{test_case, tests}
153153
{:DOWN, ^case_ref, :process, ^case_pid, error} ->
154-
{test_case.state({:failed, {:EXIT, error, []}}), []}
154+
{%{test_case | state: {:failed, {:EXIT, error, []}}}, []}
155155
end
156156
end
157157

158-
defp exec_case_setup(ExUnit.TestCase[name: case_name] = test_case) do
158+
defp exec_case_setup(%ExUnit.TestCase{name: case_name} = test_case) do
159159
{:ok, context} = case_name.__ex_unit__(:setup_all, [case: case_name])
160160
{:ok, {test_case, context}}
161161
catch
162162
kind, error ->
163-
{:error, test_case.state({:failed, {kind, Exception.normalize(kind, error), pruned_stacktrace}})}
163+
failed = {:failed, {kind, Exception.normalize(kind, error), pruned_stacktrace}}
164+
{:error, %{test_case | state: failed}}
164165
end
165166

166-
defp exec_case_teardown(ExUnit.TestCase[name: case_name] = test_case, context) do
167+
defp exec_case_teardown(%ExUnit.TestCase{name: case_name} = test_case, context) do
167168
case_name.__ex_unit__(:teardown_all, context)
168169
test_case
169170
catch
170171
kind, error ->
171-
test_case.state {:failed, {kind, Exception.normalize(kind, error), pruned_stacktrace}}
172+
failed = {:failed, {kind, Exception.normalize(kind, error), pruned_stacktrace}}
173+
%{test_case | state: failed}
172174
end
173175

174176
defp run_test(config, test, context) do
@@ -199,40 +201,42 @@ defmodule ExUnit.Runner do
199201
end
200202
end)
201203

202-
send self_pid, {self, :test_finished, test.time(us)}
204+
send self_pid, {self, :test_finished, %{test | time: us}}
203205
end)
204206

205207
receive do
206208
{^test_pid, :test_finished, test} ->
207209
test
208210
{:DOWN, ^test_ref, :process, ^test_pid, error} ->
209-
test.state {:failed, {:EXIT, error, []}}
211+
%{test | state: {:failed, {:EXIT, error, []}}}
210212
end
211213
end
212214

213-
defp exec_test_setup(ExUnit.Test[] = test, context) do
214-
{:ok, context} = test.case.__ex_unit__(:setup, context)
215+
defp exec_test_setup(%ExUnit.Test{case: case} = test, context) do
216+
{:ok, context} = case.__ex_unit__(:setup, context)
215217
{:ok, {test, context}}
216218
catch
217219
kind2, error2 ->
218-
{:error, test.state({:failed, {kind2, Exception.normalize(kind2, error2), pruned_stacktrace}})}
220+
failed = {:failed, {kind2, Exception.normalize(kind2, error2), pruned_stacktrace}}
221+
{:error, %{test | state: failed}}
219222
end
220223

221-
defp exec_test(ExUnit.Test[] = test, context) do
222-
apply(test.case, test.name, [context])
224+
defp exec_test(%ExUnit.Test{case: case, name: name} = test, context) do
225+
apply(case, name, [context])
223226
test
224227
catch
225228
kind, error ->
226-
test.state {:failed, {kind, Exception.normalize(kind, error), pruned_stacktrace}}
229+
failed = {:failed, {kind, Exception.normalize(kind, error), pruned_stacktrace}}
230+
%{test | state: failed}
227231
end
228232

229-
defp exec_test_teardown(ExUnit.Test[] = test, context) do
230-
{:ok, _context} = test.case.__ex_unit__(:teardown, context)
233+
defp exec_test_teardown(%ExUnit.Test{case: case} = test, context) do
234+
{:ok, _context} = case.__ex_unit__(:teardown, context)
231235
test
232236
catch
233237
kind, error ->
234238
if nil?(test.state) do
235-
test.state {:failed, {kind, Exception.normalize(kind, error), pruned_stacktrace}}
239+
%{test | state: {:failed, {kind, Exception.normalize(kind, error), pruned_stacktrace}}}
236240
else
237241
test
238242
end

0 commit comments

Comments
 (0)