Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ def project do
end
```

If you need, then you can add `summary: true` to print summary on the standard output. Useful in CI tools that can extract total coverage from there.

## Screenshots

![Screenshot1](screenshots/shot1.png)
Expand Down
1 change: 1 addition & 0 deletions include/covertool.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
prefix_len = 0,
cover_data = no_import,
output = "coverage.xml",
summary = false,
sources = ["src/"],
beams = ["ebin/"]}).
31 changes: 25 additions & 6 deletions src/covertool.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
-include("covertool.hrl").
-include_lib("xmerl/include/xmerl.hrl").

-define(EUNIT_DIR, ".eunit").

-record(result, {line = {0, 0},
branches = {0, 0},
data = []}).
Expand Down Expand Up @@ -137,6 +135,15 @@ generate_report(Config, Modules) ->
{packages, Result#result.data}]},
Report = xmerl:export_simple([Root], xmerl_xml, [{prolog, Prolog}]),
write_output(Report, Output),
if
Config#config.summary ->
io:format("Line total: ~s~nBranch total: ~s~n", [
percentage(Result#result.line),
percentage(Result#result.branches)
]);
true ->
ok
end,
ok.

generate_packages(AppName, PrefixLen, Modules) ->
Expand Down Expand Up @@ -202,7 +209,7 @@ generate_classes(Modules) ->
Class = generate_class(Module),
{Class#result.data, sum(Result, Class)}
end,

% Skip modules without sources
Filter = fun(Module) ->
case lookup_source(Module) of
Expand All @@ -224,10 +231,11 @@ generate_class(Module) ->
[]},
{Data, Result#result{line = LineCoverage}}
end,
{ok, Lines} = cover:analyse(Module, calls, line),
{ok, Lines0} = cover:analyse(Module, calls, line),
Lines = dedup(Lines0),

% XXX: ignore zero-indexed line, for some reason it is always present and always not hit
Filter = fun({{_Module, 0}, 0}) -> false;
% ignore zero-indexed lines, these are lines for generated code
Filter = fun({{_Module, 0}, _}) -> false;
(_Other) -> true
end,
Lines2 = lists:filter(Filter, Lines),
Expand Down Expand Up @@ -289,6 +297,10 @@ sum({Covered1, Valid1}, {Covered2, Valid2}) ->
rate({_Covered, 0}) -> "0.0";
rate({Covered, Valid}) -> float_to_list(Covered / Valid, [{decimals, 3}, compact]).

percentage({_Covered, 0}) -> "100.0%";
percentage({Covered, Valid}) ->
float_to_list(100 * Covered / Valid, [{decimals, 1}, compact]) ++ "%".

% lookup source in source directory
lookup_source(Module) ->
lookup_source(get(ebin), Module).
Expand Down Expand Up @@ -356,3 +368,10 @@ function_lines(MFA, LinesData) ->
Line = proplists:get_value(number, element(2, LineData)),
Line > Start andalso Line =< End
end, LinesData).

dedup(List) -> dedup(lists:sort(List), []).

dedup([], Agg) -> lists:reverse(Agg);
dedup([{Pos, C1}, {Pos, C2} | Rest], Agg) ->
dedup([{Pos, C1 + C2} | Rest], Agg);
dedup([Entry | Rest], Agg) -> dedup(Rest, [Entry | Agg]).
6 changes: 4 additions & 2 deletions src/mix_covertool.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
%% ===================================================================
%% Mix plugin callbacks
%% ===================================================================
start( CompilePath, _Opts ) ->
start( CompilePath, Opts ) ->
_ = cover:start(),

case cover:compile_beam_directory(binary:bin_to_list(CompilePath)) of
Expand All @@ -21,7 +21,9 @@ start( CompilePath, _Opts ) ->
AppName = proplists:get_value(app, mix_project(config)),
{ok, SrcDir} = file:get_cwd(),
BeamDir = binary:bin_to_list(mix_project(compile_path)),
Config = #config{appname = AppName, sources = [SrcDir], beams = [BeamDir]},
Summary = proplists:get_bool(summary, Opts),
Config = #config{appname = AppName, sources = [SrcDir], beams = [BeamDir],
summary = Summary},

fun() ->
covertool:generate_report(Config, cover:modules())
Expand Down
12 changes: 8 additions & 4 deletions src/rebar3_covertool_gen.erl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ init( State ) ->
NewState = rebar_state:add_provider(State, providers:create(Options)),
{ok, NewState}.


do(State) ->
OutputFiles = output_files(State),
InputFiles = input_files(State),
Expand Down Expand Up @@ -98,7 +98,7 @@ get_apps(State) ->
ProjectApps = [app_to_atom(rebar_app_info:name(PA))
|| PA <- rebar_state:project_apps(State)],
IncludeApps = include_apps(State),
lists:usort(ProjectApps ++ IncludeApps).
lists:usort(ProjectApps ++ IncludeApps).

generate_apps( State, Apps, LogFile ) ->
Result = lists:foldl( fun(App, Result) -> generate_app(State, App, Result) end, ok, Apps ),
Expand Down Expand Up @@ -167,7 +167,8 @@ coverdata_files(State) ->

covertool_opts(_State) ->
[{include_apps, $a, "include_apps", string, help(include_apps)},
{prefix_len, $p, "prefix_len", integer, help(prefix_len)}].
{prefix_len, $p, "prefix_len", integer, help(prefix_len)},
{summary, $s, "summary", boolean, help(summary)}].

help(include_apps) ->
"A CSV of OTP app dependencies to include in covertool output. "
Expand All @@ -177,7 +178,10 @@ help(prefix_len) ->
"[Optional] include the first N sections of the '_'-delimited module name in "
"the package name. For example, with a prefix_len of 2 and a module named "
"'app0_worker_srv_sup', the term 'app0.worker' would be added to the end of "
"the package name. Default: 0".
"the package name. Default: 0";
help(summary) ->
"Print summary of the results to the standard output. "
"Useful for CI jobs. Default: false".

app_to_atom(A) when is_atom(A) -> A;
app_to_atom(S) when is_list(S) -> list_to_atom(S);
Expand Down
2 changes: 2 additions & 0 deletions src/rebar_covertool.erl
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ rebar2_generate(Config, AppFile, ConfigKey) ->
{ok, CoverLog} ->
cover:import(From),
PrefixLen = rebar_config:get_local(Config, covertool_prefix_len, 0),
Summary = rebar_config:get_local(Config, covertool_summary, false),
CoverConfig = #config{appname = AppName,
prefix_len = PrefixLen,
summary = Summary,
output = To},
covertool:generate_report(CoverConfig,
cover:imported_modules()),
Expand Down