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.erl100
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}]`.";