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.

409 lines
16 KiB

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