From c7c00bccfdabda4c867de648f5eecdd754ef7a0d Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Sun, 1 Mar 2015 21:10:22 -0800 Subject: modify `ct` and `eunit` to work with isolated `ebin` dirs --- src/rebar_prv_common_test.erl | 154 ++++++++++++++++++++---------------------- src/rebar_prv_eunit.erl | 144 ++++++++++++++++++--------------------- 2 files changed, 139 insertions(+), 159 deletions(-) (limited to 'src') diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index c3f9163..1a7e483 100644 --- a/src/rebar_prv_common_test.erl +++ b/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). \ No newline at end of file diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index cd1b0f8..18cceda 100644 --- a/src/rebar_prv_eunit.erl +++ b/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}; -- cgit v1.1