|
|
@ -63,6 +63,8 @@ do(State) -> |
|
|
|
|
|
|
|
do_tests(State, Tests) -> |
|
|
|
EUnitOpts = resolve_eunit_opts(State), |
|
|
|
?DEBUG("eunit_tests ~p", [Tests]), |
|
|
|
?DEBUG("eunit_opts ~p", [EUnitOpts]), |
|
|
|
Result = eunit:test(Tests, EUnitOpts), |
|
|
|
ok = rebar_prv_cover:maybe_write_coverdata(State, ?PROVIDER), |
|
|
|
case handle_results(Result) of |
|
|
@ -76,7 +78,9 @@ do_tests(State, Tests) -> |
|
|
|
format_error(unknown_error) -> |
|
|
|
io_lib:format("Error running tests", []); |
|
|
|
format_error({error_running_tests, Reason}) -> |
|
|
|
io_lib:format("Error running tests: ~p", [Reason]). |
|
|
|
io_lib:format("Error running tests: ~p", [Reason]); |
|
|
|
format_error({error, Error}) -> |
|
|
|
format_error({error_running_tests, Error}). |
|
|
|
|
|
|
|
%% =================================================================== |
|
|
|
%% Internal functions |
|
|
@ -108,55 +112,87 @@ first_files(State) -> |
|
|
|
prepare_tests(State) -> |
|
|
|
{RawOpts, _} = rebar_state:command_parsed_args(State), |
|
|
|
ok = maybe_cover_compile(State, RawOpts), |
|
|
|
CmdTests = resolve_tests(RawOpts), |
|
|
|
CfgTests = rebar_state:get(State, eunit_tests, []), |
|
|
|
ProjectApps = project_apps(State), |
|
|
|
resolve_tests(ProjectApps, RawOpts). |
|
|
|
|
|
|
|
maybe_cover_compile(State, Opts) -> |
|
|
|
State1 = case proplists:get_value(cover, Opts, false) of |
|
|
|
true -> rebar_state:set(State, cover_enabled, true); |
|
|
|
false -> State |
|
|
|
end, |
|
|
|
rebar_prv_cover:maybe_cover_compile(State1). |
|
|
|
Tests = select_tests(ProjectApps, CmdTests, CfgTests), |
|
|
|
validate_tests(State, ProjectApps, Tests). |
|
|
|
|
|
|
|
resolve_tests(RawOpts) -> |
|
|
|
Apps = resolve(app, application, RawOpts), |
|
|
|
Dirs = resolve(dir, RawOpts), |
|
|
|
Files = resolve(file, RawOpts), |
|
|
|
Modules = resolve(module, RawOpts), |
|
|
|
Suites = resolve(suite, module, RawOpts), |
|
|
|
Apps ++ 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. |
|
|
|
|
|
|
|
resolve_tests(ProjectApps, RawOpts) -> |
|
|
|
case proplists:get_value(file, RawOpts) of |
|
|
|
undefined -> resolve_apps(ProjectApps, RawOpts); |
|
|
|
Files -> resolve_files(ProjectApps, Files, RawOpts) |
|
|
|
normalize(Key, Value) when Key == dir; Key == file -> {Key, Value}; |
|
|
|
normalize(Key, Value) -> {Key, list_to_atom(Value)}. |
|
|
|
|
|
|
|
select_tests(ProjectApps, [], []) -> default_tests(ProjectApps); |
|
|
|
select_tests(_ProjectApps, A, B) -> A ++ B. |
|
|
|
|
|
|
|
validate_tests(State, ProjectApps, Tests) -> |
|
|
|
{ok, lists:filter(fun(Elem) -> validate(State, ProjectApps, Elem) end, Tests)}. |
|
|
|
|
|
|
|
validate(_State, ProjectApps, {application, App}) -> |
|
|
|
validate_app(App, ProjectApps); |
|
|
|
validate(State, _ProjectApps, {dir, Dir}) -> |
|
|
|
ok = maybe_compile_dir(State, Dir), |
|
|
|
validate_dir(Dir); |
|
|
|
validate(State, _ProjectApps, {file, File}) -> |
|
|
|
ok = maybe_compile_file(State, File), |
|
|
|
validate_file(File); |
|
|
|
validate(_State, ProjectApps, {module, Module}) -> |
|
|
|
validate_module(Module, ProjectApps); |
|
|
|
validate(_State, ProjectApps, {suite, Module}) -> |
|
|
|
validate_module(Module, ProjectApps); |
|
|
|
validate(_State, ProjectApps, Module) when is_atom(Module) -> |
|
|
|
validate_module(Module, ProjectApps); |
|
|
|
validate(State, ProjectApps, Path) when is_list(Path) -> |
|
|
|
case ec_file:is_dir(Path) of |
|
|
|
true -> validate(State, ProjectApps, {dir, Path}); |
|
|
|
false -> validate(State, ProjectApps, {file, Path}) |
|
|
|
end; |
|
|
|
%% unrecognized tests should be included. if they're invalid eunit will error |
|
|
|
%% and rebar.config may contain arbitrarily complex tests that are effectively |
|
|
|
%% unvalidatable |
|
|
|
validate(_State, _ProjectApps, _Test) -> true. |
|
|
|
|
|
|
|
validate_app(AppName, []) -> |
|
|
|
?WARN(lists:concat(["Application `", AppName, "' not found in project."]), []), |
|
|
|
false; |
|
|
|
validate_app(AppName, [App|Rest]) -> |
|
|
|
case AppName == binary_to_atom(rebar_app_info:name(App), unicode) of |
|
|
|
true -> true; |
|
|
|
false -> validate_app(AppName, Rest) |
|
|
|
end. |
|
|
|
|
|
|
|
resolve_files(ProjectApps, Files, RawOpts) -> |
|
|
|
case {proplists:get_value(app, RawOpts), proplists:get_value(suite, RawOpts)} of |
|
|
|
{undefined, undefined} -> resolve_files(Files, []); |
|
|
|
_ -> |
|
|
|
case resolve_apps(ProjectApps, RawOpts) of |
|
|
|
{ok, TestSet} -> resolve_files(Files, TestSet); |
|
|
|
Error -> Error |
|
|
|
end |
|
|
|
validate_dir(Dir) -> |
|
|
|
case ec_file:is_dir(Dir) of |
|
|
|
true -> true; |
|
|
|
false -> ?WARN(lists:concat(["Directory `", Dir, "' not found."]), []), false |
|
|
|
end. |
|
|
|
|
|
|
|
resolve_files(Files, TestSet) -> |
|
|
|
FileNames = string:tokens(Files, [$,]), |
|
|
|
{ok, TestSet ++ set_files(FileNames, [])}. |
|
|
|
|
|
|
|
resolve_apps(ProjectApps, RawOpts) -> |
|
|
|
case proplists:get_value(app, RawOpts) of |
|
|
|
undefined -> resolve_suites(ProjectApps, RawOpts); |
|
|
|
%% convert app name strings to `rebar_app_info` objects |
|
|
|
Apps -> AppNames = string:tokens(Apps, [$,]), |
|
|
|
case filter_apps_by_name(AppNames, ProjectApps) of |
|
|
|
{ok, TestApps} -> resolve_suites(TestApps, RawOpts); |
|
|
|
Error -> Error |
|
|
|
end |
|
|
|
validate_file(File) -> |
|
|
|
case ec_file:exists(File) of |
|
|
|
true -> true; |
|
|
|
false -> ?WARN(lists:concat(["File `", File, "' not found."]), []), false |
|
|
|
end. |
|
|
|
|
|
|
|
resolve_suites(Apps, RawOpts) -> |
|
|
|
case proplists:get_value(suite, RawOpts) of |
|
|
|
undefined -> test_set(Apps, all); |
|
|
|
Suites -> SuiteNames = string:tokens(Suites, [$,]), |
|
|
|
case filter_suites_by_apps(SuiteNames, Apps) of |
|
|
|
{ok, S} -> test_set(Apps, S); |
|
|
|
Error -> Error |
|
|
|
end |
|
|
|
validate_module(Module, Apps) -> |
|
|
|
AppModules = app_modules([binary_to_atom(rebar_app_info:name(A), unicode) || A <- Apps], []), |
|
|
|
case lists:member(Module, AppModules) of |
|
|
|
true -> true; |
|
|
|
false -> ?WARN(lists:concat(["Module `", Module, "' not found in applications."]), []), false |
|
|
|
end. |
|
|
|
|
|
|
|
project_apps(State) -> |
|
|
@ -171,42 +207,6 @@ filter_checkouts([App|Rest], Acc) -> |
|
|
|
false -> filter_checkouts(Rest, [App|Acc]) |
|
|
|
end. |
|
|
|
|
|
|
|
%% make sure applications specified actually exist |
|
|
|
filter_apps_by_name(AppNames, ProjectApps) -> |
|
|
|
filter_apps_by_name(AppNames, ProjectApps, []). |
|
|
|
|
|
|
|
filter_apps_by_name([], _ProjectApps, Acc) -> {ok, lists:reverse(Acc)}; |
|
|
|
filter_apps_by_name([Name|Rest], ProjectApps, Acc) -> |
|
|
|
case find_app_by_name(Name, ProjectApps) of |
|
|
|
{error, app_not_found} -> |
|
|
|
?PRV_ERROR({error_running_tests, |
|
|
|
"Application `" ++ Name ++ "' not found in project."}); |
|
|
|
App -> |
|
|
|
filter_apps_by_name(Rest, ProjectApps, [App|Acc]) |
|
|
|
end. |
|
|
|
|
|
|
|
find_app_by_name(_, []) -> {error, app_not_found}; |
|
|
|
find_app_by_name(Name, [App|Rest]) -> |
|
|
|
case Name == binary_to_list(rebar_app_info:name(App)) of |
|
|
|
true -> App; |
|
|
|
false -> find_app_by_name(Name, Rest) |
|
|
|
end. |
|
|
|
|
|
|
|
%% ensure specified suites are in the applications included |
|
|
|
filter_suites_by_apps(Suites, ProjectApps) -> |
|
|
|
filter_suites_by_apps(Suites, ProjectApps, []). |
|
|
|
|
|
|
|
filter_suites_by_apps([], _ProjectApps, Acc) -> {ok, lists:reverse(Acc)}; |
|
|
|
filter_suites_by_apps([Suite|Rest], Apps, Acc) -> |
|
|
|
Modules = app_modules([binary_to_atom(rebar_app_info:name(A), unicode) || A <- Apps], []), |
|
|
|
case lists:member(list_to_atom(Suite), Modules) of |
|
|
|
false -> |
|
|
|
?PRV_ERROR({error_running_tests, |
|
|
|
"Module `" ++ Suite ++ "' not found in applications."}); |
|
|
|
true -> |
|
|
|
filter_suites_by_apps(Rest, Apps, [Suite|Acc]) |
|
|
|
end. |
|
|
|
|
|
|
|
app_modules([], Acc) -> Acc; |
|
|
|
app_modules([App|Rest], Acc) -> |
|
|
|
Unload = case application:load(App) of |
|
|
@ -225,22 +225,13 @@ app_modules([App|Rest], Acc) -> |
|
|
|
app_modules(Rest, NewAcc) |
|
|
|
end. |
|
|
|
|
|
|
|
test_set(Apps, all) -> {ok, set_apps(Apps, [])}; |
|
|
|
test_set(_Apps, Suites) -> {ok, set_suites(Suites, [])}. |
|
|
|
default_tests(Apps) -> set_apps(Apps, []). |
|
|
|
|
|
|
|
set_apps([], Acc) -> lists:reverse(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_suites([], Acc) -> lists:reverse(Acc); |
|
|
|
set_suites([Suite|Rest], Acc) -> |
|
|
|
set_suites(Rest, [{module, list_to_atom(Suite)}|Acc]). |
|
|
|
|
|
|
|
set_files([], Acc) -> lists:reverse(Acc); |
|
|
|
set_files([File|Rest], Acc) -> |
|
|
|
set_files(Rest, [{file, File}|Acc]). |
|
|
|
|
|
|
|
resolve_eunit_opts(State) -> |
|
|
|
{Opts, _} = rebar_state:command_parsed_args(State), |
|
|
|
EUnitOpts = rebar_state:get(State, eunit_opts, []), |
|
|
@ -256,6 +247,16 @@ set_verbose(Opts) -> |
|
|
|
false -> [verbose] ++ Opts |
|
|
|
end. |
|
|
|
|
|
|
|
maybe_compile_dir(_, _) -> ok. |
|
|
|
maybe_compile_file(_, _) -> ok. |
|
|
|
|
|
|
|
maybe_cover_compile(State, Opts) -> |
|
|
|
State1 = case proplists:get_value(cover, Opts, false) of |
|
|
|
true -> rebar_state:set(State, cover_enabled, true); |
|
|
|
false -> State |
|
|
|
end, |
|
|
|
rebar_prv_cover:maybe_cover_compile(State1). |
|
|
|
|
|
|
|
handle_results(ok) -> ok; |
|
|
|
handle_results(error) -> |
|
|
|
{error, unknown_error}; |
|
|
@ -265,12 +266,16 @@ handle_results({error, Reason}) -> |
|
|
|
eunit_opts(_State) -> |
|
|
|
[{app, undefined, "app", string, help(app)}, |
|
|
|
{cover, $c, "cover", boolean, help(cover)}, |
|
|
|
{file, $f, "file", string, help(file)}, |
|
|
|
{dir, undefined, "dir", string, help(dir)}, |
|
|
|
{file, undefined, "file", string, help(file)}, |
|
|
|
{module, undefined, "module", string, help(module)}, |
|
|
|
{suite, undefined, "suite", string, help(suite)}, |
|
|
|
{verbose, $v, "verbose", boolean, help(verbose)}]. |
|
|
|
|
|
|
|
help(app) -> "Comma seperated list of application test suites to run. Equivalent to `[{application, App}]`."; |
|
|
|
help(app) -> "Comma separated list of application test suites to run. Equivalent to `[{application, App}]`."; |
|
|
|
help(cover) -> "Generate cover data. Defaults to false."; |
|
|
|
help(file) -> "Comma seperated list of files to run. Equivalent to `[{file, File}]`."; |
|
|
|
help(suite) -> "Comma seperated list of test suites to run. Equivalent to `[{module, Suite}]`."; |
|
|
|
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(suite) -> "Comma separated list of test suites to run. Equivalent to `[{module, Suite}]`."; |
|
|
|
help(verbose) -> "Verbose output. Defaults to false.". |