Current section

Files

Jump to
brod scripts cover-print-not-covered-lines.escript
Raw

scripts/cover-print-not-covered-lines.escript

#!/usr/bin/env escript
%% -*- erlang -*-
%%! -smp enable -sname notcoveredlinessummary -pa ebin -pa ../ebin
%%%
%%% Copyright (c) 2015-2016, Klarna AB
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
%%% You may obtain a copy of the License at
%%%
%%% http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Unless required by applicable law or agreed to in writing, software
%%% distributed under the License is distributed on an "AS IS" BASIS,
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%% See the License for the specific language governing permissions and
%%% limitations under the License.
%%%
%%%=============================================================================
%%% @doc
%%% @copyright 2016 Klarna AB
%%% @end
%%%=============================================================================
-mode(compile).
main([]) ->
io:format(user, "expecting at least one coverdata file\n", []),
halt(1);
main(Files) ->
ok = import_coverdata(Files),
Modules = get_imported_modules(),
Result = [{Mod, analyse_module(Mod)} || Mod <- Modules],
lists:foreach(fun({Module, NotCoveredLines}) ->
print_mod_summary(Module, lists:sort(NotCoveredLines))
end, Result).
import_coverdata([]) -> ok;
import_coverdata([Filename | Rest]) ->
io:format(user, "Importing coverdata file: ~s\n", [Filename]),
Parent = self(),
Ref = make_ref(),
erlang:spawn_link(
fun() ->
%% shutup the chatty prints from cover:xxx calls
{ok, F} = file:open("/dev/null", [write]),
group_leader(F, self()),
ok = cover:import(Filename),
Parent ! {ok, Ref},
%% keep it alive
receive stop ->
exit(normal)
end
end),
receive
{ok, Ref} ->
import_coverdata(Rest)
end.
get_imported_modules() ->
All = cover:imported_modules(),
Filtered =
lists:filter(
fun(Mod) ->
case lists:reverse(atom_to_list(Mod)) of
"ETIUS_" ++ _ -> false; %% ignore coverage for xxx_SUITE
_ -> true
end
end, All),
lists:sort(Filtered).
analyse_module(Module) ->
{ok, Lines} = cover:analyse(Module, coverage, line),
lists:foldr(
fun({{_Mod, 0}, _}, Acc) -> Acc;
({{_Mod, _Line}, {1, 0}}, Acc) -> Acc;
({{_Mod, Line}, {0, 1}}, Acc) -> [Line | Acc]
end, [], Lines).
print_mod_summary(_Module, []) -> ok;
print_mod_summary(Module, NotCoveredLines) ->
io:format(user, "================ ~p ================\n", [Module]),
case whicherl(Module) of
Filename when is_list(Filename) ->
print_lines(Filename, NotCoveredLines);
_ ->
erlang:error({erl_file_not_found, Module})
end.
print_lines(_Filename, []) ->
ok;
print_lines(Filename, Lines) ->
{ok, Fd} = file:open(Filename, [read]),
try
print_lines(Fd, 1, Lines)
after
file:close(Fd)
end.
print_lines(_Fd, _N, []) ->
ok;
print_lines(Fd, N, [M | Rest] = Lines) ->
Continue =
case io:get_line(Fd, "") of
eof ->
erlang:error({eof, N, Lines});
Line when N =:= M ->
io:format(user, "~5p: ~s", [N, Line]),
Rest;
_ ->
Lines
end,
print_lines(Fd, N+1, Continue).
whicherl(Module) when is_atom(Module) ->
{ok, {Module, [{compile_info, Props}]}} =
beam_lib:chunks(code:which(Module), [compile_info]),
proplists:get_value(source, Props).
%%%_* Emacs ====================================================================
%%% Local Variables:
%%% allout-layout: t
%%% erlang-indent-level: 2
%%% End: