summaryrefslogtreecommitdiff
path: root/src/rebar_prv_eunit.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_prv_eunit.erl')
-rw-r--r--src/rebar_prv_eunit.erl210
1 files changed, 115 insertions, 95 deletions
diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl
index b754d87..9af2965 100644
--- a/src/rebar_prv_eunit.erl
+++ b/src/rebar_prv_eunit.erl
@@ -9,7 +9,7 @@
do/1,
format_error/1]).
%% exported solely for tests
--export([compile/2, prepare_tests/1, eunit_opts/1, validate_tests/2]).
+-export([prepare_tests/1, eunit_opts/1, validate_tests/2]).
-include("rebar.hrl").
-include_lib("providers/include/providers.hrl").
@@ -39,10 +39,12 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
Tests = prepare_tests(State),
- case compile(State, Tests) of
+ %% inject `eunit_first_files`, `eunit_compile_opts` and any
+ %% directories required by tests into the applications
+ NewState = inject_eunit_state(State, Tests),
+ case compile(NewState) of
%% successfully compiled apps
{ok, S} -> do(S, Tests);
- %% this should look like a compiler error, not an eunit error
Error -> Error
end.
@@ -95,6 +97,8 @@ format_error({error_running_tests, Reason}) ->
format_error({eunit_test_errors, Errors}) ->
io_lib:format(lists:concat(["Error Running EUnit Tests:"] ++
lists:map(fun(Error) -> "~n " ++ Error end, Errors)), []);
+format_error({badconfig, {Msg, {Value, Key}}}) ->
+ io_lib:format(Msg, [Value, Key]);
format_error({error, Error}) ->
format_error({error_running_tests, Error}).
@@ -102,53 +106,120 @@ format_error({error, Error}) ->
%% Internal functions
%% ===================================================================
-compile(State, {ok, Tests}) ->
- %% inject `eunit_first_files`, `eunit_compile_opts` and any
- %% directories required by tests into the applications
- NewState = inject_eunit_state(State, Tests),
+prepare_tests(State) ->
+ %% parse and translate command line tests
+ CmdTests = resolve_tests(State),
+ CfgTests = cfg_tests(State),
+ ProjectApps = rebar_state:project_apps(State),
+ %% prioritize tests to run first trying any command line specified
+ %% tests falling back to tests specified in the config file finally
+ %% running a default set if no other tests are present
+ select_tests(State, ProjectApps, CmdTests, CfgTests).
- case rebar_prv_compile:do(NewState) of
- %% successfully compiled apps
- {ok, S} ->
- ok = maybe_cover_compile(S),
- {ok, S};
- %% this should look like a compiler error, not an eunit error
- Error -> Error
- end;
-%% maybe compile even in the face of errors?
-compile(_State, Error) -> Error.
+resolve_tests(State) ->
+ {RawOpts, _} = rebar_state:command_parsed_args(State),
+ Apps = resolve(app, application, RawOpts),
+ Applications = resolve(application, RawOpts),
+ Dirs = resolve(dir, RawOpts),
+ Files = resolve(file, RawOpts),
+ Modules = resolve(module, RawOpts),
+ Suites = resolve(suite, module, RawOpts),
+ Apps ++ Applications ++ Dirs ++ Files ++ Modules ++ Suites.
+
+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, string:tokens(Args, [$,]))
+ 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
+ Tests when is_list(Tests) -> Tests;
+ Wrong ->
+ %% probably a single non list term
+ ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, eunit_tests}}})
+ end.
+
+select_tests(_State, _ProjectApps, {error, _} = Error, _) -> Error;
+select_tests(_State, _ProjectApps, _, {error, _} = Error) -> Error;
+select_tests(State, ProjectApps, [], []) -> {ok, default_tests(State, ProjectApps)};
+select_tests(_State, _ProjectApps, [], Tests) -> {ok, Tests};
+select_tests(_State, _ProjectApps, Tests, _) -> {ok, Tests}.
+
+default_tests(State, Apps) ->
+ Tests = set_apps(Apps, []),
+ BareTest = filename:join([rebar_state:dir(State), "test"]),
+ F = fun(App) -> rebar_app_info:dir(App) == rebar_state:dir(State) end,
+ case filelib:is_dir(BareTest) andalso not lists:any(F, Apps) of
+ %% `test` dir at root of project is already scheduled to be
+ %% included or `test` does not exist
+ false -> lists:reverse(Tests);
+ %% need to add `test` dir at root to dirs to be included
+ true -> lists:reverse([{dir, BareTest}|Tests])
+ end.
+
+set_apps([], Acc) -> Acc;
+set_apps([App|Rest], Acc) ->
+ AppName = list_to_atom(binary_to_list(rebar_app_info:name(App))),
+ set_apps(Rest, [{application, AppName}|Acc]).
-inject_eunit_state(State, Tests) ->
+inject_eunit_state(State, {ok, Tests}) ->
Apps = rebar_state:project_apps(State),
- ModdedApps = lists:map(fun(App) ->
- NewOpts = inject(rebar_app_info:opts(App)),
- rebar_app_info:opts(App, NewOpts)
- end, Apps),
- NewOpts = inject(rebar_state:opts(State)),
- NewState = rebar_state:opts(State, NewOpts),
- test_dirs(NewState, ModdedApps, Tests).
+ case inject_eunit_state(State, Apps, []) of
+ {ok, {NewState, ModdedApps}} ->
+ test_dirs(NewState, ModdedApps, Tests);
+ {error, _} = Error -> Error
+ end;
+inject_eunit_state(_State, Error) -> Error.
+
+inject_eunit_state(State, [App|Rest], Acc) ->
+ case inject(rebar_app_info:opts(App)) of
+ {error, _} = Error -> Error;
+ NewOpts ->
+ NewApp = rebar_app_info:opts(App, NewOpts),
+ inject_eunit_state(State, Rest, [NewApp|Acc])
+ end;
+inject_eunit_state(State, [], Acc) ->
+ case inject(rebar_state:opts(State)) of
+ {error, _} = Error -> Error;
+ NewOpts -> {ok, {rebar_state:opts(State, NewOpts), lists:reverse(Acc)}}
+ end.
opts(Opts, Key, Default) ->
case rebar_opts:get(Opts, Key, Default) of
Vs when is_list(Vs) -> Vs;
Wrong ->
- ?WARN("Value ~p of option `~p' is not a list, trying to adjust and continue", [Wrong, Key]),
- [Wrong]
+ ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, Key}}})
end.
-inject(Opts) ->
+inject(Opts) -> erl_opts(Opts).
+
+erl_opts(Opts) ->
%% append `eunit_compile_opts` to app defined `erl_opts`
ErlOpts = opts(Opts, erl_opts, []),
EUnitOpts = opts(Opts, eunit_compile_opts, []),
- NewErlOpts = EUnitOpts ++ ErlOpts,
+ case append(EUnitOpts, ErlOpts) of
+ {error, _} = Error -> Error;
+ NewErlOpts -> first_files(rebar_opts:set(Opts, erl_opts, NewErlOpts))
+ end.
+
+first_files(Opts) ->
%% append `eunit_first_files` to app defined `erl_first_files`
FirstFiles = opts(Opts, erl_first_files, []),
EUnitFirstFiles = opts(Opts, eunit_first_files, []),
- NewFirstFiles = EUnitFirstFiles ++ FirstFiles,
- %% insert the new keys into the opts
- lists:foldl(fun({K, V}, NewOpts) -> rebar_opts:set(NewOpts, K, V) end,
- Opts,
- [{erl_opts, NewErlOpts}, {erl_first_files, NewFirstFiles}]).
+ case append(EUnitFirstFiles, FirstFiles) of
+ {error, _} = Error -> Error;
+ NewFirstFiles -> rebar_opts:set(Opts, erl_first_files, NewFirstFiles)
+ end.
+
+append({error, _} = Error, _) -> Error;
+append(_, {error, _} = Error) -> Error;
+append(A, B) -> A ++ B.
test_dirs(State, Apps, []) -> rebar_state:project_apps(State, Apps);
test_dirs(State, Apps, [{dir, Dir}|Rest]) ->
@@ -182,71 +253,20 @@ maybe_inject_test_dir(State, AppAcc, [], Dir) ->
inject_test_dir(Opts, Dir) ->
%% append specified test targets to app defined `extra_src_dirs`
- ExtraSrcDirs = rebar_opts:get(Opts, extra_src_dirs, []),
+ ExtraSrcDirs = rebar_dir:extra_src_dirs(Opts),
rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]).
-prepare_tests(State) ->
- %% parse and translate command line tests
- CmdTests = resolve_tests(State),
- CfgTests = cfg_tests(State),
- ProjectApps = rebar_state:project_apps(State),
- %% prioritize tests to run first trying any command line specified
- %% tests falling back to tests specified in the config file finally
- %% running a default set if no other tests are present
- select_tests(State, ProjectApps, CmdTests, CfgTests).
-
-cfg_tests(State) ->
- case rebar_state:get(State, eunit_tests, []) of
- Tests when is_list(Tests) -> Tests;
- Wrong ->
- %% probably a single non list term, try wrapping it in a list and
- %% continuing
- ?WARN("Value `~p' of option `eunit_tests' is not a list, trying to adjust and continue", [Wrong]),
- [Wrong]
- end.
-
-resolve_tests(State) ->
- {RawOpts, _} = rebar_state:command_parsed_args(State),
- Apps = resolve(app, application, RawOpts),
- Applications = resolve(application, RawOpts),
- Dirs = resolve(dir, RawOpts),
- Files = resolve(file, RawOpts),
- Modules = resolve(module, RawOpts),
- Suites = resolve(suite, module, RawOpts),
- Apps ++ Applications ++ Dirs ++ Files ++ Modules ++ Suites.
-
-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, string:tokens(Args, [$,]))
- end.
-
-normalize(Key, Value) when Key == dir; Key == file -> {Key, Value};
-normalize(Key, Value) -> {Key, list_to_atom(Value)}.
-
-select_tests(State, ProjectApps, [], []) -> {ok, default_tests(State, ProjectApps)};
-select_tests(_State, _ProjectApps, [], Tests) -> {ok, Tests};
-select_tests(_State, _ProjectApps, Tests, _) -> {ok, Tests}.
-
-default_tests(State, Apps) ->
- Tests = set_apps(Apps, []),
- BareTest = filename:join([rebar_state:dir(State), "test"]),
- F = fun(App) -> rebar_app_info:dir(App) == rebar_state:dir(State) end,
- case filelib:is_dir(BareTest) andalso not lists:any(F, Apps) of
- %% `test` dir at root of project is already scheduled to be
- %% included or `test` does not exist
- false -> lists:reverse(Tests);
- %% need to add `test` dir at root to dirs to be included
- true -> lists:reverse([{dir, BareTest}|Tests])
+compile({error, _} = Error) -> Error;
+compile(State) ->
+ case rebar_prv_compile:do(State) of
+ %% successfully compiled apps
+ {ok, S} ->
+ ok = maybe_cover_compile(S),
+ {ok, S};
+ %% this should look like a compiler error, not an eunit error
+ Error -> Error
end.
-set_apps([], Acc) -> Acc;
-set_apps([App|Rest], Acc) ->
- AppName = list_to_atom(binary_to_list(rebar_app_info:name(App))),
- set_apps(Rest, [{application, AppName}|Acc]).
-
validate_tests(State, {ok, Tests}) ->
gather_tests(fun(Elem) -> validate(State, Elem) end, Tests, []);
validate_tests(_State, Error) -> Error.