From dcb821ca1adaec189cdc99509a47fb59bc4e8d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Sat, 24 Jan 2015 18:16:00 +0100 Subject: Combined coverage report for eunit and ct --- plugins/cover.mk | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/eunit.mk | 17 +++++--- 2 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 plugins/cover.mk (limited to 'plugins') diff --git a/plugins/cover.mk b/plugins/cover.mk new file mode 100644 index 0000000..7f62e33 --- /dev/null +++ b/plugins/cover.mk @@ -0,0 +1,129 @@ +# Copyright 2015, Viktor Söderqvist +# This file is part of erlang.mk and subject to the terms of the ISC License. + +COVER_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:: ct.cover.spec + +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 ct.cover.spec +endif +endif + +# Core targets + +ifdef COVER +ifneq ($(COVER_DIR),) +tests:: + @$(MAKE) make --no-print-directory cover-report +endif +endif + +clean:: coverdata-clean + +ifneq ($(COVER_DIR),) +distclean:: cover-clean +endif + +help:: + @printf "%s\n" "" \ + "Cover targets:" \ + " cover-report Generate a HTML coverage report from previously collected" \ + " cover data." \ + "" \ + "Cover-report is included in the 'tests' target by setting COVER=1." \ + "If you run 'ct' or 'eunit' separately with COVER=1, cover data is" \ + "collected but to generate a report you have to run 'cover-report'" \ + "afterwards." + +# Plugin specific targets + +.PHONY: coverdata-clean +coverdata-clean: + $(gen_verbose) rm -f *.coverdata ct.cover.spec + +# These are only defined if COVER_DIR is non-empty + +ifneq ($(COVER_DIR),) + +.PHONY: cover-clean cover-report + +cover-clean: coverdata-clean + $(gen_verbose) rm -rf $(COVER_DIR) + +COVERDATA = $(wildcard *.coverdata) + +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/\.erl:.*//;s/^src\///' | uniq)) + +cover-report: + $(gen_verbose) mkdir -p $(COVER_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_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_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_DIR),) diff --git a/plugins/eunit.mk b/plugins/eunit.mk index c59883d..b1ebe43 100644 --- a/plugins/eunit.mk +++ b/plugins/eunit.mk @@ -6,18 +6,19 @@ # Configuration +# All modules in TEST_DIR ifeq ($(strip $(TEST_DIR)),) -TAGGED_EUNIT_TESTS = {dir,"ebin"} +TEST_DIR_MODS = else -# All modules in TEST_DIR TEST_DIR_MODS = $(notdir $(basename $(shell find $(TEST_DIR) -type f -name *.beam))) +endif + # All modules in 'ebin' EUNIT_EBIN_MODS = $(notdir $(basename $(shell find ebin -type f -name *.beam))) # Only those modules in TEST_DIR with no matching module in 'ebin'. # This is done to avoid some tests being executed twice. EUNIT_MODS = $(filter-out $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(TEST_DIR_MODS)) -TAGGED_EUNIT_TESTS = {dir,"ebin"} $(foreach mod,$(EUNIT_MODS),$(shell echo $(mod) | sed -e 's/\(.*\)/{module,\1}/g')) -endif +TAGGED_EUNIT_TESTS = $(foreach mod,$(EUNIT_EBIN_MODS) $(EUNIT_MODS),{module,$(mod)}) EUNIT_OPTS ?= verbose @@ -38,10 +39,16 @@ help:: # Plugin-specific targets. +EUNIT_RUN_BEFORE ?= +EUNIT_RUN_AFTER ?= EUNIT_RUN = $(ERL) \ -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin \ -pz ebin \ - -eval 'case eunit:test([$(call str-join,$(TAGGED_EUNIT_TESTS))], [$(EUNIT_OPTS)]) of ok -> halt(0); error -> halt(1) end.' + $(EUNIT_RUN_BEFORE) \ + -eval 'case eunit:test([$(call str-join,$(TAGGED_EUNIT_TESTS))],\ + [$(EUNIT_OPTS)]) of ok -> ok; error -> halt(1) end.' \ + $(EUNIT_RUN_AFTER) \ + -eval 'halt(0).' eunit: test-build $(gen_verbose) $(EUNIT_RUN) -- cgit v1.2.3 From 8f39bed18723b0b60b17ea1aeecde4c6c371e3f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Tue, 27 Jan 2015 23:50:41 +0100 Subject: Add a target to merge coverdata files and rename COVER_DIR to COVER_REPORT_DIR --- plugins/cover.mk | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) (limited to 'plugins') diff --git a/plugins/cover.mk b/plugins/cover.mk index 7f62e33..b42311e 100644 --- a/plugins/cover.mk +++ b/plugins/cover.mk @@ -1,7 +1,7 @@ # Copyright 2015, Viktor Söderqvist # This file is part of erlang.mk and subject to the terms of the ISC License. -COVER_DIR = cover +COVER_REPORT_DIR = cover # utility variables for representing special symbols empty := @@ -44,7 +44,7 @@ endif # Core targets ifdef COVER -ifneq ($(COVER_DIR),) +ifneq ($(COVER_REPORT_DIR),) tests:: @$(MAKE) make --no-print-directory cover-report endif @@ -52,8 +52,8 @@ endif clean:: coverdata-clean -ifneq ($(COVER_DIR),) -distclean:: cover-clean +ifneq ($(COVER_REPORT_DIR),) +distclean:: cover-report-clean endif help:: @@ -61,28 +61,35 @@ help:: "Cover targets:" \ " cover-report Generate a HTML coverage report from previously collected" \ " cover data." \ + " all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \ "" \ - "Cover-report is included in the 'tests' target by setting COVER=1." \ - "If you run 'ct' or 'eunit' separately with COVER=1, cover data is" \ - "collected but to generate a report you have to run 'cover-report'" \ - "afterwards." + "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 -# These are only defined if COVER_DIR is non-empty - -ifneq ($(COVER_DIR),) +# 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).' -.PHONY: cover-clean cover-report +# 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),) -cover-clean: coverdata-clean - $(gen_verbose) rm -rf $(COVER_DIR) +.PHONY: cover-report-clean cover-report -COVERDATA = $(wildcard *.coverdata) +cover-report-clean: + $(gen_verbose) rm -rf $(COVER_REPORT_DIR) ifeq ($(COVERDATA),) cover-report: @@ -92,14 +99,14 @@ else # 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/\.erl:.*//;s/^src\///' | uniq)) + | sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq)) cover-report: - $(gen_verbose) mkdir -p $(COVER_DIR) + $(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_DIR)/" ++ atom_to_list(M) \ + [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)], \ @@ -108,7 +115,7 @@ cover-report: TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]), \ TotalN = lists:sum([N || {_, {_, N}} <- Report1]), \ TotalPerc = round(100 * TotalY / (TotalY + TotalN)), \ - {ok, F} = file:open("$(COVER_DIR)/index.html", [write]), \ + {ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]), \ io:format(F, "~n" \ "~n" \ "Coverage report~n" \ @@ -126,4 +133,4 @@ cover-report: halt().' endif -endif # ifneq ($(COVER_DIR),) +endif # ifneq ($(COVER_REPORT_DIR),) -- cgit v1.2.3