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

285 строки
10 KiB

10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
  1. -module(rebar_install_deps_SUITE).
  2. -compile(export_all).
  3. -include_lib("common_test/include/ct.hrl").
  4. -include_lib("eunit/include/eunit.hrl").
  5. -include_lib("kernel/include/file.hrl").
  6. all() -> [{group, git}, {group, pkg}].
  7. groups() ->
  8. [{all, [], [flat, pick_highest_left, pick_highest_right,
  9. pick_smallest1, pick_smallest2,
  10. circular1, circular2, circular_skip,
  11. fail_conflict, default_profile, nondefault_profile]},
  12. {git, [], [{group, all}]},
  13. {pkg, [], [{group, all}]}].
  14. init_per_suite(Config) ->
  15. application:start(meck),
  16. Config.
  17. end_per_suite(_Config) ->
  18. application:stop(meck).
  19. init_per_group(git, Config) ->
  20. [{deps_type, git} | Config];
  21. init_per_group(pkg, Config) ->
  22. [{deps_type, pkg} | Config];
  23. init_per_group(_, Config) ->
  24. Config.
  25. end_per_group(_, Config) ->
  26. Config.
  27. init_per_testcase(Case, Config) ->
  28. {Deps, Warnings, Expect} = deps(Case),
  29. Expected = case Expect of
  30. {ok, List} -> {ok, format_expected_deps(List)};
  31. Other -> Other
  32. end,
  33. DepsType = ?config(deps_type, Config),
  34. mock_warnings(),
  35. [{expect, Expected},
  36. {warnings, Warnings}
  37. | setup_project(Case, Config, rebar_test_utils:expand_deps(DepsType, Deps))].
  38. end_per_testcase(_, Config) ->
  39. meck:unload(),
  40. Config.
  41. format_expected_deps(Deps) ->
  42. [case Dep of
  43. {N,V} -> {dep, N, V};
  44. N -> {dep, N}
  45. end || Dep <- Deps].
  46. %% format:
  47. %% {Spec,
  48. %% [Warning],
  49. %% {ok, Result} | {error, Reason}}
  50. %%
  51. %% Spec is a list of levelled dependencies of two possible forms:
  52. %% - {"Name", Spec}
  53. %% - {"Name", "Vsn", Spec}
  54. %%
  55. %% Warnings are going to match on mocked ?WARN(...)
  56. %% calls to be evaluated. An empty list means we do not care about
  57. %% warnings, not that no warnings will be printed. This means
  58. %% the list of warning isn't interpreted to be exhaustive, and more
  59. %% warnings may be generated than are listed.
  60. deps(flat) ->
  61. {[{"B", []},
  62. {"C", []}],
  63. [],
  64. {ok, ["B", "C"]}};
  65. deps(pick_highest_left) ->
  66. {[{"B", [{"C", "2", []}]},
  67. {"C", "1", []}],
  68. [{"C","2"}],
  69. {ok, ["B", {"C", "1"}]}};
  70. deps(pick_highest_right) ->
  71. {[{"B", "1", []},
  72. {"C", [{"B", "2", []}]}],
  73. [{"B","2"}],
  74. {ok, [{"B","1"}, "C"]}};
  75. deps(pick_smallest1) ->
  76. {[{"B", [{"D", "1", []}]},
  77. {"C", [{"D", "2", []}]}],
  78. [{"D","2"}],
  79. %% we pick D1 because B < C
  80. {ok, ["B","C",{"D","1"}]}};
  81. deps(pick_smallest2) ->
  82. {[{"C", [{"D", "2", []}]},
  83. {"B", [{"D", "1", []}]}],
  84. [{"D","2"}],
  85. %% we pick D1 because B < C
  86. {ok, ["B","C",{"D","1"}]}};
  87. deps(circular1) ->
  88. {[{"B", [{"A", []}]}, % A is the top-level app
  89. {"C", []}],
  90. [],
  91. {error, {rebar_prv_install_deps, {cycles, [[<<"A">>,<<"B">>]]}}}};
  92. deps(circular2) ->
  93. {[{"B", [{"C", [{"B", []}]}]},
  94. {"C", []}],
  95. [],
  96. {error, {rebar_prv_install_deps, {cycles, [[<<"B">>,<<"C">>]]}}}};
  97. deps(circular_skip) ->
  98. %% Never spot the circular dep due to being to low in the deps tree
  99. %% in source deps
  100. {[{"B", [{"C", "2", [{"B", []}]}]},
  101. {"C", "1", [{"D",[]}]}],
  102. [{"C","2"}],
  103. {ok, ["B", {"C","1"}, "D"]}};
  104. deps(fail_conflict) ->
  105. {[{"B", [{"C", "2", []}]},
  106. {"C", "1", []}],
  107. [{"C","2"}],
  108. rebar_abort};
  109. deps(default_profile) ->
  110. {[{"B", []},
  111. {"C", []}],
  112. [],
  113. {ok, ["B", "C"]}};
  114. deps(nondefault_profile) ->
  115. {[{"B", []},
  116. {"C", []}],
  117. [],
  118. {ok, ["B", "C"]}}.
  119. setup_project(fail_conflict, Config0, Deps) ->
  120. DepsType = ?config(deps_type, Config0),
  121. Config = rebar_test_utils:init_rebar_state(
  122. Config0,
  123. "fail_conflict_"++atom_to_list(DepsType)++"_"
  124. ),
  125. AppDir = ?config(apps, Config),
  126. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  127. TopDeps = rebar_test_utils:top_level_deps(Deps),
  128. RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps},
  129. {deps_error_on_conflict, true}]),
  130. case DepsType of
  131. git ->
  132. mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]);
  133. pkg ->
  134. mock_pkg_resource:mock([{pkgdeps, rebar_test_utils:flat_pkgdeps(Deps)}])
  135. end,
  136. [{rebarconfig, RebarConf} | Config];
  137. setup_project(nondefault_profile, Config0, Deps) ->
  138. DepsType = ?config(deps_type, Config0),
  139. Config = rebar_test_utils:init_rebar_state(
  140. Config0,
  141. "nondefault_profile_"++atom_to_list(DepsType)++"_"
  142. ),
  143. AppDir = ?config(apps, Config),
  144. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  145. TopDeps = rebar_test_utils:top_level_deps(Deps),
  146. RebarConf = rebar_test_utils:create_config(AppDir, [{profiles, [
  147. {nondef, [{deps, TopDeps}]}
  148. ]}]),
  149. case DepsType of
  150. git ->
  151. mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]);
  152. pkg ->
  153. mock_pkg_resource:mock([{pkgdeps, rebar_test_utils:flat_pkgdeps(Deps)}])
  154. end,
  155. [{rebarconfig, RebarConf} | Config];
  156. setup_project(Case, Config0, Deps) ->
  157. DepsType = ?config(deps_type, Config0),
  158. Config = rebar_test_utils:init_rebar_state(
  159. Config0,
  160. atom_to_list(Case)++"_installdeps_"++atom_to_list(DepsType)++"_"
  161. ),
  162. AppDir = ?config(apps, Config),
  163. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  164. TopDeps = rebar_test_utils:top_level_deps(Deps),
  165. RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps}]),
  166. case DepsType of
  167. git ->
  168. mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]);
  169. pkg ->
  170. mock_pkg_resource:mock([{pkgdeps, rebar_test_utils:flat_pkgdeps(Deps)}])
  171. end,
  172. [{rebarconfig, RebarConf} | Config].
  173. mock_warnings() ->
  174. %% just let it do its thing, we check warnings through
  175. %% the call log.
  176. meck:new(rebar_log, [no_link, passthrough]).
  177. %%% TESTS %%%
  178. flat(Config) -> run(Config).
  179. pick_highest_left(Config) -> run(Config).
  180. pick_highest_right(Config) -> run(Config).
  181. pick_smallest1(Config) -> run(Config).
  182. pick_smallest2(Config) -> run(Config).
  183. circular1(Config) -> run(Config).
  184. circular2(Config) -> run(Config).
  185. circular_skip(Config) -> run(Config).
  186. fail_conflict(Config) ->
  187. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  188. rebar_test_utils:run_and_check(
  189. Config, RebarConfig, ["install_deps"], ?config(expect, Config)
  190. ),
  191. check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)).
  192. default_profile(Config) ->
  193. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  194. AppDir = ?config(apps, Config),
  195. {ok, Apps} = Expect = ?config(expect, Config),
  196. rebar_test_utils:run_and_check(
  197. Config, RebarConfig, ["as", "profile", "install_deps"], Expect
  198. ),
  199. check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)),
  200. BuildDir = filename:join([AppDir, "_build"]),
  201. [?assertMatch({ok, #file_info{type=directory}},
  202. file:read_file_info(filename:join([BuildDir, "default", "lib", App])))
  203. || {dep, App} <- Apps],
  204. [?assertMatch({ok, #file_info{type=directory}}, % somehow symlinks return dirs
  205. file:read_file_info(filename:join([BuildDir, "profile", "lib", App])))
  206. || {dep, App} <- Apps],
  207. %% A second run to another profile also links default to the right spot
  208. rebar_test_utils:run_and_check(
  209. Config, RebarConfig, ["as", "other", "install_deps"], Expect
  210. ),
  211. [?assertMatch({ok, #file_info{type=directory}}, % somehow symlinks return dirs
  212. file:read_file_info(filename:join([BuildDir, "other", "lib", App])))
  213. || {dep, App} <- Apps].
  214. nondefault_profile(Config) ->
  215. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  216. AppDir = ?config(apps, Config),
  217. {ok, Apps} = Expect = ?config(expect, Config),
  218. rebar_test_utils:run_and_check(
  219. Config, RebarConfig, ["as", "nondef", "install_deps"], Expect
  220. ),
  221. check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)),
  222. BuildDir = filename:join([AppDir, "_build"]),
  223. [?assertMatch({error, enoent},
  224. file:read_file_info(filename:join([BuildDir, "default", "lib", App])))
  225. || {dep, App} <- Apps],
  226. [?assertMatch({ok, #file_info{type=directory}},
  227. file:read_file_info(filename:join([BuildDir, "nondef", "lib", App])))
  228. || {dep, App} <- Apps],
  229. %% A second run to another profile doesn't link dependencies
  230. rebar_test_utils:run_and_check(
  231. Config, RebarConfig, ["as", "other", "install_deps"], Expect
  232. ),
  233. [?assertMatch({error, enoent},
  234. file:read_file_info(filename:join([BuildDir, "default", "lib", App])))
  235. || {dep, App} <- Apps].
  236. run(Config) ->
  237. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  238. rebar_test_utils:run_and_check(
  239. Config, RebarConfig, ["install_deps"], ?config(expect, Config)
  240. ),
  241. check_warnings(warning_calls(), ?config(warnings, Config), ?config(deps_type, Config)).
  242. warning_calls() ->
  243. History = meck:history(rebar_log),
  244. [{Str, Args} || {_, {rebar_log, log, [warn, Str, Args]}, _} <- History].
  245. error_calls() ->
  246. History = meck:history(rebar_log),
  247. [{Str, Args} || {_, {rebar_log, log, [error, Str, Args]}, _} <- History].
  248. check_warnings(_, [], _) ->
  249. ok;
  250. check_warnings(Warns, [{Name, Vsn} | Rest], Type) ->
  251. ct:pal("Checking for warning ~p in ~p", [{Name,Vsn},Warns]),
  252. ?assert(in_warnings(Type, Warns, Name, Vsn)),
  253. check_warnings(Warns, Rest, Type).
  254. in_warnings(git, Warns, NameRaw, VsnRaw) ->
  255. Name = iolist_to_binary(NameRaw),
  256. 1 =< length([1 || {_, [AppName, {git, _, {_, Vsn}}]} <- Warns,
  257. AppName =:= Name, Vsn =:= VsnRaw]);
  258. in_warnings(pkg, Warns, NameRaw, VsnRaw) ->
  259. Name = iolist_to_binary(NameRaw),
  260. Vsn = iolist_to_binary(VsnRaw),
  261. 1 =< length([1 || {_, [AppName, AppVsn]} <- Warns,
  262. AppName =:= Name, AppVsn =:= Vsn]).