Current section
Files
Jump to
Current section
Files
lib/reporters/full.ex
defmodule Doctor.Reporters.Full do
@behaviour Doctor.Reporter
alias Mix.Shell.IO
alias Doctor.ModuleReport
def generate_report(module_reports, args) do
print_divider()
print_header()
results =
Enum.reduce(module_reports, [], fn module_report, acc ->
doc_cov =
module_report.doc_coverage
|> massage_coverage()
|> gen_fixed_width_string(8)
spec_cov =
module_report.spec_coverage
|> massage_coverage()
|> gen_fixed_width_string(9)
module_doc =
module_report.has_module_doc
|> massage_module_doc()
|> gen_fixed_width_string(10)
file = gen_fixed_width_string(module_report.file, 51)
functions = gen_fixed_width_string(module_report.functions, 10)
missed_docs = gen_fixed_width_string(module_report.missed_docs, 12)
missed_specs = gen_fixed_width_string(module_report.missed_specs, 13)
if module_passed_validation?(module_report, args) do
IO.info(
"#{doc_cov}#{spec_cov}#{file}#{functions}#{missed_docs}#{missed_specs}#{module_doc}"
)
[{:ok, module_report.doc_coverage, module_report.spec_coverage} | acc]
else
IO.error(
"#{doc_cov}#{spec_cov}#{file}#{functions}#{missed_docs}#{missed_specs}#{module_doc}"
)
[{:error, module_report.doc_coverage, module_report.spec_coverage} | acc]
end
end)
overall_pass =
Enum.reduce_while(results, true, fn
{:ok, _, _}, _acc -> {:cont, true}
{:error, _, _}, _acc -> {:halt, false}
end)
{overall_passed, overall_failed} =
Enum.reduce(results, {0, 0}, fn
{:ok, _, _}, {passed, failed} -> {passed + 1, failed}
{:error, _, _}, {passed, failed} -> {passed, failed + 1}
end)
overall_doc_coverage =
Enum.reduce(results, 0.0, fn
{_, nil, _}, acc -> acc
{_, coverage, _}, acc -> acc + coverage
end) / length(results)
overall_spec_coverage =
Enum.reduce(results, 0.0, fn
{_, _, nil}, acc -> acc
{_, _, coverage}, acc -> acc + coverage
end) / length(results)
print_footer(
overall_pass,
overall_passed,
overall_failed,
overall_doc_coverage,
overall_spec_coverage
)
# TODO: Take into account config overall values
overall_pass
end
defp print_header() do
IO.info(
"DOC_COV SPEC_COV FILE FUNCTIONS MISSED_DOCS MISSED_SPECS MODULE_DOC"
)
end
defp print_divider do
IO.info(
"-----------------------------------------------------------------------------------------------------------------"
)
end
defp print_footer(pass, passed, failed, doc_coverage, spec_coverage) do
doc_coverage =
doc_coverage
|> Decimal.new()
|> Decimal.round(1)
spec_coverage =
spec_coverage
|> Decimal.new()
|> Decimal.round(1)
print_divider()
IO.info("Summary:\n")
IO.info("Passed Modules: #{passed}")
IO.info("Failed Modules: #{failed}")
IO.info("Total Doc Coverage: #{doc_coverage}%")
IO.info("Total Spec Coverage: #{spec_coverage}%\n")
if pass do
IO.info("Doctor validation has passed!")
else
IO.error("Doctor validation has failed!")
end
end
defp gen_fixed_width_string(value, width, padding \\ 2)
defp gen_fixed_width_string(value, width, padding) when is_integer(value) do
value
|> Integer.to_string()
|> gen_fixed_width_string(width, padding)
end
defp gen_fixed_width_string(value, width, padding) do
sub_string_length = width - (padding + 1)
value
|> String.slice(0..sub_string_length)
|> String.pad_trailing(width)
end
defp massage_coverage(coverage) do
if coverage do
"#{round(coverage)}%"
else
"NA"
end
end
defp massage_module_doc(module_doc) do
if module_doc, do: "YES", else: "NO"
end
defp module_passed_validation?(%ModuleReport{} = module_report, args) do
# TODO: This should be moved to a share module so reporters don't repeat it
if args.moduledoc_required do
module_report.doc_coverage >= args.min_module_doc_coverage and
module_report.spec_coverage >= args.min_module_spec_coverage and
module_report.has_module_doc
else
module_report.doc_coverage >= args.min_module_doc_coverage and
module_report.spec_coverage >= args.min_module_spec_coverage
end
end
end