Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

333 wiersze
12 KiB

  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, newly_added_after_empty_lock, http_proxy_settings, https_proxy_settings, {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_after_empty_lock, Config) ->
  26. rebar_test_utils:init_rebar_state(Config);
  27. init_per_testcase(newly_added_dep, Config) ->
  28. rebar_test_utils:init_rebar_state(Config);
  29. init_per_testcase(sub_app_deps, Config) ->
  30. rebar_test_utils:init_rebar_state(Config);
  31. init_per_testcase(http_proxy_settings, Config) ->
  32. %% Create private rebar.config
  33. Priv = ?config(priv_dir, Config),
  34. GlobalDir = filename:join(Priv, "global"),
  35. GlobalConfigDir = filename:join([GlobalDir, ".config", "rebar3"]),
  36. GlobalConfig = filename:join([GlobalDir, ".config", "rebar3", "rebar.config"]),
  37. meck:new(rebar_dir, [passthrough]),
  38. meck:expect(rebar_dir, global_config, fun() -> GlobalConfig end),
  39. meck:expect(rebar_dir, global_cache_dir, fun(_) -> GlobalDir end),
  40. %% Insert proxy variables into config
  41. rebar_test_utils:create_config(GlobalConfigDir,
  42. [{http_proxy, "http://localhost:1234"}
  43. ]),
  44. rebar_test_utils:init_rebar_state(Config);
  45. init_per_testcase(https_proxy_settings, Config) ->
  46. SupportsHttpsProxy = case erlang:system_info(otp_release) of
  47. "R16"++_ -> true;
  48. "R"++_ -> false;
  49. _ -> true % 17 and up don't have a "R" in the version
  50. end,
  51. if not SupportsHttpsProxy ->
  52. {skip, https_proxy_unsupported_before_R16};
  53. SupportsHttpsProxy ->
  54. %% Create private rebar.config
  55. Priv = ?config(priv_dir, Config),
  56. GlobalDir = filename:join(Priv, "global"),
  57. GlobalConfigDir = filename:join([GlobalDir, ".config", "rebar3"]),
  58. GlobalConfig = filename:join([GlobalDir, ".config", "rebar3", "rebar.config"]),
  59. meck:new(rebar_dir, [passthrough]),
  60. meck:expect(rebar_dir, global_config, fun() -> GlobalConfig end),
  61. meck:expect(rebar_dir, global_cache_dir, fun(_) -> GlobalDir end),
  62. %% Insert proxy variables into config
  63. rebar_test_utils:create_config(GlobalConfigDir,
  64. [{https_proxy, "http://localhost:1234"}
  65. ]),
  66. rebar_test_utils:init_rebar_state(Config)
  67. end;
  68. init_per_testcase(Case, Config) ->
  69. {Deps, Warnings, Expect} = deps(Case),
  70. Expected = case Expect of
  71. {ok, List} -> {ok, format_expected_deps(List)};
  72. {error, Reason} -> {error, Reason}
  73. end,
  74. DepsType = ?config(deps_type, Config),
  75. mock_warnings(),
  76. [{expect, Expected},
  77. {warnings, Warnings}
  78. | setup_project(Case, Config, rebar_test_utils:expand_deps(DepsType, Deps))].
  79. end_per_testcase(https_proxy_settings, Config) ->
  80. meck:unload(rebar_dir),
  81. Config;
  82. end_per_testcase(http_proxy_settings, Config) ->
  83. meck:unload(rebar_dir),
  84. Config;
  85. end_per_testcase(_, Config) ->
  86. meck:unload(),
  87. Config.
  88. format_expected_deps(Deps) ->
  89. [case Dep of
  90. {N,V} -> {dep, N, V};
  91. N -> {dep, N}
  92. end || Dep <- Deps].
  93. %% format:
  94. %% {Spec,
  95. %% [Warning],
  96. %% {ok, Result} | {error, Reason}}
  97. %%
  98. %% Spec is a list of levelled dependencies of two possible forms:
  99. %% - {"Name", Spec}
  100. %% - {"Name", "Vsn", Spec}
  101. %%
  102. %% Warnings are going to match on mocked ?WARN(...)
  103. %% calls to be evaluated. An empty list means we do not care about
  104. %% warnings, not that no warnings will be printed. This means
  105. %% the list of warning isn't interpreted to be exhaustive, and more
  106. %% warnings may be generated than are listed.
  107. deps(flat) ->
  108. {[{"B", []},
  109. {"C", []}],
  110. [],
  111. {ok, ["B", "C"]}};
  112. deps(pick_highest_left) ->
  113. {[{"B", [{"C", "2", []}]},
  114. {"C", "1", []}],
  115. [{"C","2"}],
  116. {ok, ["B", {"C", "1"}]}};
  117. deps(pick_highest_right) ->
  118. {[{"B", "1", []},
  119. {"C", [{"B", "2", []}]}],
  120. [{"B","2"}],
  121. {ok, [{"B","1"}, "C"]}};
  122. deps(pick_smallest1) ->
  123. {[{"B", [{"D", "1", []}]},
  124. {"C", [{"D", "2", []}]}],
  125. [{"D","2"}],
  126. %% we pick D1 because B < C
  127. {ok, ["B","C",{"D","1"}]}};
  128. deps(pick_smallest2) ->
  129. {[{"C", [{"D", "2", []}]},
  130. {"B", [{"D", "1", []}]}],
  131. [{"D","2"}],
  132. %% we pick D1 because B < C
  133. {ok, ["B","C",{"D","1"}]}};
  134. deps(circular1) ->
  135. {[{"B", [{"A", []}]}, % A is the top-level app
  136. {"C", []}],
  137. [],
  138. {error, {rebar_prv_install_deps, {cycles, [[<<"A">>,<<"B">>]]}}}};
  139. deps(circular2) ->
  140. {[{"B", [{"C", [{"B", []}]}]},
  141. {"C", []}],
  142. [],
  143. {error, {rebar_prv_install_deps, {cycles, [[<<"B">>,<<"C">>]]}}}};
  144. deps(circular_skip) ->
  145. %% Never spot the circular dep due to being to low in the deps tree
  146. %% in source deps
  147. {[{"B", [{"C", "2", [{"B", []}]}]},
  148. {"C", "1", [{"D",[]}]}],
  149. [{"C","2"}],
  150. {ok, ["B", {"C","1"}, "D"]}}.
  151. setup_project(Case, Config0, Deps) ->
  152. DepsType = ?config(deps_type, Config0),
  153. Config = rebar_test_utils:init_rebar_state(
  154. Config0,
  155. atom_to_list(Case)++"_"++atom_to_list(DepsType)++"_"
  156. ),
  157. AppDir = ?config(apps, Config),
  158. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  159. TopDeps = rebar_test_utils:top_level_deps(Deps),
  160. RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps}]),
  161. {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
  162. mock_git_resource:mock([{deps, SrcDeps}]),
  163. mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
  164. [{rebarconfig, RebarConf} | Config].
  165. mock_warnings() ->
  166. %% just let it do its thing, we check warnings through
  167. %% the call log.
  168. meck:new(rebar_log, [no_link, passthrough]).
  169. %%% TESTS %%%
  170. flat(Config) -> run(Config).
  171. pick_highest_left(Config) -> run(Config).
  172. pick_highest_right(Config) -> run(Config).
  173. pick_smallest1(Config) -> run(Config).
  174. pick_smallest2(Config) -> run(Config).
  175. circular1(Config) -> run(Config).
  176. circular2(Config) -> run(Config).
  177. circular_skip(Config) -> run(Config).
  178. %% Test that the deps of project apps that have their own rebar.config
  179. %% are included, but that top level rebar.config deps take precedence
  180. sub_app_deps(Config) ->
  181. AppDir = ?config(apps, Config),
  182. Deps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
  183. ,{"b", "1.0.0", []}
  184. ,{"b", "2.0.0", []}]),
  185. {SrcDeps, _} = rebar_test_utils:flat_deps(Deps),
  186. mock_git_resource:mock([{deps, SrcDeps}]),
  187. Name = rebar_test_utils:create_random_name("sub_app1_"),
  188. Vsn = rebar_test_utils:create_random_vsn(),
  189. SubAppsDir = filename:join([AppDir, "apps", Name]),
  190. SubDeps = rebar_test_utils:top_level_deps(rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
  191. ,{"b", "2.0.0", []}])),
  192. rebar_test_utils:create_app(SubAppsDir, Name, Vsn, [kernel, stdlib]),
  193. rebar_test_utils:create_config(SubAppsDir, [{deps, SubDeps}]),
  194. TopDeps = rebar_test_utils:top_level_deps(rebar_test_utils:expand_deps(git, [{"b", "1.0.0", []}])),
  195. {ok, RebarConfig} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps}])),
  196. rebar_test_utils:run_and_check(
  197. Config, RebarConfig, ["compile"],
  198. {ok, [{app, Name}, {dep, "a"}, {dep, "b", "1.0.0"}]}).
  199. %% Newly added dependency after locking
  200. newly_added_dep(Config) ->
  201. AppDir = ?config(apps, Config),
  202. Deps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
  203. ,{"b", "1.0.0", [{"c", "1.0.0", []}]}
  204. ,{"c", "2.0.0", []}]),
  205. {SrcDeps, _} = rebar_test_utils:flat_deps(Deps),
  206. mock_git_resource:mock([{deps, SrcDeps}]),
  207. Name = rebar_test_utils:create_random_name("app_"),
  208. Vsn = rebar_test_utils:create_random_vsn(),
  209. SubAppsDir = filename:join([AppDir, "apps", Name]),
  210. rebar_test_utils:create_app(SubAppsDir, Name, Vsn, [kernel, stdlib]),
  211. TopDeps = rebar_test_utils:top_level_deps(rebar_test_utils:expand_deps(git, [{"b", "1.0.0", []}])),
  212. {ok, RebarConfig} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps}])),
  213. rebar_test_utils:run_and_check(
  214. Config, RebarConfig, ["compile"],
  215. {ok, [{app, Name}, {dep, "b", "1.0.0"}, {dep, "c", "1.0.0"}]}),
  216. %% Add a and c to top level
  217. TopDeps2 = rebar_test_utils:top_level_deps(rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
  218. ,{"c", "2.0.0", []}
  219. ,{"b", "1.0.0", []}])),
  220. {ok, RebarConfig2} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps2}])),
  221. LockFile = filename:join(AppDir, "rebar.lock"),
  222. RebarConfig3 = rebar_config:merge_locks(RebarConfig2,
  223. rebar_config:consult_lock_file(LockFile)),
  224. %% a should now be installed and c should not change
  225. rebar_test_utils:run_and_check(
  226. Config, RebarConfig3, ["compile"],
  227. {ok, [{app, Name}, {dep, "a"}, {dep, "b", "1.0.0"}, {dep, "c", "1.0.0"}]}).
  228. newly_added_after_empty_lock(Config) ->
  229. AppDir = ?config(apps, Config),
  230. Deps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}]),
  231. {SrcDeps, _} = rebar_test_utils:flat_deps(Deps),
  232. mock_git_resource:mock([{deps, SrcDeps}]),
  233. Name = rebar_test_utils:create_random_name("app_"),
  234. Vsn = rebar_test_utils:create_random_vsn(),
  235. SubAppsDir = filename:join([AppDir, "apps", Name]),
  236. rebar_test_utils:create_app(SubAppsDir, Name, Vsn, [kernel, stdlib]),
  237. TopDeps = rebar_test_utils:top_level_deps(rebar_test_utils:expand_deps(git, [])),
  238. {ok, RebarConfig} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps}])),
  239. rebar_test_utils:run_and_check(
  240. Config, RebarConfig, ["compile"],
  241. {ok, []}),
  242. %% Add a and c to top level
  243. TopDeps2 = rebar_test_utils:top_level_deps(rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}])),
  244. {ok, RebarConfig2} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps2}])),
  245. LockFile = filename:join(AppDir, "rebar.lock"),
  246. RebarConfig3 = rebar_config:merge_locks(RebarConfig2,
  247. rebar_config:consult_lock_file(LockFile)),
  248. %% a should now be installed and c should not change
  249. rebar_test_utils:run_and_check(
  250. Config, RebarConfig3, ["compile"],
  251. {ok, [{app, Name}, {dep, "a", "1.0.0"}]}).
  252. http_proxy_settings(_Config) ->
  253. %% Load config
  254. rebar_utils:set_httpc_options(),
  255. rebar3:init_config(),
  256. %% Assert variable is right
  257. ?assertEqual({ok,{{"localhost", 1234}, []}},
  258. httpc:get_option(proxy, rebar)).
  259. https_proxy_settings(_Config) ->
  260. %% Load config
  261. rebar_utils:set_httpc_options(),
  262. rebar3:init_config(),
  263. %% Assert variable is right
  264. ?assertEqual({ok,{{"localhost", 1234}, []}},
  265. httpc:get_option(https_proxy, rebar)).
  266. run(Config) ->
  267. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  268. rebar_test_utils:run_and_check(
  269. Config, RebarConfig, ["install_deps"], ?config(expect, Config)
  270. ),
  271. check_warnings(warning_calls(), ?config(warnings, Config), ?config(deps_type, Config)).
  272. warning_calls() ->
  273. History = meck:history(rebar_log),
  274. [{Str, Args} || {_, {rebar_log, log, [warn, Str, Args]}, _} <- History].
  275. check_warnings(_, [], _) ->
  276. ok;
  277. check_warnings(Warns, [{Name, Vsn} | Rest], Type) ->
  278. ct:pal("Checking for warning ~p in ~p", [{Name,Vsn},Warns]),
  279. ?assert(in_warnings(Type, Warns, Name, Vsn)),
  280. check_warnings(Warns, Rest, Type).
  281. in_warnings(git, Warns, NameRaw, VsnRaw) ->
  282. Name = iolist_to_binary(NameRaw),
  283. 1 =< length([1 || {_, [AppName, {git, _, {_, Vsn}}]} <- Warns,
  284. AppName =:= Name, Vsn =:= VsnRaw]);
  285. in_warnings(pkg, Warns, NameRaw, VsnRaw) ->
  286. Name = iolist_to_binary(NameRaw),
  287. Vsn = iolist_to_binary(VsnRaw),
  288. 1 =< length([1 || {_, [AppName, {pkg, _, AppVsn}]} <- Warns,
  289. AppName =:= Name, AppVsn =:= Vsn]).