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.

343 rivejä
14 KiB

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