mirror of
https://github.com/correl/rebar.git
synced 2024-11-27 11:09:55 +00:00
Fix incorrect coverage count when prod modules include EUnit header.
Modules that include the EUnit header get an implicit test/0 fun, which cover considers a runnable line, but eunit:(TestRepresentation) never calls. Result: prod modules with tests can never reach 100% coverage. Ironic. In this case, fix it by decrementing the NotCovered counter returned by cover:analyze/3.
This commit is contained in:
parent
35a928ecf2
commit
4825353a23
2 changed files with 53 additions and 3 deletions
|
@ -238,13 +238,43 @@ cover_init(Config, BeamFiles) ->
|
||||||
cover_analyze_mod(Module) ->
|
cover_analyze_mod(Module) ->
|
||||||
case cover:analyze(Module, coverage, module) of
|
case cover:analyze(Module, coverage, module) of
|
||||||
{ok, {Module, {Covered, NotCovered}}} ->
|
{ok, {Module, {Covered, NotCovered}}} ->
|
||||||
{Module, Covered, NotCovered};
|
%% Modules that include the eunit header get an implicit
|
||||||
|
%% test/0 fun, which cover considers a runnable line, but
|
||||||
|
%% eunit:test(TestRepresentation) never calls. Decrement
|
||||||
|
%% NotCovered in this case.
|
||||||
|
align_notcovered_count(Module, Covered, NotCovered,
|
||||||
|
is_eunitized(Module));
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?ERROR("Cover analyze failed for ~p: ~p ~p\n",
|
?ERROR("Cover analyze failed for ~p: ~p ~p\n",
|
||||||
[Module, Reason, code:which(Module)]),
|
[Module, Reason, code:which(Module)]),
|
||||||
{0,0}
|
{0,0}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
is_eunitized(Mod) ->
|
||||||
|
has_eunit_test_fun(Mod) andalso
|
||||||
|
has_eunit_header(Mod).
|
||||||
|
|
||||||
|
has_eunit_test_fun(Mod) ->
|
||||||
|
length([F || {exports, Funs} <- Mod:module_info(),
|
||||||
|
{F, 0} <- Funs,
|
||||||
|
F == test]) =/= 0.
|
||||||
|
|
||||||
|
has_eunit_header(Mod) ->
|
||||||
|
OrigEnv = set_proc_env(),
|
||||||
|
try has_header(Mod, "include/eunit.hrl")
|
||||||
|
after restore_proc_env(OrigEnv)
|
||||||
|
end.
|
||||||
|
|
||||||
|
has_header(Mod, Header) ->
|
||||||
|
{ok, {_, [{abstract_code, {_, AC}}]}} = beam_lib:chunks(Mod, [abstract_code]),
|
||||||
|
length([F || {attribute, 1, file, {F, 1}} <- AC,
|
||||||
|
string:str(F, Header) =/= 0]) =/= 0.
|
||||||
|
|
||||||
|
align_notcovered_count(Module, Covered, NotCovered, false) ->
|
||||||
|
{Module, Covered, NotCovered};
|
||||||
|
align_notcovered_count(Module, Covered, NotCovered, true) ->
|
||||||
|
{Module, Covered, NotCovered - 1}.
|
||||||
|
|
||||||
cover_write_index(Coverage, SrcModules) ->
|
cover_write_index(Coverage, SrcModules) ->
|
||||||
{ok, F} = file:open(filename:join([?EUNIT_DIR, "index.html"]), [write]),
|
{ok, F} = file:open(filename:join([?EUNIT_DIR, "index.html"]), [write]),
|
||||||
ok = file:write(F, "<html><head><title>Coverage Summary</title></head>\n"),
|
ok = file:write(F, "<html><head><title>Coverage Summary</title></head>\n"),
|
||||||
|
|
|
@ -88,7 +88,7 @@ cover_with_suite_test_() ->
|
||||||
{"Only production modules get coverage reports",
|
{"Only production modules get coverage reports",
|
||||||
assert_files_not_in("the temporary eunit directory",
|
assert_files_not_in("the temporary eunit directory",
|
||||||
[".eunit/myapp_mymod_tests.COVER.html",
|
[".eunit/myapp_mymod_tests.COVER.html",
|
||||||
".eunit/mysuite"])}]}.
|
".eunit/mysuite.COVER.html"])}]}.
|
||||||
|
|
||||||
expected_cover_generated_files() ->
|
expected_cover_generated_files() ->
|
||||||
[".eunit/index.html",
|
[".eunit/index.html",
|
||||||
|
@ -96,6 +96,17 @@ expected_cover_generated_files() ->
|
||||||
".eunit/myapp_mymod.COVER.html",
|
".eunit/myapp_mymod.COVER.html",
|
||||||
".eunit/myapp_sup.COVER.html"].
|
".eunit/myapp_sup.COVER.html"].
|
||||||
|
|
||||||
|
cover_coverage_test_() ->
|
||||||
|
{"Coverage is accurately calculated",
|
||||||
|
setup, fun() -> setup_cover_project(), rebar("-v eunit") end,
|
||||||
|
fun teardown/1,
|
||||||
|
|
||||||
|
[{"Modules that include the EUnit header can still have 100% coverage",
|
||||||
|
%% cover notices the implicit EUnit test/0 func that never gets
|
||||||
|
%% called during eunit:test(TestRepresentation), so NotCounted
|
||||||
|
%% needs to be decremented in this case.
|
||||||
|
assert_full_coverage("myapp_mymod")}]}.
|
||||||
|
|
||||||
%% ====================================================================
|
%% ====================================================================
|
||||||
%% Environment and Setup Tests
|
%% Environment and Setup Tests
|
||||||
%% ====================================================================
|
%% ====================================================================
|
||||||
|
@ -227,3 +238,12 @@ assert_files_not_in(Name, [File|T]) ->
|
||||||
assert_files_not_in(Name, T)];
|
assert_files_not_in(Name, T)];
|
||||||
assert_files_not_in(_, []) -> [].
|
assert_files_not_in(_, []) -> [].
|
||||||
|
|
||||||
|
assert_full_coverage(Mod) ->
|
||||||
|
fun() ->
|
||||||
|
{ok, F} = file:read_file(".eunit/index.html"),
|
||||||
|
Result = [X || X <- string:tokens(binary_to_list(F), "\n"),
|
||||||
|
string:str(X, Mod) =/= 0,
|
||||||
|
string:str(X, "100%") =/= 0],
|
||||||
|
file:close(F),
|
||||||
|
?assert(length(Result) == 1)
|
||||||
|
end.
|
||||||
|
|
Loading…
Reference in a new issue