Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

479 строки
17 KiB

10 лет назад
10 лет назад
10 лет назад
9 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
9 лет назад
9 лет назад
10 лет назад
10 лет назад
10 лет назад
9 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
9 лет назад
10 лет назад
10 лет назад
10 лет назад
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,
  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 = rebar_string:uppercase(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 = rebar_string:uppercase(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, rebar_string:uppercase(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, rebar_string:uppercase(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]).