aboutsummaryrefslogtreecommitdiff
path: root/server/_build/default/plugins/coveralls
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
committerCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
commit49fa5aa2a127bdf8924d02bf77e5086b39c7a447 (patch)
tree61d86a7705dacc9fddccc29fa79d075d83ab8059 /server/_build/default/plugins/coveralls
i vibe coded itHEADmaster
Diffstat (limited to 'server/_build/default/plugins/coveralls')
-rw-r--r--server/_build/default/plugins/coveralls/README.md126
-rw-r--r--server/_build/default/plugins/coveralls/ebin/coveralls.app12
-rw-r--r--server/_build/default/plugins/coveralls/ebin/coveralls.beambin0 -> 11536 bytes
-rw-r--r--server/_build/default/plugins/coveralls/ebin/rebar3_coveralls.beambin0 -> 7768 bytes
-rw-r--r--server/_build/default/plugins/coveralls/hex_metadata.config17
-rw-r--r--server/_build/default/plugins/coveralls/rebar.config7
-rw-r--r--server/_build/default/plugins/coveralls/rebar.config.script7
-rw-r--r--server/_build/default/plugins/coveralls/rebar.lock6
-rw-r--r--server/_build/default/plugins/coveralls/src/coveralls.app.src11
-rw-r--r--server/_build/default/plugins/coveralls/src/coveralls.erl499
-rw-r--r--server/_build/default/plugins/coveralls/src/rebar3_coveralls.erl220
11 files changed, 905 insertions, 0 deletions
diff --git a/server/_build/default/plugins/coveralls/README.md b/server/_build/default/plugins/coveralls/README.md
new file mode 100644
index 0000000..eecfbb0
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/README.md
@@ -0,0 +1,126 @@
+coveralls-erl
+=============
+[![Build Status](https://travis-ci.org/markusn/coveralls-erl.png?branch=master)](https://travis-ci.org/markusn/coveralls-erl)
+[![Coverage Status](https://coveralls.io/repos/markusn/coveralls-erl/badge.png?branch=master)](https://coveralls.io/r/markusn/coveralls-erl?branch=master)
+[![Hex.pm](https://img.shields.io/hexpm/v/coveralls.svg?style=flat)](https://hex.pm/packages/coveralls)
+
+Erlang module to convert and send cover data to coveralls. Available as a hex package on https://hex.pm/packages/coveralls.
+
+## Example usage: rebar3 and Travis CI
+In order to use coveralls-erl + Travis CI in your project you will need to add the following lines to your
+`rebar.config.script`:
+
+```erlang
+case os:getenv("TRAVIS") of
+ "true" ->
+ JobId = os:getenv("TRAVIS_JOB_ID"),
+ lists:keystore(coveralls_service_job_id, 1, CONFIG, {coveralls_service_job_id, JobId});
+ _ ->
+ CONFIG
+end.
+```
+
+This will ensure that the rebar coveralls plugin will have access to the needed JobId and that the plugin is only run from Travis CI.
+
+You will also need to add the following lines to your `rebar.config`:
+```erlang
+{plugins , [coveralls]}. % use hex package
+{cover_enabled , true}.
+{cover_export_enabled , true}.
+{coveralls_coverdata , "_build/test/cover/eunit.coverdata"}. % or a string with wildcards or a list of files
+{coveralls_service_name , "travis-ci"}. % use "travis-pro" when using with travis-ci.com
+```
+When using with travis-ci.com coveralls repo token also has to be added as `{coveralls_repo_token, "token_goes_here"}`
+
+These changes will add `coveralls-erl` as a dependency, tell `rebar3` where to find the plugin, make sure that the coverage data is produced and exported and configure `coveralls-erl` to use this data and the service `travis-ci`.
+
+And you send the coverdata to coveralls by issuing: `rebar3 as test coveralls send`
+
+**Note:**
+If you have dependencies specific to the test profile, or if you only add the coveralls dependency or any of its' configuration variables to the test profile you need to run coveralls using: `rebar3 as test coveralls send`
+
+## Example: rebar3 and CircleCI
+Example `rebar.config.script`:
+
+```erlang
+case {os:getenv("CIRCLECI"), os:getenv("COVERALLS_REPO_TOKEN")} of
+ {"true", Token} when is_list(Token) ->
+ JobId = os:getenv("CIRCLE_BUILD_NUM"),
+ CONFIG1 = lists:keystore(coveralls_service_job_id, 1, CONFIG, {coveralls_service_job_id, JobId}),
+ lists:keystore(coveralls_repo_token, 1, CONFIG1, {coveralls_repo_token, Token});
+ _ ->
+ CONFIG
+end.
+```
+
+Example `rebar.config`:
+
+```erlang
+
+{plugins , [coveralls]}. % use hex package
+{cover_enabled , true}.
+{cover_export_enabled , true}.
+{coveralls_coverdata , "_build/test/cover/ct.coverdata"}.
+{coveralls_service_name , "circle-ci"}.
+```
+
+Note that you'll need to set `COVERALLS_REPO_TOKEN` in your CircleCI environment variables!
+
+## Example usage: rebar3 and GitHub Actions
+
+In order to use coveralls-erl + GitHub Actions in your project, you will need to add the following lines to your
+`rebar.config.script`:
+
+```erlang
+case {os:getenv("GITHUB_ACTIONS"), os:getenv("GITHUB_TOKEN")} of
+ {"true", Token} when is_list(Token) ->
+ CONFIG1 = [{coveralls_repo_token, Token},
+ {coveralls_service_job_id, os:getenv("GITHUB_RUN_ID")},
+ {coveralls_commit_sha, os:getenv("GITHUB_SHA")},
+ {coveralls_service_number, os:getenv("GITHUB_RUN_NUMBER")} | CONFIG],
+ case os:getenv("GITHUB_EVENT_NAME") =:= "pull_request"
+ andalso string:tokens(os:getenv("GITHUB_REF"), "/") of
+ [_, "pull", PRNO, _] ->
+ [{coveralls_service_pull_request, PRNO} | CONFIG1];
+ _ ->
+ CONFIG1
+ end;
+ _ ->
+ CONFIG
+end.
+```
+
+This will ensure that the rebar coveralls plugin will have access to the needed JobId and that the plugin is only run from GitHub Actions.
+
+You will also need to add the following lines to your `rebar.config`:
+```erlang
+{plugins , [coveralls]}. % use hex package
+{cover_enabled , true}.
+{cover_export_enabled , true}.
+{coveralls_coverdata , "_build/test/cover/eunit.coverdata"}. % or a string with wildcards or a list of files
+{coveralls_service_name , "github"}.
+```
+
+These changes will add `coveralls-erl` as a dependency, tell `rebar3` where to find the plugin, make sure that the coverage data is produced and exported and configure `coveralls-erl` to use this data and the service `github`.
+
+And you send the coverdata to coveralls by adding a step like:
+
+```
+- name: Coveralls
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: rebar3 as test coveralls send
+```
+
+Other available GitHub Actions Environment Variables are available [here](https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables)
+
+## Optional settings
+
+The pluging also support the `coveralls_service_pull_request` and `coveralls_parallel` settings.
+See the Coveralls documentation for the meaning of those.
+
+## Author
+Markus Ekholm (markus at botten dot org).
+
+## License
+3-clause BSD. For details see `COPYING`.
diff --git a/server/_build/default/plugins/coveralls/ebin/coveralls.app b/server/_build/default/plugins/coveralls/ebin/coveralls.app
new file mode 100644
index 0000000..c77ac5c
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/ebin/coveralls.app
@@ -0,0 +1,12 @@
+{application,coveralls,
+ [{registered,[]},
+ {description,"Coveralls for Erlang"},
+ {vsn,"2.2.0"},
+ {licenses,["BSD"]},
+ {modules,[coveralls,rebar3_coveralls]},
+ {registred,[]},
+ {applications,[kernel,stdlib]},
+ {env,[{providers,[rebar3_coveralls]}]},
+ {maintainers,["Markus Ekholm"]},
+ {links,[{"Github",
+ "https://github.com/markusn/coveralls-erl"}]}]}.
diff --git a/server/_build/default/plugins/coveralls/ebin/coveralls.beam b/server/_build/default/plugins/coveralls/ebin/coveralls.beam
new file mode 100644
index 0000000..3664b34
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/ebin/coveralls.beam
Binary files differ
diff --git a/server/_build/default/plugins/coveralls/ebin/rebar3_coveralls.beam b/server/_build/default/plugins/coveralls/ebin/rebar3_coveralls.beam
new file mode 100644
index 0000000..1ffa9b2
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/ebin/rebar3_coveralls.beam
Binary files differ
diff --git a/server/_build/default/plugins/coveralls/hex_metadata.config b/server/_build/default/plugins/coveralls/hex_metadata.config
new file mode 100644
index 0000000..c8a4f91
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/hex_metadata.config
@@ -0,0 +1,17 @@
+{<<"name">>,<<"coveralls">>}.
+{<<"version">>,<<"2.2.0">>}.
+{<<"requirements">>,
+ #{<<"jsx">> =>
+ #{<<"app">> => <<"jsx">>,<<"optional">> => false,
+ <<"requirement">> => <<"2.10.0">>}}}.
+{<<"app">>,<<"coveralls">>}.
+{<<"maintainers">>,[<<"Markus Ekholm">>]}.
+{<<"precompiled">>,false}.
+{<<"description">>,<<"Coveralls for Erlang">>}.
+{<<"files">>,
+ [<<"src/coveralls.app.src">>,<<"README.md">>,<<"rebar.config">>,
+ <<"rebar.config.script">>,<<"rebar.lock">>,<<"src/coveralls.erl">>,
+ <<"src/rebar3_coveralls.erl">>]}.
+{<<"licenses">>,[<<"BSD">>]}.
+{<<"links">>,[{<<"Github">>,<<"https://github.com/markusn/coveralls-erl">>}]}.
+{<<"build_tools">>,[<<"rebar3">>]}.
diff --git a/server/_build/default/plugins/coveralls/rebar.config b/server/_build/default/plugins/coveralls/rebar.config
new file mode 100644
index 0000000..e23984e
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/rebar.config
@@ -0,0 +1,7 @@
+{deps, [{jsx, "2.10.0"}]}.
+{profiles, [{test, [{plugins, [{coveralls, {git, "https://github.com/markusn/coveralls-erl", {branch, "master"}}}]}]}]}.
+{cover_enabled , true}.
+{cover_export_enabled , true}.
+{coveralls_coverdata , "_build/test/cover/eunit.coverdata"}. % or a string with wildcards or a list of files
+{coveralls_service_name , "travis-ci"}.
+{coveralls_parallel, true}.
diff --git a/server/_build/default/plugins/coveralls/rebar.config.script b/server/_build/default/plugins/coveralls/rebar.config.script
new file mode 100644
index 0000000..8886d94
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/rebar.config.script
@@ -0,0 +1,7 @@
+case os:getenv("TRAVIS") of
+ "true" ->
+ JobId = os:getenv("TRAVIS_JOB_ID"),
+ lists:keystore(coveralls_service_job_id, 1, CONFIG, {coveralls_service_job_id, JobId});
+ _ ->
+ CONFIG
+end. \ No newline at end of file
diff --git a/server/_build/default/plugins/coveralls/rebar.lock b/server/_build/default/plugins/coveralls/rebar.lock
new file mode 100644
index 0000000..82f478c
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/rebar.lock
@@ -0,0 +1,6 @@
+{"1.1.0",
+[{<<"jsx">>,{pkg,<<"jsx">>,<<"2.10.0">>},0}]}.
+[
+{pkg_hash,[
+ {<<"jsx">>, <<"77760560D6AC2B8C51FD4C980E9E19B784016AA70BE354CE746472C33BEB0B1C">>}]}
+].
diff --git a/server/_build/default/plugins/coveralls/src/coveralls.app.src b/server/_build/default/plugins/coveralls/src/coveralls.app.src
new file mode 100644
index 0000000..85a0d8e
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/src/coveralls.app.src
@@ -0,0 +1,11 @@
+{application,coveralls,
+ [{description,"Coveralls for Erlang"},
+ {vsn,"2.2.0"},
+ {licenses,["BSD"]},
+ {modules,[]},
+ {registred,[]},
+ {applications,[kernel,stdlib]},
+ {env,[{providers,[rebar3_coveralls]}]},
+ {maintainers,["Markus Ekholm"]},
+ {links,[{"Github",
+ "https://github.com/markusn/coveralls-erl"}]}]}.
diff --git a/server/_build/default/plugins/coveralls/src/coveralls.erl b/server/_build/default/plugins/coveralls/src/coveralls.erl
new file mode 100644
index 0000000..90954c6
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/src/coveralls.erl
@@ -0,0 +1,499 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Copyright (c) 2013-2016, Markus Ekholm
+%%% All rights reserved.
+%%% Redistribution and use in source and binary forms, with or without
+%%% modification, are permitted provided that the following conditions are met:
+%%% * Redistributions of source code must retain the above copyright
+%%% notice, this list of conditions and the following disclaimer.
+%%% * Redistributions in binary form must reproduce the above copyright
+%%% notice, this list of conditions and the following disclaimer in the
+%%% documentation and/or other materials provided with the distribution.
+%%% * Neither the name of the <organization> nor the
+%%% names of its contributors may be used to endorse or promote products
+%%% derived from this software without specific prior written permission.
+%%%
+%%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+%%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+%%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL MARKUS EKHOLM BE LIABLE FOR ANY
+%%% DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+%%% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+%%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+%%% ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+%%% (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+%%% THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+%%%
+%%% @copyright 2013-2016 (c) Markus Ekholm <markus@botten.org>
+%%% @author Markus Ekholm <markus@botten.org>
+%%% @doc coveralls
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%=============================================================================
+%% Module declaration
+
+-module(coveralls).
+
+%%=============================================================================
+%% Exports
+
+-export([ convert_file/2
+ , convert_and_send_file/2
+ ]).
+
+%%=============================================================================
+%% Records
+
+-record(s, { importer = fun cover:import/1
+ , module_lister = fun cover:imported_modules/0
+ , mod_info = fun module_info_compile/1
+ , file_reader = fun file:read_file/1
+ , wildcard_reader = fun filelib:wildcard/1
+ , analyser = fun cover:analyse/3
+ , poster = fun httpc:request/4
+ , poster_init = start_wrapper([fun ssl:start/0, fun inets:start/0])
+ }).
+
+%%=============================================================================
+%% Defines
+
+-define(COVERALLS_URL, "https://coveralls.io/api/v1/jobs").
+%%-define(COVERALLS_URL, "http://127.0.0.1:8080").
+
+-ifdef(random_only).
+-define(random, random).
+-else.
+-define(random, rand).
+-endif.
+
+%%=============================================================================
+%% API functions
+
+%% @doc Import and convert cover file(s) `Filenames' to a json string
+%% representation suitable to post to coveralls.
+%%
+%% Note that this function will crash if the modules mentioned in
+%% any of the `Filenames' are not availabe on the node.
+%% @end
+-spec convert_file(string() | [string()], map()) ->
+ string().
+convert_file(Filenames, Report) ->
+ convert_file(Filenames, Report, #s{}).
+
+%% @doc Import and convert cover files `Filenames' to a json string and send the
+%% json to coveralls.
+%% @end
+-spec convert_and_send_file(string() | [string()], map()) -> ok.
+convert_and_send_file(Filenames, Report) ->
+ convert_and_send_file(Filenames, Report, #s{}).
+
+%%=============================================================================
+%% Internal functions
+
+convert_file([L|_]=Filename, Report, S) when is_integer(L) ->
+ %% single file or wildcard was specified
+ WildcardReader = S#s.wildcard_reader,
+ Filenames = WildcardReader(Filename),
+ convert_file(Filenames, Report, S);
+convert_file([[_|_]|_]=Filenames, Report, S) ->
+ ok = lists:foreach(
+ fun(Filename) -> ok = import(S, Filename) end,
+ Filenames),
+ ConvertedModules = convert_modules(S),
+ jsx:encode(Report#{source_files => ConvertedModules}, []).
+
+convert_and_send_file(Filenames, Report, S) ->
+ send(convert_file(Filenames, Report, S), S).
+
+send(Json, #s{poster=Poster, poster_init=Init}) ->
+ ok = Init(),
+ Boundary = ["----------", integer_to_list(?random:uniform(1000))],
+ Type = "multipart/form-data; boundary=" ++ Boundary,
+ Body = to_body(Json, Boundary),
+ R = Poster(post, {?COVERALLS_URL, [], Type, Body}, [], []),
+ {ok, {{_, ReturnCode, _}, _, Message}} = R,
+ case ReturnCode of
+ 200 -> ok;
+ ErrCode -> throw({error, {ErrCode, Message}})
+ end.
+
+%%-----------------------------------------------------------------------------
+%% HTTP helpers
+
+to_body(Json, Boundary) ->
+ iolist_to_binary(["--", Boundary, "\r\n",
+ "Content-Disposition: form-data; name=\"json_file\"; "
+ "filename=\"json_file.json\" \r\n"
+ "Content-Type: application/json\r\n\r\n",
+ Json, "\r\n", "--", Boundary, "--", "\r\n"]).
+
+%%-----------------------------------------------------------------------------
+%% Callback mockery
+
+import(#s{importer=F}, File) -> F(File).
+
+imported_modules(#s{module_lister=F}) -> F().
+
+analyze(#s{analyser=F}, Mod) -> F(Mod, calls, line).
+
+compile_info(#s{mod_info=F}, Mod) -> F(Mod).
+
+-ifdef(TEST).
+module_info_compile(Mod) -> Mod:module_info(compile).
+-else.
+module_info_compile(Mod) ->
+ code:load_file(Mod),
+ case code:is_loaded(Mod) of
+ {file, _} -> Mod:module_info(compile);
+ _ -> []
+ end.
+-endif.
+
+read_file(#s{file_reader=_F}, "") -> {ok, <<"">>};
+read_file(#s{file_reader=F}, SrcFile) -> F(SrcFile).
+
+start_wrapper(Funs) ->
+ fun() ->
+ lists:foreach(fun(F) -> ok = wrap_start(F) end, Funs)
+ end.
+
+wrap_start(StartFun) ->
+ case StartFun() of
+ {error,{already_started,_}} -> ok;
+ ok -> ok
+ end.
+
+digit(I) when I < 10 -> <<($0 + I):8>>;
+digit(I) -> <<($a -10 + I):8>>.
+
+hex(<<>>) ->
+ <<>>;
+hex(<<I:4, R/bitstring>>) ->
+ <<(digit(I))/binary, (hex(R))/binary>>.
+
+%%-----------------------------------------------------------------------------
+%% Converting modules
+
+convert_modules(S) ->
+ F = fun(Mod, L) -> convert_module(Mod, S, L) end,
+ lists:foldr(F, [], imported_modules(S)).
+
+convert_module(Mod, S, L) ->
+ {ok, CoveredLines0} = analyze(S, Mod),
+ %% Remove strange 0 indexed line
+ FilterF = fun({{_, X}, _}) -> X =/= 0 end,
+ CoveredLines = lists:filter(FilterF, CoveredLines0),
+ case proplists:get_value(source, compile_info(S, Mod), "") of
+ "" -> L;
+ SrcFile ->
+ {ok, SrcBin} = read_file(S, SrcFile),
+ Src0 = lists:flatten(io_lib:format("~s", [SrcBin])),
+ SrcDigest = erlang:md5(SrcBin),
+ LinesCount = count_lines(Src0),
+ Cov = create_cov(CoveredLines, LinesCount),
+ [#{name => unicode:characters_to_binary(relative_to_cwd(SrcFile), utf8, utf8),
+ source_digest => hex(SrcDigest),
+ coverage => Cov}
+ | L]
+ end.
+
+expand(Path) -> expand(filename:split(Path), []).
+
+expand([], Acc) -> filename:join(lists:reverse(Acc));
+expand(["."|Tail], Acc) -> expand(Tail, Acc);
+expand([".."|Tail], []) -> expand(Tail, []);
+expand([".."|Tail], [_|Acc]) -> expand(Tail, Acc);
+expand([Segment|Tail], Acc) -> expand(Tail, [Segment|Acc]).
+
+realpath(Path) -> realpath(filename:split(Path), "./").
+
+realpath([], Acc) -> filename:absname(expand(Acc));
+realpath([Head | Tail], Acc) ->
+ NewAcc0 = filename:join([Acc, Head]),
+ NewAcc = case file:read_link(NewAcc0) of
+ {ok, Link} ->
+ case filename:pathtype(Link) of
+ absolute -> realpath(Link);
+ relative -> filename:join([Acc, Link])
+ end;
+ _ -> NewAcc0
+ end,
+ realpath(Tail, NewAcc).
+
+relative_to_cwd(Path) ->
+ case file:get_cwd() of
+ {ok, Base} -> relative_to(Path, Base);
+ _ -> Path
+ end.
+
+relative_to(Path, From) ->
+ Path1 = realpath(Path),
+ relative_to(filename:split(Path1), filename:split(From), Path).
+
+relative_to([H|T1], [H|T2], Original) -> relative_to(T1, T2, Original);
+relative_to([_|_] = L1, [], _Original) -> filename:join(L1);
+relative_to(_, _, Original) -> Original.
+
+create_cov(_CoveredLines, []) ->
+ [];
+create_cov(CoveredLines, LinesCount) when is_integer(LinesCount) ->
+ create_cov(CoveredLines, lists:seq(1, LinesCount));
+create_cov([{{_,LineNo},Count}|CoveredLines], [LineNo|LineNos]) ->
+ [Count | create_cov(CoveredLines, LineNos)];
+create_cov(CoveredLines, [_|LineNos]) ->
+ [null | create_cov(CoveredLines, LineNos)].
+
+%%-----------------------------------------------------------------------------
+%% Generic helpers
+
+count_lines("") -> 1;
+count_lines("\n") -> 1;
+count_lines([$\n|S]) -> 1 + count_lines(S);
+count_lines([_|S]) -> count_lines(S).
+
+%%=============================================================================
+%% Tests
+
+-ifdef(TEST).
+-define(DEBUG, true).
+-include_lib("eunit/include/eunit.hrl").
+
+normalize_json_str(Str) when is_binary(Str) ->
+ jsx:encode(jsx:decode(Str, [return_maps, {labels, existing_atom}]));
+normalize_json_str(Str) when is_list(Str) ->
+ normalize_json_str(iolist_to_binary(Str)).
+
+convert_file_test() ->
+ Expected =
+ jsx:decode(
+ <<"{\"service_job_id\": \"1234567890\","
+ " \"service_name\": \"travis-ci\","
+ " \"source_files\": ["
+ " {\"name\": \"example.rb\","
+ " \"source_digest\": \"3feb892deff06e7accbe2457eec4cd8b\","
+ " \"coverage\": [null,1,null]"
+ " },"
+ " {\"name\": \"two.rb\","
+ " \"source_digest\": \"fce46ee19702bd262b2e4907a005aff4\","
+ " \"coverage\": [null,1,0,null]"
+ " }"
+ " ]"
+ "}">>, [return_maps, {labels, existing_atom}]),
+ Report = #{service_job_id => <<"1234567890">>,
+ service_name => <<"travis-ci">>},
+ Got = jsx:decode(
+ convert_file("example.rb", Report, mock_s()),
+ [return_maps, {labels, existing_atom}]),
+ ?assertEqual(Expected, Got).
+
+convert_and_send_file_test() ->
+ Expected =
+ normalize_json_str(
+ "{\"service_job_id\": \"1234567890\","
+ " \"service_name\": \"travis-ci\","
+ " \"source_files\": ["
+ " {\"name\": \"example.rb\","
+ " \"source_digest\": \"3feb892deff06e7accbe2457eec4cd8b\","
+ " \"coverage\": [null,1,null]"
+ " },"
+ " {\"name\": \"two.rb\","
+ " \"source_digest\": \"fce46ee19702bd262b2e4907a005aff4\","
+ " \"coverage\": [null,1,0,null]"
+ " }"
+ " ]"
+ "}"),
+ Report = #{service_job_id => <<"1234567890">>,
+ service_name => <<"travis-ci">>},
+ ?assertEqual(ok, convert_and_send_file("example.rb", Report, mock_s(Expected))).
+
+send_test_() ->
+ Expected =
+ normalize_json_str(
+ "{\"service_job_id\": \"1234567890\",\n"
+ " \"service_name\": \"travis-ci\",\n"
+ " \"source_files\": [\n"
+ " {\"name\": \"example.rb\",\n"
+ " \"source_digest\": \"\tdef four\\n 4\\nend\",\n"
+ " \"coverage\": [null,1,null]\n"
+ " }"
+ " ]"
+ "}"),
+ [ ?_assertEqual(ok, send(Expected, mock_s(Expected)))
+ , ?_assertThrow({error, {_,_}}, send("foo", mock_s(<<"bar">>)))
+ ].
+
+%%-----------------------------------------------------------------------------
+%% Generic helpers tests
+
+count_lines_test_() ->
+ [ ?_assertEqual(1, count_lines(""))
+ , ?_assertEqual(1, count_lines("foo"))
+ , ?_assertEqual(1, count_lines("bar\n"))
+ , ?_assertEqual(2, count_lines("foo\nbar"))
+ , ?_assertEqual(3, count_lines("foo\n\nbar"))
+ , ?_assertEqual(2, count_lines("foo\nbar\n"))
+ ].
+
+expand_test_() ->
+ [ ?_assertEqual("/a/b", expand(["/", "a", "b"], []))
+ , ?_assertEqual("a/c" , expand(["a", "b", "..", ".", "c"], []))
+ , ?_assertEqual("/" , expand(["..", ".", "/"], []))
+ ].
+
+realpath_and_relative_test_() ->
+ {setup,
+ fun() -> %% setup
+ {ok, Cwd} = file:get_cwd(),
+ Root = string:strip(
+ os:cmd("mktemp -d -t coveralls_tests.XXX"), right, $\n),
+ ok = file:set_cwd(Root),
+ {Cwd, Root}
+ end,
+ fun({Cwd, _Root}) -> %% teardown
+ ok = file:set_cwd(Cwd)
+ end,
+ fun({_Cwd, Root}) -> %% tests
+ Filename = "file",
+ Dir1 = filename:join([Root, "_test_src", "dir1"]),
+ Dir2 = filename:join([Root, "_test_src", "dir2"]),
+ File1 = filename:join([Dir1, Filename]),
+ File2 = filename:join([Dir2, Filename]),
+ Link1 = filename:join([ Root
+ , "_test_build"
+ , "default"
+ , "lib"
+ , "mylib"
+ , "src"
+ , "dir1"
+ ]),
+ Link2 = filename:join([ Root
+ , "_test_build"
+ , "default"
+ , "lib"
+ , "mylib"
+ , "src"
+ , "dir2"
+ ]),
+ [ ?_assertEqual(ok,
+ filelib:ensure_dir(filename:join([Dir1, "dummy"])))
+ , ?_assertEqual(ok,
+ filelib:ensure_dir(filename:join([Dir2, "dummy"])))
+ , ?_assertEqual(ok,
+ file:write_file(File1, "data"))
+ , ?_assertEqual(ok,
+ file:write_file(File2, "data"))
+ , ?_assertEqual(ok,
+ filelib:ensure_dir(Link1))
+ , ?_assertEqual(ok,
+ filelib:ensure_dir(Link2))
+ , ?_assertEqual(ok,
+ file:make_symlink(Dir1, Link1))
+ , ?_assertEqual(ok,
+ file:make_symlink(filename:join([ ".."
+ , ".."
+ , ".."
+ , ".."
+ , ".."
+ , "_test_src"
+ , "dir2"
+ ])
+ , Link2))
+ , ?_assertEqual(realpath(File1),
+ realpath(filename:join([Link1, Filename])))
+ , ?_assertEqual(realpath(File2),
+ realpath(filename:join([Link2, Filename])))
+ , ?_assertEqual(realpath(File1),
+ filename:absname(
+ relative_to_cwd(
+ filename:join([Link1, Filename]))))
+ , ?_assertEqual(realpath(File2),
+ filename:absname(
+ relative_to_cwd(
+ filename:join([Link2, Filename]))))
+ ]
+ end}.
+
+%%-----------------------------------------------------------------------------
+%% Callback mockery tests
+module_info_compile_test() ->
+ ?assert(is_tuple(lists:keyfind(source, 1, module_info_compile(?MODULE)))).
+
+start_wrapper_test_() ->
+ F = fun() -> ok end,
+ StartedF = fun() -> {error,{already_started,mod}} end,
+ ErrorF = fun() -> {error, {error, mod}} end,
+ [ ?_assertEqual(ok, (start_wrapper([F, StartedF]))())
+ , ?_assertError(_, (start_wrapper([F, StartedF, ErrorF]))())
+ ].
+
+%%-----------------------------------------------------------------------------
+%% Converting modules tests
+
+create_cov_test() ->
+ ?assertEqual([null, 3, null, 4, null],
+ create_cov([{{foo, 2}, 3}, {{foo, 4}, 4}], 5)).
+
+convert_module_test() ->
+ Expected =
+ [#{name => <<"example.rb">>,
+ source_digest => <<"3feb892deff06e7accbe2457eec4cd8b">>,
+ coverage => [null,1,null]}],
+ ?assertEqual(Expected, convert_module('example.rb', mock_s(), [])).
+
+convert_modules_test() ->
+ Expected =
+ [#{name => <<"example.rb">>,
+ source_digest => <<"3feb892deff06e7accbe2457eec4cd8b">>,
+ coverage => [null,1,null]
+ },
+ #{name => <<"two.rb">>,
+ source_digest => <<"fce46ee19702bd262b2e4907a005aff4">>,
+ coverage => [null,1,0,null]
+ }],
+ ?assertEqual(Expected,
+ convert_modules(mock_s())).
+
+%%-----------------------------------------------------------------------------
+%% Setup helpers
+
+mock_s() -> mock_s("").
+
+mock_s(Json) ->
+ #s{ importer =
+ fun(_) -> ok end
+ , module_lister =
+ fun() -> ['example.rb', 'two.rb'] end
+ , mod_info =
+ fun('example.rb') -> [{source,"example.rb"}];
+ ('two.rb') -> [{source,"two.rb"}]
+ end
+ , file_reader =
+ fun("example.rb") ->
+ {ok, <<"def four\n 4\nend">>};
+ ("two.rb") ->
+ {ok, <<"def seven\n eight\n nine\nend">>}
+ end
+ , wildcard_reader = fun(AnyFile) -> [AnyFile] end
+ , analyser =
+ fun('example.rb' , calls, line) -> {ok, [ {{'example.rb', 2}, 1} ]};
+ ('two.rb' , calls, line) -> {ok, [ {{'two.rb', 2}, 1}
+ , {{'two.rb', 3}, 0}
+ ]
+ }
+ end
+ , poster_init =
+ fun() -> ok end
+ , poster =
+ fun(post, {_, _, _, Body}, _, _) ->
+ case binary:match(Body, Json) =/= nomatch of
+ true -> {ok, {{"", 200, ""}, "", ""}};
+ false -> {ok, {{"", 666, ""}, "", "Not expected"}}
+ end
+ end
+ }.
+
+-endif.
+
+%%% Local Variables:
+%%% allout-layout: t
+%%% erlang-indent-level: 2
+%%% End:
diff --git a/server/_build/default/plugins/coveralls/src/rebar3_coveralls.erl b/server/_build/default/plugins/coveralls/src/rebar3_coveralls.erl
new file mode 100644
index 0000000..01084ee
--- /dev/null
+++ b/server/_build/default/plugins/coveralls/src/rebar3_coveralls.erl
@@ -0,0 +1,220 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Copyright (c) 2013-2016, Markus Ekholm
+%%% All rights reserved.
+%%% Redistribution and use in source and binary forms, with or without
+%%% modification, are permitted provided that the following conditions are met:
+%%% * Redistributions of source code must retain the above copyright
+%%% notice, this list of conditions and the following disclaimer.
+%%% * Redistributions in binary form must reproduce the above copyright
+%%% notice, this list of conditions and the following disclaimer in the
+%%% documentation and/or other materials provided with the distribution.
+%%% * Neither the name of the <organization> nor the
+%%% names of its contributors may be used to endorse or promote products
+%%% derived from this software without specific prior written permission.
+%%%
+%%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+%%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+%%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL MARKUS EKHOLM BE LIABLE FOR ANY
+%%% DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+%%% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+%%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+%%% ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+%%% (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+%%% THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+%%%
+%%% @copyright 2013-2016 (c) Yury Gargay <yury.gargay@gmail.com>,
+%%% Markus Ekholm <markus@botten.org>
+%%% @end
+%%% @author Yury Gargay <yury.gargay@gmail.com>
+%%% @author Markus Ekholm <markus@botten.org>
+%%% @doc coveralls plugin for rebar3
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(rebar3_coveralls).
+-behaviour(provider).
+
+-export([ init/1
+ , do/1
+ , format_error/1
+ ]).
+
+-define(PROVIDER, send).
+-define(DEPS, [{default, app_discovery}]).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+ Provider = providers:create([ {name, ?PROVIDER}
+ , {module, ?MODULE}
+ , {namespace, coveralls}
+ , {bare, true}
+ , {deps, ?DEPS}
+ , {example, "rebar3 coveralls send"}
+ , {short_desc, "Send coverdata to coveralls."}
+ , {desc, "Send coveralls to coveralls."}
+ , {opts, []}
+ ]),
+ {ok, rebar_state:add_provider(State, Provider)}.
+
+-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(State) ->
+ rebar_api:info("Running coveralls...", []),
+ ConvertAndSend = fun coveralls:convert_and_send_file/2,
+ Get = fun(Key, Def) -> rebar_state:get(State, Key, Def) end,
+ GetLocal = fun(Key, Def) -> rebar_state:get(State, Key, Def) end,
+ MaybeSkip = fun() -> ok end,
+ ok = cover_paths(State),
+ try
+ do_coveralls(ConvertAndSend,
+ Get,
+ GetLocal,
+ MaybeSkip,
+ 'send-coveralls'),
+ {ok, State}
+ catch throw:{error, {ErrCode, Msg}} ->
+ io:format("Failed sending coverdata to coveralls, ~p: ~p",
+ [ErrCode, Msg]),
+ {error, rebar_abort}
+ end.
+
+-spec format_error(any()) -> iolist().
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
+
+cover_paths(State) ->
+ lists:foreach(fun(App) ->
+ AppDir = rebar_app_info:out_dir(App),
+ true = code:add_patha(filename:join([AppDir, "ebin"])),
+ _ = code:add_patha(filename:join([AppDir, "test"]))
+ end,
+ rebar_state:project_apps(State)),
+ _ = code:add_patha(filename:join([rebar_dir:base_dir(State), "test"])),
+ ok.
+
+%%=============================================================================
+%% Internal functions
+
+to_binary(List) when is_list(List) ->
+ unicode:characters_to_binary(List, utf8, utf8);
+to_binary(Atom) when is_atom(Atom) ->
+ atom_to_binary(Atom, utf8);
+to_binary(Bin) when is_binary(Bin) ->
+ Bin.
+to_boolean(true) -> true;
+to_boolean(1) -> true;
+to_boolean(_) -> false.
+
+do_coveralls(ConvertAndSend, Get, GetLocal, MaybeSkip, Task) ->
+ File = GetLocal(coveralls_coverdata, undef),
+ ServiceName = to_binary(GetLocal(coveralls_service_name, undef)),
+ ServiceJobId = to_binary(GetLocal(coveralls_service_job_id, undef)),
+ F = fun(X) -> X =:= undef orelse X =:= false end,
+ CoverExport = Get(cover_export_enabled, false),
+ case lists:any(F, [File, ServiceName, ServiceJobId, CoverExport]) of
+ true ->
+ throw({error,
+ "need to specify coveralls_* and cover_export_enabled "
+ "in rebar.config"});
+ false ->
+ ok
+ end,
+
+ Report0 =
+ #{service_job_id => ServiceJobId,
+ service_name => ServiceName},
+ Opts = [{coveralls_repo_token, repo_token, string},
+ {coveralls_service_pull_request, service_pull_request, string},
+ {coveralls_commit_sha, commit_sha, string},
+ {coveralls_service_number, service_number, string},
+ {coveralls_parallel, parallel, boolean}],
+ Report =
+ lists:foldl(fun({Cfg, Key, Conv}, R) ->
+ case GetLocal(Cfg, undef) of
+ undef -> R;
+ Value when Conv =:= string -> maps:put(Key, to_binary(Value), R);
+ Value when Conv =:= boolean -> maps:put(Key, to_boolean(Value), R);
+ Value -> maps:put(Key, Value, R)
+ end
+ end, Report0, Opts),
+
+ DoCoveralls = (GetLocal(do_coveralls_after_ct, true) andalso Task == ct)
+ orelse (GetLocal(do_coveralls_after_eunit, true) andalso Task == eunit)
+ orelse Task == 'send-coveralls',
+ case DoCoveralls of
+ true ->
+ io:format("rebar_coveralls:"
+ "Exporting cover data "
+ "from ~s using service ~s and jobid ~s~n",
+ [File, ServiceName, ServiceJobId]),
+ ok = ConvertAndSend(File, Report);
+ _ -> MaybeSkip()
+ end.
+
+
+%%=============================================================================
+%% Tests
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+task_test_() ->
+ File = "foo",
+ ServiceJobId = "123",
+ ServiceName = "bar",
+ ConvertAndSend = fun("foo", #{service_job_id := <<"123">>,
+ service_name := <<"bar">>}) -> ok end,
+ ConvertWithOpts = fun("foo", #{service_job_id := <<"123">>,
+ service_name := <<"bar">>,
+ service_pull_request := <<"PR#1">>,
+ parallel := true}) -> ok
+ end,
+ Get = fun(cover_export_enabled, _) -> true end,
+ GetLocal = fun(coveralls_coverdata, _) -> File;
+ (coveralls_service_name, _) -> ServiceName;
+ (coveralls_service_job_id, _) -> ServiceJobId;
+ (do_coveralls_after_eunit, _) -> true;
+ (do_coveralls_after_ct, _) -> true;
+ (coveralls_repo_token, _) -> [];
+ (_, Default) -> Default
+ end,
+ GetLocalAllOpt = fun(coveralls_coverdata, _) -> File;
+ (coveralls_service_name, _) -> ServiceName;
+ (coveralls_service_job_id, _) -> ServiceJobId;
+ (coveralls_service_pull_request, _) -> "PR#1";
+ (coveralls_parallel, _) -> true;
+ (do_coveralls_after_eunit, _) -> true;
+ (do_coveralls_after_ct, _) -> true;
+ (coveralls_repo_token, _) -> [];
+ (_, Default) -> Default
+ end,
+ GetLocalWithCoverallsTask
+ = fun(coveralls_coverdata, _) -> File;
+ (coveralls_service_name, _) -> ServiceName;
+ (coveralls_service_job_id, _) -> ServiceJobId;
+ (do_coveralls_after_eunit, _) -> false;
+ (do_coveralls_after_ct, _) -> false;
+ (coveralls_repo_token, _) -> [];
+ (_, Default) -> Default
+ end,
+ GetBroken = fun(cover_export_enabled, _) -> false end,
+ MaybeSkip = fun() -> skip end,
+ [ ?_assertEqual(ok, do_coveralls(ConvertAndSend, Get, GetLocal, MaybeSkip, eunit))
+ , ?_assertEqual(ok, do_coveralls(ConvertAndSend, Get, GetLocal, MaybeSkip, ct))
+ , ?_assertThrow({error, _}, do_coveralls(ConvertAndSend, GetBroken, GetLocal, MaybeSkip, eunit))
+ , ?_assertThrow({error, _}, do_coveralls(ConvertAndSend, GetBroken, GetLocal, MaybeSkip, ct))
+ , ?_assertEqual(skip, do_coveralls(ConvertAndSend, Get, GetLocalWithCoverallsTask, MaybeSkip, eunit))
+ , ?_assertEqual(skip, do_coveralls(ConvertAndSend, Get, GetLocalWithCoverallsTask, MaybeSkip, ct))
+ , ?_assertEqual(ok, do_coveralls(ConvertAndSend, Get, GetLocalWithCoverallsTask, MaybeSkip, 'send-coveralls'))
+ , ?_assertEqual(ok, do_coveralls(ConvertWithOpts, Get, GetLocalAllOpt, MaybeSkip, eunit))
+ , ?_assertEqual(ok, do_coveralls(ConvertWithOpts, Get, GetLocalAllOpt, MaybeSkip, ct))
+ ].
+
+-endif.
+
+%%% Local Variables:
+%%% allout-layout: t
+%%% erlang-indent-level: 2
+%%% End: