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 lines
10 KiB

преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 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}]}.