diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/rebar.app.src | 2 | ||||
-rw-r--r-- | src/rebar.hrl | 4 | ||||
-rw-r--r-- | src/rebar_app_info.erl | 11 | ||||
-rw-r--r-- | src/rebar_core.erl | 8 | ||||
-rw-r--r-- | src/rebar_dir.erl | 13 | ||||
-rw-r--r-- | src/rebar_prv_eunit.erl | 220 | ||||
-rw-r--r-- | src/rebar_prv_install_deps.erl | 96 | ||||
-rw-r--r-- | src/rebar_prv_lock.erl | 20 | ||||
-rw-r--r-- | src/rebar_prv_new.erl | 15 | ||||
-rw-r--r-- | src/rebar_prv_release.erl | 2 | ||||
-rw-r--r-- | src/rebar_state.erl | 66 | ||||
-rw-r--r-- | src/rebar_templater.erl | 2 | ||||
-rw-r--r-- | src/rebar_utils.erl | 1 | ||||
-rw-r--r-- | test/mock_git_resource.erl | 4 | ||||
-rw-r--r-- | test/mock_pkg_resource.erl | 6 | ||||
-rw-r--r-- | test/rebar_as_SUITE.erl | 22 | ||||
-rw-r--r-- | test/rebar_compile_SUITE.erl | 127 | ||||
-rw-r--r-- | test/rebar_eunit_SUITE.erl | 379 | ||||
-rw-r--r-- | test/rebar_new_SUITE.erl | 26 | ||||
-rw-r--r-- | test/rebar_opts_parser_SUITE.erl | 54 | ||||
-rw-r--r-- | test/rebar_profiles_SUITE.erl | 19 | ||||
-rw-r--r-- | test/rebar_release_SUITE.erl | 46 | ||||
-rw-r--r-- | test/rebar_test_utils.erl | 44 |
24 files changed, 1030 insertions, 159 deletions
@@ -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 (git, hg, etc). -3.0 Alpha +3.0 Alpha-2 ==== [DOCUMENTATION](http://www.rebar3.org/v3.0/docs) diff --git a/src/rebar.app.src b/src/rebar.app.src index 62cabf9..ee4bfd7 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-alpha"}, + {vsn, "3.0.0-alpha-2"}, {modules, []}, {registered, []}, {applications, [kernel, diff --git a/src/rebar.hrl b/src/rebar.hrl index 0dfcad0..1f051d7 100644 --- a/src/rebar.hrl +++ b/src/rebar.hrl @@ -14,7 +14,9 @@ -define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))). -define(DEFAULT_BASE_DIR, "_build"). --define(DEFAULT_PROJECT_APP_DIRS, ["_checkouts", "apps", "lib", "."]). +-define(DEFAULT_ROOT_DIR, "."). +-define(DEFAULT_PROJECT_APP_DIRS, ["apps", "lib", "."]). +-define(DEFAULT_CHECKOUTS_DIR, "_checkouts"). -define(DEFAULT_DEPS_DIR, "lib"). -define(DEFAULT_PLUGINS_DIR, "plugins"). -define(DEFAULT_TEST_DEPS_DIR, "test/lib"). diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl index 99bc25b..9659c0b 100644 --- a/src/rebar_app_info.erl +++ b/src/rebar_app_info.erl @@ -34,6 +34,8 @@ source/2, state/1, state/2, + is_checkout/1, + is_checkout/2, valid/1, valid/2]). @@ -53,6 +55,7 @@ out_dir :: file:name(), source :: string() | tuple() | undefined, state :: rebar_state:t() | undefined, + is_checkout=false :: boolean(), valid :: boolean()}). %%============================================================================ @@ -238,6 +241,14 @@ state(AppInfo=#app_info_t{}, State) -> state(#app_info_t{state=State}) -> State. +-spec is_checkout(t(), boolean()) -> t(). +is_checkout(AppInfo=#app_info_t{}, IsCheckout) -> + AppInfo#app_info_t{is_checkout=IsCheckout}. + +-spec is_checkout(t()) -> boolean(). +is_checkout(#app_info_t{is_checkout=IsCheckout}) -> + IsCheckout. + -spec valid(t()) -> boolean(). valid(AppInfo=#app_info_t{valid=undefined}) -> case rebar_app_utils:validate_application_info(AppInfo) of diff --git a/src/rebar_core.erl b/src/rebar_core.erl index db82766..6abab68 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -82,6 +82,8 @@ process_command(State, Command) -> case Command of do -> do(TargetProviders, State); + as -> + do(TargetProviders, State); _ -> Profiles = providers:profiles(CommandProvider), State1 = rebar_state:apply_profiles(State, Profiles), @@ -91,7 +93,11 @@ process_command(State, Command) -> State2 = rebar_state:command_parsed_args(State1, Args), do(TargetProviders, State2); {error, {invalid_option, Option}} -> - {error, io_lib:format("Invalid option ~s on task ~p", [Option, Command])} + {error, io_lib:format("Invalid option ~s on task ~p", [Option, Command])}; + {error, {invalid_option_arg, {Option, Arg}}} -> + {error, io_lib:format("Invalid argument ~s to option ~s", [Arg, Option])}; + {error, {missing_option_arg, Option}} -> + {error, io_lib:format("Missing argument to option ~s", [Option])} end end end. diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index 628ebd3..4a9bf09 100644 --- a/src/rebar_dir.erl +++ b/src/rebar_dir.erl @@ -3,6 +3,8 @@ -export([base_dir/1, deps_dir/1, deps_dir/2, + checkouts_dir/1, + checkouts_dir/2, plugins_dir/1, lib_dirs/1, home_dir/0, @@ -40,6 +42,17 @@ deps_dir(State) -> deps_dir(DepsDir, App) -> filename:join(DepsDir, App). +root_dir(State) -> + rebar_state:get(State, root_dir, ?DEFAULT_ROOT_DIR). + +-spec checkouts_dir(rebar_state:t()) -> file:filename_all(). +checkouts_dir(State) -> + filename:join(root_dir(State), rebar_state:get(State, checkouts_dir, ?DEFAULT_CHECKOUTS_DIR)). + +-spec checkouts_dir(rebar_state:t(), file:filename_all()) -> file:filename_all(). +checkouts_dir(State, App) -> + filename:join(checkouts_dir(State), App). + -spec plugins_dir(rebar_state:t()) -> file:filename_all(). plugins_dir(State) -> case lists:member(global, rebar_state:current_profiles(State)) of diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 0e0e937..6872c99 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -37,13 +37,14 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> ?INFO("Performing EUnit tests...", []), - {Opts, _} = rebar_state:command_parsed_args(State), - EUnitOpts = resolve_eunit_opts(State, Opts), - TestApps = filter_checkouts(rebar_state:project_apps(State)), - ok = compile_tests(State, TestApps), - ok = maybe_cover_compile(State, Opts), - AppsToTest = test_dirs(State, TestApps), - Result = eunit:test(AppsToTest, EUnitOpts), + case prepare_tests(State) of + {ok, Tests} -> do_tests(State, Tests); + Error -> Error + end. + +do_tests(State, Tests) -> + EUnitOpts = resolve_eunit_opts(State), + Result = eunit:test(Tests, EUnitOpts), ok = rebar_prv_cover:maybe_write_coverdata(State, ?PROVIDER), case handle_results(Result) of {error, Reason} -> @@ -58,45 +59,9 @@ format_error(unknown_error) -> format_error({error_running_tests, Reason}) -> io_lib:format("Error running tests: ~p", [Reason]). -eunit_opts(_State) -> - [{cover, $c, "cover", boolean, help(cover)}, - {verbose, $v, "verbose", boolean, help(verbose)}]. - -help(cover) -> "Generate cover data"; -help(verbose) -> "Verbose output". - -filter_checkouts(Apps) -> filter_checkouts(Apps, []). - -filter_checkouts([], Acc) -> lists:reverse(Acc); -filter_checkouts([App|Rest], Acc) -> - AppDir = filename:absname(rebar_app_info:dir(App)), - CheckoutsDir = filename:absname("_checkouts"), - case lists:prefix(CheckoutsDir, AppDir) of - true -> filter_checkouts(Rest, Acc); - false -> filter_checkouts(Rest, [App|Acc]) - end. - -resolve_eunit_opts(State, Opts) -> - EUnitOpts = rebar_state:get(State, eunit_opts, []), - case proplists:get_value(verbose, Opts, false) of - true -> set_verbose(EUnitOpts); - false -> EUnitOpts - end. - -test_dirs(State, TestApps) -> - %% we need to add "./ebin" if it exists but only if it's not already - %% due to be added - F = fun(App) -> rebar_app_info:dir(App) =/= rebar_dir:get_cwd() end, - BareEbin = filename:join([rebar_dir:base_dir(State), "ebin"]), - case lists:any(F, TestApps) andalso filelib:is_dir(BareEbin) of - false -> application_dirs(TestApps, []); - true -> [{dir, BareEbin}|application_dirs(TestApps, [])] - end. - -application_dirs([], Acc) -> lists:reverse(Acc); -application_dirs([App|Rest], Acc) -> - AppName = list_to_atom(binary_to_list(rebar_app_info:name(App))), - application_dirs(Rest, [{application, AppName}|Acc]). +%% =================================================================== +%% Internal functions +%% =================================================================== test_state(State) -> ErlOpts = rebar_state:get(State, eunit_compile_opts, []), @@ -120,14 +85,33 @@ first_files(State) -> EUnitFirst = rebar_state:get(State, eunit_first_files, []), [{erl_first_files, EUnitFirst}]. -set_verbose(Opts) -> - %% if `verbose` is already set don't set it again - case lists:member(verbose, Opts) of - true -> Opts; - false -> [verbose] ++ Opts +prepare_tests(State) -> + {RawOpts, _} = rebar_state:command_parsed_args(State), + resolve_apps(State, RawOpts). + +resolve_apps(State, RawOpts) -> + case proplists:get_value(app, RawOpts) of + undefined -> resolve_suites(State, project_apps(State), RawOpts); + %% convert app name strings to `rebar_app_info` objects + Apps -> AppNames = string:tokens(Apps, [$,]), + ProjectApps = project_apps(State), + case filter_apps_by_name(AppNames, ProjectApps) of + {ok, TestApps} -> resolve_suites(State, TestApps, RawOpts); + Error -> Error + end + end. + +resolve_suites(State, Apps, RawOpts) -> + case proplists:get_value(suite, RawOpts) of + undefined -> compile_tests(State, Apps, all, RawOpts); + Suites -> SuiteNames = string:tokens(Suites, [$,]), + case filter_suites_by_apps(SuiteNames, Apps) of + {ok, S} -> compile_tests(State, Apps, S, RawOpts); + Error -> Error + end end. -compile_tests(State, TestApps) -> +compile_tests(State, TestApps, Suites, RawOpts) -> F = fun(AppInfo) -> AppDir = rebar_app_info:dir(AppInfo), S = case rebar_app_info:state(AppInfo) of @@ -141,24 +125,80 @@ compile_tests(State, TestApps) -> ec_cnv:to_list(rebar_app_info:out_dir(AppInfo))) end, lists:foreach(F, TestApps), - case filelib:is_dir(filename:join([rebar_dir:get_cwd(), "test"])) of - true -> compile_bare_tests(State, TestApps); - false -> ok + ok = maybe_cover_compile(State, RawOpts), + {ok, test_set(TestApps, Suites)}. + +maybe_cover_compile(State, Opts) -> + State1 = case proplists:get_value(cover, Opts, false) of + true -> rebar_state:set(State, cover_enabled, true); + false -> State + end, + rebar_prv_cover:maybe_cover_compile(State1). + +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. + +%% make sure applications specified actually exist +filter_apps_by_name(AppNames, ProjectApps) -> + filter_apps_by_name(AppNames, ProjectApps, []). + +filter_apps_by_name([], _ProjectApps, Acc) -> {ok, lists:reverse(Acc)}; +filter_apps_by_name([Name|Rest], ProjectApps, Acc) -> + case find_app_by_name(Name, ProjectApps) of + {error, app_not_found} -> + ?PRV_ERROR({error_running_tests, + "Application `" ++ Name ++ "' not found in project."}); + App -> + filter_apps_by_name(Rest, ProjectApps, [App|Acc]) + end. + +find_app_by_name(_, []) -> {error, app_not_found}; +find_app_by_name(Name, [App|Rest]) -> + case Name == binary_to_list(rebar_app_info:name(App)) of + true -> App; + false -> find_app_by_name(Name, Rest) + end. + +%% ensure specified suites are in the applications included +filter_suites_by_apps(Suites, ProjectApps) -> + filter_suites_by_apps(Suites, ProjectApps, []). + +filter_suites_by_apps([], _ProjectApps, Acc) -> {ok, lists:reverse(Acc)}; +filter_suites_by_apps([Suite|Rest], Apps, Acc) -> + Modules = app_modules([binary_to_atom(rebar_app_info:name(A), unicode) || A <- Apps], []), + case lists:member(list_to_atom(Suite), Modules) of + false -> + ?PRV_ERROR({error_running_tests, + "Module `" ++ Suite ++ "' not found in applications."}); + true -> + filter_suites_by_apps(Rest, Apps, [Suite|Acc]) end. -compile_bare_tests(State, TestApps) -> - F = fun(App) -> rebar_app_info:dir(App) == rebar_dir:get_cwd() end, - case lists:filter(F, TestApps) of - %% compile and link just the `test` directory of the base dir - [] -> - Source = filename:join([rebar_dir:get_cwd(), "test"]), - Target = filename:join([rebar_dir:base_dir(State), "test"]), - ok = rebar_file_utils:symlink_or_copy(Source, Target), - rebar_erlc_compiler:compile(replace_src_dirs(State), - rebar_dir:base_dir(State), - filename:join([rebar_dir:base_dir(State), "ebin"])); - %% already compiled `./test` so do nothing - _ -> ok +app_modules([], Acc) -> Acc; +app_modules([App|Rest], Acc) -> + Unload = case application:load(App) of + ok -> true; + {error, {already_loaded, _}} -> false + end, + NewAcc = case application:get_key(App, modules) of + {ok, Modules} -> Modules ++ Acc; + undefined -> Acc + end, + case Unload of + true -> + application:unload(App), + app_modules(Rest, NewAcc); + false -> + app_modules(Rest, NewAcc) end. replace_src_dirs(State) -> @@ -167,15 +207,47 @@ replace_src_dirs(State) -> StrippedOpts = lists:keydelete(src_dirs, 1, ErlOpts), rebar_state:set(State, erl_opts, [{src_dirs, ["test"]}|StrippedOpts]). -maybe_cover_compile(State, Opts) -> - State1 = case proplists:get_value(cover, Opts, false) of - true -> rebar_state:set(State, cover_enabled, true); - false -> State - end, - rebar_prv_cover:maybe_cover_compile(State1). +test_set(Apps, all) -> set_apps(Apps, []); +test_set(_Apps, Suites) -> set_suites(Suites, []). + +set_apps([], Acc) -> lists:reverse(Acc); +set_apps([App|Rest], Acc) -> + AppName = list_to_atom(binary_to_list(rebar_app_info:name(App))), + set_apps(Rest, [{application, AppName}|Acc]). + +set_suites([], Acc) -> lists:reverse(Acc); +set_suites([Suite|Rest], Acc) -> + set_suites(Rest, [{module, list_to_atom(Suite)}|Acc]). + +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 + end. + +set_verbose(Opts) -> + %% if `verbose` is already set don't set it again + case lists:member(verbose, Opts) of + true -> Opts; + false -> [verbose] ++ Opts + end. handle_results(ok) -> ok; handle_results(error) -> {error, unknown_error}; handle_results({error, Reason}) -> {error, {error_running_tests, Reason}}. + +eunit_opts(_State) -> + [{app, undefined, "app", string, help(app)}, + {cover, $c, "cover", boolean, help(cover)}, + {suite, undefined, "suite", string, help(suite)}, + {verbose, $v, "verbose", boolean, help(verbose)}]. + +help(app) -> "List of application test suites to run"; +help(cover) -> "Generate cover data"; +help(suite) -> "List of test suites to run"; +help(verbose) -> "Verbose output". + diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index e4a87ee..e92a3f0 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -174,7 +174,7 @@ compile_order(Source, ProjectApps) -> case rebar_digraph:compile_order(Source) of {ok, Sort} -> %% Valid apps are compiled and good - {ok, lists:dropwhile(fun rebar_app_info:valid/1, Sort -- ProjectApps)}; + {ok, lists:dropwhile(fun not_needs_compile/1, Sort -- ProjectApps)}; {error, Error} -> {error, Error} end. @@ -214,23 +214,28 @@ handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, State) - {[AppInfo | Fetched], NewSeen, NewState}. maybe_lock(Profile, AppInfo, Seen, State, Level) -> - case Profile of - default -> - Name = rebar_app_info:name(AppInfo), - case sets:is_element(Name, Seen) of - false -> - Locks = rebar_state:lock(State), - case lists:any(fun(App) -> rebar_app_info:name(App) =:= Name end, Locks) of - true -> - {sets:add_element(Name, Seen), State}; + case rebar_app_info:is_checkout(AppInfo) of + false -> + case Profile of + default -> + Name = rebar_app_info:name(AppInfo), + case sets:is_element(Name, Seen) of false -> - {sets:add_element(Name, Seen), - rebar_state:lock(State, rebar_app_info:dep_level(AppInfo, Level))} + Locks = rebar_state:lock(State), + case lists:any(fun(App) -> rebar_app_info:name(App) =:= Name end, Locks) of + true -> + {sets:add_element(Name, Seen), State}; + false -> + {sets:add_element(Name, Seen), + rebar_state:lock(State, rebar_app_info:dep_level(AppInfo, Level))} + end; + true -> + {Seen, State} end; - true -> + _ -> {Seen, State} end; - _ -> + true -> {Seen, State} end. @@ -367,7 +372,7 @@ handle_dep(State, DepsDir, AppInfo, Locks, Level) -> maybe_fetch(AppInfo, Upgrade, Seen, State) -> AppDir = ec_cnv:to_list(rebar_app_info:dir(AppInfo)), %% Don't fetch dep if it exists in the _checkouts dir - case in_checkouts(AppInfo) of + case rebar_app_info:is_checkout(AppInfo) of true -> false; false -> @@ -392,13 +397,6 @@ maybe_fetch(AppInfo, Upgrade, Seen, State) -> end end. -in_checkouts(AppInfo) -> - Apps = rebar_app_discover:find_apps(["_checkouts"], all), - case rebar_app_utils:find(rebar_app_info:name(AppInfo), Apps) of - {ok, _} -> true; - error -> false - end. - in_default(AppInfo, State) -> Name = ec_cnv:to_list(rebar_app_info:name(AppInfo)), DefaultAppDir = filename:join([rebar_state:get(State, base_dir), "default", "lib", Name]), @@ -427,11 +425,25 @@ parse_deps(DepsDir, Deps, State, Locks, Level) -> end end, {[], []}, Deps). -parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, _DepsDir, _State) when is_list(Vsn) -> - {SrcDepsAcc, [parse_goal(ec_cnv:to_binary(Name) - ,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]}; -parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, _DepsDir, _State) when is_atom(Name) -> - {SrcDepsAcc, [ec_cnv:to_binary(Name) | PkgDepsAcc]}; +parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_list(Vsn) -> + CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), + case rebar_app_info:discover(CheckoutsDir) of + {ok, _App} -> + Dep = new_dep(DepsDir, Name, [], [], State), + {[Dep | SrcDepsAcc], PkgDepsAcc}; + not_found -> + {SrcDepsAcc, [parse_goal(ec_cnv:to_binary(Name) + ,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]} + end; +parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_atom(Name) -> + CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), + case rebar_app_info:discover(CheckoutsDir) of + {ok, _App} -> + Dep = new_dep(DepsDir, Name, [], [], State), + {[Dep | SrcDepsAcc], PkgDepsAcc}; + not_found -> + {SrcDepsAcc, [ec_cnv:to_binary(Name) | PkgDepsAcc]} + end; parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) -> Dep = new_dep(DepsDir, Name, [], Source, State), {[Dep | SrcDepsAcc], PkgDepsAcc}; @@ -445,8 +457,15 @@ parse_dep({Name, _Vsn, Source, Opts}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) ?WARN("Dependency option list ~p in ~p is not supported and will be ignored", [Opts, Name]), Dep = new_dep(DepsDir, Name, [], Source, State), {[Dep | SrcDepsAcc], PkgDepsAcc}; -parse_dep({_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, _, _) when is_integer(Level) -> - {SrcDepsAcc, [{Name, Vsn} | PkgDepsAcc]}; +parse_dep({_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_integer(Level) -> + CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), + case rebar_app_info:discover(CheckoutsDir) of + {ok, _App} -> + Dep = new_dep(DepsDir, Name, [], [], State), + {[Dep | SrcDepsAcc], PkgDepsAcc}; + not_found -> + {SrcDepsAcc, [{Name, Vsn} | PkgDepsAcc]} + end; parse_dep({Name, Source, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) , is_integer(Level) -> Dep = new_dep(DepsDir, Name, [], Source, State), @@ -456,12 +475,19 @@ parse_dep(Dep, _, _, _) -> new_dep(DepsDir, Name, Vsn, Source, State) -> - Dir = ec_cnv:to_list(filename:join(DepsDir, Name)), - {ok, Dep} = case rebar_app_info:discover(Dir) of + CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), + {ok, Dep} = case rebar_app_info:discover(CheckoutsDir) of {ok, App} -> - {ok, App}; + {ok, rebar_app_info:is_checkout(App, true)}; not_found -> - rebar_app_info:new(Name, Vsn, ec_cnv:to_list(filename:join(DepsDir, Name))) + Dir = ec_cnv:to_list(filename:join(DepsDir, Name)), + case rebar_app_info:discover(Dir) of + {ok, App} -> + {ok, App}; + not_found -> + rebar_app_info:new(Name, Vsn, + ec_cnv:to_list(filename:join(DepsDir, Name))) + end end, C = rebar_config:consult(rebar_app_info:dir(Dep)), S = rebar_state:new(rebar_state:new(), C, rebar_app_info:dir(Dep)), @@ -528,3 +554,7 @@ warn_skip_pkg({Name, Source}, State) -> false -> ?WARN(Msg, Args); true -> ?ERROR(Msg, Args), ?FAIL end. + +not_needs_compile(App) -> + not(rebar_app_info:is_checkout(App)) + andalso rebar_app_info:valid(App). diff --git a/src/rebar_prv_lock.erl b/src/rebar_prv_lock.erl index 8a69434..e839168 100644 --- a/src/rebar_prv_lock.erl +++ b/src/rebar_prv_lock.erl @@ -30,16 +30,16 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> AllDeps = rebar_state:lock(State), - Locks = lists:map(fun(Dep) -> - Dir = rebar_app_info:dir(Dep), - Source = rebar_app_info:source(Dep), - - %% If source is tuple it is a source dep - %% e.g. {git, "git://github.com/ninenines/cowboy.git", "master"} - {rebar_app_info:name(Dep) - ,rebar_fetch:lock_source(Dir, Source) - ,rebar_app_info:dep_level(Dep)} - end, AllDeps), + Locks = [begin + Dir = rebar_app_info:dir(Dep), + Source = rebar_app_info:source(Dep), + + %% If source is tuple it is a source dep + %% e.g. {git, "git://github.com/ninenines/cowboy.git", "master"} + {rebar_app_info:name(Dep) + ,rebar_fetch:lock_source(Dir, Source) + ,rebar_app_info:dep_level(Dep)} + end || Dep <- AllDeps, not(rebar_app_info:is_checkout(Dep))], Dir = rebar_state:dir(State), file:write_file(filename:join(Dir, ?LOCK_FILE), io_lib:format("~p.~n", [Locks])), {ok, State}. diff --git a/src/rebar_prv_new.erl b/src/rebar_prv_new.erl index 77e100f..58e27fd 100644 --- a/src/rebar_prv_new.erl +++ b/src/rebar_prv_new.erl @@ -33,15 +33,24 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> case rebar_state:command_args(State) of + ["help"] -> + ?CONSOLE("Call `rebar3 new help <template>` for a detailed description~n", []), + show_short_templates(rebar_templater:list_templates(State)), + {ok, State}; ["help", TemplateName] -> case lists:keyfind(TemplateName, 1, rebar_templater:list_templates(State)) of - false -> io:format("template not found.~n"); + false -> ?CONSOLE("template not found.", []); Term -> show_template(Term) end, {ok, State}; [TemplateName | Opts] -> - Force = is_forced(State), - ok = rebar_templater:new(TemplateName, parse_opts(Opts), Force, State), + case lists:keyfind(TemplateName, 1, rebar_templater:list_templates(State)) of + false -> + ?CONSOLE("template not found.", []); + _ -> + Force = is_forced(State), + ok = rebar_templater:new(TemplateName, parse_opts(Opts), Force, State) + end, {ok, State}; [] -> show_short_templates(rebar_templater:list_templates(State)), diff --git a/src/rebar_prv_release.erl b/src/rebar_prv_release.erl index 766bfe4..db012d5 100644 --- a/src/rebar_prv_release.erl +++ b/src/rebar_prv_release.erl @@ -47,7 +47,7 @@ do(State) -> ,{caller, Caller}], AllOptions); Config -> relx:main([{lib_dirs, LibDirs} - ,{config, Config} + ,{config, lists:reverse(Config)} ,{output_dir, OutputDir} ,{caller, Caller}], AllOptions) end, diff --git a/src/rebar_state.erl b/src/rebar_state.erl index fa0a963..cd127c0 100644 --- a/src/rebar_state.erl +++ b/src/rebar_state.erl @@ -240,7 +240,7 @@ merge_opts(NewOpts, OldOpts) -> true -> NewValue; false -> - lists:umerge(lists:sort(NewValue), lists:sort(OldValue)) + tup_umerge(tup_sort(NewValue), tup_sort(OldValue)) end; (_Key, NewValue, _OldValue) -> NewValue @@ -362,3 +362,67 @@ add_hook(pre, {PreHooks, PostHooks}, Hook) -> {[Hook | PreHooks], PostHooks}; add_hook(post, {PreHooks, PostHooks}, Hook) -> {PreHooks, [Hook | PostHooks]}. + + +%% Sort the list in proplist-order, meaning that `{a,b}' and `{a,c}' +%% both compare as usual, and `a' and `b' do the same, but `a' and `{a,b}' will +%% compare based on the first element of the key, and in order. So the following +%% list will sort as: +%% - `[native, {native,o3}, check]' -> `[check, native, {native, o3}]' +%% - `[native, {native,o3}, {native, o2}, check]' -> `[check,native,{native,o3},{native,o2}]' +%% Meaning that: +%% a) no deduplication takes place +%% b) the key of a tuple is what counts in being sorted, but atoms are seen as {atom} +%% as far as comparison is concerned (departing from lists:ukeysort/2) +%% c) order is preserved for similar keys and tuples no matter their size (sort is stable) +%% +%% These properties let us merge proplists fairly easily. +tup_sort(List) -> + lists:sort(fun(A, B) when is_tuple(A), is_tuple(B) -> element(1, A) =< element(1, B) + ; (A, B) when is_tuple(A) -> element(1, A) =< B + ; (A, B) when is_tuple(B) -> A =< element(1, B) + ; (A, B) -> A =< B + end, List). + +%% Custom merge functions. The objective is to behave like lists:umerge/2, +%% except that we also compare the merge elements based on the key if they're a +%% tuple, such that `{key, val1}' is always prioritized over `{key, val0}' if +%% the former is from the 'new' list. +%% +%% This lets us apply proper overrides to list of elements according to profile +%% priority. This function depends on a stable proplist sort. +tup_umerge([], Olds) -> + Olds; +tup_umerge([New|News], Olds) -> + lists:reverse(umerge(News, Olds, [], New)). + +%% This is equivalent to umerge2_2 in the stdlib, except we use the expanded +%% value/key only to compare +umerge(News, [Old|Olds], Merged, Cmp) when element(1, Cmp) == element(1, Old); + element(1, Cmp) == Old; + Cmp == element(1, Old); + Cmp =< Old -> + umerge(News, Olds, [Cmp | Merged], Cmp, Old); +umerge(News, [Old|Olds], Merged, Cmp) -> + umerge(News, Olds, [Old | Merged], Cmp); +umerge(News, [], Merged, Cmp) -> + lists:reverse(News, [Cmp | Merged]). + +%% Similar to stdlib's umerge2_1 in the stdlib, except that when the expanded +%% value/keys compare equal, we check if the element is a full dupe to clear it +%% (like the stdlib function does) or otherwise keep the duplicate around in +%% an order that prioritizes 'New' elements. +umerge([New|News], Olds, Merged, CmpMerged, Cmp) when CmpMerged == Cmp -> + umerge(News, Olds, Merged, New); +umerge([New|News], Olds, Merged, _CmpMerged, Cmp) when element(1,New) == element(1, Cmp); + element(1,New) == Cmp; + New == element(1, Cmp); + New =< Cmp -> + umerge(News, Olds, [New | Merged], New, Cmp); +umerge([New|News], Olds, Merged, _CmpMerged, Cmp) -> % > + umerge(News, Olds, [Cmp | Merged], New); +umerge([], Olds, Merged, CmpMerged, Cmp) when CmpMerged == Cmp -> + lists:reverse(Olds, Merged); +umerge([], Olds, Merged, _CmpMerged, Cmp) -> + lists:reverse(Olds, [Cmp | Merged]). + diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl index 143c28b..75cbb87 100644 --- a/src/rebar_templater.erl +++ b/src/rebar_templater.erl @@ -156,7 +156,7 @@ default_author_and_email() -> %% Ok, try mecurial case rebar_utils:sh("hg showconfig ui.username", [return_on_error]) of {ok, NameEmail} -> - case re:run(NameEmail, [{capture, [1,2], list}]) of + case re:run(NameEmail, "^(.*) <(.*)>$", [{capture, [1,2], list}]) of {match, [Name, Email]} -> {Name, Email}; _ -> diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index 2f27fd3..df25997 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -387,6 +387,7 @@ beams(Dir) -> -spec abort() -> no_return(). abort() -> throw(rebar_abort). + -spec abort(string(), [term()]) -> no_return(). abort(String, Args) -> ?ERROR(String, Args), diff --git a/test/mock_git_resource.erl b/test/mock_git_resource.erl index 6940f54..9c799fd 100644 --- a/test/mock_git_resource.erl +++ b/test/mock_git_resource.erl @@ -55,7 +55,7 @@ mock_lock(_) -> %% should be updated on a per-name basis: `{update, ["App1", "App3"]}'. mock_update(Opts) -> ToUpdate = proplists:get_value(upgrade, Opts, []), - ct:pal("TOUp: ~p", [ToUpdate]), +% ct:pal("TOUp: ~p", [ToUpdate]), meck:expect( ?MOD, needs_update, fun(_Dir, {git, Url, _Ref}) -> @@ -110,7 +110,7 @@ mock_download(Opts) -> AppDeps = proplists:get_value({App,Vsn}, Deps, []), rebar_test_utils:create_app( Dir, App, Vsn, - [element(1,D) || D <- AppDeps] + [kernel, stdlib] ++ [element(1,D) || D <- AppDeps] ), rebar_test_utils:create_config(Dir, [{deps, AppDeps}]), {ok, 'WHATEVER'} diff --git a/test/mock_pkg_resource.erl b/test/mock_pkg_resource.erl index a22d1b0..afb6fb4 100644 --- a/test/mock_pkg_resource.erl +++ b/test/mock_pkg_resource.erl @@ -78,9 +78,9 @@ mock_download(Opts) -> App = binary_to_list(AppBin), filelib:ensure_dir(Dir), AppDeps = proplists:get_value({App,Vsn}, Deps, []), - {ok, AppInfo} = rebar_test_utils:create_empty_app( - Dir, App, Vsn, - [element(1,D) || D <- AppDeps] + {ok, AppInfo} = rebar_test_utils:create_app( + Dir, App, binary_to_list(Vsn), + [kernel, stdlib] ++ [element(1,D) || D <- AppDeps] ), rebar_test_utils:create_config(Dir, [{deps, AppDeps}]), Tarball = filename:join([Dir, App++"-"++binary_to_list(Vsn)++".tar"]), diff --git a/test/rebar_as_SUITE.erl b/test/rebar_as_SUITE.erl index 864d468..1d1112b 100644 --- a/test/rebar_as_SUITE.erl +++ b/test/rebar_as_SUITE.erl @@ -11,7 +11,8 @@ as_multiple_profiles_multiple_tasks/1, as_comma_placement/1, as_comma_then_space/1, - as_dir_name/1]). + as_dir_name/1, + as_with_task_args/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -29,7 +30,7 @@ init_per_testcase(_, Config) -> all() -> [as_basic, as_multiple_profiles, as_multiple_tasks, as_multiple_profiles_multiple_tasks, as_comma_placement, as_comma_then_space, - as_dir_name]. + as_dir_name, as_with_task_args]. as_basic(Config) -> AppDir = ?config(apps, Config), @@ -118,3 +119,20 @@ as_dir_name(Config) -> true = filelib:is_dir(filename:join([AppDir, "_build", "foo+bar+baz"])). + +as_with_task_args(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("as_with_task_args_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + rebar_test_utils:run_and_check(Config, + [], + ["as", "default", "compile"], + {ok, [{app, Name}]}), + + rebar_test_utils:run_and_check(Config, + [], + ["as", "default", "clean", "-a"], + {ok, [{app, Name, invalid}]}). diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index 2c5a11f..8564754 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -4,6 +4,7 @@ init_per_suite/1, end_per_suite/1, init_per_testcase/2, + end_per_testcase/2, all/0, build_basic_app/1, build_release_apps/1, @@ -12,7 +13,9 @@ build_all_srcdirs/1, recompile_when_opts_change/1, dont_recompile_when_opts_dont_change/1, - dont_recompile_yrl_or_xrl/1]). + dont_recompile_yrl_or_xrl/1, + deps_in_path/1, + checkout_priority/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -30,12 +33,15 @@ end_per_suite(_Config) -> init_per_testcase(_, Config) -> rebar_test_utils:init_rebar_state(Config). +end_per_testcase(_, _Config) -> + catch meck:unload(). + all() -> [build_basic_app, build_release_apps, build_checkout_apps, build_checkout_deps, build_all_srcdirs, recompile_when_opts_change, dont_recompile_when_opts_dont_change, - dont_recompile_yrl_or_xrl]. + dont_recompile_yrl_or_xrl, deps_in_path, checkout_priority]. build_basic_app(Config) -> AppDir = ?config(apps, Config), @@ -88,8 +94,11 @@ build_checkout_deps(Config) -> rebar_test_utils:create_app(filename:join([CheckoutsDir,Name2]), Name2, Vsn2, [kernel, stdlib]), rebar_test_utils:create_app(filename:join([DepsDir,Name2]), Name2, Vsn1, [kernel, stdlib]), + Deps = [{list_to_atom(Name2), Vsn2, {git, "", ""}}], + {ok, RebarConfig} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, Deps}])), + rebar_test_utils:run_and_check( - Config, [], ["compile"], + Config, RebarConfig, ["compile"], {ok, [{app, Name1}, {checkout, Name2}]} ), ok = application:load(list_to_atom(Name2)), @@ -211,3 +220,115 @@ dont_recompile_yrl_or_xrl(Config) -> ?assert(ModTime == NewModTime). +deps_in_path(Config) -> + AppDir = ?config(apps, Config), + StartPaths = code:get_path(), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + DepName = rebar_test_utils:create_random_name("dep1_"), + PkgName = rebar_test_utils:create_random_name("pkg1_"), + mock_git_resource:mock([]), + mock_pkg_resource:mock([ + {pkgdeps, [{{iolist_to_binary(PkgName), iolist_to_binary(Vsn)}, []}]} + ]), + + RConfFile = rebar_test_utils:create_config(AppDir, [{deps, [ + {list_to_atom(DepName), {git, "http://site.com/user/"++DepName++".git", {tag, Vsn}}}, + {list_to_atom(PkgName), Vsn} + ]}]), + {ok, RConf} = file:consult(RConfFile), + %% Make sure apps we look for are not visible + %% Hope not to find src name + ?assertEqual([], [Path || Path <- code:get_path(), + {match, _} <- [re:run(Path, DepName)]]), + %% Hope not to find pkg name in there + ?assertEqual([], [Path || Path <- code:get_path(), + {match, _} <- [re:run(Path, PkgName)]]), + %% Build things + rebar_test_utils:run_and_check( + Config, RConf, ["compile"], + {ok, [{app, Name}, {dep, DepName}, {dep, PkgName}]} + ), + %% Find src name in there + ?assertNotEqual([], [Path || Path <- code:get_path(), + {match, _} <- [re:run(Path, DepName)]]), + %% find pkg name in there + ?assertNotEqual([], [Path || Path <- code:get_path(), + {match, _} <- [re:run(Path, PkgName)]]), + code:set_path(StartPaths), + %% Make sure apps we look for are not visible again + %% Hope not to find src name + ?assertEqual([], [Path || Path <- code:get_path(), + {match, _} <- [re:run(Path, DepName)]]), + %% Hope not to find pkg name in there + ?assertEqual([], [Path || Path <- code:get_path(), + {match, _} <- [re:run(Path, PkgName)]]), + %% Rebuild + rebar_test_utils:run_and_check( + Config, RConf, ["compile"], + {ok, [{app, Name}, {dep, DepName}, {dep, PkgName}]} + ), + %% Find src name in there + ?assertNotEqual([], [Path || Path <- code:get_path(), + {match, _} <- [re:run(Path, DepName)]]), + %% find pkg name in there + ?assertNotEqual([], [Path || Path <- code:get_path(), + {match, _} <- [re:run(Path, PkgName)]]). + +checkout_priority(Config) -> + AppDir = ?config(apps, Config), + CheckoutsDir = ?config(checkouts, Config), + StartPaths = code:get_path(), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + DepName = rebar_test_utils:create_random_name("dep1_"), + PkgName = rebar_test_utils:create_random_name("pkg1_"), + mock_git_resource:mock([]), + mock_pkg_resource:mock([ + {pkgdeps, [{{iolist_to_binary(PkgName), iolist_to_binary(Vsn)}, []}]} + ]), + + RConfFile = rebar_test_utils:create_config(AppDir, [{deps, [ + {list_to_atom(DepName), {git, "http://site.com/user/"++DepName++".git", {tag, Vsn}}}, + {list_to_atom(PkgName), Vsn} + ]}]), + {ok, RConf} = file:consult(RConfFile), + + %% Build with deps. + rebar_test_utils:run_and_check( + Config, RConf, ["compile"], + {ok, [{app, Name}, {dep, DepName}, {dep, PkgName}]} + ), + + %% Build two checkout apps similar to dependencies to be fetched, + %% but on a different version + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([CheckoutsDir,DepName]), DepName, Vsn2, [kernel, stdlib]), + rebar_test_utils:create_app(filename:join([CheckoutsDir,PkgName]), PkgName, Vsn2, [kernel, stdlib]), + + %% Rebuild and make sure the checkout apps are in path + code:set_path(StartPaths), + rebar_test_utils:run_and_check( + Config, RConf, ["compile"], + {ok, [{app, Name}, {checkout, DepName}, {checkout, PkgName}]} + ), + + [DepPath] = [Path || Path <- code:get_path(), + {match, _} <- [re:run(Path, DepName)]], + [PkgPath] = [Path || Path <- code:get_path(), + {match, _} <- [re:run(Path, PkgName)]], + + {ok, [DepApp]} = file:consult(filename:join([DepPath, DepName ++ ".app"])), + {ok, [PkgApp]} = file:consult(filename:join([PkgPath, PkgName ++ ".app"])), + + {application, _, DepProps} = DepApp, + {application, _, PkgProps} = PkgApp, + + ?assertEqual(Vsn2, proplists:get_value(vsn, DepProps)), + ?assertEqual(Vsn2, proplists:get_value(vsn, PkgProps)). diff --git a/test/rebar_eunit_SUITE.erl b/test/rebar_eunit_SUITE.erl index d2d8608..4ec92f2 100644 --- a/test/rebar_eunit_SUITE.erl +++ b/test/rebar_eunit_SUITE.erl @@ -4,8 +4,21 @@ init_per_suite/1, end_per_suite/1, init_per_testcase/2, - all/0, - test_basic_app/1]). + all/0]). +-export([test_basic_app/1, + test_multi_app/1, + test_profile/1, + test_basic_exports/1, + test_multi_exports/1, + test_basic_defines/1, + test_multi_defines/1, + test_single_app_flag/1, + test_multiple_app_flag/1, + test_nonexistent_app_flag/1, + test_single_suite_flag/1, + test_suite_in_app_flag/1, + test_suite_in_wrong_app_flag/1, + test_nonexistent_suite_flag/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -24,14 +37,372 @@ init_per_testcase(_, Config) -> rebar_test_utils:init_rebar_state(Config, "eunit_"). all() -> - [test_basic_app]. + [test_basic_app, test_multi_app, test_profile, + test_basic_exports, test_multi_exports, + test_basic_defines, test_multi_defines, + test_single_app_flag, test_multiple_app_flag, test_nonexistent_app_flag, + test_single_suite_flag, test_suite_in_app_flag, + test_suite_in_wrong_app_flag, test_nonexistent_suite_flag]. test_basic_app(Config) -> AppDir = ?config(apps, Config), Name = rebar_test_utils:create_random_name("basic_"), Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), RebarConfig = [{erl_opts, [{d, some_define}]}], rebar_test_utils:run_and_check(Config, RebarConfig, ["eunit"], {ok, [{app, Name}]}). + +test_multi_app(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("multi_app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name1]), + Name1, + Vsn1, + [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("multi_app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name2]), + Name2, + Vsn2, + [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit"], + {ok, [{app, Name1}, {app, Name2}]}). + +test_profile(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("profile_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}, + {profiles, [{test, [{erl_opts, [debug_info]}]}]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["as", "test", "eunit"], + {ok, [{app, Name}]}). + +test_basic_exports(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("basic_exports_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit"], + {ok, [{app, Name}]}), + + App = list_to_atom("not_a_real_src_" ++ Name), + Suite = list_to_atom("not_a_real_src_" ++ Name ++ "_tests"), + AppExports = App:module_info(exports), + SuiteExports = Suite:module_info(exports), + AppExpect = [{some_test_, 0}], + SuiteExpect = [{some_test_, 0}, {define_test_, 0}], + lists:foreach(fun(Expect) -> true = lists:member(Expect, AppExports) end, AppExpect), + lists:foreach(fun(Expect) -> true = lists:member(Expect, SuiteExports) end, SuiteExpect). + +test_multi_exports(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("multi_exports_app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name1]), + Name1, + Vsn1, + [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("multi_exports_app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name2]), + Name2, + Vsn2, + [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit"], + {ok, [{app, Name1}, {app, Name2}]}), + + App1 = list_to_atom("not_a_real_src_" ++ Name1), + Suite1 = list_to_atom("not_a_real_src_" ++ Name1 ++ "_tests"), + AppExports1 = App1:module_info(exports), + SuiteExports1 = Suite1:module_info(exports), + App2 = list_to_atom("not_a_real_src_" ++ Name2), + Suite2 = list_to_atom("not_a_real_src_" ++ Name2 ++ "_tests"), + AppExports2 = App2:module_info(exports), + SuiteExports2 = Suite2:module_info(exports), + AppExpect = [{some_test_, 0}], + SuiteExpect = [{some_test_, 0}, {define_test_, 0}], + lists:foreach(fun(Expect) -> true = lists:member(Expect, AppExports1) end, AppExpect), + lists:foreach(fun(Expect) -> true = lists:member(Expect, SuiteExports1) end, SuiteExpect), + lists:foreach(fun(Expect) -> true = lists:member(Expect, AppExports2) end, AppExpect), + lists:foreach(fun(Expect) -> true = lists:member(Expect, SuiteExports2) end, SuiteExpect). + +test_basic_defines(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("basic_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + rebar_test_utils:run_and_check(Config, RebarConfig, ["eunit"], {ok, [{app, Name}]}), + + App = list_to_atom("not_a_real_src_" ++ Name), + Suite = list_to_atom("not_a_real_src_" ++ Name ++ "_tests"), + AppOpts = proplists:get_value(options, App:module_info(compile), []), + SuiteOpts = proplists:get_value(options, Suite:module_info(compile), []), + Expect = [{d, some_define}], + lists:foreach(fun(Expect) -> true = lists:member(Expect, AppOpts) end, Expect), + lists:foreach(fun(Expect) -> true = lists:member(Expect, SuiteOpts) end, Expect). + +test_multi_defines(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("multi_app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name1]), + Name1, + Vsn1, + [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("multi_app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name2]), + Name2, + Vsn2, + [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit"], + {ok, [{app, Name1}, {app, Name2}]}), + + App1 = list_to_atom("not_a_real_src_" ++ Name1), + Suite1 = list_to_atom("not_a_real_src_" ++ Name1 ++ "_tests"), + AppOpts1 = proplists:get_value(options, App1:module_info(compile), []), + SuiteOpts1 = proplists:get_value(options, Suite1:module_info(compile), []), + App2 = list_to_atom("not_a_real_src_" ++ Name2), + Suite2 = list_to_atom("not_a_real_src_" ++ Name2 ++ "_tests"), + AppOpts2 = proplists:get_value(options, App2:module_info(compile), []), + SuiteOpts2 = proplists:get_value(options, Suite2:module_info(compile), []), + Expect = [{d, some_define}], + lists:foreach(fun(Expect) -> true = lists:member(Expect, AppOpts1) end, Expect), + lists:foreach(fun(Expect) -> true = lists:member(Expect, SuiteOpts1) end, Expect), + lists:foreach(fun(Expect) -> true = lists:member(Expect, AppOpts2) end, Expect), + lists:foreach(fun(Expect) -> true = lists:member(Expect, SuiteOpts2) end, Expect). + +test_single_app_flag(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("multi_exports_app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name1]), + Name1, + Vsn1, + [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("multi_exports_app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name2]), + Name2, + Vsn2, + [kernel, stdlib]), + + BareSuite = io_lib:format("-module(all_tests).\n" + "-compile(export_all).\n" + "-include_lib(\"eunit/include/eunit.hrl\").\n" + "some_test_() -> ?_assert(true).\n" + "define_test_() -> ?_assertEqual(true, ?some_define).\n", []), + FileName = filename:join([AppDir, "test", "all_tests.erl"]), + ok = filelib:ensure_dir(FileName), + ok = ec_file:write(FileName, BareSuite), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", "--app=" ++ Name1], + {ok, [{app, Name1}, {app, Name2}]}), + + Suite1 = list_to_atom("not_a_real_src_" ++ Name1 ++ "_tests"), + {module, Suite1} = code:ensure_loaded(Suite1), + Suite2 = list_to_atom("not_a_real_src_" ++ Name2 ++ "_tests"), + {error, nofile} = code:ensure_loaded(Suite2), + {error, nofile} = code:ensure_loaded(all_tests). + +test_multiple_app_flag(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("multi_exports_app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name1]), + Name1, + Vsn1, + [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("multi_exports_app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name2]), + Name2, + Vsn2, + [kernel, stdlib]), + + BareSuite = io_lib:format("-module(all_tests).\n" + "-compile(export_all).\n" + "-include_lib(\"eunit/include/eunit.hrl\").\n" + "some_test_() -> ?_assert(true).\n" + "define_test_() -> ?_assertEqual(true, ?some_define).\n", []), + FileName = filename:join([AppDir, "test", "all_tests.erl"]), + ok = filelib:ensure_dir(FileName), + ok = ec_file:write(FileName, BareSuite), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", "--app=" ++ Name1 ++ "," ++ Name2], + {ok, [{app, Name1}, {app, Name2}]}), + + Suite1 = list_to_atom("not_a_real_src_" ++ Name1 ++ "_tests"), + {module, Suite1} = code:ensure_loaded(Suite1), + Suite2 = list_to_atom("not_a_real_src_" ++ Name2 ++ "_tests"), + {module, Suite2} = code:ensure_loaded(Suite2), + {error, nofile} = code:ensure_loaded(all_tests). + +test_nonexistent_app_flag(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("multi_exports_app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name1]), + Name1, + Vsn1, + [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("multi_exports_app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name2]), + Name2, + Vsn2, + [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + {error, {_, Error}} = rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", "--app=not_a_real_app"], + return), + + Error = {error_running_tests, "Application `not_a_real_app' not found in project."}. + +test_single_suite_flag(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("multi_exports_app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name1]), + Name1, + Vsn1, + [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("multi_exports_app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name2]), + Name2, + Vsn2, + [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", "--suite=not_a_real_src_" ++ Name1], + {ok, [{app, Name1}, {app, Name2}]}), + + Suite1 = list_to_atom("not_a_real_src_" ++ Name1 ++ "_tests"), + {module, Suite1} = code:ensure_loaded(Suite1). + +test_suite_in_app_flag(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("multi_exports_app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name1]), + Name1, + Vsn1, + [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("multi_exports_app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name2]), + Name2, + Vsn2, + [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", + "--app=" ++ Name1, + "--suite=not_a_real_src_" ++ Name1], + {ok, [{app, Name1}, {app, Name2}]}), + + Suite1 = list_to_atom("not_a_real_src_" ++ Name1 ++ "_tests"), + {module, Suite1} = code:ensure_loaded(Suite1), + Suite2 = list_to_atom("not_a_real_src_" ++ Name2 ++ "_tests"), + {error, nofile} = code:ensure_loaded(Suite2). + +test_suite_in_wrong_app_flag(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("multi_exports_app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name1]), + Name1, + Vsn1, + [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("multi_exports_app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name2]), + Name2, + Vsn2, + [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + {error, {_, Error}} = rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", + "--app=" ++ Name1, + "--suite=not_a_real_src_" ++ Name2], + return), + + Error = {error_running_tests, "Module `not_a_real_src_" ++ + Name2 ++ + "' not found in applications."}. + +test_nonexistent_suite_flag(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("multi_exports_app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name1]), + Name1, + Vsn1, + [kernel, stdlib]), + Name2 = rebar_test_utils:create_random_name("multi_exports_app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(filename:join([AppDir,Name2]), + Name2, + Vsn2, + [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}], + {error, {_, Error}} = rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", "--suite=not_a_real_module"], + return), + + Error = {error_running_tests, "Module `not_a_real_module' not found in applications."}. diff --git a/test/rebar_new_SUITE.erl b/test/rebar_new_SUITE.erl index e382ae4..3cee6f2 100644 --- a/test/rebar_new_SUITE.erl +++ b/test/rebar_new_SUITE.erl @@ -6,7 +6,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -all() -> [app, app_with_fallbacks]. +all() -> [app_git_user, app_hg_user, app_with_fallbacks]. init_per_testcase(Case, Config0) -> @@ -31,7 +31,7 @@ mock_empty_escript_templates() -> meck:new(rebar_utils, [passthrough]), meck:expect(rebar_utils, escript_foldl, fun(_,_,_) -> {ok, []} end). -app(Config) -> +app_git_user(Config) -> meck:expect(rebar_utils, sh, fun("git config --global user.name", _) -> {ok, "gitname"}; ("git config --global user.email", _) -> {ok, "git@email.com"} end), @@ -73,6 +73,28 @@ app_with_fallbacks(Config) -> {filename:join(["src", Name++"_app.erl"]), [Name]} ]). +app_hg_user(Config) -> + meck:expect(rebar_utils, sh, fun("hg showconfig ui.username", _) -> {ok, "hgname <hg@email.com>"}; + (_, _) -> {error, fallback} + end), + + Name = ?config(name, Config), + rebar_test_utils:run_and_check( + Config, [], + ["new", "test_app", Name], + {ok, []} + ), + validate_files( + Config, Name, + [{"LICENSE", ["hgname", "hg@email.com"]}, + {"README.md", [Name]}, + {".gitignore", []}, + {"rebar.config", []}, + {filename:join(["src", Name++".app.src"]), [Name]}, + {filename:join(["src", Name++"_sup.erl"]), [Name]}, + {filename:join(["src", Name++"_app.erl"]), [Name]} + ]). + validate_files(_Config, Name, Checks) -> [begin Path = filename:join([Name, File]), diff --git a/test/rebar_opts_parser_SUITE.erl b/test/rebar_opts_parser_SUITE.erl new file mode 100644 index 0000000..fc738b9 --- /dev/null +++ b/test/rebar_opts_parser_SUITE.erl @@ -0,0 +1,54 @@ +-module(rebar_opts_parser_SUITE). + +-export([all/0, init_per_testcase/2]). +-export([bad_arg_to_flag/1, missing_arg_to_flag/1]). + +-include_lib("common_test/include/ct.hrl"). + + +all() -> [bad_arg_to_flag, missing_arg_to_flag]. + +init_per_testcase(_, Config) -> + rebar_test_utils:init_rebar_state(Config, "opts_parser_"). + +bad_arg_to_flag(Config) -> + ok = meck:new(getopt), + ok = meck:expect(getopt, + parse, + fun(_, _) -> {error, {invalid_option_arg, {foo, "null"}}} end), + + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("bad_arg_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + {error, Error} = rebar_test_utils:run_and_check(Config, + [], + ["compile", "--foo=null"], + return), + + true = meck:validate(getopt), + ok = meck:unload(getopt), + + "Invalid argument null to option foo" = lists:flatten(Error). + +missing_arg_to_flag(Config) -> + ok = meck:new(getopt), + ok = meck:expect(getopt, parse, fun(_, _) -> {error, {missing_option_arg, foo}} end), + + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("missing_arg_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + {error, Error} = rebar_test_utils:run_and_check(Config, + [], + ["compile", "--foo"], + return), + + true = meck:validate(getopt), + ok = meck:unload(getopt), + + "Missing argument to option foo" = lists:flatten(Error).
\ No newline at end of file diff --git a/test/rebar_profiles_SUITE.erl b/test/rebar_profiles_SUITE.erl index a4f926e..6288053 100644 --- a/test/rebar_profiles_SUITE.erl +++ b/test/rebar_profiles_SUITE.erl @@ -100,12 +100,17 @@ profile_merges(_Config) -> {test2, "hello"}, {test3, [key3]}, {test4, "oldvalue"}, + {test5, [{key5, true}]}, + {test6, [{key6, false}]}, {profiles, [{profile1, [{test1, [{key3, 5}, key1]}]}, {profile2, [{test2, "goodbye"}, {test3, []}, - {test4, []}]}]}], + {test4, []}, + {test5, [{key5, false}]}, + {test6, [{key6, true}]} + ]}]}], State = rebar_state:new(RebarConfig), State1 = rebar_state:apply_profiles(State, [profile1, profile2]), @@ -118,7 +123,9 @@ profile_merges(_Config) -> %% Check that a newvalue of []/"" doesn't override non-string oldvalues [key3] = rebar_state:get(State1, test3), - [] = rebar_state:get(State1, test4). + [] = rebar_state:get(State1, test4), + [{key5, false}, {key5, true}] = rebar_state:get(State1, test5), + [{key6, true}, {key6, false}] = rebar_state:get(State1, test6). add_to_profile(_Config) -> RebarConfig = [{foo, true}, {bar, false}], @@ -165,7 +172,7 @@ test_profile_applied_at_completion(Config) -> Name = rebar_test_utils:create_random_name("test_profile_at_completion_"), Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), RebarConfig = [{erl_opts, [{d, some_define}]}], rebar_test_utils:create_config(AppDir, RebarConfig), @@ -184,7 +191,7 @@ test_profile_applied_before_compile(Config) -> Name = rebar_test_utils:create_random_name("test_profile_before_compile_"), Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), RebarConfig = [{erl_opts, [{d, some_define}]}], rebar_test_utils:create_config(AppDir, RebarConfig), @@ -199,7 +206,7 @@ test_profile_applied_before_eunit(Config) -> Name = rebar_test_utils:create_random_name("test_profile_before_eunit_"), Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), RebarConfig = [{erl_opts, [{d, some_define}]}], rebar_test_utils:create_config(AppDir, RebarConfig), @@ -214,7 +221,7 @@ test_profile_applied_to_apps(Config) -> Name = rebar_test_utils:create_random_name("test_profile_applied_to_apps_"), Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), RebarConfig = [{erl_opts, [{d, some_define}]}], rebar_test_utils:create_config(AppDir, RebarConfig), diff --git a/test/rebar_release_SUITE.erl b/test/rebar_release_SUITE.erl index 92219a5..3809106 100644 --- a/test/rebar_release_SUITE.erl +++ b/test/rebar_release_SUITE.erl @@ -3,7 +3,10 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -all() -> [release, tar]. +all() -> [release, + dev_mode_release, + profile_dev_mode_override_release, + tar]. init_per_testcase(Case, Config0) -> Config = rebar_test_utils:init_rebar_state(Config0), @@ -33,9 +36,46 @@ release(Config) -> rebar_test_utils:run_and_check( Config, RebarConfig, ["release"], - {ok, [{release, list_to_atom(Name), Vsn}]} + {ok, [{release, list_to_atom(Name), Vsn, false}]} ). +dev_mode_release(Config) -> + AppDir = ?config(apps, Config), + Name = ?config(name, Config), + Vsn = "1.0.0", + {ok, RebarConfig} = + file:consult(rebar_test_utils:create_config(AppDir, + [{relx, [{release, {list_to_atom(Name), Vsn}, + [list_to_atom(Name)]}, + {lib_dirs, [AppDir]}, + {dev_mode, true}]}])), + rebar_test_utils:run_and_check( + Config, RebarConfig, + ["release"], + {ok, [{release, list_to_atom(Name), Vsn, true}]} + ). + + +profile_dev_mode_override_release(Config) -> + AppDir = ?config(apps, Config), + Name = ?config(name, Config), + Vsn = "1.0.0", + {ok, RebarConfig} = + file:consult(rebar_test_utils:create_config(AppDir, + [{relx, [{release, {list_to_atom(Name), Vsn}, + [list_to_atom(Name)]}, + {lib_dirs, [AppDir]}, + {dev_mode, true}]}, + {profiles, + [{ct, + [{relx, [{dev_mode, false}]}]}]}])), + rebar_test_utils:run_and_check( + Config, RebarConfig, + ["as", "ct", "release"], + {ok, [{release, list_to_atom(Name), Vsn, false}]} + ). + + tar(Config) -> AppDir = ?config(apps, Config), Name = ?config(name, Config), @@ -48,5 +88,5 @@ tar(Config) -> rebar_test_utils:run_and_check( Config, RebarConfig, ["tar"], - {ok, [{release, list_to_atom(Name), Vsn}, {tar, Name, Vsn}]} + {ok, [{release, list_to_atom(Name), Vsn, false}, {tar, Name, Vsn}]} ). diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index 7c52a18..2eb14ac 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -3,7 +3,7 @@ -include_lib("eunit/include/eunit.hrl"). -export([init_rebar_state/1, init_rebar_state/2, run_and_check/4]). -export([expand_deps/2, flat_deps/1, flat_pkgdeps/1, top_level_deps/1]). --export([create_app/4, create_empty_app/4, create_config/2]). +-export([create_app/4, create_eunit_app/4, create_empty_app/4, create_config/2]). -export([create_random_name/1, create_random_vsn/0]). %%%%%%%%%%%%%% @@ -25,7 +25,8 @@ init_rebar_state(Config, Name) -> ok = ec_file:mkdir_p(CheckoutsDir), Verbosity = rebar3:log_level(), rebar_log:init(command_line, Verbosity), - State = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])}]), + State = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])} + ,{root_dir, AppsDir}]), [{apps, AppsDir}, {checkouts, CheckoutsDir}, {state, State} | Config]. %% @doc Takes common test config, a rebar config ([] if empty), a command to @@ -69,7 +70,17 @@ run_and_check(Config, RebarConfig, Command, Expect) -> %% And returns a `rebar_app_info' object. create_app(AppDir, Name, Vsn, Deps) -> write_src_file(AppDir, Name), - write_test_file(AppDir, Name), + write_app_src_file(AppDir, Name, Vsn, Deps), + rebar_app_info:new(Name, Vsn, AppDir, Deps). + +%% @doc Creates a dummy application including: +%% - src/<file>.erl +%% - src/<file>.app.src +%% - test/<file>_tests.erl +%% And returns a `rebar_app_info' object. +create_eunit_app(AppDir, Name, Vsn, Deps) -> + write_eunitized_src_file(AppDir, Name), + write_eunit_suite_file(AppDir, Name), write_app_src_file(AppDir, Name, Vsn, Deps), rebar_app_info:new(Name, Vsn, AppDir, Deps). @@ -217,7 +228,7 @@ check_results(AppDir, Expected) -> ?assertEqual(iolist_to_binary(Vsn), iolist_to_binary(LockVsn)) end - ; ({release, Name, Vsn}) -> + ; ({release, Name, Vsn, ExpectedDevMode}) -> ct:pal("Release: ~p-~s", [Name, Vsn]), {ok, Cwd} = file:get_cwd(), try @@ -228,6 +239,15 @@ check_results(AppDir, Expected) -> {ok, RelxState2} = rlx_prv_app_discover:do(RelxState1), {ok, RelxState3} = rlx_prv_rel_discover:do(RelxState2), + LibDir = filename:join([ReleaseDir, Name, "lib"]), + {ok, RelLibs} = file:list_dir(LibDir), + IsSymLinkFun = + fun(X) -> + ec_file:is_symlink(filename:join(LibDir, X)) + end, + DevMode = lists:all(IsSymLinkFun, RelLibs), + ?assertEqual(ExpectedDevMode, DevMode), + %% throws not_found if it doesn't exist rlx_state:get_realized_release(RelxState3, Name, Vsn) catch @@ -250,10 +270,15 @@ write_src_file(Dir, Name) -> ok = filelib:ensure_dir(Erl), ok = ec_file:write(Erl, erl_src_file("not_a_real_src_" ++ Name ++ ".erl")). -write_test_file(Dir, Name) -> +write_eunitized_src_file(Dir, Name) -> + Erl = filename:join([Dir, "src", "not_a_real_src_" ++ Name ++ ".erl"]), + ok = filelib:ensure_dir(Erl), + ok = ec_file:write(Erl, erl_eunitized_src_file("not_a_real_src_" ++ Name ++ ".erl")). + +write_eunit_suite_file(Dir, Name) -> Erl = filename:join([Dir, "test", "not_a_real_src_" ++ Name ++ "_tests.erl"]), ok = filelib:ensure_dir(Erl), - ok = ec_file:write(Erl, erl_test_file("not_a_real_src_" ++ Name ++ ".erl")). + ok = ec_file:write(Erl, erl_eunit_suite_file("not_a_real_src_" ++ Name ++ ".erl")). write_app_file(Dir, Name, Version, Deps) -> Filename = filename:join([Dir, "ebin", Name ++ ".app"]), @@ -268,13 +293,18 @@ write_app_src_file(Dir, Name, Version, Deps) -> erl_src_file(Name) -> io_lib:format("-module(~s).\n" "-export([main/0]).\n" + "main() -> ok.\n", [filename:basename(Name, ".erl")]). + +erl_eunitized_src_file(Name) -> + io_lib:format("-module(~s).\n" + "-export([main/0]).\n" "main() -> ok.\n" "-ifdef(TEST).\n" "-include_lib(\"eunit/include/eunit.hrl\").\n" "some_test_() -> ?_assertEqual(ok, main()).\n" "-endif.\n", [filename:basename(Name, ".erl")]). -erl_test_file(Name) -> +erl_eunit_suite_file(Name) -> BaseName = filename:basename(Name, ".erl"), io_lib:format("-module(~s_tests).\n" "-compile(export_all).\n" |