defmodule Cover do
@moduledoc false
@threshold 90
def start(compile_path, opts) do
Mix.shell().info("Cover compiling modules ...")
_ = :cover.start()
case :cover.compile_beam_directory(compile_path |> to_charlist) do
results when is_list(results) ->
:ok
{:error, _} ->
Mix.raise("Failed to cover compile directory: " <> compile_path)
end
output = opts[:output]
fn ->
File.mkdir_p!(output)
Mix.shell().info("\nCover results")
{:result, ok, _fail} = :cover.analyse(:coverage, :module)
Mix.shell().info("Percentage | Module")
Mix.shell().info("-----------|--------------------------")
total =
ok
|> Stream.each(&display(&1, opts))
|> Stream.each(&html(&1, output))
|> Enum.reduce({0, 0}, fn {_, {cov, not_cov}}, {tot_cov, tot_not_cov} ->
{tot_cov + cov, tot_not_cov + not_cov}
end)
Mix.shell().info("-----------|--------------------------")
display({"Total", total}, opts)
end
end
defp colour(percentage, threshold) when percentage > threshold, do: :green
defp colour(_, _), do: :red
defp display({name, coverage}, opts) do
threshold = Keyword.get(opts, :threshold, @threshold)
percentage = percentage(coverage)
Mix.shell().info([
colour(percentage, threshold),
format(percentage, 9),
"%",
:reset,
" | #{name}"
])
end
defp html({mod, _}, output) do
{:ok, _} = :cover.analyse_to_file(mod, '#{output}/#{mod}.html', [:html])
end
defp percentage({0, 0}), do: 100
defp percentage({cov, not_cov}), do: cov / (cov + not_cov) * 100
defp format(num, len) when is_integer(num) do
num
|> Integer.to_string()
|> String.pad_leading(len)
end
defp format(num, len) when is_float(num) do
num
|> Float.round(2)
|> Float.to_string()
|> String.pad_leading(len)
end
end