Skip to content

Instantly share code, notes, and snippets.

@fenek
Created September 28, 2017 10:53
Show Gist options
  • Save fenek/43f3b6015741211dfb5bb0ad03be4a2b to your computer and use it in GitHub Desktop.
Save fenek/43f3b6015741211dfb5bb0ad03be4a2b to your computer and use it in GitHub Desktop.
Compares Coveralls data for two builds and shows which files are less covered in build 2
#!/usr/bin/env escript
main(_) ->
{ok, _} = application:ensure_all_started(inets),
{ok, _} = application:ensure_all_started(ssl),
true = code:add_path("./jsx/ebin"),
FileList = get_file_list(),
Build1 = "13298816",
Build2 = "13373536",
Coverage1 = get_coverage_for_build(Build1, FileList),
Coverage2 = get_coverage_for_build(Build2, FileList),
io:format("~n~n==========================================================~n~n", []),
print_total_coverage(Build1, Coverage1),
print_total_coverage(Build2, Coverage2),
print_noncovered_count_diff(Coverage1, Coverage2),
io:format("~n~n==========================================================~n~n", []),
print_decreased_coverage(Coverage1, Coverage2).
get_coverage_for_build(BuildId, FileList) ->
lists:map(fun(Filename) ->
io:format("Loading data for ~s...~n", [Filename]),
{Filename, get_coverage_for_file(BuildId, Filename)}
end, FileList).
get_file_list() ->
case catch file:consult("cache/file_list") of
{ok, [FileList]} ->
FileList;
_ ->
FileListSource = "https://coveralls.io/jobs/29571454/source_files.json",
{ok, {_, _, FileListBody}} = httpc:request(FileListSource),
PreFileList = jsx:decode(unicode:characters_to_binary(FileListBody)),
FileList = [unicode:characters_to_list(proplists:get_value(<<"name">>, F))
|| F <- jsx:decode(proplists:get_value(<<"source_files">>, PreFileList)) ],
ok = file:write_file("cache/file_list", io_lib:format("~p.", [FileList])),
FileList
end.
get_coverage_for_file(BuildId, Filename) ->
EscapedFilename = http_uri:encode(Filename),
URL = "https://coveralls.io/builds/" ++ BuildId ++ "/source.json?filename=" ++ EscapedFilename,
CacheFile = "cache/" ++ BuildId ++ "." ++ hashhex(Filename),
case catch file:consult(CacheFile) of
{ok, [{v1, FileMeta}]} ->
FileMeta;
_ ->
case httpc:request(URL) of
{ok, {_, _, CoverageBody}} ->
PerLineData =
try
jsx:decode(list_to_binary(CoverageBody))
catch
_:Err ->
io:format("Error when getting ~s: ~p", [Filename, Err]),
error(Err)
end,
{Total, Relevant, Covered}
= lists:foldl(fun (null, {Total, Relevant, Covered}) -> {Total + 1, Relevant, Covered};
(0, {Total, Relevant, Covered}) -> {Total + 1, Relevant + 1, Covered};
(Hits, {Total, Relevant, Covered}) when is_integer(Hits) ->
{Total + 1, Relevant + 1, Covered + 1}
end, {0, 0, 0}, PerLineData),
PercentCovered = case Relevant of
0 -> 0;
_ -> Covered / Relevant
end,
FileMeta = {PercentCovered, Total, Relevant, Covered, PerLineData},
ok = file:write_file(CacheFile, io_lib:format("~p.", [{v1, FileMeta}])),
FileMeta;
{error, socket_closed_remotely} ->
io:format("Waiting 5s...~n", []),
timer:sleep(5000),
get_coverage_for_file(BuildId, Filename)
end
end.
hashhex(String) ->
lists:flatmap(fun(Byte) ->
integer_to_list(Byte, 16)
end, binary_to_list(crypto:hash(sha, String))).
print_total_coverage(Name, Coverage) ->
{TR, TC} =
lists:foldl(fun({_FN, {_P, _T, R, C, _D}}, {TotalR, TotalC}) ->
{TotalR + R, TotalC + C}
end, {0, 0}, Coverage),
io:format("Total coverage for ~s: ~p%~n", [Name, (TC/TR)*100]).
noncovered_count(Coverage) ->
lists:foldl(fun({_FN, {_P, _T, R, C, _D}}, Acc) -> Acc + (R-C) end, 0, Coverage).
print_noncovered_count_diff(Coverage1, Coverage2) ->
Noncovered1 = noncovered_count(Coverage1),
Noncovered2 = noncovered_count(Coverage2),
io:format("Noncovered line count diff: ~p~n", [Noncovered1 - Noncovered2]).
print_decreased_coverage([{Filename, {Percent1, _T1, R1, C1, _D1}} | RCoverage1],
[{Filename, {Percent2, _T2, R2, C2, _D2}} | RCoverage2]) ->
case Percent2 < Percent1 of
true ->
io:format("~s: coverage: ~p% -> ~p% (~pppc less, ~p lines less), relevant lines diff: ~p~n",
[Filename, Percent1*100, Percent2*100, (Percent1-Percent2) * 100,
(R2-C2)-(R1-C1), R2-R1]);
_ ->
ok
end,
print_decreased_coverage(RCoverage1, RCoverage2);
print_decreased_coverage([], []) ->
ok.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment