summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbootstrap80
-rw-r--r--priv/shell-completion/bash/rebar34
-rw-r--r--priv/shell-completion/fish/rebar3.fish1
-rw-r--r--priv/shell-completion/zsh/_rebar31
-rw-r--r--priv/templates/otp_app.app.src1
-rw-r--r--priv/templates/otp_lib.app.src1
-rw-r--r--rebar.config6
-rw-r--r--rebar.lock12
-rw-r--r--src/cth_fail_fast.erl118
-rw-r--r--src/rebar_git_resource.erl8
-rw-r--r--src/rebar_hex_repos.erl16
-rw-r--r--src/rebar_hg_resource.erl12
-rw-r--r--src/rebar_packages.erl39
-rw-r--r--src/rebar_prv_common_test.erl26
-rw-r--r--src/rebar_prv_eunit.erl32
-rw-r--r--src/rebar_prv_packages.erl4
-rw-r--r--test/rebar_eunit_SUITE.erl35
-rw-r--r--test/rebar_pkg_SUITE.erl22
-rw-r--r--test/rebar_pkg_alias_SUITE.erl14
-rw-r--r--test/rebar_pkg_repos_SUITE.erl8
20 files changed, 361 insertions, 79 deletions
diff --git a/bootstrap b/bootstrap
index ebbf35b..1c9d879 100755
--- a/bootstrap
+++ b/bootstrap
@@ -11,6 +11,10 @@ main(_) ->
inets:start(httpc, [{profile, rebar}]),
set_httpc_options(),
+ %% Clear directories for builds since bootstrapping may require
+ %% a changed structure from an older one
+ rm_rf("_build/bootstrap"),
+
%% Fetch and build deps required to build rebar3
BaseDeps = [{providers, []}
,{getopt, []}
@@ -27,7 +31,7 @@ main(_) ->
bootstrap_rebar3(),
%% Build rebar.app from rebar.app.src
- {ok, App} = rebar_app_info:new(rebar, "3.7.5", filename:absname("_build/default/lib/rebar/")),
+ {ok, App} = rebar_app_info:new(rebar, "3.8.0", filename:absname("_build/default/lib/rebar/")),
rebar_otp_app:compile(rebar_state:new(), App),
%% Because we are compiling files that are loaded already we want to silence
@@ -135,13 +139,23 @@ compile(App, FirstFiles) ->
filelib:ensure_dir(filename:join([Dir, "ebin", "dummy.beam"])),
code:add_path(filename:join(Dir, "ebin")),
FirstFilesPaths = [filename:join([Dir, "src", Module]) || Module <- FirstFiles],
+ LeexFiles = filelib:wildcard(filename:join([Dir, "src", "*.xrl"])),
+ [compile_xrl_file(X) || X <- LeexFiles],
+ YeccFiles = filelib:wildcard(filename:join([Dir, "src", "*.yrl"])),
+ [compile_yrl_file(X) || X <- YeccFiles],
Sources = FirstFilesPaths ++ filelib:wildcard(filename:join([Dir, "src", "*.erl"])),
- [compile_file(X, [{i, filename:join(Dir, "include")}
+ [compile_erl_file(X, [{i, filename:join(Dir, "include")}
,debug_info
,{outdir, filename:join(Dir, "ebin")}
,return | additional_defines()]) || X <- Sources].
-compile_file(File, Opts) ->
+compile_xrl_file(File) ->
+ {ok, _} = leex:file(File).
+
+compile_yrl_file(File) ->
+ {ok, _} = yecc:file(File).
+
+compile_erl_file(File, Opts) ->
case compile:file(File, Opts) of
{ok, _Mod} ->
ok;
@@ -162,7 +176,7 @@ bootstrap_rebar3() ->
filename:absname("_build/default/lib/rebar/src")),
true = Res == ok orelse Res == exists,
Sources = ["src/rebar_resource_v2.erl", "src/rebar_resource.erl" | filelib:wildcard("src/*.erl")],
- [compile_file(X, [{outdir, "_build/default/lib/rebar/ebin/"}
+ [compile_erl_file(X, [{outdir, "_build/default/lib/rebar/ebin/"}
,return | additional_defines()]) || X <- Sources],
code:add_patha(filename:absname("_build/default/lib/rebar/ebin")).
@@ -205,6 +219,23 @@ symlink_or_copy(Source, Target) ->
end
end.
+-spec rm_rf(string()) -> 'ok'.
+rm_rf(Target) ->
+ case os:type() of
+ {unix, _} ->
+ EscTarget = escape_chars(Target),
+ {ok, []} = sh(?FMT("rm -rf ~ts", [EscTarget]),
+ [{use_stdout, false}, abort_on_error]),
+ ok;
+ {win32, _} ->
+ Filelist = filelib:wildcard(Target),
+ Dirs = [F || F <- Filelist, filelib:is_dir(F)],
+ Files = Filelist -- Dirs,
+ ok = delete_each(Files),
+ ok = delete_each_dir_win32(Dirs),
+ ok
+ end.
+
-spec cp_r(list(string()), file:filename()) -> 'ok'.
cp_r([], _Dest) ->
ok;
@@ -335,6 +366,27 @@ win32_ok({ok, _}) -> true;
win32_ok({error, {Rc, _}}) when Rc<9; Rc=:=16 -> true;
win32_ok(_) -> false.
+%% @private windows rm_rf helpers
+delete_each([]) ->
+ ok;
+delete_each([File | Rest]) ->
+ case file:delete(File) of
+ ok ->
+ delete_each(Rest);
+ {error, enoent} ->
+ delete_each(Rest);
+ {error, Reason} ->
+ io:format("Failed to delete file ~ts: ~p\n", [File, Reason]),
+ error
+ end.
+
+delete_each_dir_win32([]) -> ok;
+delete_each_dir_win32([Dir | Rest]) ->
+ {ok, []} = sh(?FMT("rd /q /s \"~ts\"",
+ [escape_double_quotes(filename:nativename(Dir))]),
+ [{use_stdout, false}, return_on_error]),
+ delete_each_dir_win32(Rest).
+
%%/rebar_file_utils
%%rebar_utils
@@ -398,7 +450,14 @@ expand_sh_flag(return_on_error) ->
end};
expand_sh_flag(abort_on_error) ->
{error_handler,
- fun log_and_abort/2};
+ %% moved log_and_abort/2 here because some versions somehow had trouble
+ %% interpreting it and thought `fun log_and_abort/2' was in `erl_eval'
+ fun(Command, {Rc, Output}) ->
+ io:format("sh(~ts)~n"
+ "failed with return code ~w and the following output:~n"
+ "~ts", [Command, Rc, Output]),
+ throw(bootstrap_abort)
+ end};
expand_sh_flag({use_stdout, false}) ->
{output_handler,
fun(Line, Acc) ->
@@ -443,13 +502,6 @@ expand_env_variable(InStr, VarName, RawVarValue) ->
re:replace(InStr, RegEx, [VarValue, "\\2"], ReOpts)
end.
--spec log_and_abort(string(), {integer(), string()}) -> no_return().
-log_and_abort(Command, {Rc, Output}) ->
- io:format("sh(~ts)~n"
- "failed with return code ~w and the following output:~n"
- "~ts", [Command, Rc, Output]),
- throw(bootstrap_abort).
-
%%/rebar_utils
%%rebar_dir
@@ -471,7 +523,7 @@ make_normalized_path(Path) ->
AbsPath = make_absolute_path(Path),
Components = filename:split(AbsPath),
make_normalized_path(Components, []).
-
+
make_absolute_path(Path) ->
case filename:pathtype(Path) of
absolute ->
@@ -636,7 +688,7 @@ join([], Sep) when is_list(Sep) ->
join([H|T], Sep) ->
H ++ lists:append([Sep ++ X || X <- T]).
-%% Same for chr; no non-deprecated equivalent in OTP20+
+%% Same for chr; no non-deprecated equivalent in OTP20+
chr(S, C) when is_integer(C) -> chr(S, C, 1).
chr([C|_Cs], C, I) -> I;
chr([_|Cs], C, I) -> chr(Cs, C, I+1);
diff --git a/priv/shell-completion/bash/rebar3 b/priv/shell-completion/bash/rebar3
index be9af44..41c45f2 100644
--- a/priv/shell-completion/bash/rebar3
+++ b/priv/shell-completion/bash/rebar3
@@ -93,8 +93,8 @@ _rebar3()
elif [[ ${prev} == escriptize ]] ; then
:
elif [[ ${prev} == eunit ]] ; then
- sopts="-c -e -v -d -f -m -s"
- lopts="--app --application --cover --dir --error_on_warning --file --module --suite --verbose"
+ sopts="-c -e -v -d -f -m -s -g"
+ lopts="--app --application --cover --dir --error_on_warning --file --module --suite --generator --verbose"
elif [[ ${prev} == help ]] ; then
:
elif [[ ${prev} == new ]] ; then
diff --git a/priv/shell-completion/fish/rebar3.fish b/priv/shell-completion/fish/rebar3.fish
index 9cd2c82..e578b96 100644
--- a/priv/shell-completion/fish/rebar3.fish
+++ b/priv/shell-completion/fish/rebar3.fish
@@ -136,6 +136,7 @@ complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunut' -s e -l error_on_
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s f -l file -d "Comma separated list of files to load tests from. Equivalent to `[{file, File}]`"
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s m -l module -d "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`"
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s s -l suite -d "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`"
+complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s g -l generator -d "Comma separated list of generators (the format is `module:function`) to load tests from. Equivalent to `[{generator, Module, Function}]`"
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s v -l verbose -d "Verbose output"
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -l suite -d "Lists of test suites to run"
diff --git a/priv/shell-completion/zsh/_rebar3 b/priv/shell-completion/zsh/_rebar3
index 490a824..8ae8777 100644
--- a/priv/shell-completion/zsh/_rebar3
+++ b/priv/shell-completion/zsh/_rebar3
@@ -95,6 +95,7 @@ _rebar3 () {
'(-f --file)'{-f,--file}'[Comma separated list of files to load tests from]:files' \
'(-m --module)'{-m,--module}'[Comma separated list of modules to load tests from]:modules' \
'(-s --suite)'{-s,--suite}'[Comma separated list of modules to load tests from]:modules' \
+ '(-g --generator)'{-g,--generator}'[Comma separated list of generators (the format is `module:function`) to load tests from.]:{generator, Module, Function}' \
'(-v --verbose)'{-v,--verbose}'[Verbose output]' \
&& ret=0
;;
diff --git a/priv/templates/otp_app.app.src b/priv/templates/otp_app.app.src
index 6040089..f6e9d45 100644
--- a/priv/templates/otp_app.app.src
+++ b/priv/templates/otp_app.app.src
@@ -11,7 +11,6 @@
{env,[]},
{modules, []},
- {maintainers, []},
{licenses, ["Apache 2.0"]},
{links, []}
]}.
diff --git a/priv/templates/otp_lib.app.src b/priv/templates/otp_lib.app.src
index aa31966..d14752f 100644
--- a/priv/templates/otp_lib.app.src
+++ b/priv/templates/otp_lib.app.src
@@ -9,7 +9,6 @@
{env,[]},
{modules, []},
- {maintainers, []},
{licenses, ["Apache 2.0"]},
{links, []}
]}.
diff --git a/rebar.config b/rebar.config
index 61efd8d..75601e9 100644
--- a/rebar.config
+++ b/rebar.config
@@ -1,17 +1,17 @@
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et
-{deps, [{erlware_commons, "1.3.0"},
+{deps, [{erlware_commons, "1.3.1"},
{ssl_verify_fun, "1.1.3"},
{certifi, "2.3.1"},
{parse_trans, "3.3.0"}, % force otp-21 compat
{providers, "1.7.0"},
{getopt, "1.0.1"},
{bbmustache, "1.6.0"},
- {relx, "3.27.0"},
+ {relx, "3.28.0"},
{cf, "0.2.2"},
{cth_readable, "1.4.2"},
- {hex_core, "0.2.0"},
+ {hex_core, "0.4.0"},
{eunit_formatters, "0.5.0"}]}.
{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)",
diff --git a/rebar.lock b/rebar.lock
index a17e688..180e37b 100644
--- a/rebar.lock
+++ b/rebar.lock
@@ -3,13 +3,13 @@
{<<"certifi">>,{pkg,<<"certifi">>,<<"2.3.1">>},0},
{<<"cf">>,{pkg,<<"cf">>,<<"0.2.2">>},0},
{<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.4.2">>},0},
- {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"1.3.0">>},0},
+ {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"1.3.1">>},0},
{<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.5.0">>},0},
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0},
- {<<"hex_core">>,{pkg,<<"hex_core">>,<<"0.2.0">>},0},
+ {<<"hex_core">>,{pkg,<<"hex_core">>,<<"0.4.0">>},0},
{<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.0">>},0},
{<<"providers">>,{pkg,<<"providers">>,<<"1.7.0">>},0},
- {<<"relx">>,{pkg,<<"relx">>,<<"3.27.0">>},0},
+ {<<"relx">>,{pkg,<<"relx">>,<<"3.28.0">>},0},
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.3">>},0}]}.
[
{pkg_hash,[
@@ -17,12 +17,12 @@
{<<"certifi">>, <<"D0F424232390BF47D82DA8478022301C561CF6445B5B5FB6A84D49A9E76D2639">>},
{<<"cf">>, <<"7F2913FFF90ABCABD0F489896CFEB0B0674F6C8DF6C10B17A83175448029896C">>},
{<<"cth_readable">>, <<"0F57B4EB7DA7F5438F422312245F9143A1B3118C11B6BAE5C3D1391C9EE88322">>},
- {<<"erlware_commons">>, <<"1705CF2AB4212EF235C21971A55E22E2A39055C05B9C65C8848126865F42A07A">>},
+ {<<"erlware_commons">>, <<"0CE192AD69BC6FD0880246D852D0ECE17631E234878011D1586E053641ED4C04">>},
{<<"eunit_formatters">>, <<"6A9133943D36A465D804C1C5B6E6839030434B8879C5600D7DDB5B3BAD4CCB59">>},
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>},
- {<<"hex_core">>, <<"3A7EACCFB8ADD3FF05D950C10ED5BDB5D0C48C988EBBC5D7AE2A55498F0EFF1B">>},
+ {<<"hex_core">>, <<"6A0E0B1B519850344292298DA1BFA685E71534FDF4C69434993039F81078C7FA">>},
{<<"parse_trans">>, <<"09765507A3C7590A784615CFD421D101AEC25098D50B89D7AA1D66646BC571C1">>},
{<<"providers">>, <<"BBF730563914328EC2511D205E6477A94831DB7297DE313B3872A2B26C562EAB">>},
- {<<"relx">>, <<"96CC7663EDCC02A8117AB0C64FE6D15BE79760C08726ABEAD1DAACE11BFBF75D">>},
+ {<<"relx">>, <<"CFC26899E308FAB79B5826C4298EC052A2C1D66D03679AB76E6266D766B56545">>},
{<<"ssl_verify_fun">>, <<"6C49665D4326E26CD4A5B7BD54AA442B33DADFB7C5D59A0D0CD0BF5534BBFBD7">>}]}
].
diff --git a/src/cth_fail_fast.erl b/src/cth_fail_fast.erl
new file mode 100644
index 0000000..13b3557
--- /dev/null
+++ b/src/cth_fail_fast.erl
@@ -0,0 +1,118 @@
+-module(cth_fail_fast).
+
+%% Callbacks
+-export([id/1]).
+-export([init/2]).
+
+-export([pre_init_per_suite/3]).
+-export([post_init_per_suite/4]).
+-export([pre_end_per_suite/3]).
+-export([post_end_per_suite/4]).
+
+-export([pre_init_per_group/3]).
+-export([post_init_per_group/4]).
+-export([pre_end_per_group/3]).
+-export([post_end_per_group/4]).
+
+-export([pre_init_per_testcase/3]).
+-export([post_end_per_testcase/4]).
+
+-export([on_tc_fail/3]).
+-export([on_tc_skip/3, on_tc_skip/4]).
+
+-export([terminate/1]).
+
+%% We work by setting an 'abort' variable on each test case that fails
+%% and then triggering the failure before starting the next test. This
+%% ensures that all other hooks have run for the same event, and
+%% simplifies error reporting.
+-record(state, {abort=false}).
+
+%% @doc Return a unique id for this CTH.
+id(_Opts) ->
+ {?MODULE, make_ref()}.
+
+%% @doc Always called before any other callback function. Use this to initiate
+%% any common state.
+init(_Id, _Opts) ->
+ {ok, #state{}}.
+
+%% @doc Called before init_per_suite is called.
+pre_init_per_suite(_Suite,_Config,#state{abort=true}) ->
+ abort();
+pre_init_per_suite(_Suite,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after init_per_suite.
+post_init_per_suite(_Suite,_Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before end_per_suite.
+pre_end_per_suite(_Suite,_Config,#state{abort=true}) ->
+ abort();
+pre_end_per_suite(_Suite,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after end_per_suite.
+post_end_per_suite(_Suite,_Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before each init_per_group.
+pre_init_per_group(_Group,_Config,#state{abort=true}) ->
+ abort();
+pre_init_per_group(_Group,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each init_per_group.
+post_init_per_group(_Group,_Config,Return, State) ->
+ {Return, State}.
+
+%% @doc Called after each end_per_group.
+pre_end_per_group(_Group,_Config,#state{abort=true}) ->
+ abort();
+pre_end_per_group(_Group,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each end_per_group.
+post_end_per_group(_Group,_Config,Return, State) ->
+ {Return, State}.
+
+%% @doc Called before each test case.
+pre_init_per_testcase(_TC,_Config,#state{abort=true}) ->
+ abort();
+pre_init_per_testcase(_TC,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each test case.
+post_end_per_testcase(_TC,_Config,ok,State) ->
+ {ok, State};
+post_end_per_testcase(_TC,_Config,Error,State) ->
+ {Error, State#state{abort=true}}.
+
+%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
+%% post_end_per_group and post_end_per_testcase if the suite, group or test case failed.
+on_tc_fail(_TC, _Reason, State) ->
+ State.
+
+%% @doc Called when a test case is skipped by either user action
+%% or due to an init function failing. (>= 19.3)
+on_tc_skip(_Suite, _TC, {tc_auto_skip, _}, State) ->
+ State#state{abort=true};
+on_tc_skip(_Suite, _TC, _Reason, State) ->
+ State.
+
+%% @doc Called when a test case is skipped by either user action
+%% or due to an init function failing. (Pre-19.3)
+on_tc_skip(_TC, {tc_auto_skip, _}, State) ->
+ State#state{abort=true};
+on_tc_skip(_TC, _Reason, State) ->
+ State.
+
+%% @doc Called when the scope of the CTH is done
+terminate(#state{}) ->
+ ok.
+
+%%% Helpers
+abort() ->
+ io:format(user, "Detected test failure. Aborting~n", []),
+ halt(1).
diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl
index 691f1ba..7657699 100644
--- a/src/rebar_git_resource.erl
+++ b/src/rebar_git_resource.erl
@@ -10,6 +10,10 @@
needs_update/2,
make_vsn/2]).
+%% For backward compatibilty
+-export ([ download/3
+ ]).
+
-include("rebar.hrl").
%% Regex used for parsing scp style remote url
@@ -124,6 +128,10 @@ download(TmpDir, AppInfo, State, _) ->
{error, Error}
end.
+%% For backward compatibilty
+download(Dir, AppInfo, State) ->
+ download_(Dir, AppInfo, State).
+
download_(Dir, {git, Url}, State) ->
?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.", []),
download_(Dir, {git, Url, {branch, "master"}}, State);
diff --git a/src/rebar_hex_repos.erl b/src/rebar_hex_repos.erl
index ebee191..def5f49 100644
--- a/src/rebar_hex_repos.erl
+++ b/src/rebar_hex_repos.erl
@@ -21,7 +21,8 @@
api_key => binary(),
repo_url => binary(),
repo_public_key => binary(),
- repo_verify => binary()}.
+ repo_verify => binary(),
+ repo_verify_origin => binary()}.
from_state(BaseConfig, State) ->
HexConfig = rebar_state:get(State, hex, []),
@@ -67,17 +68,20 @@ repos(HexConfig) ->
merge_repos(RepoList ++ [HexDefaultConfig])
end.
+%% merge repos must add a field repo_name to work with hex_core 0.4.0
-spec merge_repos([repo()]) -> [repo()].
merge_repos(Repos) ->
lists:foldl(fun(R=#{name := Name}, ReposAcc) ->
%% private organizations include the parent repo before a :
case rebar_string:split(Name, <<":">>) of
[Repo, Org] ->
+ %% hex_core uses repo_name for parent
update_repo_list(R#{name => Name,
+ repo_name => Repo,
organization => Org,
parent => Repo}, ReposAcc);
_ ->
- update_repo_list(R, ReposAcc)
+ update_repo_list(R#{repo_name => Name}, ReposAcc)
end
end, [], Repos).
@@ -104,7 +108,13 @@ update_repo_list(R, []) ->
default_repo() ->
HexDefaultConfig = hex_core:default_config(),
- HexDefaultConfig#{name => ?PUBLIC_HEX_REPO}.
+ HexDefaultConfig#{name => ?PUBLIC_HEX_REPO, repo_verify_origin => repo_verify_origin()}.
+
+repo_verify_origin() ->
+ case os:getenv("REBAR_NO_VERIFY_REPO_ORIGIN") of
+ "1" -> false;
+ _ -> true
+ end.
repo_list([]) ->
[];
diff --git a/src/rebar_hg_resource.erl b/src/rebar_hg_resource.erl
index 8139d04..5ae1ee0 100644
--- a/src/rebar_hg_resource.erl
+++ b/src/rebar_hg_resource.erl
@@ -10,6 +10,11 @@
needs_update/2,
make_vsn/2]).
+
+%% For backward compatibilty
+-export([ download/3
+ ]).
+
-include("rebar.hrl").
-spec init(atom(), rebar_state:t()) -> {ok, rebar_resource_v2:resource()}.
@@ -72,6 +77,10 @@ download(TmpDir, AppInfo, State, _) ->
{error, Error}
end.
+%% For backward compatibilty
+download(Dir, AppInfo, State) ->
+ download_(Dir, AppInfo, State).
+
download_(Dir, {hg, Url}, State) ->
?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.", []),
download_(Dir, {hg, Url, {branch, "default"}}, State);
@@ -191,7 +200,7 @@ check_type_support() ->
case get({is_supported, ?MODULE}) of
true ->
ok;
- false ->
+ _ ->
case rebar_utils:sh("hg --version", [{return_on_error, true},
{use_stdout, false}]) of
{error, _} ->
@@ -201,4 +210,3 @@ check_type_support() ->
ok
end
end.
-
diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl
index 757eb86..fc68cab 100644
--- a/src/rebar_packages.erl
+++ b/src/rebar_packages.erl
@@ -49,10 +49,10 @@ get(Config, Name) ->
-spec get_all_names(rebar_state:t()) -> [binary()].
-get_all_names(State) ->
+get_all_names(State) ->
verify_table(State),
lists:usort(ets:select(?PACKAGE_TABLE, [{#package{key={'$1', '_', '_'},
- _='_'},
+ _='_'},
[], ['$1']}])).
-spec get_package_versions(unicode:unicode_binary(), ec_semver:semver(),
@@ -101,14 +101,14 @@ load_and_verify_version(State) ->
?DEBUG("Package index version mismatch. Current version ~p, this rebar3 expecting ~p",
[V, ?PACKAGE_INDEX_VERSION]),
(catch ets:delete(?PACKAGE_TABLE)),
- new_package_table()
+ new_package_table()
end;
- _ ->
+ _ ->
new_package_table()
end.
handle_missing_package(PkgKey, Repo, State, Fun) ->
- Name =
+ Name =
case PkgKey of
{N, Vsn, _Repo} ->
?DEBUG("Package ~ts-~ts not found. Fetching registry updates for "
@@ -121,8 +121,8 @@ handle_missing_package(PkgKey, Repo, State, Fun) ->
end,
update_package(Name, Repo, State),
- try
- Fun(State)
+ try
+ Fun(State)
catch
_:_ ->
%% Even after an update the package is still missing, time to error out
@@ -220,7 +220,7 @@ verify_table(State) ->
ets:info(?PACKAGE_TABLE, named_table) =:= true orelse load_and_verify_version(State).
parse_deps(Deps) ->
- [{maps:get(app, D, Name), {pkg, Name, Constraint, undefined}}
+ [{maps:get(app, D, Name), {pkg, Name, Constraint, undefined}}
|| D=#{package := Name,
requirement := Constraint} <- Deps].
@@ -233,16 +233,15 @@ parse_checksum(Checksum) ->
update_package(Name, RepoConfig=#{name := Repo}, State) ->
?MODULE:verify_table(State),
- try hex_repo:get_package(RepoConfig#{repo_key => maps:get(read_key, RepoConfig, <<>>)}, Name) of
- {ok, {200, _Headers, #{releases := Releases}}} ->
+ try hex_repo:get_package(get_package_repo_config(RepoConfig), Name) of
+ {ok, {200, _Headers, Releases}} ->
_ = insert_releases(Name, Releases, Repo, ?PACKAGE_TABLE),
{ok, RegistryDir} = rebar_packages:registry_dir(State),
PackageIndex = filename:join(RegistryDir, ?INDEX_FILE),
ok = ets:tab2file(?PACKAGE_TABLE, PackageIndex);
- {ok, {403, _Headers, <<>>}} ->
- not_found;
- {ok, {404, _Headers, _}} ->
- not_found;
+ {error, unverified} ->
+ ?WARN(unverified_repo_message(), [Repo]),
+ fail;
Error ->
?DEBUG("Hex get_package request failed: ~p", [Error]),
%% TODO: add better log message. hex_core should export a format_error
@@ -254,6 +253,18 @@ update_package(Name, RepoConfig=#{name := Repo}, State) ->
fail
end.
+get_package_repo_config(RepoConfig=#{mirror_of := Repo}) ->
+ get_package_repo_config(maps:remove(mirror_of, RepoConfig#{name => Repo}));
+get_package_repo_config(RepoConfig=#{read_key := Key}) ->
+ get_package_repo_config(maps:remove(read_key, RepoConfig#{repo_key => Key}));
+get_package_repo_config(RepoConfig) ->
+ RepoConfig.
+
+unverified_repo_message() ->
+ "The registry repository ~ts uses a record format that has been deprecated for "
+ "security reasons. The repository should be updated in order to be safer. "
+ "You can disable this check by setting REBAR_NO_VERIFY_REPO_ORIGIN=1".
+
insert_releases(Name, Releases, Repo, Table) ->
[true = ets:insert(Table,
#package{key={Name, ec_semver:parse(Version), Repo},
diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl
index 3d3bd8a..c31c060 100644
--- a/src/rebar_prv_common_test.erl
+++ b/src/rebar_prv_common_test.erl
@@ -171,6 +171,9 @@ transform_opts([{cover, _}|Rest], Acc) ->
%% drop verbose from opts, ct doesn't care about it
transform_opts([{verbose, _}|Rest], Acc) ->
transform_opts(Rest, Acc);
+%% drop fail_fast from opts, ct doesn't care about it
+transform_opts([{fail_fast, _}|Rest], Acc) ->
+ transform_opts(Rest, Acc);
%% getopt should handle anything else
transform_opts([Opt|Rest], Acc) ->
transform_opts(Rest, [Opt|Acc]).
@@ -224,15 +227,21 @@ ensure_opts([V|Rest], Acc) ->
ensure_opts(Rest, [V|Acc]).
add_hooks(Opts, State) ->
+ FailFast = case fails_fast(State) of
+ true -> [cth_fail_fast];
+ false -> []
+ end,
case {readable(State), lists:keyfind(ct_hooks, 1, Opts)} of
{false, _} ->
Opts;
{Other, false} ->
- [{ct_hooks, [cth_readable_failonly, readable_shell_type(Other), cth_retry]} | Opts];
+ [{ct_hooks, [cth_readable_failonly, readable_shell_type(Other),
+ cth_retry] ++ FailFast} | Opts];
{Other, {ct_hooks, Hooks}} ->
%% Make sure hooks are there once only.
- ReadableHooks = [cth_readable_failonly, readable_shell_type(Other), cth_retry],
- AllReadableHooks = [cth_readable_failonly, cth_retry,
+ ReadableHooks = [cth_readable_failonly, readable_shell_type(Other),
+ cth_retry] ++ FailFast,
+ AllReadableHooks = [cth_readable_failonly, cth_retry, cth_fail_fast,
cth_readable_shell, cth_readable_compact_shell],
NewHooks = (Hooks -- AllReadableHooks) ++ ReadableHooks,
lists:keyreplace(ct_hooks, 1, Opts, {ct_hooks, NewHooks})
@@ -445,6 +454,10 @@ readable(State) ->
undefined -> rebar_state:get(State, ct_readable, compact)
end.
+fails_fast(State) ->
+ {RawOpts, _} = rebar_state:command_parsed_args(State),
+ proplists:get_value(fail_fast, RawOpts) == true.
+
test_dirs(State, Apps, Opts) ->
case proplists:get_value(spec, Opts) of
undefined ->
@@ -773,7 +786,8 @@ ct_opts(_State) ->
{setcookie, undefined, "setcookie", atom, help(setcookie)},
{sys_config, undefined, "sys_config", string, help(sys_config)}, %% comma-separated list
{compile_only, undefined, "compile_only", boolean, help(compile_only)},
- {retry, undefined, "retry", boolean, help(retry)}
+ {retry, undefined, "retry", boolean, help(retry)},
+ {fail_fast, undefined, "fail_fast", {boolean, false}, help(fail_fast)}
].
help(compile_only) ->
@@ -846,5 +860,9 @@ help(setcookie) ->
"Sets the cookie if the node is distributed";
help(retry) ->
"Experimental feature. If any specification for previously failing test is found, runs them.";
+help(fail_fast) ->
+ "Experimental feature. If any test fails, the run is aborted. Since common test does not "
+ "support this natively, we abort the rebar3 run on a failure. This May break CT's disk logging and "
+ "other rebar3 features.";
help(_) ->
"".
diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl
index f120926..0b00e89 100644
--- a/src/rebar_prv_eunit.erl
+++ b/src/rebar_prv_eunit.erl
@@ -105,6 +105,8 @@ format_error({eunit_test_errors, Errors}) ->
lists:map(fun(Error) -> "~n " ++ Error end, Errors)), []);
format_error({badconfig, {Msg, {Value, Key}}}) ->
io_lib:format(Msg, [Value, Key]);
+format_error({generator, Value}) ->
+ io_lib:format("Generator ~p has an invalid format", [Value]);
format_error({error, Error}) ->
format_error({error_running_tests, Error}).
@@ -134,19 +136,34 @@ resolve_tests(State) ->
Files = resolve(file, RawOpts),
Modules = resolve(module, RawOpts),
Suites = resolve(suite, module, RawOpts),
- Apps ++ Applications ++ Dirs ++ Files ++ Modules ++ Suites.
+ Generator = resolve(generator, RawOpts),
+ Apps ++ Applications ++ Dirs ++ Files ++ Modules ++ Suites ++ Generator.
resolve(Flag, RawOpts) -> resolve(Flag, Flag, RawOpts).
resolve(Flag, EUnitKey, RawOpts) ->
case proplists:get_value(Flag, RawOpts) of
undefined -> [];
- Args -> lists:map(fun(Arg) -> normalize(EUnitKey, Arg) end,
+ Args -> normalize(EUnitKey,
rebar_string:lexemes(Args, [$,]))
end.
-normalize(Key, Value) when Key == dir; Key == file -> {Key, Value};
-normalize(Key, Value) -> {Key, list_to_atom(Value)}.
+normalize(generator, Args) ->
+ lists:flatmap(fun(Value) -> normalize_(generator, Value) end, Args);
+normalize(EUnitKey, Args) ->
+ lists:map(fun(Arg) -> normalize_(EUnitKey, Arg) end, Args).
+
+normalize_(generator, Value) ->
+ case string:tokens(Value, [$:]) of
+ [Module0, Functions] ->
+ Module = list_to_atom(Module0),
+ lists:map(fun(F) -> {generator, Module, list_to_atom(F)} end,
+ string:tokens(Functions, [$;]));
+ _ ->
+ ?PRV_ERROR({generator, Value})
+ end;
+normalize_(Key, Value) when Key == dir; Key == file -> {Key, Value};
+normalize_(Key, Value) -> {Key, list_to_atom(Value)}.
cfg_tests(State) ->
case rebar_state:get(State, eunit_tests, []) of
@@ -353,6 +370,8 @@ validate(State, {module, Module}) ->
validate_module(State, Module);
validate(State, {suite, Module}) ->
validate_module(State, Module);
+validate(State, {generator, Module, Function}) ->
+ validate_generator(State, Module, Function);
validate(State, Module) when is_atom(Module) ->
validate_module(State, Module);
validate(State, Path) when is_list(Path) ->
@@ -395,6 +414,9 @@ validate_module(_State, Module) ->
_ -> ok
end.
+validate_generator(State, Module, _Function) ->
+ validate_module(State, Module).
+
resolve_eunit_opts(State) ->
{Opts, _} = rebar_state:command_parsed_args(State),
EUnitOpts = rebar_state:get(State, eunit_opts, []),
@@ -490,6 +512,7 @@ eunit_opts(_State) ->
{file, $f, "file", string, help(file)},
{module, $m, "module", string, help(module)},
{suite, $s, "suite", string, help(module)},
+ {generator, $g, "generator", string, help(generator)},
{verbose, $v, "verbose", boolean, help(verbose)},
{name, undefined, "name", atom, help(name)},
{sname, undefined, "sname", atom, help(sname)},
@@ -501,6 +524,7 @@ help(cover_export_name) -> "Base name of the coverdata file to write";
help(dir) -> "Comma separated list of dirs to load tests from. Equivalent to `[{dir, Dir}]`.";
help(file) -> "Comma separated list of files to load tests from. Equivalent to `[{file, File}]`.";
help(module) -> "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`.";
+help(generator) -> "Comma separated list of generators (the format is `module:function`) to load tests from. Equivalent to `[{generator, Module, Function}]`.";
help(verbose) -> "Verbose output. Defaults to false.";
help(name) -> "Gives a long name to the node";
help(sname) -> "Gives a short name to the node";
diff --git a/src/rebar_prv_packages.erl b/src/rebar_prv_packages.erl
index 3e54cdc..a143455 100644
--- a/src/rebar_prv_packages.erl
+++ b/src/rebar_prv_packages.erl
@@ -72,16 +72,14 @@ print_packages({RepoName, {ok, #{<<"name">> := Name,
Description = maps:get(<<"description">>, Meta, ""),
Licenses = join(maps:get(<<"licenses">>, Meta, []), <<", ">>),
Links = join_map(maps:get(<<"links">>, Meta, []), <<"\n ">>),
- Maintainers = join(maps:get(<<"maintainers">>, Meta, []), <<", ">>),
Versions = [V || #{<<"version">> := V} <- Releases],
VsnStr = join(Versions, <<", ">>),
?CONSOLE("~ts:~n"
" Name: ~ts~n"
" Description: ~ts~n"
" Licenses: ~ts~n"
- " Maintainers: ~ts~n"
" Links:~n ~ts~n"
- " Versions: ~ts~n", [RepoName, Name, Description, Licenses, Maintainers, Links, VsnStr]);
+ " Versions: ~ts~n", [RepoName, Name, Description, Licenses, Links, VsnStr]);
print_packages(_) ->
ok.
diff --git a/test/rebar_eunit_SUITE.erl b/test/rebar_eunit_SUITE.erl
index 1a8bade..87dd3ed 100644
--- a/test/rebar_eunit_SUITE.erl
+++ b/test/rebar_eunit_SUITE.erl
@@ -13,6 +13,7 @@
-export([single_application_arg/1, multi_application_arg/1, missing_application_arg/1]).
-export([single_module_arg/1, multi_module_arg/1, missing_module_arg/1]).
-export([single_suite_arg/1, multi_suite_arg/1, missing_suite_arg/1]).
+-export([single_generator_arg/1, multi_generator_arg/1, missing_generator_arg/1]).
-export([single_file_arg/1, multi_file_arg/1, missing_file_arg/1]).
-export([single_dir_arg/1, multi_dir_arg/1, missing_dir_arg/1]).
-export([multiple_arg_composition/1, multiple_arg_errors/1]).
@@ -47,6 +48,7 @@ groups() ->
single_application_arg, multi_application_arg, missing_application_arg,
single_module_arg, multi_module_arg, missing_module_arg,
single_suite_arg, multi_suite_arg, missing_suite_arg,
+ single_generator_arg, multi_generator_arg, missing_generator_arg,
single_file_arg, multi_file_arg, missing_file_arg,
single_dir_arg, multi_dir_arg, missing_dir_arg,
multiple_arg_composition, multiple_arg_errors]}].
@@ -239,7 +241,7 @@ multi_app_testset(Config) ->
Set = {ok, [{application, multi_app_baz},
{application, multi_app_bar},
{module, multi_app_bar_tests_helper},
- {module, multi_app_baz_tests_helper},
+ {module, multi_app_baz_tests_helper},
{module, multi_app_tests},
{module, multi_app_tests_helper}]},
Set = rebar_prv_eunit:prepare_tests(Result).
@@ -404,6 +406,37 @@ missing_suite_arg(Config) ->
Error = {error, {rebar_prv_eunit, {eunit_test_errors, ["Module `missing_app' not found in project."]}}},
Error = rebar_prv_eunit:validate_tests(State, rebar_prv_eunit:prepare_tests(State)).
+%% check that the --generator cmd line opt generates the correct test set
+single_generator_arg(Config) ->
+ S = ?config(result, Config),
+
+ {ok, Args} = getopt:parse(rebar_prv_eunit:eunit_opts(S), ["--generator=module_name:function_name"]),
+ State = rebar_state:command_parsed_args(S, Args),
+
+ {ok, [{generator, module_name, function_name}]} = rebar_prv_eunit:prepare_tests(State).
+
+multi_generator_arg(Config) ->
+ S = ?config(result, Config),
+
+ {ok, Args} = getopt:parse(rebar_prv_eunit:eunit_opts(S), ["--generator=module1:func1;func2,module2:func1;func2"]),
+ State = rebar_state:command_parsed_args(S, Args),
+
+ Generators = [{generator, module1, func1},
+ {generator, module1, func2},
+ {generator, module2, func1},
+ {generator, module2, func2}],
+ {ok, Generators} = rebar_prv_eunit:prepare_tests(State).
+
+%% check that an invalid --suite cmd line opt generates an error
+missing_generator_arg(Config) ->
+ S = ?config(result, Config),
+
+ {ok, Args} = getopt:parse(rebar_prv_eunit:eunit_opts(S), ["--generator=missing_module:func1"]),
+ State = rebar_state:command_parsed_args(S, Args),
+
+ Error = {error, {rebar_prv_eunit, {eunit_test_errors, ["Module `missing_module' not found in project."]}}},
+ Error = rebar_prv_eunit:validate_tests(State, rebar_prv_eunit:prepare_tests(State)).
+
%% check that the --file cmd line opt generates the correct test set
single_file_arg(Config) ->
S = ?config(result, Config),
diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl
index ee74af5..ad06abd 100644
--- a/test/rebar_pkg_SUITE.erl
+++ b/test/rebar_pkg_SUITE.erl
@@ -99,7 +99,7 @@ init_per_testcase(bad_disconnect=Name, Config0) ->
Config = mock_config(Name, Config1),
meck:expect(hex_repo, get_tarball, fun(_, _, _) ->
{error, econnrefused}
- end),
+ end),
Config;
init_per_testcase(Name, Config0) ->
Config = [{good_cache, false},
@@ -252,7 +252,7 @@ mock_config(Name, Config) ->
CacheRoot = filename:join([Priv, "cache", atom_to_list(Name)]),
TmpDir = filename:join([Priv, "tmp", atom_to_list(Name)]),
Tid = ets:new(registry_table, [public]),
- AllDeps = [
+ AllDeps = [
{{<<"badindexchk">>,<<"1.0.0">>}, [[], ?bad_checksum, [<<"rebar3">>]]},
{{<<"goodpkg">>,<<"1.0.0">>}, [[], ?good_checksum, [<<"rebar3">>]]},
{{<<"goodpkg">>,<<"1.0.1">>}, [[], ?good_checksum, [<<"rebar3">>]]},
@@ -267,7 +267,7 @@ mock_config(Name, Config) ->
ok = ets:tab2file(Tid, filename:join([CacheDir, "registry"])),
catch ets:delete(?PACKAGE_TABLE),
- rebar_packages:new_package_table(),
+ rebar_packages:new_package_table(),
lists:foreach(fun({{N, Vsn}, [Deps, Checksum, _]}) ->
case ets:member(?PACKAGE_TABLE, {ec_cnv:to_binary(N), Vsn, <<"hexpm">>}) of
false ->
@@ -279,18 +279,18 @@ mock_config(Name, Config) ->
ok
end
end, AllDeps),
-
+
meck:new(hex_repo, [passthrough]),
- meck:expect(hex_repo, get_package,
+ meck:expect(hex_repo, get_package,
fun(_Config, PkgName) ->
Matches = ets:match_object(Tid, {{PkgName,'_'}, '_'}),
- Releases =
+ Releases =
[#{checksum => Checksum,
version => Vsn,
- dependencies => Deps} ||
+ dependencies => Deps} ||
{{_, Vsn}, [Deps, Checksum, _]} <- Matches],
- {ok, {200, #{}, #{releases => Releases}}}
+ {ok, {200, #{}, Releases}}
end),
%% The state returns us a fake registry
@@ -321,7 +321,7 @@ mock_config(Name, Config) ->
%% Cache fetches are mocked -- we assume the server and clients are
%% correctly used.
GoodCache = ?config(good_cache, Config),
- {Pkg,Vsn} = ?config(pkg, Config),
+ {Pkg,Vsn} = ?config(pkg, Config),
PkgFile = <<Pkg/binary, "-", Vsn/binary, ".tar">>,
{ok, PkgContents} = file:read_file(filename:join(?config(data_dir, Config), PkgFile)),
@@ -329,8 +329,8 @@ mock_config(Name, Config) ->
{ok, {304, #{<<"etag">> => ?good_etag}, <<>>}};
(_, _, _) ->
{ok, {200, #{<<"etag">> => ?good_etag}, PkgContents}}
- end),
-
+ end),
+
[{cache_root, CacheRoot},
{cache_dir, CacheDir},
{tmp_dir, TmpDir},
diff --git a/test/rebar_pkg_alias_SUITE.erl b/test/rebar_pkg_alias_SUITE.erl
index 079a3fd..1ef5a34 100644
--- a/test/rebar_pkg_alias_SUITE.erl
+++ b/test/rebar_pkg_alias_SUITE.erl
@@ -224,7 +224,7 @@ mock_config(Name, Config) ->
meck:expect(rebar_prv_update, do, fun(State) -> {ok, State} end),
catch ets:delete(?PACKAGE_TABLE),
- rebar_packages:new_package_table(),
+ rebar_packages:new_package_table(),
lists:foreach(fun({{N, Vsn}, [Deps, Checksum, _]}) ->
case ets:member(?PACKAGE_TABLE, {ec_cnv:to_binary(N), Vsn, <<"hexpm">>}) of
@@ -238,11 +238,11 @@ mock_config(Name, Config) ->
end;
({_N, _Vsns}) ->
ok
-
- end, AllDeps),
+
+ end, AllDeps),
meck:new(hex_repo, [passthrough]),
- meck:expect(hex_repo, get_package,
+ meck:expect(hex_repo, get_package,
fun(_Config, PkgName) ->
Matches = ets:match_object(Tid, {{PkgName,'_'}, '_'}),
Releases =
@@ -251,13 +251,13 @@ mock_config(Name, Config) ->
dependencies => [{DAppName, {pkg, DN, DV, undefined}} ||
{DN, DV, _, DAppName} <- Deps]} ||
{{_, Vsn}, [Deps, Checksum, _]} <- Matches],
- {ok, {200, #{}, #{releases => Releases}}}
+ {ok, {200, #{}, Releases}}
end),
meck:expect(hex_repo, get_tarball, fun(_, _, _) ->
{ok, {304, #{<<"etag">> => EtagGood}, <<>>}}
- end),
-
+ end),
+
%% Move all packages to cache
NewConf = [{cache_root, CacheRoot},
{cache_dir, CacheDir},
diff --git a/test/rebar_pkg_repos_SUITE.erl b/test/rebar_pkg_repos_SUITE.erl
index c808475..55bc020 100644
--- a/test/rebar_pkg_repos_SUITE.erl
+++ b/test/rebar_pkg_repos_SUITE.erl
@@ -182,23 +182,25 @@ repo_merging(_Config) ->
repo_replacing(_Config) ->
Repo1 = #{name => <<"repo-1">>,
+ repo_name => <<"repo-1">>,
api_url => <<"repo-1/api">>},
Repo2 = #{name => <<"repo-2">>,
+ repo_name => <<"repo-2">>,
repo_url => <<"repo-2/repo">>,
repo_verify => false},
?assertMatch([Repo1, Repo2, #{name := <<"hexpm">>}],
rebar_hex_repos:repos([{repos, [Repo1]},
- {repos, [Repo2]}])),
+ {repos, [Repo2]}])),
%% use of replace is ignored if found in later entries than the first
?assertMatch([Repo1, Repo2, #{name := <<"hexpm">>}],
rebar_hex_repos:repos([{repos, [Repo1]},
- {repos, replace, [Repo2]}])),
+ {repos, replace, [Repo2]}])),
?assertMatch([Repo1],
rebar_hex_repos:repos([{repos, replace, [Repo1]},
- {repos, [Repo2]}])).
+ {repos, [Repo2]}])).
auth_merging(_Config) ->
Repo1 = #{name => <<"repo-1">>,