# Copyright 2015, Viktor Söderqvist # This file is part of erlang.mk and subject to the terms of the ISC License. COVER_REPORT_DIR = cover # utility variables for representing special symbols empty := space := $(empty) $(empty) comma := , # Hook in coverage to eunit ifdef COVER ifdef EUNIT_RUN EUNIT_RUN_BEFORE += -eval \ 'case cover:compile_beam_directory("ebin") of \ {error, _} -> halt(1); \ _ -> ok \ end.' EUNIT_RUN_AFTER += -eval 'cover:export("eunit.coverdata").' endif endif # Hook in coverage to ct ifdef COVER ifdef CT_RUN # All modules in 'ebin' COVER_MODS = $(notdir $(basename $(shell echo ebin/*.beam))) test-build:: $(TEST_DIR)/ct.cover.spec $(TEST_DIR)/ct.cover.spec: @echo Cover mods: $(COVER_MODS) $(gen_verbose) printf "%s\n" \ '{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \ '{export,"$(CURDIR)/ct.coverdata"}.' > $@ CT_RUN += -cover $(TEST_DIR)/ct.cover.spec endif endif # Core targets ifdef COVER ifneq ($(COVER_REPORT_DIR),) tests:: @$(MAKE) make --no-print-directory cover-report endif endif clean:: coverdata-clean ifneq ($(COVER_REPORT_DIR),) distclean:: cover-report-clean endif help:: @printf "%s\n" "" \ "Cover targets:" \ " cover-report Generate a HTML coverage report from previously collected" \ " cover data." \ " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \ "" \ "If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \ "target tests additionally generates a HTML coverage report from the combined" \ "coverdata files from each of these testing tools. HTML reports can be disabled" \ "by setting COVER_REPORT_DIR to empty." # Plugin specific targets COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata)) .PHONY: coverdata-clean coverdata-clean: $(gen_verbose) rm -f *.coverdata ct.cover.spec # Merge all coverdata files into one. all.coverdata: $(COVERDATA) $(gen_verbose) $(ERL) -eval ' \ $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \ cover:export("$@"), halt(0).' # These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to # empty if you want the coverdata files but not the HTML report. ifneq ($(COVER_REPORT_DIR),) .PHONY: cover-report-clean cover-report cover-report-clean: $(gen_verbose) rm -rf $(COVER_REPORT_DIR) ifeq ($(COVERDATA),) cover-report: else # Modules which include eunit.hrl always contain one line without coverage # because eunit defines test/0 which is never called. We compensate for this. EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \ grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \ | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq)) cover-report: $(gen_verbose) mkdir -p $(COVER_REPORT_DIR) $(gen_verbose) $(ERL) -eval ' \ $(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \ Ms = cover:imported_modules(), \ [cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M) \ ++ ".COVER.html", [html]) || M <- Ms], \ Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms], \ EunitHrlMods = [$(EUNIT_HRL_MODS)], \ Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of \ true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report], \ TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]), \ TotalN = lists:sum([N || {_, {_, N}} <- Report1]), \ TotalPerc = round(100 * TotalY / (TotalY + TotalN)), \ {ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]), \ io:format(F, "~n" \ "~n" \ "Coverage report~n" \ "~n", []), \ io:format(F, "

Coverage

~n

Total: ~p%

~n", [TotalPerc]),\ io:format(F, "~n", []), \ [io:format(F, "" \ "~n", \ [M, M, round(100 * Y / (Y + N))]) || {M, {Y, N}} <- Report1], \ How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))", \ Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")", \ io:format(F, "
ModuleCoverage
~p~p%
~n" \ "

Generated using ~s and erlang.mk on ~s.

~n" \ "", [How, Date]), \ halt().' endif endif # ifneq ($(COVER_REPORT_DIR),)