diff options
author | Fred Hebert <mononcqc@ferd.ca> | 2015-11-16 11:47:51 -0500 |
---|---|---|
committer | Fred Hebert <mononcqc@ferd.ca> | 2015-11-16 11:47:51 -0500 |
commit | 160da470dd2bc7dda146e9a21c292c165c510f13 (patch) | |
tree | 3f81e09161ddd19c6d90584470e85c4a20ee2109 /src/rebar_prv_eunit.erl | |
parent | c8b58d9232afb264b463748a1b57d7b9a4157c10 (diff) | |
parent | 25914c35086beca01aaf879c5227adba7dfe1037 (diff) |
Merge pull request #922 from talentdeficit/warn_on_bad_test_opts
warn on incorrectly specified test options in `rebar.config`
Diffstat (limited to 'src/rebar_prv_eunit.erl')
-rw-r--r-- | src/rebar_prv_eunit.erl | 196 |
1 files changed, 117 insertions, 79 deletions
diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 1884f02..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,85 +106,10 @@ 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), - - 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. - -inject_eunit_state(State, Tests) -> - Apps = rebar_state:project_apps(State), - ModdedApps = lists:map(fun(App) -> - NewOpts = inject(rebar_app_info:opts(App), State), - rebar_app_info:opts(App, NewOpts) - end, Apps), - NewOpts = inject(rebar_state:opts(State), State), - NewState = rebar_state:opts(State, NewOpts), - test_dirs(NewState, ModdedApps, Tests). - -inject(Opts, State) -> - %% append `eunit_compile_opts` to app defined `erl_opts` - ErlOpts = rebar_opts:get(Opts, erl_opts, []), - EUnitOpts = rebar_state:get(State, eunit_compile_opts, []), - NewErlOpts = EUnitOpts ++ ErlOpts, - %% append `eunit_first_files` to app defined `erl_first_files` - FirstFiles = rebar_opts:get(Opts, erl_first_files, []), - EUnitFirstFiles = rebar_state:get(State, 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}]). - -test_dirs(State, Apps, []) -> rebar_state:project_apps(State, Apps); -test_dirs(State, Apps, [{dir, Dir}|Rest]) -> - %% insert `Dir` into an app if relative, or the base state if not - %% app relative but relative to the root or not at all if outside - %% project scope - {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir), - test_dirs(NewState, NewApps, Rest); -test_dirs(State, Apps, [{file, File}|Rest]) -> - Dir = filename:dirname(File), - {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir), - test_dirs(NewState, NewApps, Rest); -test_dirs(State, Apps, [_|Rest]) -> test_dirs(State, Apps, Rest). - -maybe_inject_test_dir(State, AppAcc, [App|Rest], Dir) -> - case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of - {ok, Path} -> - Opts = inject_test_dir(rebar_app_info:opts(App), Path), - {State, AppAcc ++ [rebar_app_info:opts(App, Opts)] ++ Rest}; - {error, badparent} -> - maybe_inject_test_dir(State, AppAcc ++ [App], Rest, Dir) - end; -maybe_inject_test_dir(State, AppAcc, [], Dir) -> - case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of - {ok, Path} -> - Opts = inject_test_dir(rebar_state:opts(State), Path), - {rebar_state:opts(State, Opts), AppAcc}; - {error, badparent} -> - {State, AppAcc} - end. - -inject_test_dir(Opts, Dir) -> - %% append specified test targets to app defined `extra_src_dirs` - ExtraSrcDirs = rebar_opts:get(Opts, extra_src_dirs, []), - rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]). - prepare_tests(State) -> %% parse and translate command line tests CmdTests = resolve_tests(State), - CfgTests = rebar_state:get(State, eunit_tests, []), + 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 @@ -208,6 +137,16 @@ resolve(Flag, EUnitKey, RawOpts) -> 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}. @@ -229,6 +168,105 @@ 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, {ok, Tests}) -> + Apps = rebar_state:project_apps(State), + 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 -> + ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, Key}}}) + end. + +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, []), + 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, []), + 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]) -> + %% insert `Dir` into an app if relative, or the base state if not + %% app relative but relative to the root or not at all if outside + %% project scope + {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir), + test_dirs(NewState, NewApps, Rest); +test_dirs(State, Apps, [{file, File}|Rest]) -> + Dir = filename:dirname(File), + {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir), + test_dirs(NewState, NewApps, Rest); +test_dirs(State, Apps, [_|Rest]) -> test_dirs(State, Apps, Rest). + +maybe_inject_test_dir(State, AppAcc, [App|Rest], Dir) -> + case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of + {ok, Path} -> + Opts = inject_test_dir(rebar_app_info:opts(App), Path), + {State, AppAcc ++ [rebar_app_info:opts(App, Opts)] ++ Rest}; + {error, badparent} -> + maybe_inject_test_dir(State, AppAcc ++ [App], Rest, Dir) + end; +maybe_inject_test_dir(State, AppAcc, [], Dir) -> + case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of + {ok, Path} -> + Opts = inject_test_dir(rebar_state:opts(State), Path), + {rebar_state:opts(State, Opts), AppAcc}; + {error, badparent} -> + {State, AppAcc} + end. + +inject_test_dir(Opts, Dir) -> + %% append specified test targets to app defined `extra_src_dirs` + ExtraSrcDirs = rebar_dir:extra_src_dirs(Opts), + rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]). + +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. + validate_tests(State, {ok, Tests}) -> gather_tests(fun(Elem) -> validate(State, Elem) end, Tests, []); validate_tests(_State, Error) -> Error. |