cover.mk 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. # Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
  2. # This file is part of erlang.mk and subject to the terms of the ISC License.
  3. COVER_REPORT_DIR = cover
  4. # Hook in coverage to eunit
  5. ifdef COVER
  6. ifdef EUNIT_RUN
  7. EUNIT_RUN_BEFORE += -eval \
  8. 'case cover:compile_beam_directory("ebin") of \
  9. {error, _} -> halt(1); \
  10. _ -> ok \
  11. end.'
  12. EUNIT_RUN_AFTER += -eval 'cover:export("eunit.coverdata").'
  13. endif
  14. endif
  15. # Hook in coverage to ct
  16. ifdef COVER
  17. ifdef CT_RUN
  18. # All modules in 'ebin'
  19. COVER_MODS = $(notdir $(basename $(shell echo ebin/*.beam)))
  20. test-build:: $(TEST_DIR)/ct.cover.spec
  21. $(TEST_DIR)/ct.cover.spec:
  22. @echo Cover mods: $(COVER_MODS)
  23. $(gen_verbose) printf "%s\n" \
  24. '{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \
  25. '{export,"$(CURDIR)/ct.coverdata"}.' > $@
  26. CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
  27. endif
  28. endif
  29. # Core targets
  30. ifdef COVER
  31. ifneq ($(COVER_REPORT_DIR),)
  32. tests::
  33. @$(MAKE) --no-print-directory cover-report
  34. endif
  35. endif
  36. clean:: coverdata-clean
  37. ifneq ($(COVER_REPORT_DIR),)
  38. distclean:: cover-report-clean
  39. endif
  40. help::
  41. @printf "%s\n" "" \
  42. "Cover targets:" \
  43. " cover-report Generate a HTML coverage report from previously collected" \
  44. " cover data." \
  45. " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
  46. "" \
  47. "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
  48. "target tests additionally generates a HTML coverage report from the combined" \
  49. "coverdata files from each of these testing tools. HTML reports can be disabled" \
  50. "by setting COVER_REPORT_DIR to empty."
  51. # Plugin specific targets
  52. COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata))
  53. .PHONY: coverdata-clean
  54. coverdata-clean:
  55. $(gen_verbose) rm -f *.coverdata ct.cover.spec
  56. # Merge all coverdata files into one.
  57. all.coverdata: $(COVERDATA)
  58. $(gen_verbose) $(ERL) -eval ' \
  59. $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
  60. cover:export("$@"), halt(0).'
  61. # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
  62. # empty if you want the coverdata files but not the HTML report.
  63. ifneq ($(COVER_REPORT_DIR),)
  64. .PHONY: cover-report-clean cover-report
  65. cover-report-clean:
  66. $(gen_verbose) rm -rf $(COVER_REPORT_DIR)
  67. ifeq ($(COVERDATA),)
  68. cover-report:
  69. else
  70. # Modules which include eunit.hrl always contain one line without coverage
  71. # because eunit defines test/0 which is never called. We compensate for this.
  72. EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
  73. grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
  74. | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))
  75. define cover_report.erl
  76. $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
  77. Ms = cover:imported_modules(),
  78. [cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M)
  79. ++ ".COVER.html", [html]) || M <- Ms],
  80. Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms],
  81. EunitHrlMods = [$(EUNIT_HRL_MODS)],
  82. Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of
  83. true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report],
  84. TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]),
  85. TotalN = lists:sum([N || {_, {_, N}} <- Report1]),
  86. TotalPerc = round(100 * TotalY / (TotalY + TotalN)),
  87. {ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]),
  88. io:format(F, "<!DOCTYPE html><html>~n"
  89. "<head><meta charset=\"UTF-8\">~n"
  90. "<title>Coverage report</title></head>~n"
  91. "<body>~n", []),
  92. io:format(F, "<h1>Coverage</h1>~n<p>Total: ~p%</p>~n", [TotalPerc]),
  93. io:format(F, "<table><tr><th>Module</th><th>Coverage</th></tr>~n", []),
  94. [io:format(F, "<tr><td><a href=\"~p.COVER.html\">~p</a></td>"
  95. "<td>~p%</td></tr>~n",
  96. [M, M, round(100 * Y / (Y + N))]) || {M, {Y, N}} <- Report1],
  97. How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))",
  98. Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")",
  99. io:format(F, "</table>~n"
  100. "<p>Generated using ~s and erlang.mk on ~s.</p>~n"
  101. "</body></html>", [How, Date]),
  102. halt().
  103. endef
  104. cover-report:
  105. $(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
  106. $(gen_verbose) $(call erlang,$(cover_report.erl))
  107. endif
  108. endif # ifneq ($(COVER_REPORT_DIR),)