diff options
Diffstat (limited to 'src/rebar_prv_eunit.erl')
-rw-r--r-- | src/rebar_prv_eunit.erl | 100 |
1 files changed, 70 insertions, 30 deletions
diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index b9ac6b8..c085ee4 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -56,14 +56,14 @@ do(State, Tests) -> %% Run eunit provider prehooks Providers = rebar_state:providers(State), Cwd = rebar_dir:get_cwd(), - rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), + rebar_hooks:run_project_and_app_hooks(Cwd, pre, ?PROVIDER, Providers, State), case validate_tests(State, Tests) of {ok, T} -> case run_tests(State, T) of {ok, State1} -> %% Run eunit provider posthooks - rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State1), + rebar_hooks:run_project_and_app_hooks(Cwd, post, ?PROVIDER, Providers, State1), rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)), {ok, State1}; Error -> @@ -139,7 +139,8 @@ 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; + Tests when is_list(Tests) -> + lists:map(fun({app, App}) -> {application, App}; (T) -> T end, Tests); Wrong -> %% probably a single non list term ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, eunit_tests}}}) @@ -152,22 +153,51 @@ 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. + %% use `{application, App}` for each app in project + AppTests = set_apps(Apps), + %% additional test modules in `test` dir of each app + ModTests = set_modules(Apps, State), + AppTests ++ ModTests. + +set_apps(Apps) -> set_apps(Apps, []). 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]). +set_modules(Apps, State) -> set_modules(Apps, State, {[], []}). + +set_modules([], State, {AppAcc, TestAcc}) -> + TestSrc = gather_src([filename:join([rebar_state:dir(State), "test"])]), + dedupe_tests({AppAcc, TestAcc ++ TestSrc}); +set_modules([App|Rest], State, {AppAcc, TestAcc}) -> + F = fun(Dir) -> filename:join([rebar_app_info:dir(App), Dir]) end, + AppDirs = lists:map(F, rebar_dir:src_dirs(rebar_app_info:opts(App), ["src"])), + AppSrc = gather_src(AppDirs), + TestDirs = [filename:join([rebar_app_info:dir(App), "test"])], + TestSrc = gather_src(TestDirs), + set_modules(Rest, State, {AppSrc ++ AppAcc, TestSrc ++ TestAcc}). + +gather_src(Dirs) -> gather_src(Dirs, []). + +gather_src([], Srcs) -> Srcs; +gather_src([Dir|Rest], Srcs) -> + gather_src(Rest, Srcs ++ rebar_utils:find_files(Dir, "^[^._].*\\.erl\$", true)). + +dedupe_tests({AppMods, TestMods}) -> + %% for each modules in TestMods create a test if there is not a module + %% in AppMods that will trigger it + F = fun(Mod) -> + M = filename:basename(Mod, ".erl"), + MatchesTest = fun(Dir) -> filename:basename(Dir, ".erl") ++ "_tests" == M end, + case lists:any(MatchesTest, AppMods) of + false -> {true, {module, list_to_atom(M)}}; + true -> false + end + end, + lists:usort(rebar_utils:filtermap(F, TestMods)). + inject_eunit_state(State, {ok, Tests}) -> Apps = rebar_state:project_apps(State), case inject_eunit_state(State, Apps, []) of @@ -333,10 +363,9 @@ validate_file(State, File) -> end. validate_module(_State, Module) -> - Path = code:which(Module), - case beam_lib:chunks(Path, [exports]) of - {ok, _} -> ok; - {error, beam_lib, _} -> {error, lists:concat(["Module `", Module, "' not found in project."])} + case code:which(Module) of + non_existing -> {error, lists:concat(["Module `", Module, "' not found in project."])}; + _ -> ok end. resolve_eunit_opts(State) -> @@ -369,26 +398,37 @@ set_verbose(Opts) -> translate_paths(State, Tests) -> translate_paths(State, Tests, []). translate_paths(_State, [], Acc) -> lists:reverse(Acc); -translate_paths(State, [{dir, Dir}|Rest], Acc) -> - Apps = rebar_state:project_apps(State), - translate_paths(State, Rest, [translate(State, Apps, Dir)|Acc]); -translate_paths(State, [{file, File}|Rest], Acc) -> - Dir = filename:dirname(File), +translate_paths(State, [{K, _} = Path|Rest], Acc) when K == file; K == dir -> Apps = rebar_state:project_apps(State), - translate_paths(State, Rest, [translate(State, Apps, Dir)|Acc]); + translate_paths(State, Rest, [translate(State, Apps, Path)|Acc]); translate_paths(State, [Test|Rest], Acc) -> translate_paths(State, Rest, [Test|Acc]). -translate(State, [App|Rest], Dir) -> +translate(State, [App|Rest], {dir, Dir}) -> case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of {ok, Path} -> {dir, filename:join([rebar_app_info:out_dir(App), Path])}; - {error, badparent} -> translate(State, Rest, Dir) + {error, badparent} -> translate(State, Rest, {dir, Dir}) + end; +translate(State, [App|Rest], {file, FilePath}) -> + Dir = filename:dirname(FilePath), + File = filename:basename(FilePath), + case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of + {ok, Path} -> {file, filename:join([rebar_app_info:out_dir(App), Path, File])}; + {error, badparent} -> translate(State, Rest, {file, FilePath}) end; -translate(State, [], Dir) -> +translate(State, [], {dir, Dir}) -> case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of {ok, Path} -> {dir, filename:join([rebar_dir:base_dir(State), "extras", Path])}; %% not relative, leave as is {error, badparent} -> {dir, Dir} + end; +translate(State, [], {file, FilePath}) -> + Dir = filename:dirname(FilePath), + File = filename:basename(FilePath), + case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(State)) of + {ok, Path} -> {file, filename:join([rebar_dir:base_dir(State), "extras", Path, File])}; + %% not relative, leave as is + {error, badparent} -> {file, FilePath} end. maybe_cover_compile(State) -> @@ -417,10 +457,10 @@ eunit_opts(_State) -> [{app, undefined, "app", string, help(app)}, {application, undefined, "application", string, help(app)}, {cover, $c, "cover", boolean, help(cover)}, - {dir, undefined, "dir", string, help(dir)}, - {file, undefined, "file", string, help(file)}, - {module, undefined, "module", string, help(module)}, - {suite, undefined, "suite", string, help(module)}, + {dir, $d, "dir", string, help(dir)}, + {file, $f, "file", string, help(file)}, + {module, $m, "module", string, help(module)}, + {suite, $s, "suite", string, help(module)}, {verbose, $v, "verbose", boolean, help(verbose)}]. help(app) -> "Comma separated list of application test suites to run. Equivalent to `[{application, App}]`."; |