cover.mk 3.6 KB

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