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.

284 lines
9.7 KiB

преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
  1. -module(rebar_deps_SUITE).
  2. -compile(export_all).
  3. -include_lib("common_test/include/ct.hrl").
  4. -include_lib("eunit/include/eunit.hrl").
  5. all() -> [sub_app_deps, newly_added_dep, {group, git}, {group, pkg}].
  6. groups() ->
  7. [{all, [], [flat, pick_highest_left, pick_highest_right,
  8. pick_smallest1, pick_smallest2,
  9. circular1, circular2, circular_skip]},
  10. {git, [], [{group, all}]},
  11. {pkg, [], [{group, all}]}].
  12. init_per_suite(Config) ->
  13. application:start(meck),
  14. Config.
  15. end_per_suite(_Config) ->
  16. application:stop(meck).
  17. init_per_group(git, Config) ->
  18. [{deps_type, git} | Config];
  19. init_per_group(pkg, Config) ->
  20. [{deps_type, pkg} | Config];
  21. init_per_group(_, Config) ->
  22. Config.
  23. end_per_group(_, Config) ->
  24. Config.
  25. init_per_testcase(newly_added_dep, Config) ->
  26. rebar_test_utils:init_rebar_state(Config);
  27. init_per_testcase(sub_app_deps, Config) ->
  28. rebar_test_utils:init_rebar_state(Config);
  29. init_per_testcase(Case, Config) ->
  30. {Deps, Warnings, Expect} = deps(Case),
  31. Expected = case Expect of
  32. {ok, List} -> {ok, format_expected_deps(List)};
  33. {error, Reason} -> {error, Reason}
  34. end,
  35. DepsType = ?config(deps_type, Config),
  36. mock_warnings(),
  37. [{expect, Expected},
  38. {warnings, Warnings}
  39. | setup_project(Case, Config, expand_deps(DepsType, Deps))].
  40. end_per_testcase(_, Config) ->
  41. meck:unload(),
  42. Config.
  43. format_expected_deps(Deps) ->
  44. [case Dep of
  45. {N,V} -> {dep, N, V};
  46. N -> {dep, N}
  47. end || Dep <- Deps].
  48. %% format:
  49. %% {Spec,
  50. %% [Warning],
  51. %% {ok, Result} | {error, Reason}}
  52. %%
  53. %% Spec is a list of levelled dependencies of two possible forms:
  54. %% - {"Name", Spec}
  55. %% - {"Name", "Vsn", Spec}
  56. %%
  57. %% Warnings are going to match on mocked ?WARN(...)
  58. %% calls to be evaluated. An empty list means we do not care about
  59. %% warnings, not that no warnings will be printed. This means
  60. %% the list of warning isn't interpreted to be exhaustive, and more
  61. %% warnings may be generated than are listed.
  62. deps(flat) ->
  63. {[{"B", []},
  64. {"C", []}],
  65. [],
  66. {ok, ["B", "C"]}};
  67. deps(pick_highest_left) ->
  68. {[{"B", [{"C", "2", []}]},
  69. {"C", "1", []}],
  70. [{"C","2"}],
  71. {ok, ["B", {"C", "1"}]}};
  72. deps(pick_highest_right) ->
  73. {[{"B", "1", []},
  74. {"C", [{"B", "2", []}]}],
  75. [{"B","2"}],
  76. {ok, [{"B","1"}, "C"]}};
  77. deps(pick_smallest1) ->
  78. {[{"B", [{"D", "1", []}]},
  79. {"C", [{"D", "2", []}]}],
  80. [{"D","2"}],
  81. %% we pick D1 because B < C
  82. {ok, ["B","C",{"D","1"}]}};
  83. deps(pick_smallest2) ->
  84. {[{"C", [{"D", "2", []}]},
  85. {"B", [{"D", "1", []}]}],
  86. [{"D","2"}],
  87. %% we pick D1 because B < C
  88. {ok, ["B","C",{"D","1"}]}};
  89. deps(circular1) ->
  90. {[{"B", [{"A", []}]}, % A is the top-level app
  91. {"C", []}],
  92. [],
  93. {error, {rebar_prv_install_deps, {cycles, [[<<"A">>,<<"B">>]]}}}};
  94. deps(circular2) ->
  95. {[{"B", [{"C", [{"B", []}]}]},
  96. {"C", []}],
  97. [],
  98. {error, {rebar_prv_install_deps, {cycles, [[<<"B">>,<<"C">>]]}}}};
  99. deps(circular_skip) ->
  100. %% Never spot the circular dep due to being to low in the deps tree
  101. %% in source deps
  102. {[{"B", [{"C", "2", [{"B", []}]}]},
  103. {"C", "1", [{"D",[]}]}],
  104. [{"C","2"}],
  105. {ok, ["B", {"C","1"}, "D"]}}.
  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. setup_project(Case, Config0, Deps) ->
  120. DepsType = ?config(deps_type, Config0),
  121. Config = rebar_test_utils:init_rebar_state(
  122. Config0,
  123. atom_to_list(Case)++"_"++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 = top_level_deps(Deps),
  128. RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps}]),
  129. case DepsType of
  130. git ->
  131. mock_git_resource:mock([{deps, flat_deps(Deps)}]);
  132. pkg ->
  133. mock_pkg_resource:mock([{pkgdeps, flat_pkgdeps(Deps)}])
  134. end,
  135. [{rebarconfig, RebarConf} | Config].
  136. flat_deps([]) -> [];
  137. flat_deps([{{Name,_Vsn,Ref}, Deps} | Rest]) ->
  138. [{{Name,vsn_from_ref(Ref)}, top_level_deps(Deps)}]
  139. ++
  140. flat_deps(Deps)
  141. ++
  142. flat_deps(Rest).
  143. vsn_from_ref({git, _, {_, Vsn}}) -> Vsn;
  144. vsn_from_ref({git, _, Vsn}) -> Vsn.
  145. flat_pkgdeps([]) -> [];
  146. flat_pkgdeps([{{pkg, Name, Vsn}, Deps} | Rest]) ->
  147. [{{iolist_to_binary(Name),iolist_to_binary(Vsn)}, top_level_deps(Deps)}]
  148. ++
  149. flat_pkgdeps(Deps)
  150. ++
  151. flat_pkgdeps(Rest).
  152. top_level_deps([]) -> [];
  153. top_level_deps([{{pkg, Name, Vsn}, _} | Deps]) ->
  154. [{list_to_atom(Name), Vsn} | top_level_deps(Deps)];
  155. top_level_deps([{{Name, Vsn, Ref}, _} | Deps]) ->
  156. [{list_to_atom(Name), Vsn, Ref} | top_level_deps(Deps)].
  157. app_vsn([]) -> [];
  158. app_vsn([{Source, Deps} | Rest]) ->
  159. {Name, Vsn} = case Source of
  160. {pkg, N, V} -> {N,V};
  161. {N,V,_Ref} -> {N,V}
  162. end,
  163. [{Name, Vsn}] ++ app_vsn(Deps) ++ app_vsn(Rest).
  164. mock_warnings() ->
  165. %% just let it do its thing, we check warnings through
  166. %% the call log.
  167. meck:new(rebar_log, [no_link, passthrough]).
  168. %%% TESTS %%%
  169. flat(Config) -> run(Config).
  170. pick_highest_left(Config) -> run(Config).
  171. pick_highest_right(Config) -> run(Config).
  172. pick_smallest1(Config) -> run(Config).
  173. pick_smallest2(Config) -> run(Config).
  174. circular1(Config) -> run(Config).
  175. circular2(Config) -> run(Config).
  176. circular_skip(Config) -> run(Config).
  177. %% Test that the deps of project apps that have their own rebar.config
  178. %% are included, but that top level rebar.config deps take precedence
  179. sub_app_deps(Config) ->
  180. AppDir = ?config(apps, Config),
  181. Deps = expand_deps(git, [{"a", "1.0.0", []}
  182. ,{"b", "1.0.0", []}
  183. ,{"b", "2.0.0", []}]),
  184. mock_git_resource:mock([{deps, flat_deps(Deps)}]),
  185. Name = rebar_test_utils:create_random_name("sub_app1_"),
  186. Vsn = rebar_test_utils:create_random_vsn(),
  187. SubAppsDir = filename:join([AppDir, Name]),
  188. SubDeps = top_level_deps(expand_deps(git, [{"a", "1.0.0", []}
  189. ,{"b", "2.0.0", []}])),
  190. rebar_test_utils:create_app(SubAppsDir, Name, Vsn, [kernel, stdlib]),
  191. rebar_test_utils:create_config(SubAppsDir, [{deps, SubDeps}]),
  192. TopDeps = top_level_deps(expand_deps(git, [{"b", "1.0.0", []}])),
  193. {ok, RebarConfig} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps}])),
  194. rebar_test_utils:run_and_check(
  195. Config, RebarConfig, ["compile"],
  196. {ok, [{app, Name}, {dep, "a"}, {dep, "b", "1.0.0"}]}).
  197. %% Newly added dependency after locking
  198. newly_added_dep(Config) ->
  199. AppDir = ?config(apps, Config),
  200. Deps = expand_deps(git, [{"a", "1.0.0", []}
  201. ,{"b", "1.0.0", [{"c", "1.0.0", []}]}
  202. ,{"c", "2.0.0", []}]),
  203. mock_git_resource:mock([{deps, flat_deps(Deps)}]),
  204. Name = rebar_test_utils:create_random_name("app_"),
  205. Vsn = rebar_test_utils:create_random_vsn(),
  206. SubAppsDir = filename:join([AppDir, Name]),
  207. rebar_test_utils:create_app(SubAppsDir, Name, Vsn, [kernel, stdlib]),
  208. TopDeps = top_level_deps(expand_deps(git, [{"b", "1.0.0", []}])),
  209. {ok, RebarConfig} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps}])),
  210. rebar_test_utils:run_and_check(
  211. Config, RebarConfig, ["compile"],
  212. {ok, [{app, Name}, {dep, "b", "1.0.0"}, {dep, "c", "1.0.0"}]}),
  213. %% Add a and c to top level
  214. TopDeps2 = top_level_deps(expand_deps(git, [{"a", "1.0.0", []}
  215. ,{"c", "2.0.0", []}
  216. ,{"b", "1.0.0", []}])),
  217. {ok, RebarConfig2} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps2}])),
  218. LockFile = filename:join(AppDir, "rebar.lock"),
  219. RebarConfig3 = rebar_config:merge_locks(RebarConfig2,
  220. rebar_config:consult_file(LockFile)),
  221. %% a should now be installed and c should not change
  222. rebar_test_utils:run_and_check(
  223. Config, RebarConfig3, ["compile"],
  224. {ok, [{app, Name}, {dep, "a"}, {dep, "b", "1.0.0"}, {dep, "c", "1.0.0"}]}).
  225. run(Config) ->
  226. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  227. rebar_test_utils:run_and_check(
  228. Config, RebarConfig, ["install_deps"], ?config(expect, Config)
  229. ),
  230. check_warnings(warning_calls(), ?config(warnings, Config), ?config(deps_type, Config)).
  231. warning_calls() ->
  232. History = meck:history(rebar_log),
  233. [{Str, Args} || {_, {rebar_log, log, [warn, Str, Args]}, _} <- History].
  234. check_warnings(_, [], _) ->
  235. ok;
  236. check_warnings(Warns, [{Name, Vsn} | Rest], Type) ->
  237. ct:pal("Checking for warning ~p in ~p", [{Name,Vsn},Warns]),
  238. ?assert(in_warnings(Type, Warns, Name, Vsn)),
  239. check_warnings(Warns, Rest, Type).
  240. in_warnings(git, Warns, NameRaw, VsnRaw) ->
  241. Name = iolist_to_binary(NameRaw),
  242. 1 =< length([1 || {_, [AppName, {git, _, {_, Vsn}}]} <- Warns,
  243. AppName =:= Name, Vsn =:= VsnRaw]);
  244. in_warnings(pkg, Warns, NameRaw, VsnRaw) ->
  245. Name = iolist_to_binary(NameRaw),
  246. Vsn = iolist_to_binary(VsnRaw),
  247. 1 =< length([1 || {_, [AppName, AppVsn]} <- Warns,
  248. AppName =:= Name, AppVsn =:= Vsn]).