From 17e26f72653706e8c555082a2c9ae9e73351fff8 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Fri, 30 Oct 2015 01:01:12 -0700 Subject: delay validation of eunit tests until just before running --- src/rebar_prv_eunit.erl | 47 +++++++++++++++++++++++----------------------- test/rebar_eunit_SUITE.erl | 18 ++++++++++++------ 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index b748b06..357b92f 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -9,7 +9,7 @@ do/1, format_error/1]). %% exported solely for tests --export([compile/2, prepare_tests/1, eunit_opts/1]). +-export([compile/2, prepare_tests/1, eunit_opts/1, validate_tests/2]). -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). @@ -57,7 +57,7 @@ do(State, Tests) -> Cwd = rebar_dir:get_cwd(), rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), - case Tests of + case validate_tests(State, Tests) of {ok, T} -> case run_tests(State, T) of {ok, State1} -> @@ -204,11 +204,7 @@ prepare_tests(State) -> %% prioritize tests to run first trying any command line specified %% tests falling back to tests specified in the config file finally %% running a default set if no other tests are present - Tests = select_tests(State, ProjectApps, CmdTests, CfgTests), - %% check applications for existence in project, modules for existence - %% in applications, files and dirs for existence on disk and allow - %% any unrecognized tests through for eunit to handle - validate_tests(State, ProjectApps, Tests). + select_tests(State, ProjectApps, CmdTests, CfgTests). resolve_tests(State) -> {RawOpts, _} = rebar_state:command_parsed_args(State), @@ -231,9 +227,9 @@ resolve(Flag, EUnitKey, RawOpts) -> normalize(Key, Value) when Key == dir; Key == file -> {Key, Value}; normalize(Key, Value) -> {Key, list_to_atom(Value)}. -select_tests(State, ProjectApps, [], []) -> default_tests(State, ProjectApps); -select_tests(_State, _ProjectApps, [], Tests) -> Tests; -select_tests(_State, _ProjectApps, Tests, _) -> Tests. +select_tests(State, ProjectApps, [], []) -> {ok, default_tests(State, ProjectApps)}; +select_tests(_State, _ProjectApps, [], Tests) -> {ok, Tests}; +select_tests(_State, _ProjectApps, Tests, _) -> {ok, Tests}. default_tests(State, Apps) -> Tests = set_apps(Apps, []), @@ -252,8 +248,9 @@ set_apps([App|Rest], Acc) -> AppName = list_to_atom(binary_to_list(rebar_app_info:name(App))), set_apps(Rest, [{application, AppName}|Acc]). -validate_tests(State, ProjectApps, Tests) -> - gather_tests(fun(Elem) -> validate(State, ProjectApps, Elem) end, Tests, []). +validate_tests(State, {ok, Tests}) -> + gather_tests(fun(Elem) -> validate(State, Elem) end, Tests, []); +validate_tests(_State, Error) -> Error. gather_tests(_F, [], Acc) -> {ok, lists:reverse(Acc)}; gather_tests(F, [Test|Rest], Acc) -> @@ -270,27 +267,31 @@ gather_errors(F, [Test|Rest], Acc) -> {error, Error} -> gather_errors(F, Rest, [Error|Acc]) end. -validate(State, ProjectApps, {application, App}) -> - validate_app(State, ProjectApps, App); -validate(State, _ProjectApps, {dir, Dir}) -> +validate(State, {application, App}) -> + validate_app(State, App); +validate(State, {dir, Dir}) -> validate_dir(State, Dir); -validate(State, _ProjectApps, {file, File}) -> +validate(State, {file, File}) -> validate_file(State, File); -validate(State, _ProjectApps, {module, Module}) -> +validate(State, {module, Module}) -> validate_module(State, Module); -validate(State, _ProjectApps, {suite, Module}) -> +validate(State, {suite, Module}) -> validate_module(State, Module); -validate(State, _ProjectApps, Module) when is_atom(Module) -> +validate(State, Module) when is_atom(Module) -> validate_module(State, Module); -validate(State, ProjectApps, Path) when is_list(Path) -> +validate(State, Path) when is_list(Path) -> case ec_file:is_dir(Path) of - true -> validate(State, ProjectApps, {dir, Path}); - false -> validate(State, ProjectApps, {file, Path}) + true -> validate(State, {dir, Path}); + false -> validate(State, {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) -> ok. +validate(_State, _Test) -> ok. + +validate_app(State, AppName) -> + ProjectApps = rebar_state:project_apps(State), + validate_app(State, ProjectApps, AppName). validate_app(_State, [], AppName) -> {error, lists:concat(["Application `", AppName, "' not found in project."])}; diff --git a/test/rebar_eunit_SUITE.erl b/test/rebar_eunit_SUITE.erl index 609be51..262eb92 100644 --- a/test/rebar_eunit_SUITE.erl +++ b/test/rebar_eunit_SUITE.erl @@ -268,7 +268,7 @@ missing_application_arg(Config) -> State = rebar_state:command_parsed_args(S, Args), Error = {error, {rebar_prv_eunit, {eunit_test_errors, ["Application `missing_app' not found in project."]}}}, - Error = rebar_prv_eunit:prepare_tests(State). + Error = rebar_prv_eunit:validate_tests(State, rebar_prv_eunit:prepare_tests(State)). %% check that the --module cmd line opt generates the correct test set single_module_arg(Config) -> @@ -311,8 +311,11 @@ missing_module_arg(Config) -> {ok, Args} = getopt:parse(rebar_prv_eunit:eunit_opts(S), ["--module=missing_app"]), State = rebar_state:command_parsed_args(S, Args), + T = rebar_prv_eunit:prepare_tests(State), + Tests = rebar_prv_eunit:validate_tests(S, T), + Error = {error, {rebar_prv_eunit, {eunit_test_errors, ["Module `missing_app' not found in project."]}}}, - Error = rebar_prv_eunit:prepare_tests(State). + Error = Tests. %% check that the --suite cmd line opt generates the correct test set single_suite_arg(Config) -> @@ -356,7 +359,7 @@ missing_suite_arg(Config) -> State = rebar_state:command_parsed_args(S, Args), Error = {error, {rebar_prv_eunit, {eunit_test_errors, ["Module `missing_app' not found in project."]}}}, - Error = rebar_prv_eunit:prepare_tests(State). + Error = rebar_prv_eunit:validate_tests(State, rebar_prv_eunit:prepare_tests(State)). %% check that the --file cmd line opt generates the correct test set single_file_arg(Config) -> @@ -390,7 +393,7 @@ missing_file_arg(Config) -> State = rebar_state:command_parsed_args(S, Args), Error = {error, {rebar_prv_eunit, {eunit_test_errors, ["File `" ++ Path ++"' not found."]}}}, - Error = rebar_prv_eunit:prepare_tests(State). + Error = rebar_prv_eunit:validate_tests(State, rebar_prv_eunit:prepare_tests(State)). %% check that the --dir cmd line opt generates the correct test set single_dir_arg(Config) -> @@ -424,7 +427,7 @@ missing_dir_arg(Config) -> State = rebar_state:command_parsed_args(S, Args), Error = {error, {rebar_prv_eunit, {eunit_test_errors, ["Directory `" ++ Path ++"' not found."]}}}, - Error = rebar_prv_eunit:prepare_tests(State). + Error = rebar_prv_eunit:validate_tests(State, rebar_prv_eunit:prepare_tests(State)). %% check that multiple args are composed multiple_arg_composition(Config) -> @@ -470,11 +473,14 @@ multiple_arg_errors(Config) -> "--dir=" ++ DirPath]), State = rebar_state:command_parsed_args(S, Args), + T = rebar_prv_eunit:prepare_tests(State), + Tests = rebar_prv_eunit:validate_tests(S, T), + Expect = ["Application `missing_app' not found in project.", "Directory `" ++ DirPath ++ "' not found.", "File `" ++ FilePath ++ "' not found.", "Module `missing_app' not found in project.", "Module `missing_app' not found in project."], - {error, {rebar_prv_eunit, {eunit_test_errors, Expect}}} = rebar_prv_eunit:prepare_tests(State). + {error, {rebar_prv_eunit, {eunit_test_errors, Expect}}} = Tests. -- cgit v1.1 From d48f02dbc4f45b7e4d13e4ae847c44aad67dd873 Mon Sep 17 00:00:00 2001 From: James Fish Date: Sat, 31 Oct 2015 16:43:04 +0000 Subject: Rebuild PLT when beams no longer exist --- src/rebar_prv_dialyzer.erl | 21 ++++++++++++++++----- test/rebar_dialyzer_SUITE.erl | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index 487e9d1..2e5728a 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -173,7 +173,7 @@ do_update_proj_plt(State, Plt, Output) -> case read_plt(State, Plt) of {ok, OldFiles} -> check_plt(State, Plt, Output, OldFiles, Files); - {error, no_such_file} -> + error -> build_proj_plt(State, Plt, Output, Files) end. @@ -252,14 +252,25 @@ read_plt(_State, Plt) -> case dialyzer:plt_info(Plt) of {ok, Info} -> Files = proplists:get_value(files, Info, []), - {ok, Files}; - {error, no_such_file} = Error -> - Error; + read_plt_files(Plt, Files); + {error, no_such_file} -> + error; {error, read_error} -> Error = io_lib:format("Could not read the PLT file ~p", [Plt]), throw({dialyzer_error, Error}) end. +%% If any file no longer exists dialyzer will fail when updating the PLT. +read_plt_files(Plt, Files) -> + case [File || File <- Files, not filelib:is_file(File)] of + [] -> + {ok, Files}; + Missing -> + ?INFO("Could not find ~p files in ~p...", [length(Missing), Plt]), + ?DEBUG("Could not find files: ~p", [Missing]), + error + end. + check_plt(State, Plt, Output, OldList, FilesList) -> Old = sets:from_list(OldList), Files = sets:from_list(FilesList), @@ -337,7 +348,7 @@ update_base_plt(State, BasePlt, Output, BaseFiles) -> case read_plt(State, BasePlt) of {ok, OldBaseFiles} -> check_plt(State, BasePlt, Output, OldBaseFiles, BaseFiles); - {error, no_such_file} -> + error -> _ = filelib:ensure_dir(BasePlt), build_plt(State, BasePlt, Output, BaseFiles) end. diff --git a/test/rebar_dialyzer_SUITE.erl b/test/rebar_dialyzer_SUITE.erl index 31e02d9..22a4894 100644 --- a/test/rebar_dialyzer_SUITE.erl +++ b/test/rebar_dialyzer_SUITE.erl @@ -69,7 +69,16 @@ update_base_plt(Config) -> ?assertEqual(ErtsFiles, BasePltFiles2), {ok, PltFiles} = plt_files(Plt), - ?assertEqual(ErtsFiles, PltFiles). + ?assertEqual(ErtsFiles, PltFiles), + + add_missing_file(BasePlt), + ok = file:delete(Plt), + + rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"], + {ok, [{app, Name}]}), + + {ok, BasePltFiles3} = plt_files(BasePlt), + ?assertEqual(ErtsFiles, BasePltFiles3). update_app_plt(Config) -> @@ -103,7 +112,15 @@ update_app_plt(Config) -> {ok, [{app, Name}]}), {ok, PltFiles3} = plt_files(Plt), - ?assertEqual(ErtsFiles, PltFiles3). + ?assertEqual(ErtsFiles, PltFiles3), + + add_missing_file(Plt), + + rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"], + {ok, [{app, Name}]}), + + {ok, PltFiles4} = plt_files(Plt), + ?assertEqual(ErtsFiles, PltFiles4). build_release_plt(Config) -> AppDir = ?config(apps, Config), @@ -211,6 +228,19 @@ alter_plt(Plt) -> {files, [code:which(dialyzer)]}]), ok. +add_missing_file(Plt) -> + Source = code:which(dialyzer), + Dest = filename:join(filename:dirname(Plt), "dialyzer.beam"), + {ok, _} = file:copy(Source, Dest), + _ = try + dialyzer:run([{analysis_type, plt_add}, + {init_plt, Plt}, + {files, [Dest]}]) + after + ok = file:delete(Dest) + end, + ok. + -spec merge_config(Config, Config) -> Config when Config :: [{term(), term()}]. merge_config(NewConfig, OldConfig) -> -- cgit v1.1 From 67bf54d04d332bbff961ab4e8ab730858bcdce7a Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Sun, 1 Nov 2015 13:41:46 -0800 Subject: add an option to soft purge rather than purge old code at the cost of some SASL warnings this prevents rebar3 from terminating processes when reloading their code before running tests --- src/rebar_prv_common_test.erl | 2 +- src/rebar_prv_eunit.erl | 2 +- src/rebar_utils.erl | 18 ++++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 1f4c02d..32d4433 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -38,7 +38,7 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> ?INFO("Running Common Test suites...", []), - rebar_utils:update_code(rebar_state:code_paths(State, all_deps)), + rebar_utils:update_code(rebar_state:code_paths(State, all_deps), [soft_purge]), %% Run ct provider prehooks Providers = rebar_state:providers(State), diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 0cdc20b..d5612e8 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -49,7 +49,7 @@ do(State) -> do(State, Tests) -> ?INFO("Performing EUnit tests...", []), - rebar_utils:update_code(rebar_state:code_paths(State, all_deps)), + rebar_utils:update_code(rebar_state:code_paths(State, all_deps), [soft_purge]), %% Run eunit provider prehooks Providers = rebar_state:providers(State), diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index d00a46f..ea60e42 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -47,6 +47,7 @@ deprecated/4, indent/1, update_code/1, + update_code/2, remove_from_code_path/1, cleanup_code_path/1, args_to_tasks/1, @@ -644,7 +645,9 @@ indent(Amount) when erlang:is_integer(Amount) -> %% Replace code paths with new paths for existing apps and %% purge code of the old modules from those apps. -update_code(Paths) -> +update_code(Paths) -> update_code(Paths, []). + +update_code(Paths, Opts) -> lists:foreach(fun(Path) -> Name = filename:basename(Path, "/ebin"), App = list_to_atom(Name), @@ -654,19 +657,18 @@ update_code(Paths) -> code:add_patha(Path), ok; {ok, Modules} -> - %% stick rebar ebin dir before purging to prevent - %% inadvertent termination - RebarBin = code:lib_dir(rebar, ebin), - ok = code:stick_dir(RebarBin), %% replace_path causes problems when running %% tests in projects like erlware_commons that rebar3 %% also includes %code:replace_path(App, Path), code:del_path(App), code:add_patha(Path), - [begin code:purge(M), code:delete(M) end || M <- Modules], - %% unstick rebar dir - ok = code:unstick_dir(RebarBin) + case lists:member(soft_purge, Opts) of + true -> + [begin code:soft_purge(M), code:delete(M) end || M <- Modules]; + false -> + [begin code:purge(M), code:delete(M) end || M <- Modules] + end end end, Paths). -- cgit v1.1 From d8be03ab6a75b2f585487118ae38085c2f758d3c Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Sun, 1 Nov 2015 14:10:00 -0800 Subject: fixes for cover under new compiler/eunit/ct * only try to cover compile directories that actually exist * recover from failures where source files don't contain required attributes for cover compilation and print warning --- src/rebar_prv_cover.erl | 32 ++++++++++++++++++-------------- test/rebar_cover_SUITE.erl | 25 +++++++++++++++++++------ 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index 0b9b9bb..15a067e 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -279,21 +279,26 @@ cover_compile(State, apps) -> Apps = filter_checkouts(rebar_state:project_apps(State)), AppDirs = app_dirs(Apps), ExtraDirs = extra_src_dirs(State, Apps), - cover_compile(State, AppDirs ++ ExtraDirs); + cover_compile(State, lists:filter(fun(D) -> ec_file:is_dir(D) end, AppDirs ++ ExtraDirs)); cover_compile(State, Dirs) -> %% start the cover server if necessary {ok, CoverPid} = start_cover(), %% redirect cover output true = redirect_cover_output(State, CoverPid), - CompileResult = compile(Dirs, []), - %% print any warnings about modules that failed to cover compile - lists:foreach(fun print_cover_warnings/1, lists:flatten(CompileResult)). - -compile([], Acc) -> lists:reverse(Acc); -compile([Dir|Rest], Acc) -> - ?INFO("covering ~p", [Dir]), - Result = cover:compile_beam_directory(Dir), - compile(Rest, [Result|Acc]). + lists:foreach(fun(Dir) -> + ?DEBUG("cover compiling ~p", [Dir]), + case catch(cover:compile_beam_directory(Dir)) of + {error, eacces} -> + ?WARN("Directory ~p not readable, modules will not be included in coverage", [Dir]); + {error, enoent} -> + ?WARN("Directory ~p not found", [Dir]); + {'EXIT', {Reason, _}} -> + ?WARN("Cover compilation for directory ~p failed: ~p", [Dir, Reason]); + Results -> + %% print any warnings about modules that failed to cover compile + lists:foreach(fun print_cover_warnings/1, lists:flatten(Results)) + end + end, Dirs). app_dirs(Apps) -> lists:foldl(fun app_ebin_dirs/2, [], Apps). @@ -302,7 +307,7 @@ app_ebin_dirs(App, Acc) -> AppDir = rebar_app_info:ebin_dir(App), ExtraDirs = rebar_dir:extra_src_dirs(rebar_app_info:opts(App), []), OutDir = rebar_app_info:out_dir(App), - [filename:join([OutDir, D]) || D <- [AppDir|ExtraDirs]] ++ Acc. + [AppDir] ++ [filename:join([OutDir, D]) || D <- ExtraDirs] ++ Acc. extra_src_dirs(State, Apps) -> BaseDir = rebar_state:dir(State), @@ -339,9 +344,8 @@ redirect_cover_output(State, CoverPid) -> group_leader(F, CoverPid). print_cover_warnings({ok, _}) -> ok; -print_cover_warnings({error, File}) -> - ?WARN("Cover compilation of ~p failed, module is not included in cover data.", - [File]). +print_cover_warnings({error, Error}) -> + ?WARN("Cover compilation failed: ~p", [Error]). write_coverdata(State, Task) -> DataDir = cover_dir(State), diff --git a/test/rebar_cover_SUITE.erl b/test/rebar_cover_SUITE.erl index ba078c2..a838d7d 100644 --- a/test/rebar_cover_SUITE.erl +++ b/test/rebar_cover_SUITE.erl @@ -72,7 +72,7 @@ basic_extra_src_dirs(Config) -> Name = rebar_test_utils:create_random_name("cover_extra_"), Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), ExtraSrc = io_lib:format("-module(~ts_extra).\n-export([ok/0]).\nok() -> ok.\n", [Name]), @@ -86,8 +86,11 @@ basic_extra_src_dirs(Config) -> ["eunit", "--cover"], {ok, [{app, Name}]}), - Mod = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name]))), - {file, _} = cover:is_compiled(Mod). + Mod = list_to_atom(Name), + {file, _} = cover:is_compiled(Mod), + + ExtraMod = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name]))), + {file, _} = cover:is_compiled(ExtraMod). release_extra_src_dirs(Config) -> AppDir = ?config(apps, Config), @@ -120,10 +123,15 @@ release_extra_src_dirs(Config) -> ["eunit", "--cover"], {ok, [{app, Name1}, {app, Name2}]}), - Mod1 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name1]))), + Mod1 = list_to_atom(Name1), {file, _} = cover:is_compiled(Mod1), - Mod2 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name2]))), - {file, _} = cover:is_compiled(Mod2). + Mod2 = list_to_atom(Name2), + {file, _} = cover:is_compiled(Mod2), + + ExtraMod1 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name1]))), + {file, _} = cover:is_compiled(ExtraMod1), + ExtraMod2 = list_to_atom(lists:flatten(io_lib:format("~ts_extra", [Name2]))), + {file, _} = cover:is_compiled(ExtraMod2). root_extra_src_dirs(Config) -> AppDir = ?config(apps, Config), @@ -147,6 +155,11 @@ root_extra_src_dirs(Config) -> ["eunit", "--cover"], {ok, [{app, Name1}, {app, Name2}]}), + Mod1 = list_to_atom(Name1), + {file, _} = cover:is_compiled(Mod1), + Mod2 = list_to_atom(Name2), + {file, _} = cover:is_compiled(Mod2), + {file, _} = cover:is_compiled(extra). index_written(Config) -> -- cgit v1.1 From e5aa04f27800ee83a5d5e76fd95c9f31e6aff090 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Sun, 1 Nov 2015 19:00:09 -0600 Subject: fix #903: use app discover of entire plugins dir when listing --- .travis.yml | 2 +- src/rebar_prv_plugins.erl | 27 +++++++++++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index f435a71..7530a98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ branches: - master cache: directories: - - $HOME/.cache/rebar3/hex/com/amazonaws/s3/s3.hex.pm/tarballs/packages/ + - $HOME/.cache/rebar3/hex/default before_deploy: "rm -rf !(rebar3)" deploy: on: diff --git a/src/rebar_prv_plugins.erl b/src/rebar_prv_plugins.erl index 20bc1ea..7e6b88e 100644 --- a/src/rebar_prv_plugins.erl +++ b/src/rebar_prv_plugins.erl @@ -34,34 +34,37 @@ do(State) -> GlobalConfigFile = rebar_dir:global_config(), GlobalConfig = rebar_state:new(rebar_config:consult_file(GlobalConfigFile)), GlobalPlugins = rebar_state:get(GlobalConfig, plugins, []), - GlobalPluginsDir = filename:join(rebar_dir:global_cache_dir(rebar_state:opts(State)), "plugins"), - display_plugins("Global plugins", GlobalPluginsDir, GlobalPlugins), + GlobalPluginsDir = filename:join([rebar_dir:global_cache_dir(rebar_state:opts(State)), "plugins", "*"]), + GlobalApps = rebar_app_discover:find_apps([GlobalPluginsDir], all), + display_plugins("Global plugins", GlobalApps, GlobalPlugins), Plugins = rebar_state:get(State, plugins, []), - PluginsDir =rebar_dir:plugins_dir(State), - display_plugins("Local plugins", PluginsDir, Plugins), + PluginsDir = filename:join(rebar_dir:plugins_dir(State), "*"), + CheckoutsDir = filename:join(rebar_dir:checkouts_dir(State), "*"), + Apps = rebar_app_discover:find_apps([CheckoutsDir, PluginsDir], all), + display_plugins("Local plugins", Apps, Plugins), {ok, State}. -spec format_error(any()) -> iolist(). format_error(Reason) -> io_lib:format("~p", [Reason]). -display_plugins(_Header, _Dir, []) -> +display_plugins(_Header, _Apps, []) -> ok; -display_plugins(Header, Dir, Plugins) -> +display_plugins(Header, Apps, Plugins) -> ?CONSOLE("--- ~s ---", [Header]), - display_plugins(Dir, Plugins), + display_plugins(Apps, Plugins), ?CONSOLE("", []). -display_plugins(Dir, Plugins) -> +display_plugins(Apps, Plugins) -> lists:foreach(fun(Plugin) -> - Name = if is_atom(Plugin) -> Plugin; - is_tuple(Plugin) -> element(1, Plugin) + Name = if is_atom(Plugin) -> ec_cnv:to_binary(Plugin); + is_tuple(Plugin) -> ec_cnv:to_binary(element(1, Plugin)) end, - case rebar_app_info:discover(filename:join(Dir, Name)) of + case rebar_app_utils:find(Name, Apps) of {ok, _App} -> ?CONSOLE("~s", [Name]); - not_found -> + error -> ?DEBUG("Unable to find plugin ~s", [Name]) end end, Plugins). -- cgit v1.1 From 19a3a96893ec8978233e2a815a61e26bfa86c4e0 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Thu, 15 Oct 2015 19:58:42 -0700 Subject: refactor `rebar_prv_common_test` * remove partial support for ct `test_spec` until it can be done properly and warn if `test_spec` is present in test opts * use new compiler functionality to reduce complexity of provider * reduce command line options available to those that can be supported properly --- src/rebar_prv_common_test.erl | 684 +++++++++++++++++++----------------------- test/rebar_ct_SUITE.erl | 311 +++++++++---------- 2 files changed, 455 insertions(+), 540 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 32d4433..910a1ca 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -2,19 +2,21 @@ %% ex: ts=4 sw=4 et -module(rebar_prv_common_test). + -behaviour(provider). -export([init/1, do/1, format_error/1]). %% exported for test purposes, consider private --export([setup_ct/1]). +-export([compile/2, prepare_tests/1, translate_paths/2]). -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). -define(PROVIDER, ct). --define(DEPS, [compile]). +%% we need to modify app_info state before compile +-define(DEPS, [lock]). %% =================================================================== %% Public API @@ -31,12 +33,19 @@ init(State) -> {desc, "Run Common Tests."}, {opts, ct_opts(State)}, {profiles, [test]}]), - State1 = rebar_state:add_provider(State, Provider), - State2 = rebar_state:add_to_profile(State1, test, test_state(State1)), - {ok, State2}. + {ok, rebar_state:add_provider(State, Provider)}. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> + Tests = prepare_tests(State), + case compile(State, Tests) of + %% successfully compiled apps + {ok, S} -> do(S, Tests); + %% this should look like a compiler error, not a ct error + Error -> Error + end. + +do(State, Tests) -> ?INFO("Running Common Test suites...", []), rebar_utils:update_code(rebar_state:code_paths(State, all_deps), [soft_purge]), @@ -45,61 +54,308 @@ do(State) -> Cwd = rebar_dir:get_cwd(), rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), - try run_test(State) of - {ok, State1} = Result -> - %% Run ct provider posthooks - rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State1), - rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)), - Result; - ?PRV_ERROR(_) = Error -> + case Tests of + {ok, T} -> + case run_tests(State, T) of + ok -> + %% Run ct provider posthooks + rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State), + rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)), + {ok, State}; + Error -> + rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)), + Error + end; + Error -> rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)), Error - catch - throw:{error, Reason} -> - rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)), - ?PRV_ERROR(Reason) + end. + +run_tests(State, Opts) -> + T = translate_paths(State, Opts), + Opts1 = setup_logdir(State, T), + %% strip test spec if present + Opts2 = strip_test_spec(Opts1), + ?DEBUG("ct_opts ~p", [Opts2]), + {RawOpts, _} = rebar_state:command_parsed_args(State), + ok = maybe_write_coverdata(State), + case proplists:get_value(verbose, RawOpts, false) of + true -> run_test_verbose(Opts2); + false -> run_test_quiet(Opts2) end. -spec format_error(any()) -> iolist(). -format_error({multiple_dirs_and_suites, Opts}) -> - io_lib:format("Multiple dirs declared alongside suite in opts: ~p", [Opts]); -format_error({bad_dir_or_suite, Opts}) -> - io_lib:format("Bad value for dir or suite in opts: ~p", [Opts]); +format_error({error, Reason}) -> + io_lib:format("Error running tests:~n ~p", [Reason]); +format_error({error_running_tests, Reason}) -> + format_error({error, Reason}); format_error({failures_running_tests, {Failed, AutoSkipped}}) -> io_lib:format("Failures occured running tests: ~b", [Failed+AutoSkipped]); -format_error({error_running_tests, Reason}) -> - io_lib:format("Error running tests: ~p", [Reason]); -format_error(suite_at_project_root) -> - io_lib:format("Test suites can't be located in project root", []); -format_error({error, Reason}) -> - io_lib:format("Unknown error: ~p", [Reason]). +format_error({multiple_errors, Errors}) -> + io_lib:format(lists:concat(["Error running tests:"] ++ + lists:map(fun(Error) -> "~n " ++ Error end, Errors)), []). %% =================================================================== %% Internal functions %% =================================================================== -run_test(State) -> - case setup_ct(State) of - {error, {no_tests_specified, Opts}} -> - ?WARN("No tests specified in opts: ~p", [Opts]), - {ok, State}; - Opts -> - Opts1 = setup_logdir(State, Opts), - ?DEBUG("common test opts: ~p", [Opts1]), - run_test(State, Opts1) - end. +prepare_tests(State) -> + %% command line test options + CmdOpts = opts(State), + %% rebar.config test options + CfgOpts = rebar_state:get(State, ct_tests, []), + ProjectApps = rebar_state:project_apps(State), + %% prioritize tests to run first trying any command line specified + %% tests falling back to tests specified in the config file finally + %% running a default set if no other tests are present + select_tests(State, ProjectApps, CmdOpts, CfgOpts). -run_test(State, Opts) -> +opts(State) -> {RawOpts, _} = rebar_state:command_parsed_args(State), - ok = rebar_prv_cover:maybe_cover_compile(State, apps), - Result = case proplists:get_value(verbose, RawOpts, false) of - true -> run_test_verbose(Opts); - false -> run_test_quiet(Opts) + %% filter out opts common_test doesn't know about and convert + %% to ct acceptable forms + transform_opts(RawOpts, []). + +transform_opts([], Acc) -> lists:reverse(Acc); +transform_opts([{dir, Dirs}|Rest], Acc) -> + transform_opts(Rest, [{dir, split_string(Dirs)}|Acc]); +transform_opts([{suite, Suites}|Rest], Acc) -> + transform_opts(Rest, [{suite, split_string(Suites)}|Acc]); +transform_opts([{group, Groups}|Rest], Acc) -> + transform_opts(Rest, [{group, split_string(Groups)}|Acc]); +transform_opts([{testcase, Cases}|Rest], Acc) -> + transform_opts(Rest, [{testcase, split_string(Cases)}|Acc]); +transform_opts([{config, Configs}|Rest], Acc) -> + transform_opts(Rest, [{config, split_string(Configs)}|Acc]); +transform_opts([{include, Includes}|Rest], Acc) -> + transform_opts(Rest, [{include, split_string(Includes)}|Acc]); +transform_opts([{force_stop, "true"}|Rest], Acc) -> + transform_opts(Rest, [{force_stop, true}|Acc]); +transform_opts([{force_stop, "false"}|Rest], Acc) -> + transform_opts(Rest, [{force_stop, false}|Acc]); +transform_opts([{force_stop, "skip_rest"}|Rest], Acc) -> + transform_opts(Rest, [{force_stop, skip_rest}|Acc]); +%% drop cover from opts, ct doesn't care about it +transform_opts([{cover, _}|Rest], Acc) -> + transform_opts(Rest, Acc); +%% drop verbose from opts, ct doesn't care about it +transform_opts([{verbose, _}|Rest], Acc) -> + transform_opts(Rest, Acc); +%% getopt should handle anything else +transform_opts([Opt|Rest], Acc) -> + transform_opts(Rest, [Opt|Acc]). + +split_string(String) -> + string:tokens(String, [$,]). + +select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> + Merged = lists:ukeymerge(1, + lists:ukeysort(1, CmdOpts), + lists:ukeysort(1, CfgOpts)), + %% make sure `dir` and/or `suite` from command line go in as + %% a pair overriding both `dir` and `suite` from config if + %% they exist + Opts = case {proplists:get_value(suite, CmdOpts), proplists:get_value(dir, CmdOpts)} of + {undefined, undefined} -> Merged; + {_Suite, undefined} -> lists:keydelete(dir, 1, Merged); + {undefined, _Dir} -> lists:keydelete(suite, 1, Merged); + {_Suite, _Dir} -> Merged end, - ok = rebar_prv_cover:maybe_write_coverdata(State, ?PROVIDER), - case Result of - ok -> {ok, State}; - Error -> Error + discover_tests(State, ProjectApps, Opts). + +discover_tests(State, ProjectApps, Opts) -> + case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of + %% no dirs or suites defined, try using `$APP/test` and `$ROOT/test` + %% as suites + {undefined, undefined} -> {ok, [default_tests(State, ProjectApps)|Opts]}; + {_, _} -> {ok, Opts} + end. + +default_tests(State, ProjectApps) -> + BareTest = filename:join([rebar_state:dir(State), "test"]), + F = fun(App) -> rebar_app_info:dir(App) == rebar_state:dir(State) end, + AppTests = application_dirs(ProjectApps, []), + case filelib:is_dir(BareTest) andalso not lists:any(F, ProjectApps) of + %% `test` dir at root of project is already scheduled to be + %% included or `test` does not exist + false -> {dir, AppTests}; + %% need to add `test` dir at root to dirs to be included + true -> {dir, AppTests ++ [BareTest]} + end. + +application_dirs([], []) -> []; +application_dirs([], Acc) -> lists:reverse(Acc); +application_dirs([App|Rest], Acc) -> + TestDir = filename:join([rebar_app_info:dir(App), "test"]), + case filelib:is_dir(TestDir) of + true -> application_dirs(Rest, [TestDir|Acc]); + false -> application_dirs(Rest, Acc) + end. + +compile(State, {ok, Tests}) -> + %% inject `ct_first_files` and `ct_compile_opts` into the applications + %% to be compiled + case inject_ct_state(State, Tests) of + {ok, NewState} -> do_compile(NewState); + Error -> Error + end; +%% maybe compile even in the face of errors? +compile(_State, Error) -> Error. + +do_compile(State) -> + case rebar_prv_compile:do(State) of + %% successfully compiled apps + {ok, S} -> + ok = maybe_cover_compile(S), + {ok, S}; + %% this should look like a compiler error, not an eunit error + Error -> Error + end. + +inject_ct_state(State, Tests) -> + Apps = rebar_state:project_apps(State), + ModdedApps = lists:map(fun(App) -> + NewOpts = inject(rebar_app_info:opts(App), State), + rebar_app_info:opts(App, NewOpts) + end, Apps), + NewOpts = inject(rebar_state:opts(State), State), + NewState = rebar_state:opts(State, NewOpts), + test_dirs(NewState, ModdedApps, Tests). + +inject(Opts, State) -> + %% append `ct_compile_opts` to app defined `erl_opts` + ErlOpts = rebar_opts:get(Opts, erl_opts, []), + CTOpts = rebar_state:get(State, ct_compile_opts, []), + NewErlOpts = CTOpts ++ ErlOpts, + %% append `ct_first_files` to app defined `erl_first_files` + FirstFiles = rebar_opts:get(Opts, erl_first_files, []), + CTFirstFiles = rebar_state:get(State, ct_first_files, []), + NewFirstFiles = CTFirstFiles ++ FirstFiles, + %% insert the new keys into the opts + lists:foldl(fun({K, V}, NewOpts) -> rebar_opts:set(NewOpts, K, V) end, + Opts, + [{erl_opts, NewErlOpts}, {erl_first_files, NewFirstFiles}]). + +test_dirs(State, Apps, Opts) -> + case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of + {Suites, undefined} -> set_compile_dirs(State, Apps, {suite, Suites}); + {undefined, Dirs} -> set_compile_dirs(State, Apps, {dir, Dirs}); + {Suites, [Dir]} -> set_compile_dirs(State, Apps, join(Suites, Dir)); + {_Suites, _Dirs} -> {error, "Only a single directory may be specified when specifying suites"} + end. + +join(Suites, Dir) -> + {suite, lists:map(fun(S) -> filename:join([Dir, S]) end, Suites)}. + +set_compile_dirs(State, Apps, {dir, Dir}) when is_integer(hd(Dir)) -> + %% single directory + %% insert `Dir` into an app if relative, or the base state if not + %% app relative but relative to the root or not at all if outside + %% project scope + {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir), + {ok, rebar_state:project_apps(NewState, NewApps)}; +set_compile_dirs(State, Apps, {dir, Dirs}) -> + %% multiple directories + F = fun(Dir, {S, A}) -> maybe_inject_test_dir(S, [], A, Dir) end, + {NewState, NewApps} = lists:foldl(F, {State, Apps}, Dirs), + {ok, rebar_state:project_apps(NewState, NewApps)}; +set_compile_dirs(State, Apps, {suite, Suites}) -> + %% suites with dir component + Dirs = find_suite_dirs(Suites), + F = fun(Dir, {S, A}) -> maybe_inject_test_dir(S, [], A, Dir) end, + {NewState, NewApps} = lists:foldl(F, {State, Apps}, Dirs), + {ok, rebar_state:project_apps(NewState, NewApps)}. + +find_suite_dirs(Suites) -> + AllDirs = lists:map(fun(S) -> filename:dirname(filename:absname(S)) end, Suites), + %% eliminate duplicates + lists:usort(AllDirs). + +maybe_inject_test_dir(State, AppAcc, [App|Rest], Dir) -> + case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of + {ok, Path} -> + Opts = inject_test_dir(rebar_app_info:opts(App), Path), + {State, AppAcc ++ [rebar_app_info:opts(App, Opts)] ++ Rest}; + {error, badparent} -> + maybe_inject_test_dir(State, AppAcc ++ [App], Rest, Dir) + end; +maybe_inject_test_dir(State, AppAcc, [], Dir) -> + case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of + {ok, []} -> + ?WARN("Can't have suites in root of project dir, dropping from tests", []), + {State, AppAcc}; + {ok, Path} -> + Opts = inject_test_dir(rebar_state:opts(State), Path), + {rebar_state:opts(State, Opts), AppAcc}; + {error, badparent} -> + {State, AppAcc} + end. + +inject_test_dir(Opts, Dir) -> + %% append specified test targets to app defined `extra_src_dirs` + ExtraSrcDirs = rebar_opts:get(Opts, extra_src_dirs, []), + rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]). + +translate_paths(State, Opts) -> + case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of + {_Suites, undefined} -> translate_suites(State, Opts, []); + {undefined, _Dirs} -> translate_dirs(State, Opts, []); + %% both dirs and suites are defined, only translate dir paths + _ -> translate_dirs(State, Opts, []) + end. + +translate_dirs(_State, [], Acc) -> lists:reverse(Acc); +translate_dirs(State, [{dir, Dir}|Rest], Acc) when is_integer(hd(Dir)) -> + %% single dir + Apps = rebar_state:project_apps(State), + translate_dirs(State, Rest, [{dir, translate(State, Apps, Dir)}|Acc]); +translate_dirs(State, [{dir, Dirs}|Rest], Acc) -> + %% multiple dirs + Apps = rebar_state:project_apps(State), + NewDirs = {dir, lists:map(fun(Dir) -> translate(State, Apps, Dir) end, Dirs)}, + translate_dirs(State, Rest, [NewDirs|Acc]); +translate_dirs(State, [Test|Rest], Acc) -> + translate_dirs(State, Rest, [Test|Acc]). + +translate_suites(_State, [], Acc) -> lists:reverse(Acc); +translate_suites(State, [{suite, Suite}|Rest], Acc) when is_integer(hd(Suite)) -> + %% single suite + Apps = rebar_state:project_apps(State), + translate_suites(State, Rest, [{suite, translate(State, Apps, Suite)}|Acc]); +translate_suites(State, [{suite, Suites}|Rest], Acc) -> + %% multiple suites + Apps = rebar_state:project_apps(State), + NewSuites = {suite, lists:map(fun(Suite) -> translate(State, Apps, Suite) end, Suites)}, + translate_suites(State, Rest, [NewSuites|Acc]); +translate_suites(State, [Test|Rest], Acc) -> + translate_suites(State, Rest, [Test|Acc]). + +translate(State, [App|Rest], Path) -> + case rebar_file_utils:path_from_ancestor(Path, rebar_app_info:dir(App)) of + {ok, P} -> filename:join([rebar_app_info:out_dir(App), P]); + {error, badparent} -> translate(State, Rest, Path) + end; +translate(State, [], Path) -> + case rebar_file_utils:path_from_ancestor(Path, rebar_state:dir(State)) of + {ok, P} -> filename:join([rebar_dir:base_dir(State), "extras", P]); + %% not relative, leave as is + {error, badparent} -> Path + end. + +setup_logdir(State, Opts) -> + Logdir = case proplists:get_value(logdir, Opts) of + undefined -> filename:join([rebar_dir:base_dir(State), "logs"]); + Dir -> Dir + end, + filelib:ensure_dir(filename:join([Logdir, "dummy.beam"])), + [{logdir, Logdir}|lists:keydelete(logdir, 1, Opts)]. + +strip_test_spec(Opts) -> + case proplists:get_value(test_spec, Opts) of + undefined -> Opts; + _ -> + ?WARN("Test specs not supported", []), + lists:keydelete(test_spec, 1, Opts) end. run_test_verbose(Opts) -> handle_results(ct:run_test(Opts)). @@ -171,272 +427,38 @@ format_skipped({0, 0}) -> format_skipped({User, Auto}) -> io_lib:format("Skipped ~p (~p, ~p) tests. ", [User+Auto, User, Auto]). -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) -> - CTFirst = rebar_state:get(State, ct_first_files, []), - {erl_first_files, CTFirst}. - -setup_ct(State) -> - Opts = resolve_ct_opts(State), - Opts1 = discover_tests(State, Opts), - copy_and_compile_tests(State, Opts1). - -resolve_ct_opts(State) -> - {RawOpts, _} = rebar_state:command_parsed_args(State), - CmdOpts = transform_opts(RawOpts), - CfgOpts = rebar_state:get(State, ct_opts, []), - Merged = lists:ukeymerge(1, - lists:ukeysort(1, CmdOpts), - lists:ukeysort(1, CfgOpts)), - %% make sure `dir` and/or `suite` from command line go in as - %% a pair overriding both `dir` and `suite` from config if - %% they exist - case {proplists:get_value(suite, CmdOpts), proplists:get_value(dir, CmdOpts)} of - {undefined, undefined} -> Merged; - {_Suite, undefined} -> lists:keydelete(dir, 1, Merged); - {undefined, _Dir} -> lists:keydelete(suite, 1, Merged); - {_Suite, _Dir} -> Merged - end. - -discover_tests(State, Opts) -> - case proplists:get_value(spec, Opts) of - undefined -> discover_dirs_and_suites(State, Opts); - TestSpec -> discover_testspec(TestSpec, Opts) - end. - -discover_dirs_and_suites(State, Opts) -> - case {proplists:get_value(dir, Opts), proplists:get_value(suite, Opts)} of - %% no dirs or suites defined, try using `$APP/test` and `$ROOT/test` - %% as suites - {undefined, undefined} -> test_dirs(State, Opts); - %% no dirs defined - {undefined, _} -> Opts; - %% no suites defined - {_, undefined} -> Opts; - %% a single dir defined, this is ok - {Dirs, Suites} when is_integer(hd(Dirs)), is_list(Suites) -> Opts; - %% still a single dir defined, adjust to make acceptable to ct - {[Dir], Suites} when is_integer(hd(Dir)), is_list(Suites) -> - [{dir, Dir}|lists:keydelete(dir, 1, Opts)]; - %% multiple dirs and suites, error now to simplify later steps - {_, _} -> throw({error, {multiple_dirs_and_suites, Opts}}) - end. - -discover_testspec(_TestSpec, Opts) -> - lists:keydelete(auto_compile, 1, Opts). - -copy_and_compile_tests(State, Opts) -> - %% possibly enable cover +maybe_cover_compile(State) -> {RawOpts, _} = rebar_state:command_parsed_args(State), State1 = case proplists:get_value(cover, RawOpts, false) of true -> rebar_state:set(State, cover_enabled, true); false -> State end, - copy_and_compile_test_suites(State1, Opts). - -copy_and_compile_test_suites(State, Opts) -> - case proplists:get_value(suite, Opts) of - %% no suites, try dirs - undefined -> copy_and_compile_test_dirs(State, Opts); - Suites -> - Dir = proplists:get_value(dir, Opts, undefined), - AllSuites = join(Dir, Suites), - Dirs = find_suite_dirs(AllSuites), - lists:foreach(fun(S) -> - NewPath = copy(State, S), - compile_dir(State, NewPath) - end, Dirs), - NewSuites = lists:map(fun(S) -> retarget_path(State, S) end, AllSuites), - [{suite, NewSuites}|lists:keydelete(suite, 1, Opts)] - end. - -copy_and_compile_test_dirs(State, Opts) -> - case proplists:get_value(dir, Opts) of - undefined -> {error, {no_tests_specified, Opts}}; - %% dir is a single directory - Dir when is_list(Dir), is_integer(hd(Dir)) -> - NewPath = copy(State, Dir), - [{dir, compile_dir(State, NewPath)}|lists:keydelete(dir, 1, Opts)]; - %% dir is a list of directories - Dirs when is_list(Dirs) -> - NewDirs = lists:map(fun(Dir) -> - NewPath = copy(State, Dir), - compile_dir(State, NewPath) - end, Dirs), - [{dir, NewDirs}|lists:keydelete(dir, 1, Opts)] - end. - -join(undefined, Suites) -> Suites; -join(Dir, Suites) when is_list(Dir), is_integer(hd(Dir)) -> - lists:map(fun(S) -> filename:join([Dir, S]) end, Suites); -%% multiple dirs or a bad dir argument, try to continue anyways -join(_, Suites) -> Suites. - -find_suite_dirs(Suites) -> - AllDirs = lists:map(fun(S) -> filename:dirname(filename:absname(S)) end, Suites), - %% eliminate duplicates - lists:usort(AllDirs). - -copy(State, Dir) -> - From = reduce_path(Dir), - retarget_path(State, From). - -compile_dir(State, Dir) -> - NewState = replace_src_dirs(State, [filename:absname(Dir)]), - ok = rebar_erlc_compiler:compile(rebar_state:opts(NewState), rebar_dir:base_dir(State), Dir), - ok = maybe_cover_compile(State, Dir), - Dir. - -retarget_path(State, Path) -> - ProjectApps = rebar_state:project_apps(State), - retarget_path(State, Path, ProjectApps). - -%% not relative to any apps in project, check to see it's relative to -%% project root -retarget_path(State, Path, []) -> - case relative_path(reduce_path(Path), rebar_state:dir(State)) of - {ok, NewPath} -> filename:join([rebar_dir:base_dir(State), NewPath]); - %% not relative to project root, don't modify - {error, not_relative} -> Path - end; -%% relative to current app, retarget to the same dir relative to -%% the app's out_dir -retarget_path(State, Path, [App|Rest]) -> - case relative_path(reduce_path(Path), rebar_app_info:dir(App)) of - {ok, NewPath} -> filename:join([rebar_app_info:out_dir(App), NewPath]); - {error, not_relative} -> retarget_path(State, Path, Rest) - end. - -relative_path(Target, To) -> - relative_path1(filename:split(filename:absname(Target)), - filename:split(filename:absname(To))). - -relative_path1([Part|Target], [Part|To]) -> relative_path1(Target, To); -relative_path1([], []) -> {ok, ""}; -relative_path1(Target, []) -> {ok, filename:join(Target)}; -relative_path1(_, _) -> {error, not_relative}. - -reduce_path(Dir) -> reduce_path([], filename:split(filename:absname(Dir))). - -reduce_path([], []) -> filename:nativename("/"); -reduce_path(Acc, []) -> filename:join(lists:reverse(Acc)); -reduce_path(Acc, ["."|Rest]) -> reduce_path(Acc, Rest); -reduce_path([_|Acc], [".."|Rest]) -> reduce_path(Acc, Rest); -reduce_path([], [".."|Rest]) -> reduce_path([], Rest); -reduce_path(Acc, [Component|Rest]) -> reduce_path([Component|Acc], Rest). - -replace_src_dirs(State, Dirs) -> - %% replace any `src_dirs` with the test dirs - ErlOpts = rebar_state:get(State, erl_opts, []), - StrippedErlOpts = filter_src_dirs(ErlOpts), - State1 = rebar_state:set(State, erl_opts, StrippedErlOpts), - State2 = rebar_state:set(State1, src_dirs, []), - rebar_state:set(State2, extra_src_dirs, Dirs). + rebar_prv_cover:maybe_cover_compile(State1). -filter_src_dirs(ErlOpts) -> - lists:filter(fun({src_dirs, _}) -> false; ({extra_src_dirs, _}) -> false; (_) -> true end, ErlOpts). - -test_dirs(State, Opts) -> - BareTest = filename:join([rebar_state:dir(State), "test"]), - F = fun(App) -> rebar_app_info:dir(App) == rebar_state:dir(State) end, - TestApps = project_apps(State), - case filelib:is_dir(BareTest) andalso not lists:any(F, TestApps) of - %% `test` dir at root of project is already scheduled to be - %% included or `test` does not exist - false -> application_dirs(TestApps, Opts, []); - %% need to add `test` dir at root to dirs to be included - true -> application_dirs(TestApps, Opts, [BareTest]) - end. - -project_apps(State) -> - filter_checkouts(rebar_state:project_apps(State)). - -filter_checkouts(Apps) -> filter_checkouts(Apps, []). - -filter_checkouts([], Acc) -> lists:reverse(Acc); -filter_checkouts([App|Rest], Acc) -> - case rebar_app_info:is_checkout(App) of - true -> filter_checkouts(Rest, Acc); - false -> filter_checkouts(Rest, [App|Acc]) - end. - -application_dirs([], Opts, []) -> Opts; -application_dirs([], Opts, [Acc]) -> [{dir, Acc}|Opts]; -application_dirs([], Opts, Acc) -> [{dir, lists:reverse(Acc)}|Opts]; -application_dirs([App|Rest], Opts, Acc) -> - TestDir = filename:join([rebar_app_info:dir(App), "test"]), - case filelib:is_dir(TestDir) of - true -> application_dirs(Rest, Opts, [TestDir|Acc]); - false -> application_dirs(Rest, Opts, Acc) - end. - -setup_logdir(State, Opts) -> - Logdir = case proplists:get_value(logdir, Opts) of - undefined -> filename:join([rebar_dir:base_dir(State), "logs"]); - Dir -> Dir - end, - ensure_dir([Logdir]), - [{logdir, Logdir}|lists:keydelete(logdir, 1, Opts)]. - -ensure_dir([]) -> ok; -ensure_dir([Dir|Rest]) -> - case ec_file:is_dir(Dir) of - true -> - ok; - false -> - ec_file:mkdir_path(Dir) - end, - ensure_dir(Rest). - -maybe_cover_compile(State, Dir) -> - {Opts, _} = rebar_state:command_parsed_args(State), - State1 = case proplists:get_value(cover, Opts, false) of +maybe_write_coverdata(State) -> + {RawOpts, _} = rebar_state:command_parsed_args(State), + State1 = case proplists:get_value(cover, RawOpts, false) of true -> rebar_state:set(State, cover_enabled, true); false -> State end, - rebar_prv_cover:maybe_cover_compile(State1, [Dir]). + rebar_prv_cover:maybe_write_coverdata(State1, ?PROVIDER). ct_opts(_State) -> [{dir, undefined, "dir", string, help(dir)}, %% comma-seperated list {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 - {spec, undefined, "spec", string, help(spec)}, %% comma-seperated list - {join_specs, undefined, "join_specs", boolean, help(join_specs)}, %% Boolean {label, undefined, "label", string, help(label)}, %% String {config, undefined, "config", string, help(config)}, %% comma-seperated list - {userconfig, undefined, "userconfig", string, help(userconfig)}, %% [{CallbackMod, CfgStrings}] | {CallbackMod, CfgStrings} {allow_user_terms, undefined, "allow_user_terms", boolean, help(allow_user_terms)}, %% Bool {logdir, undefined, "logdir", string, help(logdir)}, %% dir - {logopts, undefined, "logopts", string, help(logopts)}, %% enum, no_nl | no_src - {verbosity, undefined, "verbosity", string, help(verbosity)}, %% Integer OR [{Category, VLevel}] - {silent_connections, undefined, "silent_connections", string, - help(silent_connections)}, % all OR %% comma-seperated list - {stylesheet, undefined, "stylesheet", string, help(stylesheet)}, %% file + {verbosity, undefined, "verbosity", integer, help(verbosity)}, %% Integer {cover, $c, "cover", {boolean, false}, help(cover)}, - {cover_spec, undefined, "cover_spec", string, help(cover_spec)}, %% file - {cover_stop, undefined, "cover_stop", boolean, help(cover_stop)}, %% Boolean - {event_handler, undefined, "event_handler", string, help(event_handler)}, %% EH | [EH] WHERE EH atom() | {atom(), InitArgs} | {[atom()], InitArgs} {include, undefined, "include", string, help(include)}, % comma-seperated list - {abort_if_missing_suites, undefined, "abort_if_missing_suites", {boolean, true}, - help(abort_if_missing_suites)}, %% boolean - {multiply_timetraps, undefined, "multiply_timetraps", integer, - help(multiply_timetraps)}, %% integer - {scale_timetraps, undefined, "scale_timetraps", boolean, help(scale_timetraps)}, %% Boolean - {create_priv_dir, undefined, "create_priv_dir", string, help(create_priv_dir)}, %% enum: auto_per_run | auto_per_tc | manual_per_tc {repeat, undefined, "repeat", integer, help(repeat)}, %% integer {duration, undefined, "duration", string, help(duration)}, % format: HHMMSS {until, undefined, "until", string, help(until)}, %% format: YYMoMoDD[HHMMSS] - {force_stop, undefined, "force_stop", string, help(force_stop)}, % enum: skip_rest, bool {basic_html, undefined, "basic_html", boolean, help(basic_html)}, %% Booloean - {ct_hooks, undefined, "ct_hooks", string, help(ct_hooks)}, %% List: [CTHModule | {CTHModule, CTHInitArgs}] where CTHModule is atom CthInitArgs is term - {auto_compile, undefined, "auto_compile", {boolean, false}, help(auto_compile)}, {verbose, $v, "verbose", boolean, help(verbose)} ]. @@ -448,36 +470,26 @@ help(group) -> "List of test groups to run"; help(testcase) -> "List of test cases to run"; -help(spec) -> - "List of test specs to run"; help(label) -> "Test label"; help(config) -> "List of config files"; +help(allow_user_terms) -> + "Allow user defined config values in config files"; help(logdir) -> "Log folder"; help(verbosity) -> "Verbosity"; -help(stylesheet) -> - "Stylesheet to use for test results"; help(cover) -> "Generate cover data"; -help(cover_spec) -> - "Cover file to use"; -help(event_handler) -> - "Event handlers to attach to the runner"; help(include) -> - "Include folder"; -help(abort_if_missing_suites) -> - "Abort if suites are missing"; + "Include folders"; help(repeat) -> "How often to repeat tests"; help(duration) -> "Max runtime (format: HHMMSS)"; help(until) -> "Run until (format: HHMMSS)"; -help(force_stop) -> - "Force stop after time"; help(basic_html) -> "Show basic HTML"; help(verbose) -> @@ -485,77 +497,3 @@ help(verbose) -> help(_) -> "". -transform_opts(Opts) -> - transform_opts(Opts, []). - -transform_opts([], Acc) -> Acc; -%% drop `cover` and `verbose` so they're not passed as an option to common_test -transform_opts([{cover, _}|Rest], Acc) -> - transform_opts(Rest, Acc); -transform_opts([{cover_spec, CoverSpec}|Rest], Acc) -> - transform_opts(Rest, [{cover, CoverSpec}|Acc]); -transform_opts([{verbose, _}|Rest], Acc) -> - transform_opts(Rest, Acc); -transform_opts([{ct_hooks, CtHooks}|Rest], Acc) -> - transform_opts(Rest, [{ct_hooks, parse_term(CtHooks)}|Acc]); -transform_opts([{force_stop, "skip_rest"}|Rest], Acc) -> - transform_opts(Rest, [{force_stop, skip_rest}|Acc]); -transform_opts([{force_stop, _}|Rest], Acc) -> - transform_opts(Rest, [{force_stop, true}|Acc]); -transform_opts([{repeat, Repeat}|Rest], Acc) -> - transform_opts(Rest, [{repeat, - ec_cnv:to_integer(Repeat)}|Acc]); -transform_opts([{create_priv_dir, CreatePrivDir}|Rest], Acc) -> - transform_opts(Rest, [{create_priv_dir, - to_atoms(CreatePrivDir)}|Acc]); -transform_opts([{multiply_timetraps, MultiplyTimetraps}|Rest], Acc) -> - transform_opts(Rest, [{multiply_timetraps, - ec_cnv:to_integer(MultiplyTimetraps)}|Acc]); -transform_opts([{event_handler, EventHandler}|Rest], Acc) -> - transform_opts(Rest, [{event_handler, parse_term(EventHandler)}|Acc]); -transform_opts([{silent_connections, "all"}|Rest], Acc) -> - transform_opts(Rest, [{silent_connections, all}|Acc]); -transform_opts([{silent_connections, SilentConnections}|Rest], Acc) -> - transform_opts(Rest, [{silent_connections, - to_atoms(split_string(SilentConnections))}|Acc]); -transform_opts([{verbosity, Verbosity}|Rest], Acc) -> - transform_opts(Rest, [{verbosity, parse_term(Verbosity)}|Acc]); -transform_opts([{logopts, LogOpts}|Rest], Acc) -> - transform_opts(Rest, [{logopts, to_atoms(split_string(LogOpts))}|Acc]); -transform_opts([{userconfig, UserConfig}|Rest], Acc) -> - transform_opts(Rest, [{userconfig, parse_term(UserConfig)}|Acc]); -transform_opts([{testcase, Testcase}|Rest], Acc) -> - transform_opts(Rest, [{testcase, to_atoms(split_string(Testcase))}|Acc]); -transform_opts([{group, Group}|Rest], Acc) -> % @TODO handle "" - % Input is a list or an atom. It can also be a nested list. - transform_opts(Rest, [{group, parse_term(Group)}|Acc]); -transform_opts([{suite, Suite}|Rest], Acc) -> - transform_opts(Rest, [{suite, split_string(Suite)}|Acc]); -transform_opts([{Key, Val}|Rest], Acc) when is_list(Val) -> - % Default to splitting a string on comma, that works fine for both flat - % lists of which there are many and single-items. - Val1 = case split_string(Val) of - [Val2] -> - Val2; - Val2 -> - Val2 - end, - transform_opts(Rest, [{Key, Val1}|Acc]); -transform_opts([{Key, Val}|Rest], Acc) -> - transform_opts(Rest, [{Key, Val}|Acc]). - -to_atoms(List) -> - lists:map(fun(X) -> list_to_atom(X) end, List). - -split_string(String) -> - string:tokens(String, ","). - -parse_term(String) -> - String1 = "[" ++ String ++ "].", - {ok, Tokens, _} = erl_scan:string(String1), - case erl_parse:parse_term(Tokens) of - {ok, [Terms]} -> - Terms; - Term -> - Term - end. diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index cdd3774..821a16e 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -17,7 +17,6 @@ multi_suite/1, all_suite/1, single_dir_and_single_suite/1, - symlinked_dir_overwritten_fix/1, data_dir_correct/1]). -include_lib("common_test/include/ct.hrl"). @@ -39,8 +38,7 @@ groups() -> [{basic_app, [], [basic_app_default_dirs, single_unmanaged_suite, multi_suite, all_suite, - single_dir_and_single_suite, - symlinked_dir_overwritten_fix]}, + single_dir_and_single_suite]}, {data_dirs, [], [data_dir_correct]}]. init_per_group(basic_app, Config) -> @@ -56,22 +54,14 @@ init_per_group(basic_app, Config) -> ok = filelib:ensure_dir(Suite), ok = file:write_file(Suite, test_suite(Name)), - {ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "compile"], return), + {ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "lock"], return), - LibDirs = rebar_dir:lib_dirs(State), - State1 = rebar_app_discover:do(State, LibDirs), - - Providers = rebar_state:providers(State1), - Namespace = rebar_state:namespace(State1), - CommandProvider = providers:get_provider(ct, Providers, Namespace), - GetOptSpec = providers:opts(CommandProvider), - {ok, GetOptResult} = getopt:parse(GetOptSpec, []), + Tests = rebar_prv_common_test:prepare_tests(State), + {ok, NewState} = rebar_prv_common_test:compile(State, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), - State2 = rebar_state:command_parsed_args(State1, GetOptResult), - - Result = rebar_prv_common_test:setup_ct(State2), - - [{result, Result}, {appnames, [Name]}|C]; + [{result, Opts}, {appnames, [Name]}|C]; init_per_group(multi_app, Config) -> C = rebar_test_utils:init_rebar_state(Config, "ct_"), @@ -99,22 +89,14 @@ init_per_group(multi_app, Config) -> ok = filelib:ensure_dir(Suite3), ok = file:write_file(Suite3, test_suite("extras")), - {ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "compile"], return), + {ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "lock"], return), - LibDirs = rebar_dir:lib_dirs(State), - State1 = rebar_app_discover:do(State, LibDirs), + Tests = rebar_prv_common_test:prepare_tests(State), + {ok, NewState} = rebar_prv_common_test:compile(State, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), - Providers = rebar_state:providers(State1), - Namespace = rebar_state:namespace(State1), - CommandProvider = providers:get_provider(ct, Providers, Namespace), - GetOptSpec = providers:opts(CommandProvider), - {ok, GetOptResult} = getopt:parse(GetOptSpec, []), - - State2 = rebar_state:command_parsed_args(State1, GetOptResult), - - Result = rebar_prv_common_test:setup_ct(State2), - - [{result, Result}, {appnames, [Name1, Name2]}|C]; + [{result, Opts}, {appnames, [Name1, Name2]}|C]; init_per_group(dirs_and_suites, Config) -> C = rebar_test_utils:init_rebar_state(Config, "ct_"), @@ -142,7 +124,9 @@ init_per_group(dirs_and_suites, Config) -> ok = filelib:ensure_dir(Suite3), ok = file:write_file(Suite3, test_suite("extras")), - [{appnames, [Name1, Name2]}|C]; + {ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "lock"], return), + + [{s, State}, {appnames, [Name1, Name2]}|C]; init_per_group(_, Config) -> Config. end_per_group(_Group, _Config) -> ok. @@ -152,10 +136,10 @@ basic_app_default_dirs(Config) -> [Name] = ?config(appnames, Config), Result = ?config(result, Config), - Expect = filename:absname(filename:join([AppDir, "_build", "test", "lib", Name, "test"])), + Expect = filename:join([AppDir, "_build", "test", "lib", Name, "test"]), Dir = proplists:get_value(dir, Result), - Expect = Dir. + [Expect] = Dir. basic_app_default_beams(Config) -> AppDir = ?config(apps, Config), @@ -178,7 +162,7 @@ multi_app_default_dirs(Config) -> Expect1 = filename:absname(filename:join([AppDir, "_build", "test", "lib", Name1, "test"])), Expect2 = filename:absname(filename:join([AppDir, "_build", "test", "lib", Name2, "test"])), - Expect3 = filename:absname(filename:join([AppDir, "_build", "test", "test"])), + Expect3 = filename:absname(filename:join([AppDir, "_build", "test", "extras", "test"])), Dirs = proplists:get_value(dir, Result), true = (lists:sort([Expect1, Expect2, Expect3]) == lists:sort(Dirs)). @@ -215,8 +199,7 @@ multi_app_default_beams(Config) -> single_app_dir(Config) -> AppDir = ?config(apps, Config), [Name1, _Name2] = ?config(appnames, Config), - - {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), + State = ?config(s, Config), LibDirs = rebar_dir:lib_dirs(State), State1 = rebar_app_discover:do(State, LibDirs), @@ -233,17 +216,19 @@ single_app_dir(Config) -> State2 = rebar_state:command_parsed_args(State1, GetOptResult), - Result = rebar_prv_common_test:setup_ct(State2), + Tests = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState} = rebar_prv_common_test:compile(State2, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), - Expect = filename:absname(filename:join([AppDir, "_build", "test", "lib", Name1, "test"])), - Dir = proplists:get_value(dir, Result), + Expect = filename:join([AppDir, "_build", "test", "lib", Name1, "test"]), + Dir = proplists:get_value(dir, Opts), - Expect = Dir. + [Expect] = Dir. single_extra_dir(Config) -> AppDir = ?config(apps, Config), - - {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), + State = ?config(s, Config), LibDirs = rebar_dir:lib_dirs(State), State1 = rebar_app_discover:do(State, LibDirs), @@ -257,22 +242,24 @@ single_extra_dir(Config) -> State2 = rebar_state:command_parsed_args(State1, GetOptResult), - Result = rebar_prv_common_test:setup_ct(State2), + Tests = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState} = rebar_prv_common_test:compile(State2, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), - Expect = filename:absname(filename:join([AppDir, "_build", "test", "test"])), - Dir = proplists:get_value(dir, Result), + Expect = filename:join([AppDir, "_build", "test", "extras", "test"]), + Dir = proplists:get_value(dir, Opts), - Expect = Dir. + [Expect] = Dir. single_unmanaged_dir(Config) -> PrivDir = ?config(priv_dir, Config), + State = ?config(s, Config), Suite = filename:join([PrivDir, "unmanaged_dir", "unmanaged_dir_SUITE.erl"]), ok = filelib:ensure_dir(Suite), ok = file:write_file(Suite, test_suite("unmanaged_dir")), - {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), - LibDirs = rebar_dir:lib_dirs(State), State1 = rebar_app_discover:do(State, LibDirs), @@ -285,18 +272,20 @@ single_unmanaged_dir(Config) -> State2 = rebar_state:command_parsed_args(State1, GetOptResult), - Result = rebar_prv_common_test:setup_ct(State2), + Tests = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState} = rebar_prv_common_test:compile(State2, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), - Expect = filename:absname(filename:join([PrivDir, "unmanaged_dir"])), - Dir = proplists:get_value(dir, Result), + Expect = filename:join([PrivDir, "unmanaged_dir"]), + Dir = proplists:get_value(dir, Opts), - Expect = Dir. + [Expect] = Dir. single_suite(Config) -> AppDir = ?config(apps, Config), [Name1, _Name2] = ?config(appnames, Config), - - {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), + State = ?config(s, Config), LibDirs = rebar_dir:lib_dirs(State), State1 = rebar_app_discover:do(State, LibDirs), @@ -314,24 +303,26 @@ single_suite(Config) -> State2 = rebar_state:command_parsed_args(State1, GetOptResult), - Result = rebar_prv_common_test:setup_ct(State2), + Tests = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState} = rebar_prv_common_test:compile(State2, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), - Expect = [filename:absname(filename:join([AppDir, - "_build", - "test", - "lib", - Name1, - "test", - Name1 ++ "_SUITE"]))], - Suite = proplists:get_value(suite, Result), + Expect = filename:join([AppDir, + "_build", + "test", + "lib", + Name1, + "test", + Name1 ++ "_SUITE"]), + Suite = proplists:get_value(suite, Opts), - Expect = Suite. + [Expect] = Suite. single_extra_suite(Config) -> AppDir = ?config(apps, Config), [_Name1, _Name2] = ?config(appnames, Config), - - {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), + State = ?config(s, Config), LibDirs = rebar_dir:lib_dirs(State), State1 = rebar_app_discover:do(State, LibDirs), @@ -347,27 +338,30 @@ single_extra_suite(Config) -> State2 = rebar_state:command_parsed_args(State1, GetOptResult), - Result = rebar_prv_common_test:setup_ct(State2), + Tests = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState} = rebar_prv_common_test:compile(State2, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), - Expect = [filename:absname(filename:join([AppDir, - "_build", - "test", - "test", - "extra_SUITE"]))], - Suite = proplists:get_value(suite, Result), + Expect = filename:join([AppDir, + "_build", + "test", + "extras", + "test", + "extra_SUITE"]), + Suite = proplists:get_value(suite, Opts), - Expect = Suite. + [Expect] = Suite. single_unmanaged_suite(Config) -> PrivDir = ?config(priv_dir, Config), [_Name1, _Name2] = ?config(appnames, Config), + State = ?config(s, Config), Suite = filename:join([PrivDir, "unmanaged", "unmanaged_SUITE.erl"]), ok = filelib:ensure_dir(Suite), ok = file:write_file(Suite, test_suite("unmanaged")), - {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), - LibDirs = rebar_dir:lib_dirs(State), State1 = rebar_app_discover:do(State, LibDirs), @@ -382,20 +376,22 @@ single_unmanaged_suite(Config) -> State2 = rebar_state:command_parsed_args(State1, GetOptResult), - Result = rebar_prv_common_test:setup_ct(State2), + Tests = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState} = rebar_prv_common_test:compile(State2, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), - Expect = [filename:absname(filename:join([PrivDir, - "unmanaged", - "unmanaged_SUITE"]))], - SuitePath = proplists:get_value(suite, Result), + Expect = filename:join([PrivDir, + "unmanaged", + "unmanaged_SUITE"]), + SuitePath = proplists:get_value(suite, Opts), - Expect = SuitePath. + [Expect] = SuitePath. multi_suite(Config) -> AppDir = ?config(apps, Config), [Name1, Name2] = ?config(appnames, Config), - - {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), + State = ?config(s, Config), LibDirs = rebar_dir:lib_dirs(State), State1 = rebar_app_discover:do(State, LibDirs), @@ -417,31 +413,33 @@ multi_suite(Config) -> State2 = rebar_state:command_parsed_args(State1, GetOptResult), - Result = rebar_prv_common_test:setup_ct(State2), - - Expect1 = filename:absname(filename:join([AppDir, - "_build", - "test", - "lib", - Name1, - "test", - Name1 ++ "_SUITE"])), - Expect2 = filename:absname(filename:join([AppDir, - "_build", - "test", - "lib", - Name2, - "test", - Name2 ++ "_SUITE"])), - Suites = proplists:get_value(suite, Result), + Tests = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState} = rebar_prv_common_test:compile(State2, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), + + Expect1 = filename:join([AppDir, + "_build", + "test", + "lib", + Name1, + "test", + Name1 ++ "_SUITE"]), + Expect2 = filename:join([AppDir, + "_build", + "test", + "lib", + Name2, + "test", + Name2 ++ "_SUITE"]), + Suites = proplists:get_value(suite, Opts), true = (lists:sort([Expect1, Expect2]) == lists:sort(Suites)). all_suite(Config) -> AppDir = ?config(apps, Config), [Name1, Name2] = ?config(appnames, Config), - - {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), + State = ?config(s, Config), LibDirs = rebar_dir:lib_dirs(State), State1 = rebar_app_discover:do(State, LibDirs), @@ -465,36 +463,39 @@ all_suite(Config) -> State2 = rebar_state:command_parsed_args(State1, GetOptResult), - Result = rebar_prv_common_test:setup_ct(State2), - - Expect1 = filename:absname(filename:join([AppDir, - "_build", - "test", - "lib", - Name1, - "test", - Name1 ++ "_SUITE"])), - Expect2 = filename:absname(filename:join([AppDir, - "_build", - "test", - "lib", - Name2, - "test", - Name2 ++ "_SUITE"])), - Expect3 = filename:absname(filename:join([AppDir, - "_build", - "test", - "test", - "extra_SUITE"])), - Suites = proplists:get_value(suite, Result), + Tests = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState} = rebar_prv_common_test:compile(State2, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), + + Expect1 = filename:join([AppDir, + "_build", + "test", + "lib", + Name1, + "test", + Name1 ++ "_SUITE"]), + Expect2 = filename:join([AppDir, + "_build", + "test", + "lib", + Name2, + "test", + Name2 ++ "_SUITE"]), + Expect3 = filename:join([AppDir, + "_build", + "test", + "extras", + "test", + "extra_SUITE"]), + Suites = proplists:get_value(suite, Opts), true = (lists:sort([Expect1, Expect2, Expect3]) == lists:sort(Suites)). single_dir_and_single_suite(Config) -> AppDir = ?config(apps, Config), [_Name1, _Name2] = ?config(appnames, Config), - - {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), + State = ?config(s, Config), LibDirs = rebar_dir:lib_dirs(State), State1 = rebar_app_discover:do(State, LibDirs), @@ -509,45 +510,21 @@ single_dir_and_single_suite(Config) -> State2 = rebar_state:command_parsed_args(State1, GetOptResult), - Result = rebar_prv_common_test:setup_ct(State2), - - Expect = [filename:absname(filename:join([AppDir, - "_build", - "test", - "test", - "extra_SUITE"]))], - Suite = proplists:get_value(suite, Result), - - Expect = Suite. - -symlinked_dir_overwritten_fix(Config) -> - AppDir = ?config(apps, Config), - [Name1, _Name2] = ?config(appnames, Config), - - {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), - - LibDirs = rebar_dir:lib_dirs(State), - State1 = rebar_app_discover:do(State, LibDirs), - - Providers = rebar_state:providers(State1), - Namespace = rebar_state:namespace(State1), - CommandProvider = providers:get_provider(ct, Providers, Namespace), - GetOptSpec = providers:opts(CommandProvider), - {ok, GetOptResult} = getopt:parse(GetOptSpec, - ["--dir=" ++ filename:join([AppDir, - "apps", - Name1])]), - - State2 = rebar_state:command_parsed_args(State1, GetOptResult), - - Result = rebar_prv_common_test:setup_ct(State2), - - Expect = filename:absname(filename:join([AppDir, "_build", "test", "lib", Name1])), - Dir = proplists:get_value(dir, Result), - - Expect = Dir, - - {ok, _} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return). + Tests = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState} = rebar_prv_common_test:compile(State2, Tests), + {ok, T} = Tests, + Opts = rebar_prv_common_test:translate_paths(NewState, T), + + Expect = filename:join([AppDir, + "_build", + "test", + "extras", + "test"]), + Dir = proplists:get_value(dir, Opts), + [Expect] = Dir, + + Suite = proplists:get_value(suite, Opts), + ["extra_SUITE"] = Suite. %% this test probably only fails when this suite is run via rebar3 with the --cover flag data_dir_correct(Config) -> -- cgit v1.1 From f73307bb65bfe83685836ef5f39efcfe5467ef3b Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Fri, 30 Oct 2015 00:17:53 -0700 Subject: support atom suites in `ct_tests` --- src/rebar_prv_common_test.erl | 46 +++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 910a1ca..ede88dc 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -74,14 +74,12 @@ do(State, Tests) -> run_tests(State, Opts) -> T = translate_paths(State, Opts), Opts1 = setup_logdir(State, T), - %% strip test spec if present - Opts2 = strip_test_spec(Opts1), - ?DEBUG("ct_opts ~p", [Opts2]), + ?DEBUG("ct_opts ~p", [Opts1]), {RawOpts, _} = rebar_state:command_parsed_args(State), ok = maybe_write_coverdata(State), case proplists:get_value(verbose, RawOpts, false) of - true -> run_test_verbose(Opts2); - false -> run_test_quiet(Opts2) + true -> run_test_verbose(Opts1); + false -> run_test_quiet(Opts1) end. -spec format_error(any()) -> iolist(). @@ -101,16 +99,16 @@ format_error({multiple_errors, Errors}) -> prepare_tests(State) -> %% command line test options - CmdOpts = opts(State), + CmdOpts = cmdopts(State), %% rebar.config test options - CfgOpts = rebar_state:get(State, ct_tests, []), + CfgOpts = cfgopts(State), ProjectApps = rebar_state:project_apps(State), %% prioritize tests to run first trying any command line specified %% tests falling back to tests specified in the config file finally %% running a default set if no other tests are present select_tests(State, ProjectApps, CmdOpts, CfgOpts). -opts(State) -> +cmdopts(State) -> {RawOpts, _} = rebar_state:command_parsed_args(State), %% filter out opts common_test doesn't know about and convert %% to ct acceptable forms @@ -148,6 +146,23 @@ transform_opts([Opt|Rest], Acc) -> split_string(String) -> string:tokens(String, [$,]). +cfgopts(State) -> + Opts = rebar_state:get(State, ct_tests, []), + rebar_utils:filtermap(fun filter_opts/1, Opts). + +filter_opts({test_spec, _}) -> + ?WARN("Test specs not supported", []), + false; +filter_opts({suite, Suite}) when is_integer(hd(Suite)) -> true; +filter_opts({suite, Suite}) when is_atom(Suite) -> + {true, {suite, atom_to_list(Suite)}}; +filter_opts({suite, Suites}) -> + {true, {suite, lists:map(fun(S) when is_atom(S) -> atom_to_list(S); + (S) when is_list(S) -> S + end, + Suites)}}; +filter_opts(_) -> true. + select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> Merged = lists:ukeymerge(1, lists:ukeysort(1, CmdOpts), @@ -240,10 +255,15 @@ test_dirs(State, Apps, Opts) -> case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of {Suites, undefined} -> set_compile_dirs(State, Apps, {suite, Suites}); {undefined, Dirs} -> set_compile_dirs(State, Apps, {dir, Dirs}); - {Suites, [Dir]} -> set_compile_dirs(State, Apps, join(Suites, Dir)); + {Suites, Dir} when is_integer(hd(Dir)) -> + set_compile_dirs(State, Apps, join(Suites, Dir)); + {Suites, [Dir]} when is_integer(hd(Dir)) -> + set_compile_dirs(State, Apps, join(Suites, Dir)); {_Suites, _Dirs} -> {error, "Only a single directory may be specified when specifying suites"} end. +join(Suite, Dir) when is_integer(hd(Suite)) -> + {suite, [filename:join([Dir, Suite])]}; join(Suites, Dir) -> {suite, lists:map(fun(S) -> filename:join([Dir, S]) end, Suites)}. @@ -350,14 +370,6 @@ setup_logdir(State, Opts) -> filelib:ensure_dir(filename:join([Logdir, "dummy.beam"])), [{logdir, Logdir}|lists:keydelete(logdir, 1, Opts)]. -strip_test_spec(Opts) -> - case proplists:get_value(test_spec, Opts) of - undefined -> Opts; - _ -> - ?WARN("Test specs not supported", []), - lists:keydelete(test_spec, 1, Opts) - end. - run_test_verbose(Opts) -> handle_results(ct:run_test(Opts)). run_test_quiet(Opts) -> -- cgit v1.1 From 94169fbdf27bbba9c56313e7b32c743ea95be047 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Sun, 1 Nov 2015 18:49:06 -0800 Subject: reenable support for most command line options and rename `ct_tests` to `ct_opts` --- src/rebar_prv_common_test.erl | 43 ++++- test/rebar_ct_SUITE.erl | 403 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 435 insertions(+), 11 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index ede88dc..7af2819 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -125,14 +125,16 @@ transform_opts([{testcase, Cases}|Rest], Acc) -> transform_opts(Rest, [{testcase, split_string(Cases)}|Acc]); transform_opts([{config, Configs}|Rest], Acc) -> transform_opts(Rest, [{config, split_string(Configs)}|Acc]); -transform_opts([{include, Includes}|Rest], Acc) -> - transform_opts(Rest, [{include, split_string(Includes)}|Acc]); +transform_opts([{logopts, LogOpts}|Rest], Acc) -> + transform_opts(Rest, [{logopts, lists:map(fun(P) -> list_to_atom(P) end, split_string(LogOpts))}|Acc]); transform_opts([{force_stop, "true"}|Rest], Acc) -> transform_opts(Rest, [{force_stop, true}|Acc]); transform_opts([{force_stop, "false"}|Rest], Acc) -> transform_opts(Rest, [{force_stop, false}|Acc]); transform_opts([{force_stop, "skip_rest"}|Rest], Acc) -> transform_opts(Rest, [{force_stop, skip_rest}|Acc]); +transform_opts([{create_priv_dir, CreatePrivDir}|Rest], Acc) -> + transform_opts(Rest, [{create_priv_dir, list_to_atom(CreatePrivDir)}|Acc]); %% drop cover from opts, ct doesn't care about it transform_opts([{cover, _}|Rest], Acc) -> transform_opts(Rest, Acc); @@ -147,7 +149,7 @@ split_string(String) -> string:tokens(String, [$,]). cfgopts(State) -> - Opts = rebar_state:get(State, ct_tests, []), + Opts = rebar_state:get(State, ct_opts, []), rebar_utils:filtermap(fun filter_opts/1, Opts). filter_opts({test_spec, _}) -> @@ -164,9 +166,10 @@ filter_opts({suite, Suites}) -> filter_opts(_) -> true. select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> + FixedOpts = lists:filter(fun({_, _}) -> true; (V) -> ?WARN("`~p` is not a valid option for `ct_opts`", [V]) end, CfgOpts), Merged = lists:ukeymerge(1, lists:ukeysort(1, CmdOpts), - lists:ukeysort(1, CfgOpts)), + lists:ukeysort(1, FixedOpts)), %% make sure `dir` and/or `suite` from command line go in as %% a pair overriding both `dir` and `suite` from config if %% they exist @@ -464,13 +467,21 @@ ct_opts(_State) -> {config, undefined, "config", string, help(config)}, %% comma-seperated list {allow_user_terms, undefined, "allow_user_terms", boolean, help(allow_user_terms)}, %% Bool {logdir, undefined, "logdir", string, help(logdir)}, %% dir + {logopts, undefined, "logopts", string, help(logopts)}, %% comma seperated list {verbosity, undefined, "verbosity", integer, help(verbosity)}, %% Integer {cover, $c, "cover", {boolean, false}, help(cover)}, - {include, undefined, "include", string, help(include)}, % comma-seperated list {repeat, undefined, "repeat", integer, help(repeat)}, %% integer {duration, undefined, "duration", string, help(duration)}, % format: HHMMSS {until, undefined, "until", string, help(until)}, %% format: YYMoMoDD[HHMMSS] - {basic_html, undefined, "basic_html", boolean, help(basic_html)}, %% Booloean + {force_stop, undefined, "force_stop", string, help(force_stop)}, %% String + {basic_html, undefined, "basic_html", boolean, help(basic_html)}, %% Boolean + {stylesheet, undefined, "stylesheet", string, help(stylesheet)}, %% String + {decrypt_key, undefined, "decrypt_key", string, help(decrypt_key)}, %% String + {decrypt_file, undefined, "decrypt_file", string, help(decrypt_file)}, %% String + {abort_if_missing_suites, undefined, "abort_if_missing_suites", {boolean, true}, help(abort_if_missing_suites)}, %% Boolean + {multiply_timetraps, undefined, "multiply_timetraps", integer, help(multiple_timetraps)}, %% Integer + {scale_timetraps, undefined, "scale_timetraps", boolean, help(scale_timetraps)}, + {create_priv_dir, undefined, "create_priv_dir", string, help(create_priv_dir)}, {verbose, $v, "verbose", boolean, help(verbose)} ]. @@ -490,20 +501,36 @@ help(allow_user_terms) -> "Allow user defined config values in config files"; help(logdir) -> "Log folder"; +help(logopts) -> + "Options for common test logging"; help(verbosity) -> "Verbosity"; help(cover) -> "Generate cover data"; -help(include) -> - "Include folders"; help(repeat) -> "How often to repeat tests"; help(duration) -> "Max runtime (format: HHMMSS)"; help(until) -> "Run until (format: HHMMSS)"; +help(force_stop) -> + "Force stop on test timeout (true | false | skip_rest)"; help(basic_html) -> "Show basic HTML"; +help(stylesheet) -> + "CSS stylesheet to apply to html output"; +help(decrypt_key) -> + "Path to key for decrypting config"; +help(decrypt_file) -> + "Path to file containing key for decrypting config"; +help(abort_if_missing_suites) -> + "Abort if suites are missing"; +help(multiply_timetraps) -> + "Multiply timetraps"; +help(scale_timetraps) -> + "Scale timetraps"; +help(create_priv_dir) -> + "Create priv dir (auto_per_run | auto_per_tc | manual_per_tc)"; help(verbose) -> "Verbose output"; help(_) -> diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index 821a16e..ac016b1 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -17,14 +17,40 @@ multi_suite/1, all_suite/1, single_dir_and_single_suite/1, - data_dir_correct/1]). + data_dir_correct/1, + cmd_label/1, + cmd_config/1, + cmd_allow_user_terms/1, + cmd_logdir/1, + cmd_logopts/1, + cmd_verbosity/1, + cmd_repeat/1, + cmd_duration/1, + cmd_until/1, + cmd_force_stop/1, + cmd_basic_html/1, + cmd_stylesheet/1, + cmd_decrypt_key/1, + cmd_decrypt_file/1, + cmd_abort_if_missing_suites/1, + cmd_multiply_timetraps/1, + cmd_scale_timetraps/1, + cmd_create_priv_dir/1, + cfg_opts/1, + cfg_arbitrary_opts/1, + cfg_test_spec_filtered/1, + cfg_atom_suites/1]). -include_lib("common_test/include/ct.hrl"). all() -> [{group, basic_app}, {group, multi_app}, {group, dirs_and_suites}, - {group, data_dirs}]. + {group, data_dirs}, + {group, ct_opts}, + cfg_opts, cfg_arbitrary_opts, + cfg_test_spec_filtered, + cfg_atom_suites]. groups() -> [{basic_app, [], [basic_app_default_dirs, basic_app_default_beams]}, @@ -39,7 +65,25 @@ groups() -> [{basic_app, [], [basic_app_default_dirs, multi_suite, all_suite, single_dir_and_single_suite]}, - {data_dirs, [], [data_dir_correct]}]. + {data_dirs, [], [data_dir_correct]}, + {ct_opts, [], [cmd_label, + cmd_config, + cmd_allow_user_terms, + cmd_logdir, + cmd_logopts, + cmd_verbosity, + cmd_repeat, + cmd_duration, + cmd_until, + cmd_force_stop, + cmd_basic_html, + cmd_stylesheet, + cmd_decrypt_key, + cmd_decrypt_file, + cmd_abort_if_missing_suites, + cmd_multiply_timetraps, + cmd_scale_timetraps, + cmd_create_priv_dir]}]. init_per_group(basic_app, Config) -> C = rebar_test_utils:init_rebar_state(Config, "ct_"), @@ -127,6 +171,18 @@ init_per_group(dirs_and_suites, Config) -> {ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "lock"], return), [{s, State}, {appnames, [Name1, Name2]}|C]; +init_per_group(ct_opts, Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_opts"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_opts_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + {ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "lock"], return), + + [{result, State}|C]; init_per_group(_, Config) -> Config. end_per_group(_Group, _Config) -> ok. @@ -532,6 +588,347 @@ data_dir_correct(Config) -> Parts = filename:split(DataDir), ["rebar_ct_SUITE_data","test","rebar","lib","test","_build"|_] = lists:reverse(Parts). +cmd_label(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--label=this_is_a_label"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({label, "this_is_a_label"}, TestOpts). + +cmd_config(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--config=config/foo,config/bar,config/baz"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({config, ["config/foo", "config/bar", "config/baz"]}, TestOpts). + +cmd_allow_user_terms(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--allow_user_terms=true"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({allow_user_terms, true}, TestOpts). + +cmd_logdir(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--logdir=/tmp/ct_logs"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({logdir, "/tmp/ct_logs"}, TestOpts). + +cmd_logopts(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--logopts=no_src,no_nl"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({logopts, [no_src, no_nl]}, TestOpts). + +cmd_verbosity(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--verbosity=43"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({verbosity, 43}, TestOpts). + +cmd_repeat(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--repeat=3"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({repeat, 3}, TestOpts). + +cmd_duration(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--duration=001500"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({duration, "001500"}, TestOpts). + +cmd_until(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--until=001500"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({until, "001500"}, TestOpts). + +cmd_force_stop(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--force_stop=skip_rest"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({force_stop, skip_rest}, TestOpts). + +cmd_basic_html(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--basic_html"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({basic_html, true}, TestOpts). + +cmd_stylesheet(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--stylesheet=resources/tests.css"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({stylesheet, "resources/tests.css"}, TestOpts). + +cmd_decrypt_key(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--decrypt_key==ac467e30"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({decrypt_key, "=ac467e30"}, TestOpts). + +cmd_decrypt_file(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--decrypt_file=../keyfile.pem"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({decrypt_file, "../keyfile.pem"}, TestOpts). + +cmd_abort_if_missing_suites(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--abort_if_missing_suites"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({abort_if_missing_suites, true}, TestOpts). + +cmd_multiply_timetraps(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--multiply_timetraps=3"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({multiply_timetraps, 3}, TestOpts). + +cmd_scale_timetraps(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--scale_timetraps"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({scale_timetraps, true}, TestOpts). + +cmd_create_priv_dir(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--create_priv_dir=manual_per_tc"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({create_priv_dir, manual_per_tc}, TestOpts). + +cfg_opts(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_opts_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_cfg_opts_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{ct_opts, [{label, "this_is_a_label"}, {decrypt_file, "../keyfile.pem"}]}], + + {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(State), + + true = lists:member({label, "this_is_a_label"}, TestOpts), + true = lists:member({decrypt_file, "../keyfile.pem"}, TestOpts). + +%% allow even nonsensical opts to be passed to ct_run for futureproofing +cfg_arbitrary_opts(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_arbitrary_opts_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_cfg_arbitrary_opts_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{ct_opts, [{foo, 1}, {bar, 2}, {baz, 3}]}], + + {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(State), + + true = lists:member({foo, 1}, TestOpts), + true = lists:member({bar, 2}, TestOpts), + true = lists:member({baz, 3}, TestOpts). + +cfg_test_spec_filtered(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_test_spec_filtered_opts_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_cfg_test_spec_filtered_opts_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{ct_opts, [{test_spec, "spec/foo.spec"}]}], + + {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(State), + + false = lists:keysearch(test_spec, 1, TestOpts). + +cfg_atom_suites(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_atom_suites_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_cfg_atom_suites_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{ct_opts, [{suite, [foo, bar, baz]}]}], + + {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(State), + + true = lists:member({suite, ["foo", "bar", "baz"]}, TestOpts). %% helper for generating test data test_suite(Name) -> -- cgit v1.1 From cf66dfd6ba65bc6f2926ce3b6b67f195a3faa715 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Sun, 1 Nov 2015 18:49:41 -0800 Subject: adjust shell completions to match available command line options --- priv/shell-completion/bash/rebar3 | 23 +++++++++-------------- priv/shell-completion/fish/rebar3.fish | 22 ++++++++++++++++++++-- priv/shell-completion/zsh/_rebar3 | 24 +++++++++--------------- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/priv/shell-completion/bash/rebar3 b/priv/shell-completion/bash/rebar3 index 87ee9eb..771297c 100644 --- a/priv/shell-completion/bash/rebar3 +++ b/priv/shell-completion/bash/rebar3 @@ -54,11 +54,7 @@ _rebar3() --suite \ --group \ --case \ - --spec \ - --join_specs \ - --label \ --config \ - --userconfig \ --allow_user_terms \ --logdir \ --logopts \ @@ -66,21 +62,20 @@ _rebar3() --silent_connections \ --stylesheet \ --cover \ - --cover_spec \ - --cover_stop \ - --event_handler \ - --include \ - --abort_if_missing_suites \ - --multiply_timetraps \ - --scale_timetraps \ - --create_priv_dir \ --repeat \ --duration \ --until \ --force_stop \ --basic_html \ - --ct_hooks \ - --verbose" + --stylesheet \ + --decrypt_key \ + --decrypt_file \ + --abort_if_missing_suites \ + --multiply_timetraps \ + --scale_timetraps \ + --create_priv_dir \ + --verbose" \ + --auto_compile elif [[ ${prev} == deps ]] ; then : elif [[ ${prev} == dialyzer ]] ; then diff --git a/priv/shell-completion/fish/rebar3.fish b/priv/shell-completion/fish/rebar3.fish index 0d5d302..7b63e20 100644 --- a/priv/shell-completion/fish/rebar3.fish +++ b/priv/shell-completion/fish/rebar3.fish @@ -90,10 +90,28 @@ complete -f -c 'rebar3' -n '__fish_rebar3_needs_command' -a ct -d "Run Common Te complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l dir -d "Compile and run all test suites in the specified directories." complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l suites -d "Compile and run all test suites specified. Must be specified by full path, either absolute or relative to the current directory." complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l group -d "Test groups to run." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l label -d "Test label." complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l config -d "Config files to use when running tests." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l allow_user_terms -d "Allow user defined terms in config files." complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l logdir -d "The directory in which test logs will be written. Default: _build/test/logs" -complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -s v -l verbose -d "Enable verbose output. Default: false" -complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -s c -l cover -d "Generate cover data" +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l logopts -d "Options for common test logging." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l verbosity -d "Verbosity." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -s c -l cover -d "Generate cover data." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l include -d "Include folders." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l repeat -d "How often to repeat tests." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l duration -d "Max runtime (format: HHMMSS)." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l until -d "Run until (format: HHMMSS)." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l force_stop -d "Force stop on test timeout." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l basic_html -d "Show basic HTML." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l stylesheet -d "CSS stylesheet to apply to html output." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l decrypt_key -d "Path to key for decrypting config." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l decrypt_file -d "Path to file containing key for decrypting config." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l abort_if_missing_suites -d "Abort if suites are missing." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l multiply_timetraps -d "Multiply timetraps." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l scale_timetraps -d "Scale timetraps." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l create_priv_dir -d "Create priv dir (auto_per_run | auto_per_tc | manual_per_tc)." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -s v -l verbose -d "Enable verbose output. Default: false." +complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l auto_compile -d "Let common test compile test suites instead of rebar3." complete -f -c 'rebar3' -n '__fish_rebar3_needs_command' -a deps -d "List dependencies" diff --git a/priv/shell-completion/zsh/_rebar3 b/priv/shell-completion/zsh/_rebar3 index d4e1c35..f0fb351 100644 --- a/priv/shell-completion/zsh/_rebar3 +++ b/priv/shell-completion/zsh/_rebar3 @@ -43,33 +43,27 @@ _rebar3 () { '(--suite)--suite[List of test suites to run]:suites' \ '(--group)--group[List of test groups to run]:groups' \ '(--case)--case[List of test cases to run]:cases' \ - '(--spec)--spec[List of test specs to run]:specs' \ - '(--join_specs)--join_specs' \ '(--label)--label[Test label]:label' \ '(--config)--config[List of config files]:config files:_files' \ - '(--userconfig)--userconfig' \ '(--allow_user_terms)--allow_user_terms' \ '(--logdir)--logdir[Log folder]:log folder:_files -/' \ '(--logopts)--logopts' \ '(--verbosity)--verbosity[Verbosity]:verbosity' \ - '(--silent_connections)--silent_connections' \ - '(--stylesheet)--stylesheet[Stylesheet to use for test results]:stylesheet:_files' \ '(-c --cover)'{-c,--cover}'[Generate cover data]' \ - '(--cover_spec)--cover_spec[Cover file to use]:cover file:_files' \ - '(--cover_stop)--cover_stop' \ - '(--event_handler)--event_handler[Event handlers to attach to the runner]:event handlers' \ - '(--include)--include[Include folder]:include directory:_files -/' \ - '(--abort_if_missing_suites)--abort_if_missing_suites[Abort if suites are missing]:abort missing suites:(true false)' \ - '(--multiply_timetraps)--multiply_timetraps' \ - '(--scale_timetraps)--scale_timetraps' \ - '(--create_priv_dir)--create_priv_dir' \ '(--repeat)--repeat[How often to repeat tests]:repeat test count' \ '(--duration)--duration[Max runtime (format: HHMMSS)]:max run time' \ '(--until)--until[Run until (format: HHMMSS)]:run until time' \ - '(--force_stop)--force_stop[Force stop after time]' \ + '(--force_stop)--force_stop[Force stop on test timeout]:skip_rest' \ '(--basic_html)--basic_html[Show basic HTML]' \ - '(--ct_hooks)--ct_hooks:ct hooks' \ + '(--stylesheet)--stylesheet[Stylesheet to use for test results]:stylesheet:_files' \ + '(--decrypt_key)--decrypt_key[Path to key for decrypting config]:decrypt key:_files' \ + '(--decrypt_file)--decrypt_file[Path to file containing key for decrypting config]:decrypt file:_files' \ + '(--abort_if_missing_suites)--abort_if_missing_suites[Abort if suites are missing]:abort missing suites:(true false)' \ + '(--multiply_timetraps)--multiply_timetraps' \ + '(--scale_timetraps)--scale_timetraps' \ + '(--create_priv_dir)--create_priv_dir' \ '(-v --verbose)'{-v,--verbose}'[Print coverage analysis]' \ + '(--auto_compile)--auto_compile' \ && ret=0 ;; (deps) -- cgit v1.1 From 80f5bc61526f50e2fe3d4642faea6abff43cbd96 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Mon, 2 Nov 2015 07:06:06 -0800 Subject: add warning about `auto_compile` --- src/rebar_prv_common_test.erl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 7af2819..7a088e5 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -74,12 +74,13 @@ do(State, Tests) -> run_tests(State, Opts) -> T = translate_paths(State, Opts), Opts1 = setup_logdir(State, T), - ?DEBUG("ct_opts ~p", [Opts1]), + Opts2 = turn_off_auto_compile(Opts1), + ?DEBUG("ct_opts ~p", [Opts2]), {RawOpts, _} = rebar_state:command_parsed_args(State), ok = maybe_write_coverdata(State), case proplists:get_value(verbose, RawOpts, false) of - true -> run_test_verbose(Opts1); - false -> run_test_quiet(Opts1) + true -> run_test_verbose(Opts2); + false -> run_test_quiet(Opts2) end. -spec format_error(any()) -> iolist(). @@ -155,6 +156,9 @@ cfgopts(State) -> filter_opts({test_spec, _}) -> ?WARN("Test specs not supported", []), false; +filter_opts({auto_compile, _}) -> + ?WARN("Auto compile not supported", []), + false; filter_opts({suite, Suite}) when is_integer(hd(Suite)) -> true; filter_opts({suite, Suite}) when is_atom(Suite) -> {true, {suite, atom_to_list(Suite)}}; @@ -373,6 +377,9 @@ setup_logdir(State, Opts) -> filelib:ensure_dir(filename:join([Logdir, "dummy.beam"])), [{logdir, Logdir}|lists:keydelete(logdir, 1, Opts)]. +turn_off_auto_compile(Opts) -> + [{auto_compile, false}|lists:keydelete(auto_compile, 1, Opts)]. + run_test_verbose(Opts) -> handle_results(ct:run_test(Opts)). run_test_quiet(Opts) -> -- cgit v1.1 From 0461729fd923f01796510f93f03ad6c848e6c73e Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Thu, 5 Nov 2015 00:38:06 -0800 Subject: calculate coverage info as late as possible in ct provider execution --- src/rebar_prv_common_test.erl | 7 ++++--- test/rebar_ct_SUITE.erl | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 7a088e5..977a5f6 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -77,11 +77,12 @@ run_tests(State, Opts) -> Opts2 = turn_off_auto_compile(Opts1), ?DEBUG("ct_opts ~p", [Opts2]), {RawOpts, _} = rebar_state:command_parsed_args(State), - ok = maybe_write_coverdata(State), - case proplists:get_value(verbose, RawOpts, false) of + Result = case proplists:get_value(verbose, RawOpts, false) of true -> run_test_verbose(Opts2); false -> run_test_quiet(Opts2) - end. + end, + ok = maybe_write_coverdata(State), + Result. -spec format_error(any()) -> iolist(). format_error({error, Reason}) -> diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index ac016b1..c4fc4dc 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -39,7 +39,8 @@ cfg_opts/1, cfg_arbitrary_opts/1, cfg_test_spec_filtered/1, - cfg_atom_suites/1]). + cfg_atom_suites/1, + cover_compiled/1]). -include_lib("common_test/include/ct.hrl"). @@ -48,6 +49,7 @@ all() -> [{group, basic_app}, {group, dirs_and_suites}, {group, data_dirs}, {group, ct_opts}, + {group, cover}, cfg_opts, cfg_arbitrary_opts, cfg_test_spec_filtered, cfg_atom_suites]. @@ -83,7 +85,8 @@ groups() -> [{basic_app, [], [basic_app_default_dirs, cmd_abort_if_missing_suites, cmd_multiply_timetraps, cmd_scale_timetraps, - cmd_create_priv_dir]}]. + cmd_create_priv_dir]}, + {cover, [], [cover_compiled]}]. init_per_group(basic_app, Config) -> C = rebar_test_utils:init_rebar_state(Config, "ct_"), @@ -183,6 +186,18 @@ init_per_group(ct_opts, Config) -> {ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "lock"], return), [{result, State}|C]; +init_per_group(cover, Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_opts"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_opts_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + {ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "lock"], return), + + [{result, State}, {name, Name}|C]; init_per_group(_, Config) -> Config. end_per_group(_Group, _Config) -> ok. @@ -930,6 +945,25 @@ cfg_atom_suites(Config) -> true = lists:member({suite, ["foo", "bar", "baz"]}, TestOpts). +cover_compiled(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--cover"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + Tests = rebar_prv_common_test:prepare_tests(NewState), + {ok, _} = rebar_prv_common_test:compile(NewState, Tests), + + Name = ?config(name, Config), + Mod = list_to_atom(Name), + {file, _} = cover:is_compiled(Mod). + + %% helper for generating test data test_suite(Name) -> io_lib:format("-module(~ts_SUITE).\n" -- cgit v1.1 From 6f673c592e89e3308528cbcfdf51abd384eac05a Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Fri, 6 Nov 2015 09:27:29 -0600 Subject: upgrade relx to 3.8.0, fixes bug in upgrade_install escript --- rebar.config | 2 +- rebar.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index eb3dd78..c7bf381 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,7 @@ {providers, "1.5.0"}, {getopt, "0.8.2"}, {bbmustache, "1.0.4"}, - {relx, "3.7.1"}, + {relx, "3.8.0"}, {cf, "0.1.3"}]}. {escript_name, rebar3}. diff --git a/rebar.lock b/rebar.lock index 6ee294f..88685e2 100644 --- a/rebar.lock +++ b/rebar.lock @@ -4,5 +4,5 @@ {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.16.0">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.5.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.7.1">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.8.0">>},0}, {<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}]. -- cgit v1.1 From d2cd2710b11d9bc4af5055f8bb614d41fb5a3a42 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Sat, 7 Nov 2015 09:01:48 -0600 Subject: do not sort mib_first_files during merge_opts, order must be kept --- src/rebar_opts.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rebar_opts.erl b/src/rebar_opts.erl index 47451c5..97f39b8 100644 --- a/src/rebar_opts.erl +++ b/src/rebar_opts.erl @@ -111,6 +111,10 @@ merge_opts(NewOpts, OldOpts) -> NewValue; (profiles, NewValue, OldValue) -> dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue))); + (mib_first_files, Value, Value) -> + Value; + (mib_first_files, NewValue, OldValue) -> + OldValue ++ NewValue; (_Key, NewValue, OldValue) when is_list(NewValue) -> case io_lib:printable_list(NewValue) of true when NewValue =:= [] -> -- cgit v1.1 From 9184a42ef32f1ed06f9f2e3c8c0d4e949cc0c579 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Thu, 12 Nov 2015 19:16:08 -0600 Subject: fix typo of guarantee. h/t evan --- src/rebar_packages.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl index 7be3372..99391ed 100644 --- a/src/rebar_packages.erl +++ b/src/rebar_packages.erl @@ -138,7 +138,7 @@ handle_single_vsn(Dep, Vsn, Constraint) -> {ok, Vsn}; false -> ?WARN("Only existing version of ~s is ~s which does not match constraint ~~> ~s. " - "Using anyway, but it is not guarenteed to work.", [Dep, Vsn, Constraint]), + "Using anyway, but it is not guaranteed to work.", [Dep, Vsn, Constraint]), {ok, Vsn} end. -- cgit v1.1 From d31e663a67a483b6a740f973f52db41fd01e5861 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 13 Nov 2015 14:13:59 +0000 Subject: Prettify all of common test output. This uses cth_readable to: - silence error_logger output to the shell unless a test fails - silence ct:pal output to the shell unless a test fails I have currently not baked in any way to disable this behaviour, but I figured if it is required, there is time to do it before the final 3.0.0 release. --- rebar.config | 3 ++- rebar.lock | 1 + src/rebar_prv_common_test.erl | 21 +++++++++++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index c7bf381..31d65df 100644 --- a/rebar.config +++ b/rebar.config @@ -8,7 +8,8 @@ {getopt, "0.8.2"}, {bbmustache, "1.0.4"}, {relx, "3.8.0"}, - {cf, "0.1.3"}]}. + {cf, "0.1.3"}, + {cth_readable, "1.0.0"}]}. {escript_name, rebar3}. {escript_emu_args, "%%! +sbtu +A0\n"}. diff --git a/rebar.lock b/rebar.lock index 88685e2..36d4802 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,6 +1,7 @@ [{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, {<<"certifi">>,{pkg,<<"certifi">>,<<"0.1.1">>},0}, {<<"cf">>,{pkg,<<"cf">>,<<"0.1.3">>},0}, + {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.0.0">>},0}, {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.16.0">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.5.0">>},0}, diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 977a5f6..525ddaf 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -152,7 +152,7 @@ split_string(String) -> cfgopts(State) -> Opts = rebar_state:get(State, ct_opts, []), - rebar_utils:filtermap(fun filter_opts/1, Opts). + add_hooks(rebar_utils:filtermap(fun filter_opts/1, Opts)). filter_opts({test_spec, _}) -> ?WARN("Test specs not supported", []), @@ -170,6 +170,18 @@ filter_opts({suite, Suites}) -> Suites)}}; filter_opts(_) -> true. +add_hooks(Opts) -> + %% cth_readable hooks + case lists:keyfind(ct_hooks, 1, Opts) of + false -> + [{ct_hooks, [cth_readable_failonly, cth_readable_shell]} | Opts]; + {ct_hooks, Hooks} -> + %% Make sure hooks are there once only. + ReadableHooks = [cth_readable_failonly, cth_readable_shell], + NewHooks = (Hooks -- ReadableHooks) ++ ReadableHooks, + lists:keyreplace(ct_hooks, 1, Opts, {ct_hooks, NewHooks}) + end. + select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> FixedOpts = lists:filter(fun({_, _}) -> true; (V) -> ?WARN("`~p` is not a valid option for `ct_opts`", [V]) end, CfgOpts), Merged = lists:ukeymerge(1, @@ -249,7 +261,7 @@ inject(Opts, State) -> %% append `ct_compile_opts` to app defined `erl_opts` ErlOpts = rebar_opts:get(Opts, erl_opts, []), CTOpts = rebar_state:get(State, ct_compile_opts, []), - NewErlOpts = CTOpts ++ ErlOpts, + NewErlOpts = add_transforms(CTOpts) ++ ErlOpts, %% append `ct_first_files` to app defined `erl_first_files` FirstFiles = rebar_opts:get(Opts, erl_first_files, []), CTFirstFiles = rebar_state:get(State, ct_first_files, []), @@ -259,6 +271,11 @@ inject(Opts, State) -> Opts, [{erl_opts, NewErlOpts}, {erl_first_files, NewFirstFiles}]). +add_transforms(CTOpts) -> + ReadableTransform = [{parse_transform, cth_readable_transform}], + (CTOpts -- ReadableTransform) ++ ReadableTransform. + + test_dirs(State, Apps, Opts) -> case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of {Suites, undefined} -> set_compile_dirs(State, Apps, {suite, Suites}); -- cgit v1.1 From 2cb1aca6859c7f1d0e0e4687be82ad7628fbe33a Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 13 Nov 2015 14:46:50 +0000 Subject: Work in R15 --- rebar.config | 2 +- rebar.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 31d65df..f19d81d 100644 --- a/rebar.config +++ b/rebar.config @@ -9,7 +9,7 @@ {bbmustache, "1.0.4"}, {relx, "3.8.0"}, {cf, "0.1.3"}, - {cth_readable, "1.0.0"}]}. + {cth_readable, "1.0.1"}]}. {escript_name, rebar3}. {escript_emu_args, "%%! +sbtu +A0\n"}. diff --git a/rebar.lock b/rebar.lock index 36d4802..1d0ab42 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ [{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, {<<"certifi">>,{pkg,<<"certifi">>,<<"0.1.1">>},0}, {<<"cf">>,{pkg,<<"cf">>,<<"0.1.3">>},0}, - {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.0.0">>},0}, + {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.0.1">>},0}, {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.16.0">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.5.0">>},0}, -- cgit v1.1 From d4981b7913e63247a0db0a92742e7937fd804334 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 13 Nov 2015 16:59:48 +0000 Subject: Add cth_readable to dep list --- src/rebar.app.src | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rebar.app.src b/src/rebar.app.src index 5ab3ddd..14c08c9 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -25,6 +25,7 @@ bbmustache, ssl_verify_hostname, certifi, + cth_readable, relx, inets]}, {env, [ -- cgit v1.1 From b9c15df8736a8af737fc7fff5eb6f15d2789ea9a Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 13 Nov 2015 17:07:45 +0000 Subject: Optionally disable readable output --- src/rebar_prv_common_test.erl | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 525ddaf..7a65dd5 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -152,7 +152,7 @@ split_string(String) -> cfgopts(State) -> Opts = rebar_state:get(State, ct_opts, []), - add_hooks(rebar_utils:filtermap(fun filter_opts/1, Opts)). + add_hooks(rebar_utils:filtermap(fun filter_opts/1, Opts), State). filter_opts({test_spec, _}) -> ?WARN("Test specs not supported", []), @@ -170,12 +170,13 @@ filter_opts({suite, Suites}) -> Suites)}}; filter_opts(_) -> true. -add_hooks(Opts) -> - %% cth_readable hooks - case lists:keyfind(ct_hooks, 1, Opts) of - false -> +add_hooks(Opts, State) -> + case {readable(State), lists:keyfind(ct_hooks, 1, Opts)} of + {false, _} -> + Opts; + {true, false} -> [{ct_hooks, [cth_readable_failonly, cth_readable_shell]} | Opts]; - {ct_hooks, Hooks} -> + {true, {ct_hooks, Hooks}} -> %% Make sure hooks are there once only. ReadableHooks = [cth_readable_failonly, cth_readable_shell], NewHooks = (Hooks -- ReadableHooks) ++ ReadableHooks, @@ -261,7 +262,7 @@ inject(Opts, State) -> %% append `ct_compile_opts` to app defined `erl_opts` ErlOpts = rebar_opts:get(Opts, erl_opts, []), CTOpts = rebar_state:get(State, ct_compile_opts, []), - NewErlOpts = add_transforms(CTOpts) ++ ErlOpts, + NewErlOpts = add_transforms(CTOpts, State) ++ ErlOpts, %% append `ct_first_files` to app defined `erl_first_files` FirstFiles = rebar_opts:get(Opts, erl_first_files, []), CTFirstFiles = rebar_state:get(State, ct_first_files, []), @@ -271,10 +272,22 @@ inject(Opts, State) -> Opts, [{erl_opts, NewErlOpts}, {erl_first_files, NewFirstFiles}]). -add_transforms(CTOpts) -> - ReadableTransform = [{parse_transform, cth_readable_transform}], - (CTOpts -- ReadableTransform) ++ ReadableTransform. +add_transforms(CTOpts, State) -> + case readable(State) of + true -> + ReadableTransform = [{parse_transform, cth_readable_transform}], + (CTOpts -- ReadableTransform) ++ ReadableTransform; + false -> + CTOpts + end. +readable(State) -> + {RawOpts, _} = rebar_state:command_parsed_args(State), + case proplists:get_value(readable, RawOpts) of + true -> true; + false -> false; + undefined -> rebar_state:get(State, ct_readable, true) + end. test_dirs(State, Apps, Opts) -> case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of @@ -507,6 +520,7 @@ ct_opts(_State) -> {multiply_timetraps, undefined, "multiply_timetraps", integer, help(multiple_timetraps)}, %% Integer {scale_timetraps, undefined, "scale_timetraps", boolean, help(scale_timetraps)}, {create_priv_dir, undefined, "create_priv_dir", string, help(create_priv_dir)}, + {readable, undefined, "readable", boolean, help(readable)}, {verbose, $v, "verbose", boolean, help(verbose)} ]. @@ -556,6 +570,8 @@ help(scale_timetraps) -> "Scale timetraps"; help(create_priv_dir) -> "Create priv dir (auto_per_run | auto_per_tc | manual_per_tc)"; +help(readable) -> + "Shows test case names and only displays logs to shell on failures"; help(verbose) -> "Verbose output"; help(_) -> -- cgit v1.1 From bca4d4070311afb035a72f976edbe4d8023e1517 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Fri, 13 Nov 2015 10:30:13 -0600 Subject: include Sean Cribbs eunit formatter by default --- rebar.config | 3 ++- rebar.lock | 1 + src/rebar.app.src | 3 ++- src/rebar_prv_eunit.erl | 17 ++++++++++++++--- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/rebar.config b/rebar.config index f19d81d..dcae904 100644 --- a/rebar.config +++ b/rebar.config @@ -9,7 +9,8 @@ {bbmustache, "1.0.4"}, {relx, "3.8.0"}, {cf, "0.1.3"}, - {cth_readable, "1.0.1"}]}. + {cth_readable, "1.0.1"}, + {eunit_formatters, "0.2.0"}]}. {escript_name, rebar3}. {escript_emu_args, "%%! +sbtu +A0\n"}. diff --git a/rebar.lock b/rebar.lock index 1d0ab42..14e80d6 100644 --- a/rebar.lock +++ b/rebar.lock @@ -3,6 +3,7 @@ {<<"cf">>,{pkg,<<"cf">>,<<"0.1.3">>},0}, {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.0.1">>},0}, {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.16.0">>},0}, + {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.2.0">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.5.0">>},0}, {<<"relx">>,{pkg,<<"relx">>,<<"3.8.0">>},0}, diff --git a/src/rebar.app.src b/src/rebar.app.src index 14c08c9..67973eb 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -27,7 +27,8 @@ certifi, cth_readable, relx, - inets]}, + inets, + eunit_formatters]}, {env, [ %% Default log level {log_level, warn}, diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index d5612e8..0f9976b 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -304,9 +304,20 @@ validate_module(_State, Module) -> resolve_eunit_opts(State) -> {Opts, _} = rebar_state:command_parsed_args(State), EUnitOpts = rebar_state:get(State, eunit_opts, []), - case proplists:get_value(verbose, Opts, false) of - true -> set_verbose(EUnitOpts); - false -> EUnitOpts + EUnitOpts1 = case proplists:get_value(verbose, Opts, false) of + true -> set_verbose(EUnitOpts); + false -> EUnitOpts + end, + case proplists:get_value(eunit_formatters, Opts, true) of + true -> custom_eunit_formatters(EUnitOpts1); + false -> EUnitOpts1 + end. + +custom_eunit_formatters(Opts) -> + %% If `report` is already set then treat that like `eunit_formatters` is false + case lists:keymember(report, 1, Opts) of + true -> Opts; + false -> [no_tty, {report, {eunit_progress, [colored, profile]}} | Opts] end. set_verbose(Opts) -> -- cgit v1.1 From 87d9edc537d2af620f64e453797040b5ec7d0aca Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Fri, 13 Nov 2015 13:01:24 -0600 Subject: add space before end so eyes don't bleed --- src/rebar_prv_eunit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 0f9976b..1884f02 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -307,7 +307,7 @@ resolve_eunit_opts(State) -> EUnitOpts1 = case proplists:get_value(verbose, Opts, false) of true -> set_verbose(EUnitOpts); false -> EUnitOpts - end, + end, case proplists:get_value(eunit_formatters, Opts, true) of true -> custom_eunit_formatters(EUnitOpts1); false -> EUnitOpts1 -- cgit v1.1 From 748142838cd11ce5cd532dd598d220cab8424f75 Mon Sep 17 00:00:00 2001 From: Geoff Cant Date: Fri, 13 Nov 2015 13:18:09 -0800 Subject: Feature: rebar shell [--script ] Adds the ability to run an escript before starting the apps and interactive shell for a project. This is intended to improve the local development experience for projects by providing an easy way to run companion services (mock rest APIs, databases etc) that the project relies on. This patch also adds {shell, Defaults} to the rebar config file so that a project can supply default values for many of the new or improved 'rebar3 shell' options: * {apps, OTPApps} * {script_file, EscriptFileName} * {config, ConfigFileName} The order of option precedence is command line, rebar.config, relx. --- src/rebar_prv_shell.erl | 160 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 131 insertions(+), 29 deletions(-) diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 4cf1e04..d93a21f 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -56,12 +56,23 @@ init(State) -> {short_desc, "Run shell with project apps and deps in path."}, {desc, info()}, {opts, [{config, undefined, "config", string, - "Path to the config file to use. Defaults to the " - "sys_config defined for relx, if present."}, + "Path to the config file to use. Defaults to " + "{shell, [{config, File}]} and then the relx " + "sys.config file if not specified."}, {name, undefined, "name", atom, "Gives a long name to the node."}, {sname, undefined, "sname", atom, - "Gives a short name to the node."}]} + "Gives a short name to the node."}, + {script_file, undefined, "script", string, + "Path to an escript file to run before " + "starting the project apps. Defaults to " + "rebar.config {shell, [{script_file, File}]} " + "if not specified."}, + {apps, undefined, "apps", string, + "A list of apps to boot before starting the " + "shell. (E.g. --apps app1,app2,app3) Defaults " + "to rebar.config {shell, [{apps, Apps}]} or " + "relx apps if not specified."}]} ]) ), {ok, State1}. @@ -86,6 +97,7 @@ shell(State) -> setup_name(State), setup_paths(State), setup_shell(), + maybe_run_script(State), %% apps must be started after the change in shell because otherwise %% their application masters never gets the new group leader (held in %% their internal state) @@ -134,6 +146,51 @@ setup_paths(State) -> %% add project app test paths ok = add_test_paths(State). +maybe_run_script(State) -> + case first_value([fun find_script_option/1, + fun find_script_rebar/1], State) of + no_value -> + ?DEBUG("No script_file specified.", []), + ok; + "none" -> + ?DEBUG("Shell script execution skipped (--script none).", []), + ok; + RelFile -> + File = filename:absname(RelFile), + try run_script_file(File) + catch + C:E -> + ?ABORT("Couldn't run shell escript ~p - ~p:~p~nStack: ~p", + [File, C, E, erlang:get_stacktrace()]) + end + end. + +-spec find_script_option(rebar_state:t()) -> no_value | list(). +find_script_option(State) -> + {Opts, _} = rebar_state:command_parsed_args(State), + debug_get_value(script_file, Opts, no_value, + "Found script file from command line option."). + +-spec find_script_rebar(rebar_state:t()) -> no_value | list(). +find_script_rebar(State) -> + Config = rebar_state:get(State, shell, []), + %% Either a string, or undefined + debug_get_value(script_file, Config, no_value, + "Found script file from rebar config file."). + +run_script_file(File) -> + ?DEBUG("Extracting escript from ~p", [File]), + {ok, Script} = escript:extract(File, [compile_source]), + Beam = proplists:get_value(source, Script), + Mod = proplists:get_value(module, beam_lib:info(Beam)), + ?DEBUG("Compiled escript as ~p", [Mod]), + FakeFile = "/fake_path/" ++ atom_to_list(Mod), + {module, Mod} = code:load_binary(Mod, FakeFile, Beam), + ?DEBUG("Evaling ~p:main([]).", [Mod]), + Result = Mod:main([]), + ?DEBUG("Result: ~p", [Result]), + Result. + maybe_boot_apps(State) -> case find_apps_to_boot(State) of undefined -> @@ -172,17 +229,42 @@ check_epmd(_) -> find_apps_to_boot(State) -> %% Try the shell_apps option - case rebar_state:get(State, shell_apps, undefined) of - undefined -> - %% Get to the relx tuple instead - case lists:keyfind(release, 1, rebar_state:get(State, relx, [])) of - {_, _, Apps} -> Apps; - false -> undefined - end; + case first_value([fun find_apps_option/1, + fun find_apps_rebar/1, + fun find_apps_relx/1], State) of + no_value -> + undefined; Apps -> Apps end. +-spec find_apps_option(rebar_state:t()) -> no_value | [atom()]. +find_apps_option(State) -> + {Opts, _} = rebar_state:command_parsed_args(State), + case debug_get_value(apps, Opts, no_value, + "Found shell apps from command line option.") of + no_value -> no_value; + AppsStr -> + [ list_to_atom(AppStr) + || AppStr <- string:tokens(AppsStr, " ,:") ] + end. + +-spec find_apps_rebar(rebar_state:t()) -> no_value | list(). +find_apps_rebar(State) -> + ShellOpts = rebar_state:get(State, shell, []), + debug_get_value(apps, ShellOpts, no_value, + "Found shell opts from command line option."). + +-spec find_apps_relx(rebar_state:t()) -> no_value | list(). +find_apps_relx(State) -> + case lists:keyfind(release, 1, rebar_state:get(State, relx, [])) of + {_, _, Apps} -> + ?DEBUG("Found shell apps from relx.", []), + Apps; + false -> + no_value + end. + load_apps(Apps) -> [case application:load(App) of ok -> @@ -261,31 +343,51 @@ add_test_paths(State) -> % First try the --config flag, then try the relx sys_config -spec find_config(rebar_state:t()) -> [tuple()] | no_config. find_config(State) -> - case find_config_option(State) of - no_config -> - find_config_relx(State); - Result -> - Result + case first_value([fun find_config_option/1, + fun find_config_rebar/1, + fun find_config_relx/1], State) of + no_value -> + no_config; + Filename when is_list(Filename) -> + consult_config(State, Filename) + end. + +-spec first_value([Fun], State) -> no_value | Value when + Value :: any(), + State :: rebar_state:t(), + Fun :: fun ((State) -> no_value | Value). +first_value([], _) -> no_value; +first_value([Fun | Rest], State) -> + case Fun(State) of + no_value -> + first_value(Rest, State); + Value -> + Value end. --spec find_config_option(rebar_state:t()) -> [tuple()] | no_config. +debug_get_value(Key, List, Default, Description) -> + case proplists:get_value(Key, List, Default) of + Default -> Default; + Value -> + ?DEBUG(Description, []), + Value + end. + +-spec find_config_option(rebar_state:t()) -> Filename::list() | no_value. find_config_option(State) -> {Opts, _} = rebar_state:command_parsed_args(State), - case proplists:get_value(config, Opts) of - undefined -> - no_config; - Filename -> - consult_config(State, Filename) - end. + debug_get_value(config, Opts, no_value, + "Found config from command line option."). --spec find_config_relx(rebar_state:t()) -> [tuple()] | no_config. +-spec find_config_rebar(rebar_state:t()) -> [tuple()] | no_value. +find_config_rebar(State) -> + debug_get_value(config, rebar_state:get(State, shell, []), no_value, + "Found config from rebar config file."). + +-spec find_config_relx(rebar_state:t()) -> [tuple()] | no_value. find_config_relx(State) -> - case proplists:get_value(sys_config, rebar_state:get(State, relx, [])) of - undefined -> - no_config; - Filename -> - consult_config(State, Filename) - end. + debug_get_value(sys_config, rebar_state:get(State, relx, []), no_value, + "Found config from relx."). -spec consult_config(rebar_state:t(), string()) -> [tuple()]. consult_config(State, Filename) -> -- cgit v1.1 From 5f7351d78473634e717c956362d233b8000854c4 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 14 Nov 2015 14:56:56 +0000 Subject: Fix IO locking up in shell apps Application masters are booted at the root of apps, and take over the group leader role to redirect IO. To cut the chain short and properly have their role inherited, they are their own leader, and keep a reference to the old leader in their internal state, which we cannot change. This is done so process ownership to a given application can be established, and allows to properly clean up resources outside the supervision tree when an app is shut down. This patch goes around and finds all processes whose group leaders are application masters older than the new `user' process booted by the shell providers, and swaps them with that new `user'. This lets the application masters survive, and fixes the blocking IO issue (resolving issue #899) This may mean an incomplete clean up is down on application shutdown, but that seems like a fair compromise. --- src/rebar_prv_shell.erl | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index d93a21f..c644930 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -113,20 +113,34 @@ info() -> setup_shell() -> %% scan all processes for any with references to the old user and save them to %% update later - NeedsUpdate = [Pid || Pid <- erlang:processes(), - proplists:get_value(group_leader, erlang:process_info(Pid)) == whereis(user) - ], + OldUser = whereis(user), %% terminate the current user ok = supervisor:terminate_child(kernel_sup, user), %% start a new shell (this also starts a new user under the correct group) _ = user_drv:start(), %% wait until user_drv and user have been registered (max 3 seconds) ok = wait_until_user_started(3000), + NewUser = whereis(user), %% set any process that had a reference to the old user's group leader to the %% new user process. Catch the race condition when the Pid exited after the %% liveness check. - _ = [catch erlang:group_leader(whereis(user), Pid) || Pid <- NeedsUpdate, - is_process_alive(Pid)], + _ = [catch erlang:group_leader(NewUser, Pid) + || Pid <- erlang:processes(), + proplists:get_value(group_leader, erlang:process_info(Pid)) == OldUser, + is_process_alive(Pid)], + %% Application masters have the same problem, but they hold the old group + %% leader in their state and hold on to it. Re-point the processes whose + %% leaders are application masters. This can mess up a few things around + %% shutdown time, but is nicer than the current lock-up. + OldMasters = [Pid + || Pid <- erlang:processes(), + Pid < NewUser, % only change old masters + {_,Dict} <- [erlang:process_info(Pid, dictionary)], + {application_master,init,4} == proplists:get_value('$initial_call', Dict)], + _ = [catch erlang:group_leader(NewUser, Pid) + || Pid <- erlang:processes(), + lists:member(proplists:get_value(group_leader, erlang:process_info(Pid)), + OldMasters)], try %% enable error_logger's tty output error_logger:swap_handler(tty), -- cgit v1.1 From 50802d9f2ee054181ba45131911bde43808d4f71 Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Sat, 14 Nov 2015 17:28:42 +0100 Subject: Update cf --- rebar.config | 2 +- rebar.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index dcae904..4fc3fa7 100644 --- a/rebar.config +++ b/rebar.config @@ -8,7 +8,7 @@ {getopt, "0.8.2"}, {bbmustache, "1.0.4"}, {relx, "3.8.0"}, - {cf, "0.1.3"}, + {cf, "0.2.1"}, {cth_readable, "1.0.1"}, {eunit_formatters, "0.2.0"}]}. diff --git a/rebar.lock b/rebar.lock index 14e80d6..02f429e 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,6 +1,6 @@ [{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, {<<"certifi">>,{pkg,<<"certifi">>,<<"0.1.1">>},0}, - {<<"cf">>,{pkg,<<"cf">>,<<"0.1.3">>},0}, + {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.0.1">>},0}, {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.16.0">>},0}, {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.2.0">>},0}, -- cgit v1.1 From 356ac5033d12b8b91ed0d6c4d308b00070ab12b9 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 14 Nov 2015 15:07:56 -0500 Subject: Bump to beta-4 --- README.md | 2 +- src/rebar.app.src | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 69fd4f6..cddb843 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ configuration work. rebar also provides dependency management, enabling application writers to easily re-use common libraries from a variety of locations ([hex.pm](http://hex.pm), git, hg, and so on). -3.0 Beta-3 +3.0 Beta-4 ==== [DOCUMENTATION](http://www.rebar3.org/v3.0/docs) diff --git a/src/rebar.app.src b/src/rebar.app.src index 67973eb..0314218 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "git"}, + {vsn, "3.0.0-beta.4"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From cc11b7bcecfa80f5670cf5fb9e6aa41bb2f1eb54 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 14 Nov 2015 15:23:39 -0500 Subject: Back to git versions for source --- src/rebar.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar.app.src b/src/rebar.app.src index 0314218..67973eb 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "3.0.0-beta.4"}, + {vsn, "git"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From f84c51b37a63c406ac3051b2e9538593a212e174 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 14 Nov 2015 21:42:26 +0000 Subject: Fix error reports on missing include paths In some cases (nested includes?) paths end up in such a way that joining them breaks up and hard-crashes rebar3. This patch specifically handles this scenario to fix things by avoiding passing empty lists to filename:join. --- src/rebar_erlc_compiler.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 57b7387..fb5796c 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -688,7 +688,7 @@ warn_and_find_path(File, Dir) -> true -> [SrcHeader]; false -> - IncludeDir = filename:join(filename:join(rebar_utils:droplast(filename:split(Dir))), "include"), + IncludeDir = filename:join(rebar_utils:droplast(filename:split(Dir))++["include"]), IncludeHeader = filename:join(IncludeDir, File), case filelib:is_regular(IncludeHeader) of true -> -- cgit v1.1 From aea9809bdbeb9d7f1aa2805398d5ae1011ac4836 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Sat, 14 Nov 2015 17:46:46 -0800 Subject: warn on incorrectly specified test options in `rebar.config` when `ct_opts`, `eunit_tests`, `eunit_first_files`, `ct_first_files`, `erl_first_files`, `eunit_compile_opts`, `ct_compile_opts` and `erl_opts` have values that are single non-list terms warn and try wrapping them in a list when processing them in the `eunit` and `ct` providers --- src/rebar_prv_common_test.erl | 27 +++++++++++++++++++++------ src/rebar_prv_eunit.erl | 34 ++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 7a65dd5..608261a 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -151,8 +151,15 @@ split_string(String) -> string:tokens(String, [$,]). cfgopts(State) -> - Opts = rebar_state:get(State, ct_opts, []), - add_hooks(rebar_utils:filtermap(fun filter_opts/1, Opts), State). + case rebar_state:get(State, ct_opts, []) of + Opts when is_list(Opts) -> + add_hooks(rebar_utils:filtermap(fun filter_opts/1, Opts), State); + Wrong -> + %% probably a single non list term, try wrapping it in a list and + %% continuing + ?WARN("Value `~p' of option `ct_opts' is not a list, trying to adjust and continue", [Wrong]), + add_hooks(rebar_utils:filtermap(fun filter_opts/1, [Wrong]), State) + end. filter_opts({test_spec, _}) -> ?WARN("Test specs not supported", []), @@ -258,14 +265,22 @@ inject_ct_state(State, Tests) -> NewState = rebar_state:opts(State, NewOpts), test_dirs(NewState, ModdedApps, Tests). +opts(Opts, Key, Default) -> + case rebar_opts:get(Opts, Key, Default) of + Vs when is_list(Vs) -> Vs; + Wrong -> + ?WARN("Value `~p' of option `~p' is not a list, trying to adjust and continue", [Wrong, Key]), + [Wrong] + end. + inject(Opts, State) -> %% append `ct_compile_opts` to app defined `erl_opts` - ErlOpts = rebar_opts:get(Opts, erl_opts, []), - CTOpts = rebar_state:get(State, ct_compile_opts, []), + ErlOpts = opts(Opts, erl_opts, []), + CTOpts = opts(Opts, ct_compile_opts, []), NewErlOpts = add_transforms(CTOpts, State) ++ ErlOpts, %% append `ct_first_files` to app defined `erl_first_files` - FirstFiles = rebar_opts:get(Opts, erl_first_files, []), - CTFirstFiles = rebar_state:get(State, ct_first_files, []), + FirstFiles = opts(Opts, erl_first_files, []), + CTFirstFiles = opts(Opts, ct_first_files, []), NewFirstFiles = CTFirstFiles ++ FirstFiles, %% insert the new keys into the opts lists:foldl(fun({K, V}, NewOpts) -> rebar_opts:set(NewOpts, K, V) end, diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 1884f02..b754d87 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -121,21 +121,29 @@ compile(_State, Error) -> Error. inject_eunit_state(State, Tests) -> Apps = rebar_state:project_apps(State), ModdedApps = lists:map(fun(App) -> - NewOpts = inject(rebar_app_info:opts(App), State), + NewOpts = inject(rebar_app_info:opts(App)), rebar_app_info:opts(App, NewOpts) end, Apps), - NewOpts = inject(rebar_state:opts(State), State), + NewOpts = inject(rebar_state:opts(State)), NewState = rebar_state:opts(State, NewOpts), test_dirs(NewState, ModdedApps, Tests). -inject(Opts, State) -> +opts(Opts, Key, Default) -> + case rebar_opts:get(Opts, Key, Default) of + Vs when is_list(Vs) -> Vs; + Wrong -> + ?WARN("Value ~p of option `~p' is not a list, trying to adjust and continue", [Wrong, Key]), + [Wrong] + end. + +inject(Opts) -> %% append `eunit_compile_opts` to app defined `erl_opts` - ErlOpts = rebar_opts:get(Opts, erl_opts, []), - EUnitOpts = rebar_state:get(State, eunit_compile_opts, []), + ErlOpts = opts(Opts, erl_opts, []), + EUnitOpts = opts(Opts, eunit_compile_opts, []), NewErlOpts = EUnitOpts ++ ErlOpts, %% append `eunit_first_files` to app defined `erl_first_files` - FirstFiles = rebar_opts:get(Opts, erl_first_files, []), - EUnitFirstFiles = rebar_state:get(State, eunit_first_files, []), + FirstFiles = opts(Opts, erl_first_files, []), + EUnitFirstFiles = opts(Opts, eunit_first_files, []), NewFirstFiles = EUnitFirstFiles ++ FirstFiles, %% insert the new keys into the opts lists:foldl(fun({K, V}, NewOpts) -> rebar_opts:set(NewOpts, K, V) end, @@ -180,13 +188,23 @@ inject_test_dir(Opts, Dir) -> prepare_tests(State) -> %% parse and translate command line tests CmdTests = resolve_tests(State), - CfgTests = rebar_state:get(State, eunit_tests, []), + CfgTests = cfg_tests(State), ProjectApps = rebar_state:project_apps(State), %% prioritize tests to run first trying any command line specified %% tests falling back to tests specified in the config file finally %% running a default set if no other tests are present select_tests(State, ProjectApps, CmdTests, CfgTests). +cfg_tests(State) -> + case rebar_state:get(State, eunit_tests, []) of + Tests when is_list(Tests) -> Tests; + Wrong -> + %% probably a single non list term, try wrapping it in a list and + %% continuing + ?WARN("Value `~p' of option `eunit_tests' is not a list, trying to adjust and continue", [Wrong]), + [Wrong] + end. + resolve_tests(State) -> {RawOpts, _} = rebar_state:command_parsed_args(State), Apps = resolve(app, application, RawOpts), -- cgit v1.1 From 25914c35086beca01aaf879c5227adba7dfe1037 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Sun, 15 Nov 2015 15:52:19 -0800 Subject: error on ct/eunit argument errors instead of warning --- src/rebar_prv_common_test.erl | 113 ++++++++++++++--------- src/rebar_prv_eunit.erl | 210 +++++++++++++++++++++++------------------- test/rebar_ct_SUITE.erl | 78 ++++++++++++++-- test/rebar_eunit_SUITE.erl | 65 ++++++++++++- 4 files changed, 318 insertions(+), 148 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 608261a..05a1dc6 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -91,6 +91,10 @@ format_error({error_running_tests, Reason}) -> format_error({error, Reason}); format_error({failures_running_tests, {Failed, AutoSkipped}}) -> io_lib:format("Failures occured running tests: ~b", [Failed+AutoSkipped]); +format_error({badconfig, {Msg, {Value, Key}}}) -> + io_lib:format(Msg, [Value, Key]); +format_error({badconfig, Msg}) -> + io_lib:format(Msg, []); format_error({multiple_errors, Errors}) -> io_lib:format(lists:concat(["Error running tests:"] ++ lists:map(fun(Error) -> "~n " ++ Error end, Errors)), []). @@ -153,29 +157,31 @@ split_string(String) -> cfgopts(State) -> case rebar_state:get(State, ct_opts, []) of Opts when is_list(Opts) -> - add_hooks(rebar_utils:filtermap(fun filter_opts/1, Opts), State); + ensure_opts(add_hooks(Opts, State), []); Wrong -> - %% probably a single non list term, try wrapping it in a list and - %% continuing - ?WARN("Value `~p' of option `ct_opts' is not a list, trying to adjust and continue", [Wrong]), - add_hooks(rebar_utils:filtermap(fun filter_opts/1, [Wrong]), State) + %% probably a single non list term + ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, ct_opts}}}) end. -filter_opts({test_spec, _}) -> - ?WARN("Test specs not supported", []), - false; -filter_opts({auto_compile, _}) -> - ?WARN("Auto compile not supported", []), - false; -filter_opts({suite, Suite}) when is_integer(hd(Suite)) -> true; -filter_opts({suite, Suite}) when is_atom(Suite) -> - {true, {suite, atom_to_list(Suite)}}; -filter_opts({suite, Suites}) -> - {true, {suite, lists:map(fun(S) when is_atom(S) -> atom_to_list(S); - (S) when is_list(S) -> S - end, - Suites)}}; -filter_opts(_) -> true. +ensure_opts([], Acc) -> lists:reverse(Acc); +ensure_opts([{test_spec, _}|_Rest], _Acc) -> + ?PRV_ERROR({badconfig, "Test specs not supported"}); +ensure_opts([{auto_compile, _}|_Rest], _Acc) -> + ?PRV_ERROR({badconfig, "Auto compile not supported"}); +ensure_opts([{suite, Suite}|Rest], Acc) when is_integer(hd(Suite)) -> + ensure_opts(Rest, [{suite, Suite}|Acc]); +ensure_opts([{suite, Suite}|Rest], Acc) when is_atom(Suite) -> + ensure_opts(Rest, [{suite, atom_to_list(Suite)}|Acc]); +ensure_opts([{suite, Suites}|Rest], Acc) -> + NewSuites = {suite, lists:map(fun(S) when is_atom(S) -> atom_to_list(S); + (S) when is_list(S) -> S + end, + Suites)}, + ensure_opts(Rest, [NewSuites|Acc]); +ensure_opts([{K, V}|Rest], Acc) -> + ensure_opts(Rest, [{K, V}|Acc]); +ensure_opts([V|_Rest], _Acc) -> + ?PRV_ERROR({badconfig, {"Member `~p' of option `~p' must be a 2-tuple", {V, ct_opts}}}). add_hooks(Opts, State) -> case {readable(State), lists:keyfind(ct_hooks, 1, Opts)} of @@ -190,11 +196,12 @@ add_hooks(Opts, State) -> lists:keyreplace(ct_hooks, 1, Opts, {ct_hooks, NewHooks}) end. +select_tests(_, _, {error, _} = Error, _) -> Error; +select_tests(_, _, _, {error, _} = Error) -> Error; select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> - FixedOpts = lists:filter(fun({_, _}) -> true; (V) -> ?WARN("`~p` is not a valid option for `ct_opts`", [V]) end, CfgOpts), Merged = lists:ukeymerge(1, lists:ukeysort(1, CmdOpts), - lists:ukeysort(1, FixedOpts)), + lists:ukeysort(1, CfgOpts)), %% make sure `dir` and/or `suite` from command line go in as %% a pair overriding both `dir` and `suite` from config if %% they exist @@ -235,7 +242,7 @@ application_dirs([App|Rest], Acc) -> false -> application_dirs(Rest, Acc) end. -compile(State, {ok, Tests}) -> +compile(State, {ok, _} = Tests) -> %% inject `ct_first_files` and `ct_compile_opts` into the applications %% to be compiled case inject_ct_state(State, Tests) of @@ -255,46 +262,68 @@ do_compile(State) -> Error -> Error end. -inject_ct_state(State, Tests) -> +inject_ct_state(State, {ok, Tests}) -> Apps = rebar_state:project_apps(State), - ModdedApps = lists:map(fun(App) -> - NewOpts = inject(rebar_app_info:opts(App), State), - rebar_app_info:opts(App, NewOpts) - end, Apps), - NewOpts = inject(rebar_state:opts(State), State), - NewState = rebar_state:opts(State, NewOpts), - test_dirs(NewState, ModdedApps, Tests). + case inject_ct_state(State, Apps, []) of + {ok, {NewState, ModdedApps}} -> + test_dirs(NewState, ModdedApps, Tests); + {error, _} = Error -> Error + end; +inject_ct_state(_State, Error) -> Error. + +inject_ct_state(State, [App|Rest], Acc) -> + case inject(rebar_app_info:opts(App), State) of + {error, _} = Error -> Error; + NewOpts -> + NewApp = rebar_app_info:opts(App, NewOpts), + inject_ct_state(State, Rest, [NewApp|Acc]) + end; +inject_ct_state(State, [], Acc) -> + case inject(rebar_state:opts(State), State) of + {error, _} = Error -> Error; + NewOpts -> {ok, {rebar_state:opts(State, NewOpts), lists:reverse(Acc)}} + end. opts(Opts, Key, Default) -> case rebar_opts:get(Opts, Key, Default) of Vs when is_list(Vs) -> Vs; Wrong -> - ?WARN("Value `~p' of option `~p' is not a list, trying to adjust and continue", [Wrong, Key]), - [Wrong] + ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, Key}}}) end. -inject(Opts, State) -> +inject(Opts, State) -> erl_opts(Opts, State). + +erl_opts(Opts, State) -> %% append `ct_compile_opts` to app defined `erl_opts` ErlOpts = opts(Opts, erl_opts, []), CTOpts = opts(Opts, ct_compile_opts, []), - NewErlOpts = add_transforms(CTOpts, State) ++ ErlOpts, + case add_transforms(append(CTOpts, ErlOpts), State) of + {error, Error} -> {error, Error}; + NewErlOpts -> first_files(rebar_opts:set(Opts, erl_opts, NewErlOpts)) + end. + +first_files(Opts) -> %% append `ct_first_files` to app defined `erl_first_files` FirstFiles = opts(Opts, erl_first_files, []), CTFirstFiles = opts(Opts, ct_first_files, []), - NewFirstFiles = CTFirstFiles ++ FirstFiles, - %% insert the new keys into the opts - lists:foldl(fun({K, V}, NewOpts) -> rebar_opts:set(NewOpts, K, V) end, - Opts, - [{erl_opts, NewErlOpts}, {erl_first_files, NewFirstFiles}]). + case append(CTFirstFiles, FirstFiles) of + {error, _} = Error -> Error; + NewFirstFiles -> rebar_opts:set(Opts, erl_first_files, NewFirstFiles) + end. + +append({error, _} = Error, _) -> Error; +append(_, {error, _} = Error) -> Error; +append(A, B) -> A ++ B. -add_transforms(CTOpts, State) -> +add_transforms(CTOpts, State) when is_list(CTOpts) -> case readable(State) of true -> ReadableTransform = [{parse_transform, cth_readable_transform}], (CTOpts -- ReadableTransform) ++ ReadableTransform; false -> CTOpts - end. + end; +add_transforms({error, _} = Error, _State) -> Error. readable(State) -> {RawOpts, _} = rebar_state:command_parsed_args(State), diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index b754d87..9af2965 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -9,7 +9,7 @@ do/1, format_error/1]). %% exported solely for tests --export([compile/2, prepare_tests/1, eunit_opts/1, validate_tests/2]). +-export([prepare_tests/1, eunit_opts/1, validate_tests/2]). -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). @@ -39,10 +39,12 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> Tests = prepare_tests(State), - case compile(State, Tests) of + %% inject `eunit_first_files`, `eunit_compile_opts` and any + %% directories required by tests into the applications + NewState = inject_eunit_state(State, Tests), + case compile(NewState) of %% successfully compiled apps {ok, S} -> do(S, Tests); - %% this should look like a compiler error, not an eunit error Error -> Error end. @@ -95,6 +97,8 @@ format_error({error_running_tests, Reason}) -> format_error({eunit_test_errors, Errors}) -> io_lib:format(lists:concat(["Error Running EUnit Tests:"] ++ lists:map(fun(Error) -> "~n " ++ Error end, Errors)), []); +format_error({badconfig, {Msg, {Value, Key}}}) -> + io_lib:format(Msg, [Value, Key]); format_error({error, Error}) -> format_error({error_running_tests, Error}). @@ -102,53 +106,120 @@ format_error({error, Error}) -> %% Internal functions %% =================================================================== -compile(State, {ok, Tests}) -> - %% inject `eunit_first_files`, `eunit_compile_opts` and any - %% directories required by tests into the applications - NewState = inject_eunit_state(State, Tests), +prepare_tests(State) -> + %% parse and translate command line tests + CmdTests = resolve_tests(State), + CfgTests = cfg_tests(State), + ProjectApps = rebar_state:project_apps(State), + %% prioritize tests to run first trying any command line specified + %% tests falling back to tests specified in the config file finally + %% running a default set if no other tests are present + select_tests(State, ProjectApps, CmdTests, CfgTests). - case rebar_prv_compile:do(NewState) of - %% successfully compiled apps - {ok, S} -> - ok = maybe_cover_compile(S), - {ok, S}; - %% this should look like a compiler error, not an eunit error - Error -> Error - end; -%% maybe compile even in the face of errors? -compile(_State, Error) -> Error. +resolve_tests(State) -> + {RawOpts, _} = rebar_state:command_parsed_args(State), + Apps = resolve(app, application, RawOpts), + Applications = resolve(application, RawOpts), + Dirs = resolve(dir, RawOpts), + Files = resolve(file, RawOpts), + Modules = resolve(module, RawOpts), + Suites = resolve(suite, module, RawOpts), + Apps ++ Applications ++ 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. + +normalize(Key, Value) when Key == dir; Key == file -> {Key, Value}; +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; + Wrong -> + %% probably a single non list term + ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, eunit_tests}}}) + end. + +select_tests(_State, _ProjectApps, {error, _} = Error, _) -> Error; +select_tests(_State, _ProjectApps, _, {error, _} = Error) -> Error; +select_tests(State, ProjectApps, [], []) -> {ok, default_tests(State, ProjectApps)}; +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. + +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]). -inject_eunit_state(State, Tests) -> +inject_eunit_state(State, {ok, Tests}) -> Apps = rebar_state:project_apps(State), - ModdedApps = lists:map(fun(App) -> - NewOpts = inject(rebar_app_info:opts(App)), - rebar_app_info:opts(App, NewOpts) - end, Apps), - NewOpts = inject(rebar_state:opts(State)), - NewState = rebar_state:opts(State, NewOpts), - test_dirs(NewState, ModdedApps, Tests). + case inject_eunit_state(State, Apps, []) of + {ok, {NewState, ModdedApps}} -> + test_dirs(NewState, ModdedApps, Tests); + {error, _} = Error -> Error + end; +inject_eunit_state(_State, Error) -> Error. + +inject_eunit_state(State, [App|Rest], Acc) -> + case inject(rebar_app_info:opts(App)) of + {error, _} = Error -> Error; + NewOpts -> + NewApp = rebar_app_info:opts(App, NewOpts), + inject_eunit_state(State, Rest, [NewApp|Acc]) + end; +inject_eunit_state(State, [], Acc) -> + case inject(rebar_state:opts(State)) of + {error, _} = Error -> Error; + NewOpts -> {ok, {rebar_state:opts(State, NewOpts), lists:reverse(Acc)}} + end. opts(Opts, Key, Default) -> case rebar_opts:get(Opts, Key, Default) of Vs when is_list(Vs) -> Vs; Wrong -> - ?WARN("Value ~p of option `~p' is not a list, trying to adjust and continue", [Wrong, Key]), - [Wrong] + ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, Key}}}) end. -inject(Opts) -> +inject(Opts) -> erl_opts(Opts). + +erl_opts(Opts) -> %% append `eunit_compile_opts` to app defined `erl_opts` ErlOpts = opts(Opts, erl_opts, []), EUnitOpts = opts(Opts, eunit_compile_opts, []), - NewErlOpts = EUnitOpts ++ ErlOpts, + case append(EUnitOpts, ErlOpts) of + {error, _} = Error -> Error; + NewErlOpts -> first_files(rebar_opts:set(Opts, erl_opts, NewErlOpts)) + end. + +first_files(Opts) -> %% append `eunit_first_files` to app defined `erl_first_files` FirstFiles = opts(Opts, erl_first_files, []), EUnitFirstFiles = opts(Opts, eunit_first_files, []), - NewFirstFiles = EUnitFirstFiles ++ FirstFiles, - %% insert the new keys into the opts - lists:foldl(fun({K, V}, NewOpts) -> rebar_opts:set(NewOpts, K, V) end, - Opts, - [{erl_opts, NewErlOpts}, {erl_first_files, NewFirstFiles}]). + case append(EUnitFirstFiles, FirstFiles) of + {error, _} = Error -> Error; + NewFirstFiles -> rebar_opts:set(Opts, erl_first_files, NewFirstFiles) + end. + +append({error, _} = Error, _) -> Error; +append(_, {error, _} = Error) -> Error; +append(A, B) -> A ++ B. test_dirs(State, Apps, []) -> rebar_state:project_apps(State, Apps); test_dirs(State, Apps, [{dir, Dir}|Rest]) -> @@ -182,71 +253,20 @@ maybe_inject_test_dir(State, AppAcc, [], Dir) -> inject_test_dir(Opts, Dir) -> %% append specified test targets to app defined `extra_src_dirs` - ExtraSrcDirs = rebar_opts:get(Opts, extra_src_dirs, []), + ExtraSrcDirs = rebar_dir:extra_src_dirs(Opts), rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]). -prepare_tests(State) -> - %% parse and translate command line tests - CmdTests = resolve_tests(State), - CfgTests = cfg_tests(State), - ProjectApps = rebar_state:project_apps(State), - %% prioritize tests to run first trying any command line specified - %% tests falling back to tests specified in the config file finally - %% running a default set if no other tests are present - select_tests(State, ProjectApps, CmdTests, CfgTests). - -cfg_tests(State) -> - case rebar_state:get(State, eunit_tests, []) of - Tests when is_list(Tests) -> Tests; - Wrong -> - %% probably a single non list term, try wrapping it in a list and - %% continuing - ?WARN("Value `~p' of option `eunit_tests' is not a list, trying to adjust and continue", [Wrong]), - [Wrong] - end. - -resolve_tests(State) -> - {RawOpts, _} = rebar_state:command_parsed_args(State), - Apps = resolve(app, application, RawOpts), - Applications = resolve(application, RawOpts), - Dirs = resolve(dir, RawOpts), - Files = resolve(file, RawOpts), - Modules = resolve(module, RawOpts), - Suites = resolve(suite, module, RawOpts), - Apps ++ Applications ++ 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. - -normalize(Key, Value) when Key == dir; Key == file -> {Key, Value}; -normalize(Key, Value) -> {Key, list_to_atom(Value)}. - -select_tests(State, ProjectApps, [], []) -> {ok, default_tests(State, ProjectApps)}; -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]) +compile({error, _} = Error) -> Error; +compile(State) -> + case rebar_prv_compile:do(State) of + %% successfully compiled apps + {ok, S} -> + ok = maybe_cover_compile(S), + {ok, S}; + %% this should look like a compiler error, not an eunit error + Error -> Error end. -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]). - validate_tests(State, {ok, Tests}) -> gather_tests(fun(Elem) -> validate(State, Elem) end, Tests, []); validate_tests(_State, Error) -> Error. diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index c4fc4dc..267a9ee 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -38,9 +38,12 @@ cmd_create_priv_dir/1, cfg_opts/1, cfg_arbitrary_opts/1, - cfg_test_spec_filtered/1, + cfg_test_spec/1, cfg_atom_suites/1, - cover_compiled/1]). + cover_compiled/1, + misspecified_ct_opts/1, + misspecified_ct_compile_opts/1, + misspecified_ct_first_files/1]). -include_lib("common_test/include/ct.hrl"). @@ -51,8 +54,11 @@ all() -> [{group, basic_app}, {group, ct_opts}, {group, cover}, cfg_opts, cfg_arbitrary_opts, - cfg_test_spec_filtered, - cfg_atom_suites]. + cfg_test_spec, + cfg_atom_suites, + misspecified_ct_opts, + misspecified_ct_compile_opts, + misspecified_ct_first_files]. groups() -> [{basic_app, [], [basic_app_default_dirs, basic_app_default_beams]}, @@ -911,12 +917,12 @@ cfg_arbitrary_opts(Config) -> true = lists:member({bar, 2}, TestOpts), true = lists:member({baz, 3}, TestOpts). -cfg_test_spec_filtered(Config) -> - C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_test_spec_filtered_opts_"), +cfg_test_spec(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_test_spec_opts_"), AppDir = ?config(apps, C), - Name = rebar_test_utils:create_random_name("ct_cfg_test_spec_filtered_opts_"), + Name = rebar_test_utils:create_random_name("ct_cfg_test_spec_opts_"), Vsn = rebar_test_utils:create_random_vsn(), rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), @@ -924,9 +930,9 @@ cfg_test_spec_filtered(Config) -> {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), - {ok, TestOpts} = rebar_prv_common_test:prepare_tests(State), + {error, {rebar_prv_common_test, Error}} = rebar_prv_common_test:prepare_tests(State), - false = lists:keysearch(test_spec, 1, TestOpts). + {badconfig, "Test specs not supported"} = Error. cfg_atom_suites(Config) -> C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_atom_suites_"), @@ -962,7 +968,59 @@ cover_compiled(Config) -> Name = ?config(name, Config), Mod = list_to_atom(Name), {file, _} = cover:is_compiled(Mod). - + +misspecified_ct_opts(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_atom_suites_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_cfg_atom_suites_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{ct_opts, {basic_html, false}}], + + {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), + + {error, {rebar_prv_common_test, Error}} = rebar_prv_common_test:prepare_tests(State), + + {badconfig, {"Value `~p' of option `~p' must be a list", {{basic_html, false}, ct_opts}}} = Error. + +misspecified_ct_compile_opts(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_atom_suites_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_cfg_atom_suites_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{ct_compile_opts, {d, whatever}}], + + {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), + + Tests = rebar_prv_common_test:prepare_tests(State), + {error, {rebar_prv_common_test, Error}} = rebar_prv_common_test:compile(State, Tests), + + {badconfig, {"Value `~p' of option `~p' must be a list", {{d, whatever}, ct_compile_opts}}} = Error. + +misspecified_ct_first_files(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_atom_suites_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_cfg_atom_suites_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{ct_first_files, some_file}], + + {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), + + Tests = rebar_prv_common_test:prepare_tests(State), + {error, {rebar_prv_common_test, Error}} = rebar_prv_common_test:compile(State, Tests), + + {badconfig, {"Value `~p' of option `~p' must be a list", {some_file, ct_first_files}}} = Error. %% helper for generating test data test_suite(Name) -> diff --git a/test/rebar_eunit_SUITE.erl b/test/rebar_eunit_SUITE.erl index 262eb92..1b11c5a 100644 --- a/test/rebar_eunit_SUITE.erl +++ b/test/rebar_eunit_SUITE.erl @@ -11,13 +11,19 @@ -export([single_file_arg/1, multi_file_arg/1, missing_file_arg/1]). -export([single_dir_arg/1, multi_dir_arg/1, missing_dir_arg/1]). -export([multiple_arg_composition/1, multiple_arg_errors/1]). +-export([misspecified_eunit_tests/1]). +-export([misspecified_eunit_compile_opts/1]). +-export([misspecified_eunit_first_files/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -include_lib("kernel/include/file.hrl"). all() -> - [{group, basic_app}, {group, multi_app}, {group, cmd_line_args}]. + [{group, basic_app}, {group, multi_app}, {group, cmd_line_args}, + misspecified_eunit_tests, + misspecified_eunit_compile_opts, + misspecified_eunit_first_files]. groups() -> [{basic_app, [sequence], [basic_app_compiles, {group, basic_app_results}]}, @@ -484,3 +490,60 @@ multiple_arg_errors(Config) -> {error, {rebar_prv_eunit, {eunit_test_errors, Expect}}} = Tests. +misspecified_eunit_tests(Config) -> + State = rebar_test_utils:init_rebar_state(Config, "basic_app_"), + + AppDir = ?config(apps, State), + PrivDir = ?config(priv_dir, State), + + AppDirs = ["src", "include", "test"], + + lists:foreach(fun(F) -> ec_file:copy(filename:join([PrivDir, "basic_app", F]), + filename:join([AppDir, F]), + [recursive]) end, AppDirs), + + BaseConfig = [{erl_opts, [{d, config_define}]}, {eunit_compile_opts, [{d, eunit_compile_define}]}], + + RebarConfig = [{eunit_tests, {dir, "test"}}|BaseConfig], + + {error, {rebar_prv_eunit, Error}} = rebar_test_utils:run_and_check(State, RebarConfig, ["eunit"], return), + + {badconfig, {"Value `~p' of option `~p' must be a list", {{dir, "test"}, eunit_tests}}} = Error. + +misspecified_eunit_compile_opts(Config) -> + State = rebar_test_utils:init_rebar_state(Config, "basic_app_"), + + AppDir = ?config(apps, State), + PrivDir = ?config(priv_dir, State), + + AppDirs = ["src", "include", "test"], + + lists:foreach(fun(F) -> ec_file:copy(filename:join([PrivDir, "basic_app", F]), + filename:join([AppDir, F]), + [recursive]) end, AppDirs), + + RebarConfig = [{erl_opts, [{d, config_define}]}, {eunit_compile_opts, {d, eunit_compile_define}}], + + {error, {rebar_prv_eunit, Error}} = rebar_test_utils:run_and_check(State, RebarConfig, ["eunit"], return), + + {badconfig, {"Value `~p' of option `~p' must be a list", {{d, eunit_compile_define}, eunit_compile_opts}}} = Error. + +misspecified_eunit_first_files(Config) -> + State = rebar_test_utils:init_rebar_state(Config, "basic_app_"), + + AppDir = ?config(apps, State), + PrivDir = ?config(priv_dir, State), + + AppDirs = ["src", "include", "test"], + + lists:foreach(fun(F) -> ec_file:copy(filename:join([PrivDir, "basic_app", F]), + filename:join([AppDir, F]), + [recursive]) end, AppDirs), + + BaseConfig = [{erl_opts, [{d, config_define}]}, {eunit_compile_opts, [{d, eunit_compile_define}]}], + + RebarConfig = [{eunit_first_files, some_file}|BaseConfig], + + {error, {rebar_prv_eunit, Error}} = rebar_test_utils:run_and_check(State, RebarConfig, ["eunit"], return), + + {badconfig, {"Value `~p' of option `~p' must be a list", {some_file, eunit_first_files}}} = Error. \ No newline at end of file -- cgit v1.1 From d4e876500772657a5a6339905d82424c81d73d2a Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Tue, 17 Nov 2015 14:16:58 -0600 Subject: only add package list of versions to registry if it has the right buildtool support --- src/rebar_prv_update.erl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl index 6637ebe..33d757a 100644 --- a/src/rebar_prv_update.erl +++ b/src/rebar_prv_update.erl @@ -92,12 +92,24 @@ hex_to_index(State) -> false -> true end; - ({Pkg, [Vsns]}, _) when is_binary(Pkg) -> - ets:insert(?PACKAGE_TABLE, {Pkg, Vsns}); (_, _) -> true end, true, Registry), + ets:foldl(fun({Pkg, [[]]}, _) when is_binary(Pkg) -> + true; + ({Pkg, [Vsns=[Vsn | _Rest]]}, _) when is_binary(Pkg) -> + %% Verify the package is of the right build tool by checking if the first + %% version exists in the table from the foldl above + case ets:member(?PACKAGE_TABLE, {Pkg, Vsn}) of + true -> + ets:insert(?PACKAGE_TABLE, {Pkg, Vsns}); + false -> + true + end; + (_, _) -> + true + end, true, Registry), ets:insert(?PACKAGE_TABLE, {package_index_version, ?PACKAGE_INDEX_VERSION}), ?INFO("Writing index to ~s", [PackageIndex]), ets:tab2file(?PACKAGE_TABLE, PackageIndex), -- cgit v1.1 From be0c6a5a2da2252f67202734227972bf802e229a Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Tue, 17 Nov 2015 17:06:24 -0600 Subject: upgrade eunit_formatters for OTP 18 support --- rebar.config | 2 +- rebar.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 4fc3fa7..592f806 100644 --- a/rebar.config +++ b/rebar.config @@ -10,7 +10,7 @@ {relx, "3.8.0"}, {cf, "0.2.1"}, {cth_readable, "1.0.1"}, - {eunit_formatters, "0.2.0"}]}. + {eunit_formatters, "0.3.0"}]}. {escript_name, rebar3}. {escript_emu_args, "%%! +sbtu +A0\n"}. diff --git a/rebar.lock b/rebar.lock index 02f429e..97e296e 100644 --- a/rebar.lock +++ b/rebar.lock @@ -3,7 +3,7 @@ {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.0.1">>},0}, {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.16.0">>},0}, - {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.2.0">>},0}, + {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.0">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.5.0">>},0}, {<<"relx">>,{pkg,<<"relx">>,<<"3.8.0">>},0}, -- cgit v1.1 From 035af1ac60c814866d1f24a5faaaa697f6cdb8cb Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Wed, 18 Nov 2015 09:58:10 -0600 Subject: upgrade eunit_formatters to 0.3.1 for OTP18 bug fix --- rebar.config | 2 +- rebar.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 592f806..3fd49f0 100644 --- a/rebar.config +++ b/rebar.config @@ -10,7 +10,7 @@ {relx, "3.8.0"}, {cf, "0.2.1"}, {cth_readable, "1.0.1"}, - {eunit_formatters, "0.3.0"}]}. + {eunit_formatters, "0.3.1"}]}. {escript_name, rebar3}. {escript_emu_args, "%%! +sbtu +A0\n"}. diff --git a/rebar.lock b/rebar.lock index 97e296e..58c1457 100644 --- a/rebar.lock +++ b/rebar.lock @@ -3,7 +3,7 @@ {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.0.1">>},0}, {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.16.0">>},0}, - {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.0">>},0}, + {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.5.0">>},0}, {<<"relx">>,{pkg,<<"relx">>,<<"3.8.0">>},0}, -- cgit v1.1 From 69254eac408da9689bdf94e1a0f90c2f953c8061 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Wed, 18 Nov 2015 15:23:24 -0500 Subject: Ct output improvements - eunit assert look better - lager logs to the html logs --- rebar.config | 2 +- rebar.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 3fd49f0..7cd2ee7 100644 --- a/rebar.config +++ b/rebar.config @@ -9,7 +9,7 @@ {bbmustache, "1.0.4"}, {relx, "3.8.0"}, {cf, "0.2.1"}, - {cth_readable, "1.0.1"}, + {cth_readable, "1.1.0"}, {eunit_formatters, "0.3.1"}]}. {escript_name, rebar3}. diff --git a/rebar.lock b/rebar.lock index 58c1457..bd20f34 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ [{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, {<<"certifi">>,{pkg,<<"certifi">>,<<"0.1.1">>},0}, {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, - {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.0.1">>},0}, + {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.1.0">>},0}, {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.16.0">>},0}, {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, -- cgit v1.1 From bf347caa55588b1f38cfaed267111d4c0163d522 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Thu, 19 Nov 2015 23:43:50 +0000 Subject: Handle force flags in leading position The checking of flags and the parsing of arguments is separated up. --- src/rebar_prv_new.erl | 6 +++++- test/rebar_new_SUITE.erl | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/rebar_prv_new.erl b/src/rebar_prv_new.erl index 6574aca..28572a9 100644 --- a/src/rebar_prv_new.erl +++ b/src/rebar_prv_new.erl @@ -32,7 +32,7 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> - case rebar_state:command_args(State) of + case strip_flags(rebar_state:command_args(State)) of ["help"] -> ?CONSOLE("Call `rebar3 new help