mirror of
https://github.com/correl/rebar.git
synced 2024-11-23 11:09:55 +00:00
Add qualified name tests specification (see #118)
Augment 'tests' option of 'rebar eunit' command with ability to specify tests to run using module-qualified names. This change also forced me to change the way modules for coverage and for testing itself are selected - module-qualified tests specifications are now taken into consideration. Extend tests to cover new functionality. Update dialyzer_reference accordingly.
This commit is contained in:
parent
f47af30e65
commit
93689703c1
3 changed files with 144 additions and 69 deletions
|
@ -1,3 +1,3 @@
|
||||||
|
|
||||||
rebar_eunit.erl:434: Call to missing or unexported function eunit_test:function_wrapper/2
|
rebar_eunit.erl:469: Call to missing or unexported function eunit_test:function_wrapper/2
|
||||||
rebar_utils.erl:164: Call to missing or unexported function escript:foldl/3
|
rebar_utils.erl:164: Call to missing or unexported function escript:foldl/3
|
||||||
|
|
|
@ -155,12 +155,10 @@ run_eunit(Config, CodePath, SrcErls) ->
|
||||||
[filename:rootname(N) ++ "_tests.beam" || N <- AllBeamFiles],
|
[filename:rootname(N) ++ "_tests.beam" || N <- AllBeamFiles],
|
||||||
ModuleBeamFiles = randomize_suites(Config, BeamFiles ++ OtherBeamFiles),
|
ModuleBeamFiles = randomize_suites(Config, BeamFiles ++ OtherBeamFiles),
|
||||||
|
|
||||||
%% Get modules to be run in eunit
|
%% Get matching tests and modules
|
||||||
AllModules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- AllBeamFiles],
|
AllModules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- AllBeamFiles],
|
||||||
{SuitesProvided, FilteredModules} = filter_suites(Config, AllModules),
|
{Tests, CoverageModules} =
|
||||||
|
get_tests_and_modules(Config, ModuleBeamFiles, AllModules),
|
||||||
%% Get matching tests
|
|
||||||
Tests = get_tests(Config, SuitesProvided, ModuleBeamFiles, FilteredModules),
|
|
||||||
|
|
||||||
SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls],
|
SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls],
|
||||||
|
|
||||||
|
@ -169,7 +167,7 @@ run_eunit(Config, CodePath, SrcErls) ->
|
||||||
StatusBefore = status_before_eunit(),
|
StatusBefore = status_before_eunit(),
|
||||||
EunitResult = perform_eunit(Config, Tests),
|
EunitResult = perform_eunit(Config, Tests),
|
||||||
|
|
||||||
perform_cover(Config, FilteredModules, SrcModules),
|
perform_cover(Config, CoverageModules, SrcModules),
|
||||||
cover_close(CoverLog),
|
cover_close(CoverLog),
|
||||||
|
|
||||||
case proplists:get_value(reset_after_eunit, get_eunit_opts(Config),
|
case proplists:get_value(reset_after_eunit, get_eunit_opts(Config),
|
||||||
|
@ -214,18 +212,23 @@ setup_code_path() ->
|
||||||
CodePath.
|
CodePath.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% == filter suites ==
|
%% == get matching tests ==
|
||||||
%%
|
%%
|
||||||
|
get_tests_and_modules(Config, ModuleBeamFiles, AllModules) ->
|
||||||
|
SelectedSuites = get_selected_suites(Config, AllModules),
|
||||||
|
{Tests, QualifiedTests} = get_qualified_and_unqualified_tests(Config),
|
||||||
|
Modules = get_test_modules(SelectedSuites, Tests,
|
||||||
|
QualifiedTests, ModuleBeamFiles),
|
||||||
|
CoverageModules = get_coverage_modules(AllModules, Modules, QualifiedTests),
|
||||||
|
MatchedTests = get_matching_tests(Modules, Tests, QualifiedTests),
|
||||||
|
{MatchedTests, CoverageModules}.
|
||||||
|
|
||||||
filter_suites(Config, Modules) ->
|
%%
|
||||||
|
%% == get suites specified via 'suites' option ==
|
||||||
|
%%
|
||||||
|
get_selected_suites(Config, Modules) ->
|
||||||
RawSuites = get_suites(Config),
|
RawSuites = get_suites(Config),
|
||||||
SuitesProvided = RawSuites =/= "",
|
|
||||||
Suites = [list_to_atom(Suite) || Suite <- string:tokens(RawSuites, ",")],
|
Suites = [list_to_atom(Suite) || Suite <- string:tokens(RawSuites, ",")],
|
||||||
{SuitesProvided, filter_suites1(Modules, Suites)}.
|
|
||||||
|
|
||||||
filter_suites1(Modules, []) ->
|
|
||||||
Modules;
|
|
||||||
filter_suites1(Modules, Suites) ->
|
|
||||||
[M || M <- Suites, lists:member(M, Modules)].
|
[M || M <- Suites, lists:member(M, Modules)].
|
||||||
|
|
||||||
get_suites(Config) ->
|
get_suites(Config) ->
|
||||||
|
@ -236,6 +239,32 @@ get_suites(Config) ->
|
||||||
Suites
|
Suites
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
get_qualified_and_unqualified_tests(Config) ->
|
||||||
|
RawFunctions = rebar_utils:get_experimental_global(Config, tests, ""),
|
||||||
|
FunctionNames = [FunctionName ||
|
||||||
|
FunctionName <- string:tokens(RawFunctions, ",")],
|
||||||
|
get_qualified_and_unqualified_tests1(FunctionNames, [], []).
|
||||||
|
|
||||||
|
get_qualified_and_unqualified_tests1([], Functions, QualifiedFunctions) ->
|
||||||
|
{Functions, QualifiedFunctions};
|
||||||
|
get_qualified_and_unqualified_tests1([TestName|TestNames], Functions,
|
||||||
|
QualifiedFunctions) ->
|
||||||
|
case string:tokens(TestName, ":") of
|
||||||
|
[TestName] ->
|
||||||
|
Function = list_to_atom(TestName),
|
||||||
|
get_qualified_and_unqualified_tests1(
|
||||||
|
TestNames, [Function|Functions], QualifiedFunctions);
|
||||||
|
[ModuleName, FunctionName] ->
|
||||||
|
M = list_to_atom(ModuleName),
|
||||||
|
F = list_to_atom(FunctionName),
|
||||||
|
get_qualified_and_unqualified_tests1(TestNames, Functions,
|
||||||
|
[{M, F}|QualifiedFunctions]);
|
||||||
|
_ ->
|
||||||
|
?ABORT("Unsupported test function specification: ~s~n", [TestName])
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% Provide modules which are to be searched for tests.
|
||||||
|
%% Several scenarios are possible:
|
||||||
%%
|
%%
|
||||||
%% == randomize suites ==
|
%% == randomize suites ==
|
||||||
%%
|
%%
|
||||||
|
@ -265,60 +294,66 @@ randomize_suites1(Modules, Seed) ->
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% == get matching tests ==
|
%% == get matching tests ==
|
||||||
|
%% 1) Specific tests have been provided and/or
|
||||||
|
%% no unqualified tests have been specified and
|
||||||
|
%% there were some qualified tests, then we can search for
|
||||||
|
%% functions in specified suites (or in empty set of suites).
|
||||||
%%
|
%%
|
||||||
get_tests(Config, SuitesProvided, ModuleBeamFiles, FilteredModules) ->
|
%% 2) Neither specific suites nor qualified test names have been
|
||||||
Modules = case SuitesProvided of
|
%% provided use ModuleBeamFiles which filters out "*_tests"
|
||||||
false ->
|
%% modules so EUnit won't doubly run them and cover only
|
||||||
%% No specific suites have been provided, use
|
%% calculates coverage on production code. However,
|
||||||
%% ModuleBeamFiles which filters out "*_tests" modules
|
%% keep "*_tests" modules that are not automatically
|
||||||
%% so eunit won't doubly run them and cover only
|
%% included by EUnit.
|
||||||
%% calculates coverage on production code. However,
|
%%
|
||||||
%% keep "*_tests" modules that are not automatically
|
%% From 'Primitives' in the EUnit User's Guide
|
||||||
%% included by eunit.
|
%% http://www.erlang.org/doc/apps/eunit/chapter.html
|
||||||
%%
|
%% "In addition, EUnit will also look for another
|
||||||
%% From 'Primitives' in the EUnit User's Guide
|
%% module whose name is ModuleName plus the suffix
|
||||||
%% http://www.erlang.org/doc/apps/eunit/chapter.html
|
%% _tests, and if it exists, all the tests from that
|
||||||
%% "In addition, EUnit will also look for another
|
%% module will also be added. (If ModuleName already
|
||||||
%% module whose name is ModuleName plus the suffix
|
%% contains the suffix _tests, this is not done.) E.g.,
|
||||||
%% _tests, and if it exists, all the tests from that
|
%% the specification {module, mymodule} will run all
|
||||||
%% module will also be added. (If ModuleName already
|
%% tests in the modules mymodule and mymodule_tests.
|
||||||
%% contains the suffix _tests, this is not done.) E.g.,
|
%% Typically, the _tests module should only contain
|
||||||
%% the specification {module, mymodule} will run all
|
%% test cases that use the public interface of the main
|
||||||
%% tests in the modules mymodule and mymodule_tests.
|
%% module (and no other code)."
|
||||||
%% Typically, the _tests module should only contain
|
get_test_modules(SelectedSuites, Tests, QualifiedTests, ModuleBeamFiles) ->
|
||||||
%% test cases that use the public interface of the main
|
SuitesProvided = SelectedSuites =/= [],
|
||||||
%% module (and no other code)."
|
OnlyQualifiedTestsProvided = QualifiedTests =/= [] andalso Tests =:= [],
|
||||||
[rebar_utils:beam_to_mod(?EUNIT_DIR, N) ||
|
if
|
||||||
N <- ModuleBeamFiles];
|
SuitesProvided orelse OnlyQualifiedTestsProvided ->
|
||||||
true ->
|
SelectedSuites;
|
||||||
%% Specific suites have been provided, return the
|
true ->
|
||||||
%% filtered modules
|
[rebar_utils:beam_to_mod(?EUNIT_DIR, N) ||
|
||||||
FilteredModules
|
N <- ModuleBeamFiles]
|
||||||
end,
|
|
||||||
get_matching_tests(Config, Modules).
|
|
||||||
|
|
||||||
get_tests(Config) ->
|
|
||||||
case rebar_config:get_global(Config, tests, "") of
|
|
||||||
"" ->
|
|
||||||
rebar_config:get_global(Config, test, "");
|
|
||||||
Suites ->
|
|
||||||
Suites
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_matching_tests(Config, Modules) ->
|
get_coverage_modules(AllModules, Modules, QualifiedTests) ->
|
||||||
RawFunctions = get_tests(Config),
|
ModuleFilterMapper =
|
||||||
Tests = [list_to_atom(F1) || F1 <- string:tokens(RawFunctions, ",")],
|
fun({M, _}) ->
|
||||||
case Tests of
|
case lists:member(M, AllModules) of
|
||||||
[] ->
|
true -> {true, M};
|
||||||
Modules;
|
_-> false
|
||||||
Functions ->
|
end
|
||||||
case get_matching_tests1(Modules, Functions, []) of
|
end,
|
||||||
[] ->
|
ModulesFromQualifiedTests = lists:zf(ModuleFilterMapper, QualifiedTests),
|
||||||
[];
|
lists:usort(Modules ++ ModulesFromQualifiedTests).
|
||||||
RawTests ->
|
|
||||||
make_test_primitives(RawTests)
|
get_matching_tests(Modules, [], []) ->
|
||||||
end
|
Modules;
|
||||||
end.
|
get_matching_tests(Modules, [], QualifiedTests) ->
|
||||||
|
FilteredQualifiedTests = filter_qualified_tests(Modules, QualifiedTests),
|
||||||
|
lists:merge(Modules, make_test_primitives(FilteredQualifiedTests));
|
||||||
|
get_matching_tests(Modules, Tests, QualifiedTests) ->
|
||||||
|
AllTests = lists:merge(QualifiedTests,
|
||||||
|
get_matching_tests1(Modules, Tests, [])),
|
||||||
|
make_test_primitives(AllTests).
|
||||||
|
|
||||||
|
filter_qualified_tests(Modules, QualifiedTests) ->
|
||||||
|
TestsFilter = fun({Module, _Function}) ->
|
||||||
|
lists:all(fun(M) -> M =/= Module end, Modules) end,
|
||||||
|
lists:filter(TestsFilter, QualifiedTests).
|
||||||
|
|
||||||
get_matching_tests1([], _Functions, TestFunctions) ->
|
get_matching_tests1([], _Functions, TestFunctions) ->
|
||||||
TestFunctions;
|
TestFunctions;
|
||||||
|
@ -608,9 +643,9 @@ align_notcovered_count(Module, Covered, NotCovered, true) ->
|
||||||
cover_write_index(Coverage, SrcModules) ->
|
cover_write_index(Coverage, SrcModules) ->
|
||||||
{ok, F} = file:open(filename:join([?EUNIT_DIR, "index.html"]), [write]),
|
{ok, F} = file:open(filename:join([?EUNIT_DIR, "index.html"]), [write]),
|
||||||
ok = file:write(F, "<!DOCTYPE HTML><html>\n"
|
ok = file:write(F, "<!DOCTYPE HTML><html>\n"
|
||||||
"<head><meta charset=\"utf-8\">"
|
"<head><meta charset=\"utf-8\">"
|
||||||
"<title>Coverage Summary</title></head>\n"
|
"<title>Coverage Summary</title></head>\n"
|
||||||
"<body>\n"),
|
"<body>\n"),
|
||||||
IsSrcCoverage = fun({Mod,_C,_N}) -> lists:member(Mod, SrcModules) end,
|
IsSrcCoverage = fun({Mod,_C,_N}) -> lists:member(Mod, SrcModules) end,
|
||||||
{SrcCoverage, TestCoverage} = lists:partition(IsSrcCoverage, Coverage),
|
{SrcCoverage, TestCoverage} = lists:partition(IsSrcCoverage, Coverage),
|
||||||
cover_write_index_section(F, "Source", SrcCoverage),
|
cover_write_index_section(F, "Source", SrcCoverage),
|
||||||
|
|
|
@ -191,6 +191,46 @@ eunit_with_suites_and_tests_test_() ->
|
||||||
|
|
||||||
{"Selected suite tests is run once",
|
{"Selected suite tests is run once",
|
||||||
?_assert(string:str(RebarOut, "All 2 tests passed") =/= 0)}]
|
?_assert(string:str(RebarOut, "All 2 tests passed") =/= 0)}]
|
||||||
|
end},
|
||||||
|
{"Ensure EUnit runs a specific test by qualified function name",
|
||||||
|
setup,
|
||||||
|
fun() ->
|
||||||
|
setup_project_with_multiple_modules(),
|
||||||
|
rebar("-v eunit tests=myapp_mymod:myprivate_test")
|
||||||
|
end,
|
||||||
|
fun teardown/1,
|
||||||
|
fun(RebarOut) ->
|
||||||
|
[{"Selected test is run",
|
||||||
|
[?_assert(string:str(RebarOut,
|
||||||
|
"myapp_mymod:myprivate_test/0")
|
||||||
|
=/= 0)]},
|
||||||
|
|
||||||
|
{"Only selected test is run",
|
||||||
|
[?_assert(string:str(RebarOut,
|
||||||
|
"Test passed.") =/= 0)]}]
|
||||||
|
end},
|
||||||
|
{"Ensure EUnit runs a specific test by qualified function "
|
||||||
|
++ "name and tests from other module",
|
||||||
|
setup,
|
||||||
|
fun() ->
|
||||||
|
setup_project_with_multiple_modules(),
|
||||||
|
rebar("-v eunit suites=myapp_mymod3 "
|
||||||
|
++ "tests=myapp_mymod:myprivate_test")
|
||||||
|
end,
|
||||||
|
fun teardown/1,
|
||||||
|
fun(RebarOut) ->
|
||||||
|
[{"Selected test is run",
|
||||||
|
[?_assert(string:str(RebarOut,
|
||||||
|
"myapp_mymod:myprivate_test/0") =/= 0)]},
|
||||||
|
|
||||||
|
{"Tests from module are run",
|
||||||
|
[?_assert(string:str(RebarOut,
|
||||||
|
"myapp_mymod3:") =/= 0)]},
|
||||||
|
|
||||||
|
{"Only selected tests are run",
|
||||||
|
[?_assert(string:str(RebarOut,
|
||||||
|
"Failed: 1. Skipped: 0. Passed: 1")
|
||||||
|
=/= 0)]}]
|
||||||
end}].
|
end}].
|
||||||
|
|
||||||
cover_test_() ->
|
cover_test_() ->
|
||||||
|
|
Loading…
Reference in a new issue