Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

487 рядки
17 KiB

10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
9 роки тому
10 роки тому
10 роки тому
9 роки тому
10 роки тому
9 роки тому
9 роки тому
9 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
9 роки тому
9 роки тому
9 роки тому
9 роки тому
9 роки тому
9 роки тому
9 роки тому
9 роки тому
9 роки тому
9 роки тому
9 роки тому
9 роки тому
10 роки тому
9 роки тому
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}, {group, mixed}].
  7. groups() ->
  8. [{unique, [], [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. nondefault_pick_highest]},
  13. {git, [], [{group, unique}]},
  14. {pkg, [], [{group, unique}]},
  15. {mixed, [], [
  16. m_flat1, m_flat2, m_circular1, m_circular2, m_circular3,
  17. m_pick_source1, m_pick_source2, m_pick_source3,
  18. m_pick_source4, m_pick_source5, m_source_to_pkg,
  19. m_pkg_level1, m_pkg_level2, m_pkg_level3, m_pkg_level3_alpha_order
  20. ]}
  21. ].
  22. init_per_suite(Config) ->
  23. application:start(meck),
  24. Config.
  25. end_per_suite(_Config) ->
  26. application:stop(meck).
  27. init_per_group(git, Config) ->
  28. [{deps_type, git} | Config];
  29. init_per_group(pkg, Config) ->
  30. [{deps_type, pkg} | Config];
  31. init_per_group(mixed, Config) ->
  32. [{deps_type, mixed} | Config];
  33. init_per_group(_, Config) ->
  34. Config.
  35. end_per_group(_, Config) ->
  36. Config.
  37. init_per_testcase(Case, Config) when is_atom(Case) ->
  38. DepsType = ?config(deps_type, Config),
  39. init_per_testcase({DepsType, Case}, Config);
  40. init_per_testcase({mixed, Case}, Config) ->
  41. {Deps, Warnings, Expect} = mdeps(Case),
  42. Expected = case Expect of
  43. {ok, List} -> {ok, format_expected_mdeps(List)};
  44. Other -> Other
  45. end,
  46. mock_warnings(),
  47. [{expect, Expected},
  48. {warnings, format_expected_mixed_warnings(Warnings)}
  49. | setup_project(Case, Config, rebar_test_utils:expand_deps(mixed, Deps))];
  50. init_per_testcase({DepsType, Case}, Config) ->
  51. {Deps, Warnings, Expect} = deps(Case),
  52. Expected = case Expect of
  53. {ok, List} -> {ok, format_expected_deps(List)};
  54. Other -> Other
  55. end,
  56. mock_warnings(),
  57. [{expect, Expected},
  58. {warnings, Warnings}
  59. | setup_project(Case, Config, rebar_test_utils:expand_deps(DepsType, Deps))].
  60. end_per_testcase(_, Config) ->
  61. meck:unload(),
  62. Config.
  63. format_expected_deps(Deps) ->
  64. lists:append([case Dep of
  65. {N,V} -> [{dep, N, V}, {lock, N, V}];
  66. N -> [{dep, N}, {lock, N}]
  67. end || Dep <- Deps]).
  68. format_expected_mdeps(Deps) ->
  69. %% for mixed deps, lowercase is a package, uppercase is source.
  70. %% We can't check which was used from the dep, but the lock contains
  71. %% the type and we can use that information.
  72. lists:append([
  73. case Dep of
  74. {N,V} when hd(N) >= $a, hd(N) =< $z ->
  75. UN = string:to_upper(N),
  76. [{dep, UN, V}, {lock, pkg, UN, V}];
  77. {N,V} when hd(N) >= $A, hd(N) =< $Z ->
  78. [{dep, N, V}, {lock, src, N, V}];
  79. N when hd(N) >= $a, hd(N) =< $z ->
  80. UN = string:to_upper(N),
  81. [{dep, UN}, {lock, pkg, UN, "0.0.0"}];
  82. N when hd(N) >= $A, hd(N) =< $Z ->
  83. [{dep, N}, {lock, src, N, "0.0.0"}]
  84. end || Dep <- Deps]).
  85. format_expected_mixed_warnings(Warnings) ->
  86. [case W of
  87. {N, Vsn} when hd(N) >= $a, hd(N) =< $z -> {pkg, string:to_upper(N), Vsn};
  88. {N, Vsn} when hd(N) >= $A, hd(N) =< $Z -> {src, N, Vsn};
  89. N when hd(N) >= $a, hd(N) =< $z -> {pkg, string:to_upper(N), "0.0.0"};
  90. N when hd(N) >= $A, hd(N) =< $Z -> {src, N, "0.0.0"}
  91. end || W <- Warnings].
  92. %% format:
  93. %% {Spec,
  94. %% [Warning],
  95. %% {ok, Result} | {error, Reason}}
  96. %%
  97. %% Spec is a list of levelled dependencies of two possible forms:
  98. %% - {"Name", Spec}
  99. %% - {"Name", "Vsn", Spec}
  100. %%
  101. %% Warnings are going to match on mocked ?WARN(...)
  102. %% calls to be evaluated. An empty list means we do not care about
  103. %% warnings, not that no warnings will be printed. This means
  104. %% the list of warning isn't interpreted to be exhaustive, and more
  105. %% warnings may be generated than are listed.
  106. deps(flat) ->
  107. {[{"B", []},
  108. {"C", []}],
  109. [],
  110. {ok, ["B", "C"]}};
  111. deps(pick_highest_left) ->
  112. {[{"B", [{"C", "2", []}]},
  113. {"C", "1", []}],
  114. [{"C","2"}],
  115. {ok, ["B", {"C", "1"}]}};
  116. deps(pick_highest_right) ->
  117. {[{"B", "1", []},
  118. {"C", [{"B", "2", []}]}],
  119. [{"B","2"}],
  120. {ok, [{"B","1"}, "C"]}};
  121. deps(pick_smallest1) ->
  122. {[{"B", [{"D", "1", []}]},
  123. {"C", [{"D", "2", []}]}],
  124. [{"D","2"}],
  125. %% we pick D1 because B < C
  126. {ok, ["B","C",{"D","1"}]}};
  127. deps(pick_smallest2) ->
  128. {[{"C", [{"D", "2", []}]},
  129. {"B", [{"D", "1", []}]}],
  130. [{"D","2"}],
  131. %% we pick D1 because B < C
  132. {ok, ["B","C",{"D","1"}]}};
  133. deps(circular1) ->
  134. {[{"B", [{"A", []}]}, % A is the top-level app
  135. {"C", []}],
  136. [],
  137. {error, {rebar_prv_install_deps, {cycles, [[<<"A">>,<<"B">>]]}}}};
  138. deps(circular2) ->
  139. {[{"B", [{"C", [{"B", []}]}]},
  140. {"C", []}],
  141. [],
  142. {error, {rebar_prv_install_deps, {cycles, [[<<"B">>,<<"C">>]]}}}};
  143. deps(circular_skip) ->
  144. %% Never spot the circular dep due to being to low in the deps tree
  145. %% in source deps
  146. {[{"B", [{"C", "2", [{"B", []}]}]},
  147. {"C", "1", [{"D",[]}]}],
  148. [{"C","2"}],
  149. {ok, ["B", {"C","1"}, "D"]}};
  150. deps(fail_conflict) ->
  151. {[{"B", [{"C", "2", []}]},
  152. {"C", "1", []}],
  153. [{"C","2"}],
  154. rebar_abort};
  155. deps(default_profile) ->
  156. {[{"B", []},
  157. {"C", []}],
  158. [],
  159. {ok, ["B", "C"]}};
  160. deps(nondefault_profile) ->
  161. {[{"B", []},
  162. {"C", []}],
  163. [],
  164. {ok, ["B", "C"]}};
  165. deps(nondefault_pick_highest) ->
  166. %% This is all handled in setup_project
  167. {[],[],{ok,[]}}.
  168. %% format:
  169. %% Same as `deps/1' except "A" is a source dep
  170. %% and "a" is a package dep.
  171. mdeps(m_flat1) ->
  172. {[{"c", []},
  173. {"B", []}],
  174. [],
  175. {ok, ["B","c"]}};
  176. mdeps(m_flat2) ->
  177. {[{"B", []},
  178. {"c", []}],
  179. [],
  180. {ok, ["B","c"]}};
  181. mdeps(m_circular1) ->
  182. {[{"b", [{"a",[]}]}], % "A" is the top app
  183. [],
  184. {error, {rebar_prv_install_deps, {cycles, [[<<"A">>,<<"B">>]]}}}};
  185. mdeps(m_circular2) ->
  186. {[{"B", [{"c", [{"b", []}]}]}],
  187. [],
  188. {error, {rebar_prv_install_deps, {cycles, [[<<"B">>,<<"C">>]]}}}};
  189. mdeps(m_circular3) ->
  190. %% Spot the circular dep due to being to low in the deps tree
  191. %% but as a source dep, taking precedence over packages
  192. {[{"B", [{"C", "2", [{"B", []}]}]},
  193. {"c", "1", [{"d",[]}]}],
  194. [],
  195. {error, {rebar_prv_install_deps, {cycles, [[<<"B">>,<<"C">>]]}}}};
  196. mdeps(m_pick_source1) ->
  197. {[{"B", [{"D", []}]},
  198. {"c", [{"d", []}]}],
  199. ["d"],
  200. {ok, ["B", "c", "D"]}};
  201. mdeps(m_pick_source2) ->
  202. {[{"b", [{"d", []}]},
  203. {"C", [{"D", []}]}],
  204. ["d"],
  205. {ok, ["b", "C", "D"]}};
  206. mdeps(m_pick_source3) ->
  207. %% The order of declaration is important.
  208. {[{"b", []},
  209. {"B", []}],
  210. [],
  211. {ok, ["b"]}};
  212. mdeps(m_pick_source4) ->
  213. {[{"B", []},
  214. {"b", []}],
  215. [],
  216. {ok, ["B"]}};
  217. mdeps(m_pick_source5) ->
  218. {[{"B", [{"d", []}]},
  219. {"C", [{"D", []}]}],
  220. ["d"],
  221. {ok, ["B", "C", "D"]}};
  222. mdeps(m_source_to_pkg) ->
  223. {[{"B", [{"c",[{"d", []}]}]}],
  224. [],
  225. {ok, ["B", "c", "d"]}};
  226. mdeps(m_pkg_level1) ->
  227. {[{"B", [{"D", [{"e", "2", []}]}]},
  228. {"C", [{"e", "1", []}]}],
  229. [{"e","2"}],
  230. {ok, ["B","C","D",{"e","1"}]}};
  231. mdeps(m_pkg_level2) ->
  232. {[{"B", [{"e", "1", []}]},
  233. {"C", [{"D", [{"e", "2", []}]}]}],
  234. [{"e","2"}],
  235. {ok, ["B","C","D",{"e","1"}]}};
  236. mdeps(m_pkg_level3_alpha_order) ->
  237. {[{"B", [{"d", [{"f", "1", []}]}]},
  238. {"C", [{"E", [{"f", "2", []}]}]}],
  239. [{"f","2"}],
  240. {ok, ["B","C","d","E",{"f","1"}]}};
  241. mdeps(m_pkg_level3) ->
  242. {[{"B", [{"d", [{"f", "1", []}]}]},
  243. {"C", [{"E", [{"G", [{"f", "2", []}]}]}]}],
  244. [{"f","2"}],
  245. {ok, ["B","C","d","E","G",{"f","1"}]}}.
  246. setup_project(fail_conflict, Config0, Deps) ->
  247. DepsType = ?config(deps_type, Config0),
  248. Config = rebar_test_utils:init_rebar_state(
  249. Config0,
  250. "fail_conflict_"++atom_to_list(DepsType)++"_"
  251. ),
  252. AppDir = ?config(apps, Config),
  253. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  254. TopDeps = rebar_test_utils:top_level_deps(Deps),
  255. RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps},
  256. {deps_error_on_conflict, true}]),
  257. {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
  258. mock_git_resource:mock([{deps, SrcDeps}]),
  259. mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
  260. [{rebarconfig, RebarConf} | Config];
  261. setup_project(nondefault_profile, Config0, Deps) ->
  262. DepsType = ?config(deps_type, Config0),
  263. Config = rebar_test_utils:init_rebar_state(
  264. Config0,
  265. "nondefault_profile_"++atom_to_list(DepsType)++"_"
  266. ),
  267. AppDir = ?config(apps, Config),
  268. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  269. TopDeps = rebar_test_utils:top_level_deps(Deps),
  270. RebarConf = rebar_test_utils:create_config(AppDir, [{profiles, [
  271. {nondef, [{deps, TopDeps}]}
  272. ]}]),
  273. {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
  274. mock_git_resource:mock([{deps, SrcDeps}]),
  275. mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
  276. [{rebarconfig, RebarConf} | Config];
  277. setup_project(nondefault_pick_highest, Config0, _) ->
  278. DepsType = ?config(deps_type, Config0),
  279. Config = rebar_test_utils:init_rebar_state(
  280. Config0,
  281. "nondefault_pick_highest_"++atom_to_list(DepsType)++"_"
  282. ),
  283. AppDir = ?config(apps, Config),
  284. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  285. DefaultDeps = rebar_test_utils:expand_deps(DepsType, [{"B", [{"C", "1", []}]}]),
  286. ProfileDeps = rebar_test_utils:expand_deps(DepsType, [{"C", "2", []}]),
  287. DefaultTop = rebar_test_utils:top_level_deps(DefaultDeps),
  288. ProfileTop = rebar_test_utils:top_level_deps(ProfileDeps),
  289. RebarConf = rebar_test_utils:create_config(
  290. AppDir,
  291. [{deps, DefaultTop},
  292. {profiles, [{nondef, [{deps, ProfileTop}]}]}]
  293. ),
  294. case DepsType of
  295. git ->
  296. {SrcDeps, _} = rebar_test_utils:flat_deps(DefaultDeps++ProfileDeps),
  297. mock_git_resource:mock([{deps, SrcDeps}]);
  298. pkg ->
  299. {_, PkgDeps} = rebar_test_utils:flat_deps(DefaultDeps++ProfileDeps),
  300. mock_pkg_resource:mock([{pkgdeps, PkgDeps}])
  301. end,
  302. [{rebarconfig, RebarConf} | Config];
  303. setup_project(Case, Config0, Deps) ->
  304. DepsType = ?config(deps_type, Config0),
  305. Config = rebar_test_utils:init_rebar_state(
  306. Config0,
  307. atom_to_list(Case)++"_installdeps_"++atom_to_list(DepsType)++"_"
  308. ),
  309. AppDir = ?config(apps, Config),
  310. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  311. TopDeps = rebar_test_utils:top_level_deps(Deps),
  312. RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps}]),
  313. {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
  314. mock_git_resource:mock([{deps, SrcDeps}]),
  315. mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
  316. [{rebarconfig, RebarConf} | Config].
  317. mock_warnings() ->
  318. %% just let it do its thing, we check warnings through
  319. %% the call log.
  320. meck:new(rebar_log, [no_link, passthrough]).
  321. %%% TESTS %%%
  322. flat(Config) -> run(Config).
  323. pick_highest_left(Config) -> run(Config).
  324. pick_highest_right(Config) -> run(Config).
  325. pick_smallest1(Config) -> run(Config).
  326. pick_smallest2(Config) -> run(Config).
  327. circular1(Config) -> run(Config).
  328. circular2(Config) -> run(Config).
  329. circular_skip(Config) -> run(Config).
  330. fail_conflict(Config) ->
  331. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  332. rebar_test_utils:run_and_check(
  333. Config, RebarConfig, ["lock"], ?config(expect, Config)
  334. ),
  335. check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)).
  336. default_profile(Config) ->
  337. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  338. AppDir = ?config(apps, Config),
  339. {ok, Apps} = Expect = ?config(expect, Config),
  340. rebar_test_utils:run_and_check(
  341. Config, RebarConfig, ["lock"], Expect
  342. ),
  343. rebar_test_utils:run_and_check(
  344. Config, RebarConfig, ["as", "profile", "lock"], Expect
  345. ),
  346. check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)),
  347. BuildDir = filename:join([AppDir, "_build"]),
  348. [?assertMatch({ok, #file_info{type=directory}},
  349. file:read_file_info(filename:join([BuildDir, "default", "lib", App])))
  350. || {dep, App} <- Apps],
  351. [?assertMatch({ok, #file_info{type=directory}}, % somehow symlinks return dirs
  352. file:read_file_info(filename:join([BuildDir, "profile", "lib", App])))
  353. || {dep, App} <- Apps],
  354. %% A second run to another profile also links default to the right spot
  355. rebar_test_utils:run_and_check(
  356. Config, RebarConfig, ["lock"], Expect
  357. ),
  358. rebar_test_utils:run_and_check(
  359. Config, RebarConfig, ["as", "other", "lock"], Expect
  360. ),
  361. [?assertMatch({ok, #file_info{type=directory}}, % somehow symlinks return dirs
  362. file:read_file_info(filename:join([BuildDir, "other", "lib", App])))
  363. || {dep, App} <- Apps].
  364. nondefault_profile(Config) ->
  365. %% The dependencies here are saved directly to the
  366. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  367. AppDir = ?config(apps, Config),
  368. {ok, AppLocks} = ?config(expect, Config),
  369. try
  370. rebar_test_utils:run_and_check(
  371. Config, RebarConfig, ["as", "nondef", "lock"], {ok, AppLocks}
  372. ),
  373. error(generated_locks)
  374. catch
  375. error:generated_locks -> error(generated_locks);
  376. _:_ -> ok
  377. end,
  378. Apps = [App || App = {dep, _} <- AppLocks],
  379. Expect = {ok, Apps},
  380. rebar_test_utils:run_and_check(
  381. Config, RebarConfig, ["as", "nondef", "lock"], Expect
  382. ),
  383. check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)),
  384. BuildDir = filename:join([AppDir, "_build"]),
  385. [?assertMatch({error, enoent},
  386. file:read_file_info(filename:join([BuildDir, "default", "lib", App])))
  387. || {dep, App} <- Apps],
  388. [?assertMatch({ok, #file_info{type=directory}},
  389. file:read_file_info(filename:join([BuildDir, "nondef", "lib", App])))
  390. || {dep, App} <- Apps],
  391. %% A second run to another profile doesn't link dependencies
  392. rebar_test_utils:run_and_check(
  393. Config, RebarConfig, ["as", "other", "lock"], Expect
  394. ),
  395. [?assertMatch({error, enoent},
  396. file:read_file_info(filename:join([BuildDir, "default", "lib", App])))
  397. || {dep, App} <- Apps].
  398. nondefault_pick_highest(Config) ->
  399. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  400. rebar_test_utils:run_and_check(
  401. Config, RebarConfig, ["lock"],
  402. {ok, [{dep, "B"}, {lock, "B"}, {lock, "C", "1"}, {dep, "C", "1"}], "default"}
  403. ),
  404. rebar_test_utils:run_and_check(
  405. Config, RebarConfig, ["as", "nondef", "lock"],
  406. {ok, [{dep, "B"}, {lock, "B"}, {lock, "C", "1"}, {dep, "C", "2"}], "nondef"}
  407. ),
  408. rebar_test_utils:run_and_check(
  409. Config, RebarConfig, ["lock"],
  410. {ok, [{dep, "B"}, {lock, "B"}, {dep, "C", "1"}, {lock, "C", "1"}], "default"}
  411. ),
  412. rebar_test_utils:run_and_check(
  413. Config, RebarConfig, ["as", "nondef", "lock"],
  414. {ok, [{dep, "B"}, {lock, "B"}, {lock, "C", "1"}, {dep, "C", "2"}], "nondef"}
  415. ).
  416. m_flat1(Config) -> run(Config).
  417. m_flat2(Config) -> run(Config).
  418. m_circular1(Config) -> run(Config).
  419. m_circular2(Config) -> run(Config).
  420. m_circular3(Config) -> run(Config).
  421. m_pick_source1(Config) -> run(Config).
  422. m_pick_source2(Config) -> run(Config).
  423. m_pick_source3(Config) -> run(Config).
  424. m_pick_source4(Config) -> run(Config).
  425. m_pick_source5(Config) -> run(Config).
  426. m_source_to_pkg(Config) -> run(Config).
  427. m_pkg_level1(Config) -> run(Config).
  428. m_pkg_level2(Config) -> run(Config).
  429. m_pkg_level3(Config) -> run(Config).
  430. m_pkg_level3_alpha_order(Config) -> run(Config).
  431. run(Config) ->
  432. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  433. rebar_test_utils:run_and_check(
  434. Config, RebarConfig, ["lock"], ?config(expect, Config)
  435. ),
  436. check_warnings(warning_calls(), ?config(warnings, Config), ?config(deps_type, Config)).
  437. warning_calls() ->
  438. History = meck:history(rebar_log),
  439. [{Str, Args} || {_, {rebar_log, log, [warn, Str, Args]}, _} <- History].
  440. error_calls() ->
  441. History = meck:history(rebar_log),
  442. [{Str, Args} || {_, {rebar_log, log, [error, Str, Args]}, _} <- History].
  443. check_warnings(_, [], _) ->
  444. ok;
  445. check_warnings(Warns, [{Type, Name, Vsn} | Rest], mixed) ->
  446. ct:pal("Checking for warning ~p in ~p", [{Name,Vsn},Warns]),
  447. ?assert(in_warnings(Type, Warns, Name, Vsn)),
  448. check_warnings(Warns, Rest, mixed);
  449. check_warnings(Warns, [{Name, Vsn} | Rest], Type) ->
  450. ct:pal("Checking for warning ~p in ~p", [{Name,Vsn},Warns]),
  451. ?assert(in_warnings(Type, Warns, Name, Vsn)),
  452. check_warnings(Warns, Rest, Type).
  453. in_warnings(git, Warns, NameRaw, VsnRaw) ->
  454. Name = iolist_to_binary(NameRaw),
  455. 1 =< length([1 || {_, [AppName, {git, _, {_, Vsn}}]} <- Warns,
  456. AppName =:= Name, Vsn =:= VsnRaw]);
  457. in_warnings(pkg, Warns, NameRaw, VsnRaw) ->
  458. Name = iolist_to_binary(NameRaw),
  459. Vsn = iolist_to_binary(VsnRaw),
  460. 1 =< length([1 || {_, [AppName, AppVsn]} <- Warns,
  461. AppName =:= Name, AppVsn =:= Vsn]).