Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

293 строки
12 KiB

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