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.

479 rivejä
17 KiB

10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
  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,
  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 -> {git, 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 -> {git, 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_pick_source1) ->
  190. {[{"B", [{"D", []}]},
  191. {"c", [{"d", []}]}],
  192. ["d"],
  193. {ok, ["B", "c", "D"]}};
  194. mdeps(m_pick_source2) ->
  195. %% The order of declaration is important.
  196. {[{"b", []},
  197. {"B", []}],
  198. [],
  199. {ok, ["b"]}};
  200. mdeps(m_pick_source3) ->
  201. {[{"B", []},
  202. {"b", []}],
  203. [],
  204. {ok, ["B"]}};
  205. mdeps(m_pick_source4) ->
  206. {[{"b", [{"d", "1", []}]},
  207. {"C", [{"D", "1", []}]}],
  208. [{"D", "1"}],
  209. {ok, ["b", "C", {"d", "1"}]}};
  210. mdeps(m_pick_source5) ->
  211. {[{"B", [{"d", "1", []}]},
  212. {"C", [{"D", "1", []}]}],
  213. [{"D", "1"}],
  214. {ok, ["B", "C", {"d", "1"}]}};
  215. mdeps(m_source_to_pkg) ->
  216. {[{"B", [{"c",[{"d", []}]}]}],
  217. [],
  218. {ok, ["B", "c", "d"]}};
  219. mdeps(m_pkg_level1) ->
  220. {[{"B", [{"D", [{"e", "2", []}]}]},
  221. {"C", [{"e", "1", []}]}],
  222. [{"e","2"}],
  223. {ok, ["B","C","D",{"e","1"}]}};
  224. mdeps(m_pkg_level2) ->
  225. {[{"B", [{"e", "1", []}]},
  226. {"C", [{"D", [{"e", "2", []}]}]}],
  227. [{"e","2"}],
  228. {ok, ["B","C","D",{"e","1"}]}};
  229. mdeps(m_pkg_level3_alpha_order) ->
  230. {[{"B", [{"d", [{"f", "1", []}]}]},
  231. {"C", [{"E", [{"f", "2", []}]}]}],
  232. [{"f","2"}],
  233. {ok, ["B","C","d","E",{"f","1"}]}};
  234. mdeps(m_pkg_level3) ->
  235. {[{"B", [{"d", [{"f", "1", []}]}]},
  236. {"C", [{"E", [{"G", [{"f", "2", []}]}]}]}],
  237. [{"f","2"}],
  238. {ok, ["B","C","d","E","G",{"f","1"}]}}.
  239. setup_project(fail_conflict, Config0, Deps) ->
  240. DepsType = ?config(deps_type, Config0),
  241. Config = rebar_test_utils:init_rebar_state(
  242. Config0,
  243. "fail_conflict_"++atom_to_list(DepsType)++"_"
  244. ),
  245. AppDir = ?config(apps, Config),
  246. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  247. TopDeps = rebar_test_utils:top_level_deps(Deps),
  248. RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps},
  249. {deps_error_on_conflict, true}]),
  250. {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
  251. mock_git_resource:mock([{deps, SrcDeps}]),
  252. mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
  253. [{rebarconfig, RebarConf} | Config];
  254. setup_project(nondefault_profile, Config0, Deps) ->
  255. DepsType = ?config(deps_type, Config0),
  256. Config = rebar_test_utils:init_rebar_state(
  257. Config0,
  258. "nondefault_profile_"++atom_to_list(DepsType)++"_"
  259. ),
  260. AppDir = ?config(apps, Config),
  261. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  262. TopDeps = rebar_test_utils:top_level_deps(Deps),
  263. RebarConf = rebar_test_utils:create_config(AppDir, [{profiles, [
  264. {nondef, [{deps, TopDeps}]}
  265. ]}]),
  266. {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
  267. mock_git_resource:mock([{deps, SrcDeps}]),
  268. mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
  269. [{rebarconfig, RebarConf} | Config];
  270. setup_project(nondefault_pick_highest, Config0, _) ->
  271. DepsType = ?config(deps_type, Config0),
  272. Config = rebar_test_utils:init_rebar_state(
  273. Config0,
  274. "nondefault_pick_highest_"++atom_to_list(DepsType)++"_"
  275. ),
  276. AppDir = ?config(apps, Config),
  277. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  278. DefaultDeps = rebar_test_utils:expand_deps(DepsType, [{"B", [{"C", "1", []}]}]),
  279. ProfileDeps = rebar_test_utils:expand_deps(DepsType, [{"C", "2", []}]),
  280. DefaultTop = rebar_test_utils:top_level_deps(DefaultDeps),
  281. ProfileTop = rebar_test_utils:top_level_deps(ProfileDeps),
  282. RebarConf = rebar_test_utils:create_config(
  283. AppDir,
  284. [{deps, DefaultTop},
  285. {profiles, [{nondef, [{deps, ProfileTop}]}]}]
  286. ),
  287. case DepsType of
  288. git ->
  289. {SrcDeps, _} = rebar_test_utils:flat_deps(DefaultDeps++ProfileDeps),
  290. mock_git_resource:mock([{deps, SrcDeps}]);
  291. pkg ->
  292. {_, PkgDeps} = rebar_test_utils:flat_deps(DefaultDeps++ProfileDeps),
  293. mock_pkg_resource:mock([{pkgdeps, PkgDeps}])
  294. end,
  295. [{rebarconfig, RebarConf} | Config];
  296. setup_project(Case, Config0, Deps) ->
  297. DepsType = ?config(deps_type, Config0),
  298. Config = rebar_test_utils:init_rebar_state(
  299. Config0,
  300. atom_to_list(Case)++"_installdeps_"++atom_to_list(DepsType)++"_"
  301. ),
  302. AppDir = ?config(apps, Config),
  303. rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]),
  304. TopDeps = rebar_test_utils:top_level_deps(Deps),
  305. RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps}]),
  306. {SrcDeps, PkgDeps} = rebar_test_utils:flat_deps(Deps),
  307. mock_git_resource:mock([{deps, SrcDeps}]),
  308. mock_pkg_resource:mock([{pkgdeps, PkgDeps}]),
  309. [{rebarconfig, RebarConf} | Config].
  310. mock_warnings() ->
  311. %% just let it do its thing, we check warnings through
  312. %% the call log.
  313. meck:new(rebar_log, [no_link, passthrough]).
  314. %%% TESTS %%%
  315. flat(Config) -> run(Config).
  316. pick_highest_left(Config) -> run(Config).
  317. pick_highest_right(Config) -> run(Config).
  318. pick_smallest1(Config) -> run(Config).
  319. pick_smallest2(Config) -> run(Config).
  320. circular1(Config) -> run(Config).
  321. circular2(Config) -> run(Config).
  322. circular_skip(Config) -> run(Config).
  323. fail_conflict(Config) ->
  324. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  325. rebar_test_utils:run_and_check(
  326. Config, RebarConfig, ["lock"], ?config(expect, Config)
  327. ),
  328. check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)).
  329. default_profile(Config) ->
  330. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  331. AppDir = ?config(apps, Config),
  332. {ok, Apps} = Expect = ?config(expect, Config),
  333. rebar_test_utils:run_and_check(
  334. Config, RebarConfig, ["lock"], Expect
  335. ),
  336. rebar_test_utils:run_and_check(
  337. Config, RebarConfig, ["as", "profile", "lock"], Expect
  338. ),
  339. check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)),
  340. BuildDir = filename:join([AppDir, "_build"]),
  341. [?assertMatch({ok, #file_info{type=directory}},
  342. file:read_file_info(filename:join([BuildDir, "default", "lib", App])))
  343. || {dep, App} <- Apps],
  344. [?assertMatch({ok, #file_info{type=directory}}, % somehow symlinks return dirs
  345. file:read_file_info(filename:join([BuildDir, "profile", "lib", App])))
  346. || {dep, App} <- Apps],
  347. %% A second run to another profile also links default to the right spot
  348. rebar_test_utils:run_and_check(
  349. Config, RebarConfig, ["lock"], Expect
  350. ),
  351. rebar_test_utils:run_and_check(
  352. Config, RebarConfig, ["as", "other", "lock"], Expect
  353. ),
  354. [?assertMatch({ok, #file_info{type=directory}}, % somehow symlinks return dirs
  355. file:read_file_info(filename:join([BuildDir, "other", "lib", App])))
  356. || {dep, App} <- Apps].
  357. nondefault_profile(Config) ->
  358. %% The dependencies here are saved directly to the
  359. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  360. AppDir = ?config(apps, Config),
  361. {ok, AppLocks} = ?config(expect, Config),
  362. try
  363. rebar_test_utils:run_and_check(
  364. Config, RebarConfig, ["as", "nondef", "lock"], {ok, AppLocks}
  365. ),
  366. error(generated_locks)
  367. catch
  368. error:generated_locks -> error(generated_locks);
  369. _:_ -> ok
  370. end,
  371. Apps = [App || App = {dep, _} <- AppLocks],
  372. Expect = {ok, Apps},
  373. rebar_test_utils:run_and_check(
  374. Config, RebarConfig, ["as", "nondef", "lock"], Expect
  375. ),
  376. check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)),
  377. BuildDir = filename:join([AppDir, "_build"]),
  378. [?assertMatch({error, enoent},
  379. file:read_file_info(filename:join([BuildDir, "default", "lib", App])))
  380. || {dep, App} <- Apps],
  381. [?assertMatch({ok, #file_info{type=directory}},
  382. file:read_file_info(filename:join([BuildDir, "nondef", "lib", App])))
  383. || {dep, App} <- Apps],
  384. %% A second run to another profile doesn't link dependencies
  385. rebar_test_utils:run_and_check(
  386. Config, RebarConfig, ["as", "other", "lock"], Expect
  387. ),
  388. [?assertMatch({error, enoent},
  389. file:read_file_info(filename:join([BuildDir, "default", "lib", App])))
  390. || {dep, App} <- Apps].
  391. nondefault_pick_highest(Config) ->
  392. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  393. rebar_test_utils:run_and_check(
  394. Config, RebarConfig, ["lock"],
  395. {ok, [{dep, "B"}, {lock, "B"}, {lock, "C", "1"}, {dep, "C", "1"}], "default"}
  396. ),
  397. rebar_test_utils:run_and_check(
  398. Config, RebarConfig, ["as", "nondef", "lock"],
  399. {ok, [{dep, "B"}, {lock, "B"}, {lock, "C", "1"}, {dep, "C", "2"}], "nondef"}
  400. ),
  401. rebar_test_utils:run_and_check(
  402. Config, RebarConfig, ["lock"],
  403. {ok, [{dep, "B"}, {lock, "B"}, {dep, "C", "1"}, {lock, "C", "1"}], "default"}
  404. ),
  405. rebar_test_utils:run_and_check(
  406. Config, RebarConfig, ["as", "nondef", "lock"],
  407. {ok, [{dep, "B"}, {lock, "B"}, {lock, "C", "1"}, {dep, "C", "2"}], "nondef"}
  408. ).
  409. m_flat1(Config) -> run(Config).
  410. m_flat2(Config) -> run(Config).
  411. m_circular1(Config) -> run(Config).
  412. m_circular2(Config) -> run(Config).
  413. m_pick_source1(Config) -> run(Config).
  414. m_pick_source2(Config) -> run(Config).
  415. m_pick_source3(Config) -> run(Config).
  416. m_pick_source4(Config) -> run(Config).
  417. m_pick_source5(Config) -> run(Config).
  418. m_source_to_pkg(Config) -> run(Config).
  419. m_pkg_level1(Config) -> run(Config).
  420. m_pkg_level2(Config) -> run(Config).
  421. m_pkg_level3(Config) -> run(Config).
  422. m_pkg_level3_alpha_order(Config) -> run(Config).
  423. run(Config) ->
  424. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  425. rebar_test_utils:run_and_check(
  426. Config, RebarConfig, ["lock"], ?config(expect, Config)
  427. ),
  428. check_warnings(warning_calls(), ?config(warnings, Config), ?config(deps_type, Config)).
  429. warning_calls() ->
  430. History = meck:history(rebar_log),
  431. [{Str, Args} || {_, {rebar_log, log, [warn, Str, Args]}, _} <- History].
  432. error_calls() ->
  433. History = meck:history(rebar_log),
  434. [{Str, Args} || {_, {rebar_log, log, [error, Str, Args]}, _} <- History].
  435. check_warnings(_, [], _) ->
  436. ok;
  437. check_warnings(Warns, [{Type, Name, Vsn} | Rest], mixed) ->
  438. ct:pal("Checking for warning ~p in ~p", [{Name,Vsn},Warns]),
  439. ?assert(in_warnings(Type, Warns, Name, Vsn)),
  440. check_warnings(Warns, Rest, mixed);
  441. check_warnings(Warns, [{Name, Vsn} | Rest], Type) ->
  442. ct:pal("Checking for warning ~p in ~p", [{Name,Vsn},Warns]),
  443. ?assert(in_warnings(Type, Warns, Name, Vsn)),
  444. check_warnings(Warns, Rest, Type).
  445. in_warnings(git, Warns, NameRaw, VsnRaw) ->
  446. Name = iolist_to_binary(NameRaw),
  447. 1 =< length([1 || {_, [AppName, {git, _, {_, Vsn}}]} <- Warns,
  448. AppName =:= Name, Vsn =:= VsnRaw]);
  449. in_warnings(pkg, Warns, NameRaw, VsnRaw) ->
  450. Name = iolist_to_binary(NameRaw),
  451. Vsn = iolist_to_binary(VsnRaw),
  452. 1 =< length([1 || {_, [AppName, {pkg, _, AppVsn, _}]} <- Warns,
  453. AppName =:= Name, AppVsn =:= Vsn]).