Skip to content

Commit 17e819d

Browse files
Use ets table for gathering cover data in test task (#7850)
1 parent 43f80d3 commit 17e819d

File tree

1 file changed

+33
-45
lines changed

1 file changed

+33
-45
lines changed

lib/mix/lib/mix/tasks/test.ex

Lines changed: 33 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,14 @@ defmodule Mix.Tasks.Test do
2020
Mix.shell().info("\nGenerating cover results ...\n")
2121
{:result, ok, _fail} = :cover.analyse(:coverage, :line)
2222

23-
results =
24-
ok
25-
|> gather_coverage(:cover.modules())
26-
|> Enum.sort_by(&percentage(elem(&1, 1)), &>=/2)
23+
{module_results, totals} = gather_coverage(ok, :cover.modules())
24+
module_results = Enum.sort_by(module_results, &percentage(elem(&1, 1)), &>=/2)
2725

2826
if summary_opts = Keyword.get(opts, :summary, true) do
29-
console(results, summary_opts)
27+
console(module_results, totals, summary_opts)
3028
end
3129

32-
html(results, opts)
30+
html(module_results, opts)
3331
end
3432
end
3533

@@ -40,50 +38,44 @@ defmodule Mix.Tasks.Test do
4038
# We may also have multiple entries on the same line.
4139
# Each line is only considered once.
4240
#
43-
# We use the process dictionary for performance, to avoid
44-
# working with nested maps, but we clean it up afterwards.
45-
modules =
46-
Enum.reduce(results, MapSet.new(), fn
47-
{{module, 0}, _}, acc ->
48-
MapSet.put(acc, module)
49-
50-
{{module, line}, {1, 0}}, acc ->
51-
coverage = Process.get(module) || %{}
52-
Process.put(module, Map.put(coverage, line, true))
53-
MapSet.put(acc, module)
54-
55-
{{module, line}, {0, 1}}, acc ->
56-
coverage = Process.get(module) || %{}
57-
Process.put(module, Map.put_new(coverage, line, false))
58-
MapSet.put(acc, module)
41+
# We use ets for performance, to avoid working with nested maps
42+
table = :ets.new(__MODULE__, [:set, :private])
43+
44+
try do
45+
Enum.each(results, fn
46+
{{module, 0}, _} -> :ets.insert(table, {{module, 0}, :dummy})
47+
{{module, line}, {1, 0}} -> :ets.insert(table, {{module, line}, true})
48+
{{module, line}, {0, 1}} -> :ets.insert_new(table, {{module, line}, false})
5949
end)
6050

61-
for module <- modules, results = Process.delete(module) || %{}, module in keep do
62-
result =
63-
Enum.reduce(results, {0, 0}, fn
64-
{_line, true}, {covered, not_covered} -> {covered + 1, not_covered}
65-
{_line, false}, {covered, not_covered} -> {covered, not_covered + 1}
66-
end)
51+
module_results =
52+
for module <- keep,
53+
results = read_module_cover_results(table, module),
54+
do: {module, results}
6755

68-
{module, result}
56+
total_covered = :ets.select_count(table, [{{:_, true}, [], [true]}])
57+
total_not_covered = :ets.select_count(table, [{{:_, false}, [], [true]}])
58+
59+
{module_results, {total_covered, total_not_covered}}
60+
after
61+
:ets.delete(table)
6962
end
7063
end
7164

72-
defp console(results, true), do: console(results, [])
65+
defp read_module_cover_results(table, module) do
66+
covered = :ets.select_count(table, [{{{module, :_}, true}, [], [true]}])
67+
not_covered = :ets.select_count(table, [{{{module, :_}, false}, [], [true]}])
68+
{covered, not_covered}
69+
end
70+
71+
defp console(results, totals, true), do: console(results, totals, [])
7372

74-
defp console(results, opts) when is_list(opts) do
73+
defp console(results, totals, opts) when is_list(opts) do
7574
Mix.shell().info("Percentage | Module")
7675
Mix.shell().info("-----------|--------------------------")
7776
Enum.each(results, &display(&1, opts))
7877
Mix.shell().info("-----------|--------------------------")
79-
80-
total =
81-
Enum.reduce(results, {0, 0}, fn {_, {covered, not_covered}},
82-
{total_covered, total_not_covered} ->
83-
{total_covered + covered, total_not_covered + not_covered}
84-
end)
85-
86-
display({"Total", total}, opts)
78+
display({"Total", totals}, opts)
8779
Mix.shell().info("")
8880
end
8981

@@ -121,14 +113,10 @@ defmodule Mix.Tasks.Test do
121113
])
122114
end
123115

124-
defp percentage({0, 0}), do: 100
116+
defp percentage({0, 0}), do: 100.0
125117
defp percentage({covered, not_covered}), do: covered / (covered + not_covered) * 100
126118

127-
defp format(number, length) when is_integer(number),
128-
do: :io_lib.format("~#{length}b", [number])
129-
130-
defp format(number, length) when is_float(number),
131-
do: :io_lib.format("~#{length}.2f", [number])
119+
defp format(number, length), do: :io_lib.format("~#{length}.2f", [number])
132120

133121
defp format_name(name) when is_binary(name), do: name
134122
defp format_name(mod) when is_atom(mod), do: inspect(mod)

0 commit comments

Comments
 (0)