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.

425 line
13 KiB

  1. -module(rebar_upgrade_SUITE).
  2. -include_lib("common_test/include/ct.hrl").
  3. -include_lib("eunit/include/eunit.hrl").
  4. -compile(export_all).
  5. all() -> [{group, git}].%, {group, pkg}].
  6. groups() ->
  7. [{all, [], [top_a, top_b, top_c, top_d1, top_d2, top_e,
  8. pair_a, pair_b, pair_ab, pair_c,
  9. triplet_a, triplet_b, triplet_c,
  10. tree_a, tree_b, tree_c, tree_c2, tree_ac]},
  11. {git, [], [{group, all}]},
  12. {pkg, [], [{group, all}]}].
  13. init_per_suite(Config) ->
  14. application:start(meck),
  15. Config.
  16. end_per_suite(_Config) ->
  17. application:stop(meck).
  18. init_per_group(git, Config) ->
  19. [{deps_type, git} | Config];
  20. init_per_group(pkg, Config) ->
  21. [{deps_type, pkg} | Config];
  22. init_per_group(_, Config) ->
  23. Config.
  24. end_per_group(_, Config) ->
  25. Config.
  26. init_per_testcase(Case, Config) ->
  27. DepsType = ?config(deps_type, Config),
  28. {Deps, UpDeps, ToUp, Expectations} = upgrades(Case),
  29. Expanded = expand_deps(DepsType, Deps),
  30. UpExpanded = expand_deps(DepsType, UpDeps),
  31. [{expected, normalize_unlocks(Expectations)},
  32. {mock, fun() -> mock_deps(DepsType, Expanded, []) end},
  33. {mock_update, fun() -> mock_deps(DepsType, UpExpanded, ToUp) end}
  34. | setup_project(Case, Config, Expanded, UpExpanded)].
  35. end_per_testcase(_, Config) ->
  36. meck:unload(),
  37. Config.
  38. setup_project(Case, Config0, Deps, UpDeps) ->
  39. DepsType = ?config(deps_type, Config0),
  40. Config = rebar_test_utils:init_rebar_state(
  41. Config0,
  42. atom_to_list(Case)++"_"++atom_to_list(DepsType)++"_"
  43. ),
  44. AppDir = ?config(apps, Config),
  45. rebar_test_utils:create_app(AppDir, "Root", "0.0.0", [kernel, stdlib]),
  46. TopDeps = top_level_deps(Deps),
  47. RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps}]),
  48. [{rebarconfig, RebarConf},
  49. {next_top_deps, top_level_deps(UpDeps)} | Config].
  50. upgrades(top_a) ->
  51. %% Original tree
  52. {[{"A", "1", [{"B", [{"D", "1", []}]},
  53. {"C", [{"D", "2", []}]}]}
  54. ],
  55. %% Updated tree
  56. [{"A", "1", [{"B", [{"D", "3", []}]},
  57. {"C", [{"D", "2", []}]}]}
  58. ],
  59. %% Modified apps, gobally
  60. ["A","B","D"],
  61. %% upgrade vs. new tree
  62. {"A", [{"A","1"}, "B", "C", {"D","3"}]}};
  63. upgrades(top_b) ->
  64. %% Original tree
  65. {[{"A", "1", [{"B", [{"D", "1", []}]},
  66. {"C", [{"D", "2", []}]}]}
  67. ],
  68. %% Updated tree
  69. [{"A", "1", [{"B", [{"D", "3", []}]},
  70. {"C", [{"D", "2", []}]}]}
  71. ],
  72. %% Modified apps, gobally
  73. ["A","B","D"],
  74. %% upgrade vs. new tree
  75. {"B", {error, {transitive_dependency, <<"B">>}}}};
  76. upgrades(top_c) ->
  77. %% Original tree
  78. {[{"A", "1", [{"B", [{"D", "1", []}]},
  79. {"C", [{"D", "2", []}]}]}
  80. ],
  81. %% Updated tree
  82. [{"A", "1", [{"B", [{"D", "3", []}]},
  83. {"C", [{"D", "2", []}]}]}
  84. ],
  85. %% Modified apps, gobally
  86. ["A","B","D"],
  87. %% upgrade vs. new tree
  88. {"C", {error, {transitive_dependency, <<"C">>}}}};
  89. upgrades(top_d1) ->
  90. %% Original tree
  91. {[{"A", "1", [{"B", [{"D", "1", []}]},
  92. {"C", [{"D", "2", []}]}]}
  93. ],
  94. %% Updated tree
  95. [{"A", "1", [{"B", [{"D", "3", []}]},
  96. {"C", [{"D", "2", []}]}]}
  97. ],
  98. %% Modified apps, gobally
  99. ["A","B","D"],
  100. %% upgrade vs. new tree
  101. {"D", {error, {transitive_dependency, <<"D">>}}}};
  102. upgrades(top_d2) ->
  103. %% Original tree
  104. {[{"A", "1", [{"B", [{"D", "1", []}]},
  105. {"C", [{"D", "2", []}]}]}
  106. ],
  107. %% Updated tree
  108. [{"A", "1", [{"B", [{"D", "3", []}]},
  109. {"C", [{"D", "2", []}]}]}
  110. ],
  111. %% Modified apps, gobally
  112. ["A","B","D"],
  113. %% upgrade vs. new tree
  114. {"D", {error, {transitive_dependency, <<"D">>}}}};
  115. upgrades(top_e) ->
  116. %% Original tree
  117. {[{"A", "1", [{"B", [{"D", "1", []}]},
  118. {"C", [{"D", "2", []}]}]}
  119. ],
  120. %% Updated tree
  121. [{"A", "1", [{"B", [{"D", "3", []}]},
  122. {"C", [{"D", "2", []}]}]}
  123. ],
  124. %% Modified apps, gobally
  125. ["A","B","D"],
  126. %% upgrade vs. new tree
  127. {"E", {error, {unknown_dependency, <<"E">>}}}};
  128. upgrades(pair_a) ->
  129. {[{"A", "1", [{"C", "1", []}]},
  130. {"B", "1", [{"D", "1", []}]}
  131. ],
  132. [{"A", "2", [{"C", "2", []}]},
  133. {"B", "2", [{"D", "2", []}]}
  134. ],
  135. ["A","B","C","D"],
  136. {"A", [{"A","2"},{"C","2"},{"B","1"},{"D","1"}]}};
  137. upgrades(pair_b) ->
  138. {[{"A", "1", [{"C", "1", []}]},
  139. {"B", "1", [{"D", "1", []}]}
  140. ],
  141. [{"A", "2", [{"C", "2", []}]},
  142. {"B", "2", [{"D", "2", []}]}
  143. ],
  144. ["A","B","C","D"],
  145. {"B", [{"A","1"},{"C","1"},{"B","2"},{"D","2"}]}};
  146. upgrades(pair_ab) ->
  147. {[{"A", "1", [{"C", "1", []}]},
  148. {"B", "1", [{"D", "1", []}]}
  149. ],
  150. [{"A", "2", [{"C", "2", []}]},
  151. {"B", "2", [{"D", "2", []}]}
  152. ],
  153. ["A","B","C","D"],
  154. {"A,B", [{"A","2"},{"C","2"},{"B","2"},{"D","2"}]}};
  155. upgrades(pair_c) ->
  156. {[{"A", "1", [{"C", "1", []}]},
  157. {"B", "1", [{"D", "1", []}]}
  158. ],
  159. [{"A", "2", [{"C", "2", []}]},
  160. {"B", "2", [{"D", "2", []}]}
  161. ],
  162. ["A","B","C","D"],
  163. {"C", {error, {transitive_dependency, <<"C">>}}}};
  164. upgrades(triplet_a) ->
  165. {[{"A", "1", [{"D",[]},
  166. {"E","3",[]}]},
  167. {"B", "1", [{"F","1",[]},
  168. {"G",[]}]},
  169. {"C", "0", [{"H","3",[]},
  170. {"I",[]}]}],
  171. [{"A", "1", [{"D",[]},
  172. {"E","2",[]}]},
  173. {"B", "1", [{"F","1",[]},
  174. {"G",[]}]},
  175. {"C", "1", [{"H","4",[]},
  176. {"I",[]}]}],
  177. ["A","C","E","H"],
  178. {"A", [{"A","1"}, "D", {"E","2"},
  179. {"B","1"}, {"F","1"}, "G",
  180. {"C","0"}, {"H","3"}, "I"]}};
  181. upgrades(triplet_b) ->
  182. {[{"A", "1", [{"D",[]},
  183. {"E","3",[]}]},
  184. {"B", "1", [{"F","1",[]},
  185. {"G",[]}]},
  186. {"C", "0", [{"H","3",[]},
  187. {"I",[]}]}],
  188. [{"A", "1", [{"D",[]},
  189. {"E","2",[]}]},
  190. {"B", "1", [{"F","1",[]},
  191. {"G",[]}]},
  192. {"C", "1", [{"H","4",[]},
  193. {"I",[]}]}],
  194. ["A","C","E","H"],
  195. {"B", [{"A","1"}, "D", {"E","3"},
  196. {"B","1"}, {"F","1"}, "G",
  197. {"C","0"}, {"H","3"}, "I"]}};
  198. upgrades(triplet_c) ->
  199. {[{"A", "1", [{"D",[]},
  200. {"E","3",[]}]},
  201. {"B", "1", [{"F","1",[]},
  202. {"G",[]}]},
  203. {"C", "0", [{"H","3",[]},
  204. {"I",[]}]}],
  205. [{"A", "1", [{"D",[]},
  206. {"E","2",[]}]},
  207. {"B", "1", [{"F","1",[]},
  208. {"G",[]}]},
  209. {"C", "1", [{"H","4",[]},
  210. {"I",[]}]}],
  211. ["A","C","E","H"],
  212. {"C", [{"A","1"}, "D", {"E","3"},
  213. {"B","1"}, {"F","1"}, "G",
  214. {"C","1"}, {"H","4"}, "I"]}};
  215. upgrades(tree_a) ->
  216. {[{"A", "1", [{"D",[{"J",[]}]},
  217. {"E",[{"I","1",[]}]}]},
  218. {"B", "1", [{"F",[]},
  219. {"G",[]}]},
  220. {"C", "1", [{"H",[]},
  221. {"I","2",[]}]}
  222. ],
  223. [{"A", "1", [{"D",[{"J",[]}]},
  224. {"E",[{"I","1",[]}]}]},
  225. {"B", "1", [{"F",[]},
  226. {"G",[]}]},
  227. {"C", "1", [{"H",[]}]}
  228. ],
  229. ["C"],
  230. {"A", [{"A","1"}, "D", "J", "E",
  231. {"B","1"}, "F", "G",
  232. {"C","1"}, "H", {"I","2"}]}};
  233. upgrades(tree_b) ->
  234. {[{"A", "1", [{"D",[{"J",[]}]},
  235. {"E",[{"I","1",[]}]}]},
  236. {"B", "1", [{"F",[]},
  237. {"G",[]}]},
  238. {"C", "1", [{"H",[]},
  239. {"I","2",[]}]}
  240. ],
  241. [{"A", "1", [{"D",[{"J",[]}]},
  242. {"E",[{"I","1",[]}]}]},
  243. {"B", "1", [{"F",[]},
  244. {"G",[]}]},
  245. {"C", "1", [{"H",[]}]}
  246. ],
  247. ["C"],
  248. {"B", [{"A","1"}, "D", "J", "E",
  249. {"B","1"}, "F", "G",
  250. {"C","1"}, "H", {"I","2"}]}};
  251. upgrades(tree_c) ->
  252. {[{"A", "1", [{"D",[{"J",[]}]},
  253. {"E",[{"I","1",[]}]}]},
  254. {"B", "1", [{"F",[]},
  255. {"G",[]}]},
  256. {"C", "1", [{"H",[]},
  257. {"I","2",[]}]}
  258. ],
  259. [{"A", "1", [{"D",[{"J",[]}]},
  260. {"E",[{"I","1",[]}]}]},
  261. {"B", "1", [{"F",[]},
  262. {"G",[]}]},
  263. {"C", "1", [{"H",[]}]}
  264. ],
  265. ["C","I"],
  266. {"C", [{"A","1"}, "D", "J", "E", {"I","1"},
  267. {"B","1"}, "F", "G",
  268. {"C","1"}, "H"]}};
  269. upgrades(tree_c2) ->
  270. {[{"A", "1", [{"D",[{"J",[]}]},
  271. {"E",[{"I","1",[]}]}]},
  272. {"B", "1", [{"F",[]},
  273. {"G",[]}]},
  274. {"C", "1", [{"H",[]},
  275. {"I","2",[]}]}
  276. ],
  277. [{"A", "1", [{"D",[{"J",[]}]},
  278. {"E",[{"I","1",[]}]}]},
  279. {"B", "1", [{"F",[]},
  280. {"G",[]}]},
  281. {"C", "1", [{"H",[{"K",[]}]},
  282. {"I","2",[]}]}
  283. ],
  284. ["C", "H"],
  285. {"C", [{"A","1"}, "D", "J", "E",
  286. {"B","1"}, "F", "G",
  287. {"C","1"}, "H", {"I", "2"}, "K"]}};
  288. upgrades(tree_ac) ->
  289. {[{"A", "1", [{"D",[{"J",[]}]},
  290. {"E",[{"I","1",[]}]}]},
  291. {"B", "1", [{"F",[]},
  292. {"G",[]}]},
  293. {"C", "1", [{"H",[]},
  294. {"I","2",[]}]}
  295. ],
  296. [{"A", "1", [{"D",[{"J",[]}]},
  297. {"E",[{"I","1",[]}]}]},
  298. {"B", "1", [{"F",[]},
  299. {"G",[]}]},
  300. {"C", "1", [{"H",[]}]}
  301. ],
  302. ["C","I"],
  303. {"C, A", [{"A","1"}, "D", "J", "E", {"I","1"},
  304. {"B","1"}, "F", "G",
  305. {"C","1"}, "H"]}}.
  306. %% TODO: add a test that verifies that unlocking files and then
  307. %% running the upgrade code is enough to properly upgrade things.
  308. top_level_deps([]) -> [];
  309. top_level_deps([{{Name, Vsn, Ref}, _} | Deps]) ->
  310. [{list_to_atom(Name), Vsn, Ref} | top_level_deps(Deps)];
  311. top_level_deps([{{pkg, Name, Vsn, _URL}, _} | Deps]) ->
  312. [{list_to_atom(Name), Vsn} | top_level_deps(Deps)].
  313. mock_deps(git, Deps, Upgrades) ->
  314. catch mock_git_resource:unmock(),
  315. ct:pal("mocked: ~p", [flat_deps(Deps)]),
  316. mock_git_resource:mock([{deps, flat_deps(Deps)}, {upgrade, Upgrades}]);
  317. mock_deps(pkg, Deps, Upgrades) ->
  318. catch mock_pkg_resource:unmock(),
  319. mock_pkg_resource:mock([{pkgdeps, flat_pkgdeps(Deps)}, {upgrade, Upgrades}]).
  320. flat_deps([]) -> [];
  321. flat_deps([{{Name,_Vsn,Ref}, Deps} | Rest]) ->
  322. [{{Name,vsn_from_ref(Ref)}, top_level_deps(Deps)}]
  323. ++
  324. flat_deps(Deps)
  325. ++
  326. flat_deps(Rest).
  327. vsn_from_ref({git, _, {_, Vsn}}) -> Vsn;
  328. vsn_from_ref({git, _, Vsn}) -> Vsn.
  329. flat_pkgdeps([]) -> [];
  330. flat_pkgdeps([{{pkg, Name, Vsn, _Url}, Deps} | Rest]) ->
  331. [{{iolist_to_binary(Name),iolist_to_binary(Vsn)}, top_level_deps(Deps)}]
  332. ++
  333. flat_pkgdeps(Deps)
  334. ++
  335. flat_pkgdeps(Rest).
  336. expand_deps(_, []) -> [];
  337. expand_deps(git, [{Name, Deps} | Rest]) ->
  338. Dep = {Name, ".*", {git, "https://example.org/user/"++Name++".git", "master"}},
  339. [{Dep, expand_deps(git, Deps)} | expand_deps(git, Rest)];
  340. expand_deps(git, [{Name, Vsn, Deps} | Rest]) ->
  341. Dep = {Name, Vsn, {git, "https://example.org/user/"++Name++".git", {tag, Vsn}}},
  342. [{Dep, expand_deps(git, Deps)} | expand_deps(git, Rest)];
  343. expand_deps(pkg, [{Name, Deps} | Rest]) ->
  344. Dep = {pkg, Name, "0.0.0", "https://example.org/user/"++Name++".tar.gz"},
  345. [{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)];
  346. expand_deps(pkg, [{Name, Vsn, Deps} | Rest]) ->
  347. Dep = {pkg, Name, Vsn, "https://example.org/user/"++Name++".tar.gz"},
  348. [{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)].
  349. normalize_unlocks({App, Locks}) ->
  350. {iolist_to_binary(App),
  351. normalize_unlocks_expect(Locks)};
  352. normalize_unlocks({App, Vsn, Locks}) ->
  353. {iolist_to_binary(App), iolist_to_binary(Vsn),
  354. normalize_unlocks_expect(Locks)}.
  355. normalize_unlocks_expect({error, Reason}) ->
  356. {error, Reason};
  357. normalize_unlocks_expect([]) ->
  358. [];
  359. normalize_unlocks_expect([{App,Vsn} | Rest]) ->
  360. [{dep, App, Vsn}
  361. | normalize_unlocks_expect(Rest)];
  362. normalize_unlocks_expect([App | Rest]) ->
  363. [{dep, App} | normalize_unlocks_expect(Rest)].
  364. top_a(Config) -> run(Config).
  365. top_b(Config) -> run(Config).
  366. top_c(Config) -> run(Config).
  367. top_d1(Config) -> run(Config).
  368. top_d2(Config) -> run(Config).
  369. top_e(Config) -> run(Config).
  370. pair_a(Config) -> run(Config).
  371. pair_b(Config) -> run(Config).
  372. pair_ab(Config) -> run(Config).
  373. pair_c(Config) -> run(Config).
  374. triplet_a(Config) -> run(Config).
  375. triplet_b(Config) -> run(Config).
  376. triplet_c(Config) -> run(Config).
  377. tree_a(Config) -> run(Config).
  378. tree_b(Config) -> run(Config).
  379. tree_c(Config) -> run(Config).
  380. tree_c2(Config) -> run(Config).
  381. tree_ac(Config) -> run(Config).
  382. run(Config) ->
  383. apply(?config(mock, Config), []),
  384. {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
  385. %% Install dependencies before re-mocking for an upgrade
  386. rebar_test_utils:run_and_check(Config, RebarConfig, ["lock"], {ok, []}),
  387. {App, Unlocks} = ?config(expected, Config),
  388. ct:pal("Upgrades: ~p -> ~p", [App, Unlocks]),
  389. Expectation = case Unlocks of
  390. {error, Term} -> {error, Term};
  391. _ -> {ok, Unlocks}
  392. end,
  393. apply(?config(mock_update, Config), []),
  394. NewRebarConf = rebar_test_utils:create_config(?config(apps, Config),
  395. [{deps, ?config(next_top_deps, Config)}]),
  396. {ok, NewRebarConfig} = file:consult(NewRebarConf),
  397. rebar_test_utils:run_and_check(
  398. Config, NewRebarConfig, ["upgrade", App], Expectation
  399. ).