You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

245 line
10 KiB

10 年之前
  1. -module(rebar_test_utils).
  2. -include_lib("common_test/include/ct.hrl").
  3. -include_lib("eunit/include/eunit.hrl").
  4. -export([init_rebar_state/1, init_rebar_state/2, run_and_check/4]).
  5. -export([create_app/4, create_empty_app/4, create_config/2]).
  6. -export([create_random_name/1, create_random_vsn/0]).
  7. %%%%%%%%%%%%%%
  8. %%% Public %%%
  9. %%%%%%%%%%%%%%
  10. %% @doc {@see init_rebar_state/2}
  11. init_rebar_state(Config) -> init_rebar_state(Config, "apps_dir1_").
  12. %% @doc Takes a common test config and a name (string) and sets up
  13. %% a basic OTP app directory with a pre-configured rebar state to
  14. %% run tests with.
  15. init_rebar_state(Config, Name) ->
  16. application:load(rebar),
  17. DataDir = ?config(priv_dir, Config),
  18. AppsDir = filename:join([DataDir, create_random_name(Name)]),
  19. CheckoutsDir = filename:join([AppsDir, "_checkouts"]),
  20. ok = ec_file:mkdir_p(AppsDir),
  21. ok = ec_file:mkdir_p(CheckoutsDir),
  22. Verbosity = rebar3:log_level(),
  23. rebar_log:init(command_line, Verbosity),
  24. State = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])}]),
  25. [{apps, AppsDir}, {checkouts, CheckoutsDir}, {state, State} | Config].
  26. %% @doc Takes common test config, a rebar config ([] if empty), a command to
  27. %% run ("install_deps", "compile", etc.), and a list of expected applications
  28. %% and/or dependencies to be present, and verifies whether they are all in
  29. %% place.
  30. %%
  31. %% The expectation list takes elements of the form:
  32. %% - `{app, Name :: string()}': checks that the app is properly built.
  33. %% - `{dep, Name :: string()}': checks that the dependency has been fetched.
  34. %% Ignores the build status of the dependency.
  35. %% - `{dep, Name :: string(), Vsn :: string()}': checks that the dependency
  36. %% has been fetched, and that a given version has been chosen. Useful to
  37. %% test for conflict resolution. Also ignores the build status of the
  38. %% dependency.
  39. %%
  40. %% This function assumes `init_rebar_state/1-2' has run before, in order to
  41. %% fetch the `apps' and `state' values from the CT config.
  42. run_and_check(Config, RebarConfig, Command, Expect) ->
  43. %% Assumes init_rebar_state has run first
  44. AppDir = ?config(apps, Config),
  45. State = ?config(state, Config),
  46. Res = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), Command),
  47. case Expect of
  48. {error, Reason} ->
  49. ?assertEqual({error, Reason}, Res);
  50. {ok, Expected} ->
  51. {ok, _} = Res,
  52. check_results(AppDir, Expected);
  53. return ->
  54. Res
  55. end.
  56. %% @doc Creates a dummy application including:
  57. %% - src/<file>.erl
  58. %% - src/<file>.app.src
  59. %% And returns a `rebar_app_info' object.
  60. create_app(AppDir, Name, Vsn, Deps) ->
  61. write_src_file(AppDir, Name),
  62. write_test_file(AppDir, Name),
  63. write_app_src_file(AppDir, Name, Vsn, Deps),
  64. rebar_app_info:new(Name, Vsn, AppDir, Deps).
  65. %% @doc Creates a dummy application including:
  66. %% - ebin/<file>.app
  67. %% And returns a `rebar_app_info' object.
  68. create_empty_app(AppDir, Name, Vsn, Deps) ->
  69. write_app_file(AppDir, Name, Vsn, Deps),
  70. rebar_app_info:new(Name, Vsn, AppDir, Deps).
  71. %% @doc Creates a rebar.config file. The function accepts a list of terms,
  72. %% each of which will be dumped as a consult file. For example, the list
  73. %% `[a, b, c]' will return the consult file `a. b. c.'.
  74. create_config(AppDir, Contents) ->
  75. Conf = filename:join([AppDir, "rebar.config"]),
  76. ok = filelib:ensure_dir(Conf),
  77. Config = lists:flatten([io_lib:fwrite("~p.~n", [Term]) || Term <- Contents]),
  78. ok = ec_file:write(Conf, Config),
  79. Conf.
  80. %% @doc Util to create a random variation of a given name.
  81. create_random_name(Name) ->
  82. random:seed(erlang:now()),
  83. Name ++ erlang:integer_to_list(random:uniform(1000000)).
  84. %% @doc Util to create a random variation of a given version.
  85. create_random_vsn() ->
  86. random:seed(erlang:now()),
  87. lists:flatten([erlang:integer_to_list(random:uniform(100)),
  88. ".", erlang:integer_to_list(random:uniform(100)),
  89. ".", erlang:integer_to_list(random:uniform(100))]).
  90. %%%%%%%%%%%%%%%
  91. %%% Helpers %%%
  92. %%%%%%%%%%%%%%%
  93. check_results(AppDir, Expected) ->
  94. BuildDir = filename:join([AppDir, "_build", "lib"]),
  95. CheckoutsDir = filename:join([AppDir, "_checkouts"]),
  96. LockFile = filename:join([AppDir, "rebar.lock"]),
  97. Locks = lists:flatten(rebar_config:consult_file(LockFile)),
  98. Apps = rebar_app_discover:find_apps([AppDir]),
  99. InvalidApps = rebar_app_discover:find_apps([AppDir], invalid),
  100. ValidApps = rebar_app_discover:find_apps([AppDir], valid),
  101. AppsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Apps],
  102. InvalidAppsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- InvalidApps],
  103. ValidAppsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- ValidApps],
  104. Deps = rebar_app_discover:find_apps([BuildDir], all),
  105. DepsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Deps],
  106. Checkouts = rebar_app_discover:find_apps([CheckoutsDir], all),
  107. CheckoutsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Checkouts],
  108. lists:foreach(
  109. fun({app, Name}) ->
  110. ct:pal("Name: ~p", [Name]),
  111. case lists:keyfind(Name, 1, AppsNames) of
  112. false ->
  113. error({app_not_found, Name});
  114. {Name, _App} ->
  115. ok
  116. end
  117. ; ({app, Name, invalid}) ->
  118. ct:pal("Name: ~p", [Name]),
  119. case lists:keyfind(Name, 1, InvalidAppsNames) of
  120. false ->
  121. error({app_not_found, Name});
  122. {Name, _App} ->
  123. ok
  124. end
  125. ; ({app, Name, valid}) ->
  126. ct:pal("Name: ~p", [Name]),
  127. case lists:keyfind(Name, 1, ValidAppsNames) of
  128. false ->
  129. error({app_not_found, Name});
  130. {Name, _App} ->
  131. ok
  132. end
  133. ; ({checkout, Name}) ->
  134. ct:pal("Name: ~p", [Name]),
  135. ?assertNotEqual(false, lists:keyfind(Name, 1, CheckoutsNames))
  136. ; ({dep, Name}) ->
  137. ct:pal("Name: ~p", [Name]),
  138. ?assertNotEqual(false, lists:keyfind(Name, 1, DepsNames))
  139. ; ({dep, Name, Vsn}) ->
  140. ct:pal("Name: ~p, Vsn: ~p", [Name, Vsn]),
  141. case lists:keyfind(Name, 1, DepsNames) of
  142. false ->
  143. error({dep_not_found, Name});
  144. {Name, App} ->
  145. ?assertEqual(iolist_to_binary(Vsn),
  146. iolist_to_binary(rebar_app_info:original_vsn(App)))
  147. end
  148. ; ({lock, Name}) ->
  149. ct:pal("Name: ~p", [Name]),
  150. ?assertNotEqual(false, lists:keyfind(iolist_to_binary(Name), 1, Locks))
  151. ; ({lock, Name, Vsn}) ->
  152. ct:pal("Name: ~p, Vsn: ~p", [Name, Vsn]),
  153. case lists:keyfind(iolist_to_binary(Name), 1, Locks) of
  154. false ->
  155. error({lock_not_found, Name});
  156. {_LockName, {_, _, {ref, LockVsn}}, _} ->
  157. ?assertEqual(iolist_to_binary(Vsn),
  158. iolist_to_binary(LockVsn))
  159. end
  160. ; ({release, Name, Vsn}) ->
  161. ct:pal("Release: ~p-~s", [Name, Vsn]),
  162. {ok, Cwd} = file:get_cwd(),
  163. try
  164. file:set_cwd(AppDir),
  165. ReleaseDir = filename:join([AppDir, "_build", "rel"]),
  166. RelxState = rlx_state:new("", [], []),
  167. RelxState1 = rlx_state:base_output_dir(RelxState, ReleaseDir),
  168. {ok, RelxState2} = rlx_prv_app_discover:do(RelxState1),
  169. {ok, RelxState3} = rlx_prv_rel_discover:do(RelxState2),
  170. %% throws not_found if it doesn't exist
  171. rlx_state:get_realized_release(RelxState3, Name, Vsn)
  172. catch
  173. _ ->
  174. ct:fail(release_not_found)
  175. after
  176. file:set_cwd(Cwd)
  177. end
  178. ; ({tar, Name, Vsn}) ->
  179. ct:pal("Tarball: ~s-~s", [Name, Vsn]),
  180. Tarball = filename:join([AppDir, "_build", "rel", Name, Name++"-"++Vsn++".tar.gz"]),
  181. ?assertNotEqual([], filelib:is_file(Tarball))
  182. end, Expected).
  183. write_src_file(Dir, Name) ->
  184. Erl = filename:join([Dir, "src", "not_a_real_src_" ++ Name ++ ".erl"]),
  185. ok = filelib:ensure_dir(Erl),
  186. ok = ec_file:write(Erl, erl_src_file("not_a_real_src_" ++ Name ++ ".erl")).
  187. write_test_file(Dir, Name) ->
  188. Erl = filename:join([Dir, "test", "not_a_real_src_" ++ Name ++ "_tests.erl"]),
  189. ok = filelib:ensure_dir(Erl),
  190. ok = ec_file:write(Erl, erl_test_file("not_a_real_src_" ++ Name ++ ".erl")).
  191. write_app_file(Dir, Name, Version, Deps) ->
  192. Filename = filename:join([Dir, "ebin", Name ++ ".app"]),
  193. ok = filelib:ensure_dir(Filename),
  194. ok = ec_file:write_term(Filename, get_app_metadata(ec_cnv:to_list(Name), Version, Deps)).
  195. write_app_src_file(Dir, Name, Version, Deps) ->
  196. Filename = filename:join([Dir, "src", Name ++ ".app.src"]),
  197. ok = filelib:ensure_dir(Filename),
  198. ok = ec_file:write_term(Filename, get_app_metadata(ec_cnv:to_list(Name), Version, Deps)).
  199. erl_src_file(Name) ->
  200. io_lib:format("-module(~s).\n"
  201. "-export([main/0]).\n"
  202. "main() -> ok.\n"
  203. "-ifdef(TEST).\n"
  204. "-include_lib(\"eunit/include/eunit.hrl\").\n"
  205. "some_test_() -> ?_assertEqual(ok, main()).\n"
  206. "-endif.\n", [filename:basename(Name, ".erl")]).
  207. erl_test_file(Name) ->
  208. BaseName = filename:basename(Name, ".erl"),
  209. io_lib:format("-module(~s_tests).\n"
  210. "-compile(export_all).\n"
  211. "-ifndef(some_define).\n"
  212. "-define(some_define, false).\n"
  213. "-endif.\n"
  214. "-ifdef(TEST).\n"
  215. "-include_lib(\"eunit/include/eunit.hrl\").\n"
  216. "some_test_() -> ?_assertEqual(ok, ~s:main()).\n"
  217. "define_test_() -> ?_assertEqual(true, ?some_define).\n"
  218. "-endif.\n", [BaseName, BaseName]).
  219. get_app_metadata(Name, Vsn, Deps) ->
  220. {application, erlang:list_to_atom(Name),
  221. [{description, ""},
  222. {vsn, Vsn},
  223. {modules, []},
  224. {included_applications, []},
  225. {registered, []},
  226. {applications, Deps}]}.