瀏覽代碼

modify `ct` and `eunit` to work with isolated `ebin` dirs

pull/201/head
alisdair sullivan 10 年之前
父節點
當前提交
c7c00bccfd
共有 2 個檔案被更改,包括 139 行新增159 行删除
  1. +75
    -79
      src/rebar_prv_common_test.erl
  2. +64
    -80
      src/rebar_prv_eunit.erl

+ 75
- 79
src/rebar_prv_common_test.erl 查看文件

@ -30,35 +30,22 @@ init(State) ->
{opts, ct_opts(State)},
{profiles, [test]}]),
State1 = rebar_state:add_provider(State, Provider),
{ok, State1}.
State2 = rebar_state:add_to_profile(State1, test, test_state(State1)),
{ok, State2}.
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
?INFO("Running Common Test suites...", []),
{RawOpts, _} = rebar_state:command_parsed_args(State),
{InDirs, OutDir} = split_ct_dirs(State, RawOpts),
Opts = transform_opts(RawOpts),
TestApps = filter_checkouts(rebar_state:project_apps(State)),
ok = create_dirs(Opts),
?DEBUG("Compiling Common Test suites in: ~p", [OutDir]),
lists:foreach(fun(App) ->
AppDir = rebar_app_info:dir(App),
AppOutDir = rebar_app_info:out_dir(App),
C = rebar_config:consult(AppDir),
S = rebar_state:new(State, C, AppDir),
%% combine `erl_first_files` and `common_test_first_files` and
%% adjust compile opts to include `common_test_compile_opts`
%% and `{src_dirs, "test"}`
TestState = test_state(S, InDirs, OutDir),
ok = rebar_erlc_compiler:compile(TestState, AppDir, AppOutDir)
end, TestApps),
ok = maybe_compile_extra_tests(TestApps, State, InDirs, OutDir),
Path = code:get_path(),
true = code:add_patha(OutDir),
CTOpts = resolve_ct_opts(State, Opts, OutDir),
InDirs = in_dirs(State, RawOpts),
ok = compile_tests(State, TestApps, InDirs),
CTOpts = resolve_ct_opts(State, Opts),
Verbose = proplists:get_value(verbose, Opts, false),
Result = run_test(CTOpts, Verbose),
true = code:set_path(Path),
TestDirs = test_dirs(State, TestApps),
Result = run_test([{dir, TestDirs}|CTOpts], Verbose),
case Result of
{error, Reason} ->
{error, {?MODULE, Reason}};
@ -86,9 +73,8 @@ run_test(CTOpts, false) ->
receive Result -> handle_quiet_results(CTOpts, Result) end.
ct_opts(State) ->
DefaultLogsDir = filename:join([rebar_state:dir(State), "logs"]),
DefaultLogsDir = filename:join([rebar_state:dir(State), "_logs"]),
[{dir, undefined, "dir", string, help(dir)}, %% comma-seperated list
{outdir, undefined, "outdir", string, help(outdir)}, %% string
{suite, undefined, "suite", string, help(suite)}, %% comma-seperated list
{group, undefined, "group", string, help(group)}, %% comma-seperated list
{testcase, undefined, "case", string, help(testcase)}, %% comma-seperated list
@ -123,8 +109,6 @@ ct_opts(State) ->
{verbose, $v, "verbose", boolean, help(verbose)}
].
help(outdir) ->
"Output directory for compiled modules";
help(dir) ->
"List of additional directories containing test suites";
help(suite) ->
@ -186,27 +170,6 @@ help(userconfig) ->
help(verbose) ->
"Verbose output".
split_ct_dirs(State, RawOpts) ->
%% preserve the override nature of command line opts by only checking
%% `rebar.config` defined additional test dirs if none are defined via
%% command line flag
InDirs = case proplists:get_value(dir, RawOpts) of
undefined ->
CTOpts = rebar_state:get(State, common_test_opts, []),
proplists:get_value(dir, CTOpts, []);
Dirs -> split_string(Dirs)
end,
OutDir = proplists:get_value(outdir, RawOpts, default_test_dir(State)),
{InDirs, OutDir}.
default_test_dir(State) ->
Tmp = rebar_file_utils:system_tmpdir(),
Root = filename:join([rebar_state:dir(State), Tmp]),
Project = filename:basename(rebar_state:dir(State)),
OutDir = filename:join([Root, Project ++ "_rebar3_ct"]),
ok = rebar_file_utils:reset_dir(OutDir),
OutDir.
transform_opts(Opts) ->
transform_opts(Opts, []).
@ -302,52 +265,85 @@ ensure_dir([Dir|Rest]) ->
end,
ensure_dir(Rest).
test_state(State, InDirs, OutDir) ->
ErlOpts = rebar_state:get(State, common_test_compile_opts, []) ++
rebar_utils:erl_opts(State),
TestOpts = [{outdir, OutDir}] ++
add_test_dir(ErlOpts, InDirs),
first_files(rebar_state:set(State, erl_opts, TestOpts)).
add_test_dir(Opts, InDirs) ->
%% if no src_dirs are set we have to specify `src` or it won't
%% be built
case proplists:append_values(src_dirs, Opts) of
[] -> [{src_dirs, ["src", "test" | InDirs]} | Opts];
_ -> [{src_dirs, ["test" | InDirs]} | Opts]
in_dirs(State, Opts) ->
%% preserve the override nature of command line opts by only checking
%% `rebar.config` defined additional test dirs if none are defined via
%% command line flag
case proplists:get_value(dir, Opts) of
undefined ->
CTOpts = rebar_state:get(State, ct_opts, []),
proplists:get_value(dir, CTOpts, []);
Dirs -> split_string(Dirs)
end.
test_dirs(State, TestApps) ->
%% we need to add "./ebin" if it exists but only if it's not already
%% due to be added
F = fun(App) -> rebar_app_info:dir(App) =/= rebar_dir:get_cwd() end,
BareEbin = filename:join([rebar_dir:base_dir(State), "ebin"]),
case lists:any(F, TestApps) andalso filelib:is_dir(BareEbin) of
false -> application_dirs(TestApps, []);
true -> [BareEbin|application_dirs(TestApps, [])]
end.
application_dirs([], Acc) -> lists:reverse(Acc);
application_dirs([App|Rest], Acc) ->
application_dirs(Rest, [rebar_app_info:ebin_dir(App)|Acc]).
test_state(State) ->
TestOpts = case rebar_state:get(State, ct_compile_opts, []) of
[] -> [];
Opts -> [{erl_opts, Opts}]
end,
[first_files(State)|TestOpts].
first_files(State) ->
BaseFirst = rebar_state:get(State, erl_first_files, []),
CTFirst = rebar_state:get(State, common_test_first_files, []),
rebar_state:set(State, erl_first_files, BaseFirst ++ CTFirst).
CTFirst = rebar_state:get(State, ct_first_files, []),
{erl_first_files, CTFirst}.
resolve_ct_opts(State, CmdLineOpts, OutDir) ->
CTOpts = rebar_state:get(State, common_test_opts, []),
resolve_ct_opts(State, CmdLineOpts) ->
CTOpts = rebar_state:get(State, ct_opts, []),
Opts = lists:ukeymerge(1,
lists:ukeysort(1, CmdLineOpts),
lists:ukeysort(1, CTOpts)),
%% rebar has seperate input and output directories whereas `common_test`
%% uses only a single directory so set `dir` to our precompiled `OutDir`
%% and disable `auto_compile`
[{auto_compile, false}, {dir, OutDir}] ++ lists:keydelete(dir, 1, Opts).
%% disable `auto_compile` and remove `dir` from the opts
[{auto_compile, false}|lists:keydelete(dir, 1, Opts)].
maybe_compile_extra_tests(TestApps, State, InDirs, OutDir) ->
compile_tests(State, TestApps, InDirs) ->
State1 = replace_src_dirs(State, InDirs),
F = fun(AppInfo) ->
AppDir = rebar_app_info:dir(AppInfo),
S = case rebar_app_info:state(AppInfo) of
undefined ->
C = rebar_config:consult(AppDir),
rebar_state:new(State1, C, AppDir);
AppState ->
AppState
end,
ok = rebar_erlc_compiler:compile(S,
ec_cnv:to_list(rebar_app_info:dir(AppInfo)),
ec_cnv:to_list(rebar_app_info:out_dir(AppInfo)))
end,
lists:foreach(F, TestApps),
compile_bare_tests(State1, TestApps).
compile_bare_tests(State, TestApps) ->
F = fun(App) -> rebar_app_info:dir(App) == rebar_dir:get_cwd() end,
case lists:filter(F, TestApps) of
%% compile just the `test` and extra test directories of the base dir
[] ->
ErlOpts = rebar_state:get(State, common_test_compile_opts, []) ++
rebar_utils:erl_opts(State),
TestOpts = [{outdir, OutDir}] ++
[{src_dirs, ["test"|InDirs]}] ++
lists:keydelete(src_dirs, 1, ErlOpts),
TestState = first_files(rebar_state:set(State, erl_opts, TestOpts)),
rebar_erlc_compiler:compile(TestState, rebar_dir:get_cwd(), rebar_dir:get_cwd());
%% compile just the `test` directory of the base dir
[] -> rebar_erlc_compiler:compile(State,
rebar_dir:get_cwd(),
rebar_dir:base_dir(State));
%% already compiled `./test` so do nothing
_ -> ok
_ -> ok
end.
replace_src_dirs(State, InDirs) ->
%% replace any `src_dirs` with just the `test` dir and any `InDirs`
ErlOpts = rebar_state:get(State, erl_opts, []),
StrippedOpts = lists:keydelete(src_dirs, 1, ErlOpts),
rebar_state:set(State, erl_opts, [{src_dirs, ["test"|InDirs]}|StrippedOpts]).
handle_results([Result]) ->
handle_results(Result);
handle_results([Result|Results]) when is_list(Results) ->
@ -372,4 +368,4 @@ handle_quiet_results(CTOpts, {_, Failed, _}) ->
io:format(" ~p tests failed.~n Results written to ~p.~n", [Failed, Index]);
handle_quiet_results(_CTOpts, {'DOWN', _, _, _, Reason}) ->
handle_results({error, Reason});
handle_quiet_results(_CTOpts, Result) -> handle_results(Result).
handle_quiet_results(_CTOpts, Result) -> handle_results(Result).

+ 64
- 80
src/rebar_prv_eunit.erl 查看文件

@ -30,35 +30,18 @@ init(State) ->
{opts, eunit_opts(State)},
{profiles, [test]}]),
State1 = rebar_state:add_provider(State, Provider),
{ok, State1}.
State2 = rebar_state:add_to_profile(State1, test, test_state(State1)),
{ok, State2}.
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
?INFO("Performing EUnit tests...", []),
{RawOpts, _} = rebar_state:command_parsed_args(State),
Opts = transform_opts(RawOpts, State),
TestApps = filter_checkouts(rebar_state:project_apps(State)),
OutDir = proplists:get_value(outdir, Opts, default_test_dir(State)),
?DEBUG("Compiling EUnit instrumented modules in: ~p", [OutDir]),
lists:foreach(fun(App) ->
AppDir = rebar_app_info:dir(App),
AppOutDir = rebar_app_info:out_dir(App),
C = rebar_config:consult(AppDir),
S = rebar_state:new(State, C, AppDir),
%% combine `erl_first_files` and `eunit_first_files` and adjust
%% compile opts to include `eunit_compile_opts`, `{d, 'TEST'}`
%% and `{src_dirs, "test"}`
TestState = first_files(test_state(S, OutDir)),
ok = rebar_erlc_compiler:compile(TestState, AppDir, AppOutDir)
end, TestApps),
ok = maybe_compile_extra_tests(TestApps, State, OutDir),
Path = code:get_path(),
true = code:add_patha(OutDir),
{Opts, _} = rebar_state:command_parsed_args(State),
EUnitOpts = resolve_eunit_opts(State, Opts),
AppsToTest = [{application, erlang:binary_to_atom(rebar_app_info:name(App), unicode)}
|| App <- TestApps],
TestApps = filter_checkouts(rebar_state:project_apps(State)),
ok = compile_tests(State, TestApps),
AppsToTest = test_dirs(State, TestApps),
Result = eunit:test(AppsToTest, EUnitOpts),
true = code:set_path(Path),
case handle_results(Result) of
{error, Reason} ->
{error, {?MODULE, Reason}};
@ -73,24 +56,10 @@ format_error({error_running_tests, Reason}) ->
io_lib:format("Error running tests: ~p", [Reason]).
eunit_opts(_State) ->
[{outdir, $o, "outdir", string, help(outdir)},
{verbose, $v, "verbose", boolean, help(verbose)}].
[{verbose, $v, "verbose", boolean, help(verbose)}].
help(outdir) -> "Output directory for EUnit compiled modules";
help(verbose) -> "Verbose output".
transform_opts(Opts, State) -> transform_opts(Opts, State, []).
transform_opts([], _State, Acc) -> Acc;
transform_opts([{outdir, Path}|Rest], State, Acc) ->
NewAcc = case filename:pathtype(Path) of
absolute -> [{outdir, Path}] ++ Acc;
_ -> [{outdir, filename:join([rebar_state:dir(State), Path])}] ++ Acc
end,
transform_opts(Rest, State, NewAcc);
transform_opts([{Key, Val}|Rest], State, Acc) ->
transform_opts(Rest, State, [{Key, Val}|Acc]).
filter_checkouts(Apps) -> filter_checkouts(Apps, []).
filter_checkouts([], Acc) -> lists:reverse(Acc);
@ -102,30 +71,33 @@ filter_checkouts([App|Rest], Acc) ->
false -> filter_checkouts(Rest, [App|Acc])
end.
default_test_dir(State) ->
Tmp = rebar_file_utils:system_tmpdir(),
Root = filename:join([rebar_state:dir(State), Tmp]),
Project = filename:basename(rebar_state:dir(State)),
OutDir = filename:join([Root, Project ++ "_rebar3_eunit"]),
ok = rebar_file_utils:reset_dir(OutDir),
OutDir.
test_state(State, TmpDir) ->
ErlOpts = rebar_state:get(State, eunit_compile_opts, []) ++
rebar_utils:erl_opts(State),
ErlOpts1 = [{outdir, TmpDir}] ++
add_test_dir(ErlOpts),
TestOpts = safe_define_test_macro(ErlOpts1),
rebar_state:set(State, erl_opts, TestOpts).
add_test_dir(Opts) ->
%% if no src_dirs are set we have to specify `src` or it won't
%% be built
case proplists:append_values(src_dirs, Opts) of
[] -> [{src_dirs, ["src", "test"]} | Opts];
_ -> [{src_dirs, ["test"]} | Opts]
resolve_eunit_opts(State, Opts) ->
EUnitOpts = rebar_state:get(State, eunit_opts, []),
case proplists:get_value(verbose, Opts, false) of
true -> set_verbose(EUnitOpts);
false -> EUnitOpts
end.
test_dirs(State, TestApps) ->
%% we need to add "./ebin" if it exists but only if it's not already
%% due to be added
F = fun(App) -> rebar_app_info:dir(App) =/= rebar_dir:get_cwd() end,
BareEbin = filename:join([rebar_dir:base_dir(State), "ebin"]),
case lists:any(F, TestApps) andalso filelib:is_dir(BareEbin) of
false -> application_dirs(TestApps, []);
true -> [{dir, BareEbin}|application_dirs(TestApps, [])]
end.
application_dirs([], Acc) -> lists:reverse(Acc);
application_dirs([App|Rest], Acc) ->
AppName = list_to_atom(binary_to_list(rebar_app_info:name(App))),
application_dirs(Rest, [{application, AppName}|Acc]).
test_state(State) ->
ErlOpts = rebar_state:get(State, eunit_compile_opts, []),
TestOpts = safe_define_test_macro(ErlOpts),
first_files(State) ++ [{erl_opts, TestOpts}].
safe_define_test_macro(Opts) ->
%% defining a compile macro twice results in an exception so
%% make sure 'TEST' is only defined once
@ -140,39 +112,51 @@ test_defined([_|Rest]) -> test_defined(Rest);
test_defined([]) -> false.
first_files(State) ->
BaseFirst = rebar_state:get(State, erl_first_files, []),
EUnitFirst = rebar_state:get(State, eunit_first_files, []),
rebar_state:set(State, erl_first_files, BaseFirst ++ EUnitFirst).
resolve_eunit_opts(State, Opts) ->
EUnitOpts = rebar_state:get(State, eunit_opts, []),
case lists:member({verbose, true}, Opts) of
true -> set_verbose(EUnitOpts);
false -> EUnitOpts
end.
[{erl_first_files, EUnitFirst}].
set_verbose(Opts) ->
%% if `verbose` is already set don't set it again
case lists:member(verbose, Opts) of
true -> Opts;
false -> [verbose] ++ Opts
end.
maybe_compile_extra_tests(TestApps, State, OutDir) ->
compile_tests(State, TestApps) ->
State1 = replace_src_dirs(State),
F = fun(AppInfo) ->
AppDir = rebar_app_info:dir(AppInfo),
S = case rebar_app_info:state(AppInfo) of
undefined ->
C = rebar_config:consult(AppDir),
rebar_state:new(State1, C, AppDir);
AppState ->
AppState
end,
ok = rebar_erlc_compiler:compile(S,
ec_cnv:to_list(rebar_app_info:dir(AppInfo)),
ec_cnv:to_list(rebar_app_info:out_dir(AppInfo)))
end,
lists:foreach(F, TestApps),
compile_bare_tests(State1, TestApps).
compile_bare_tests(State, TestApps) ->
F = fun(App) -> rebar_app_info:dir(App) == rebar_dir:get_cwd() end,
case lists:filter(F, TestApps) of
%% compile just the `test` and extra test directories of the base dir
[] ->
ErlOpts = rebar_state:get(State, common_test_compile_opts, []) ++
rebar_utils:erl_opts(State),
TestOpts = [{outdir, OutDir}] ++
[{src_dirs, ["test"]}] ++
safe_define_test_macro(lists:keydelete(src_dirs, 1, ErlOpts)),
TestState = first_files(rebar_state:set(State, erl_opts, TestOpts)),
rebar_erlc_compiler:compile(TestState, rebar_dir:get_cwd(), rebar_dir:get_cwd());
%% compile just the `test` directory of the base dir
[] -> rebar_erlc_compiler:compile(State,
rebar_dir:get_cwd(),
rebar_dir:base_dir(State));
%% already compiled `./test` so do nothing
_ -> ok
_ -> ok
end.
replace_src_dirs(State) ->
%% replace any `src_dirs` with just the `test` dir
ErlOpts = rebar_state:get(State, erl_opts, []),
StrippedOpts = lists:keydelete(src_dirs, 1, ErlOpts),
rebar_state:set(State, erl_opts, [{src_dirs, ["test"]}|StrippedOpts]).
handle_results(ok) -> ok;
handle_results(error) ->
{error, unknown_error};

Loading…
取消
儲存