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.

333 lines
12 KiB

пре 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, 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]).