summaryrefslogtreecommitdiff
path: root/test/rebar_upgrade_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'test/rebar_upgrade_SUITE.erl')
-rw-r--r--test/rebar_upgrade_SUITE.erl475
1 files changed, 475 insertions, 0 deletions
diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl
new file mode 100644
index 0000000..39b9687
--- /dev/null
+++ b/test/rebar_upgrade_SUITE.erl
@@ -0,0 +1,475 @@
+-module(rebar_upgrade_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+-compile(export_all).
+
+all() -> [{group, git}].%, {group, pkg}].
+
+groups() ->
+ [{all, [], [top_a, top_b, top_c, top_d1, top_d2, top_e,
+ pair_a, pair_b, pair_ab, pair_c, pair_all,
+ triplet_a, triplet_b, triplet_c,
+ tree_a, tree_b, tree_c, tree_c2, tree_ac, tree_all,
+ delete_d]},
+ {git, [], [{group, all}]},
+ {pkg, [], [{group, all}]}].
+
+init_per_suite(Config) ->
+ application:start(meck),
+ Config.
+
+end_per_suite(_Config) ->
+ application:stop(meck).
+
+init_per_group(git, Config) ->
+ [{deps_type, git} | Config];
+init_per_group(pkg, Config) ->
+ [{deps_type, pkg} | Config];
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_, Config) ->
+ Config.
+
+init_per_testcase(Case, Config) ->
+ DepsType = ?config(deps_type, Config),
+ {Deps, UpDeps, ToUp, Expectations} = upgrades(Case),
+ Expanded = expand_deps(DepsType, Deps),
+ UpExpanded = expand_deps(DepsType, UpDeps),
+ [{expected, normalize_unlocks(Expectations)},
+ {mock, fun() -> mock_deps(DepsType, Expanded, []) end},
+ {mock_update, fun() -> mock_deps(DepsType, UpExpanded, ToUp) end}
+ | setup_project(Case, Config, Expanded, UpExpanded)].
+
+end_per_testcase(_, Config) ->
+ meck:unload(),
+ Config.
+
+setup_project(Case, Config0, Deps, UpDeps) ->
+ DepsType = ?config(deps_type, Config0),
+ Config = rebar_test_utils:init_rebar_state(
+ Config0,
+ atom_to_list(Case)++"_"++atom_to_list(DepsType)++"_"
+ ),
+ AppDir = ?config(apps, Config),
+ rebar_test_utils:create_app(AppDir, "Root", "0.0.0", [kernel, stdlib]),
+ TopDeps = top_level_deps(Deps),
+ RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps}]),
+ [{rebarconfig, RebarConf},
+ {next_top_deps, top_level_deps(UpDeps)} | Config].
+
+
+upgrades(top_a) ->
+ %% Original tree
+ {[{"A", "1", [{"B", [{"D", "1", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Updated tree
+ [{"A", "1", [{"B", [{"D", "3", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Modified apps, gobally
+ ["A","B","D"],
+ %% upgrade vs. new tree
+ {"A", [{"A","1"}, "B", "C", {"D","3"}]}};
+upgrades(top_b) ->
+ %% Original tree
+ {[{"A", "1", [{"B", [{"D", "1", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Updated tree
+ [{"A", "1", [{"B", [{"D", "3", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Modified apps, gobally
+ ["A","B","D"],
+ %% upgrade vs. new tree
+ {"B", {error, {rebar_prv_upgrade, {transitive_dependency, <<"B">>}}}}};
+upgrades(top_c) ->
+ %% Original tree
+ {[{"A", "1", [{"B", [{"D", "1", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Updated tree
+ [{"A", "1", [{"B", [{"D", "3", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Modified apps, gobally
+ ["A","B","D"],
+ %% upgrade vs. new tree
+ {"C", {error, {rebar_prv_upgrade, {transitive_dependency, <<"C">>}}}}};
+upgrades(top_d1) ->
+ %% Original tree
+ {[{"A", "1", [{"B", [{"D", "1", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Updated tree
+ [{"A", "1", [{"B", [{"D", "3", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Modified apps, gobally
+ ["A","B","D"],
+ %% upgrade vs. new tree
+ {"D", {error, {rebar_prv_upgrade, {transitive_dependency, <<"D">>}}}}};
+upgrades(top_d2) ->
+ %% Original tree
+ {[{"A", "1", [{"B", [{"D", "1", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Updated tree
+ [{"A", "1", [{"B", [{"D", "3", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Modified apps, gobally
+ ["A","B","D"],
+ %% upgrade vs. new tree
+ {"D", {error, {rebar_prv_upgrade, {transitive_dependency, <<"D">>}}}}};
+upgrades(top_e) ->
+ %% Original tree
+ {[{"A", "1", [{"B", [{"D", "1", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Updated tree
+ [{"A", "1", [{"B", [{"D", "3", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ %% Modified apps, gobally
+ ["A","B","D"],
+ %% upgrade vs. new tree
+ {"E", {error, {rebar_prv_upgrade, {unknown_dependency, <<"E">>}}}}};
+upgrades(pair_a) ->
+ {[{"A", "1", [{"C", "1", []}]},
+ {"B", "1", [{"D", "1", []}]}
+ ],
+ [{"A", "2", [{"C", "2", []}]},
+ {"B", "2", [{"D", "2", []}]}
+ ],
+ ["A","B","C","D"],
+ {"A", [{"A","2"},{"C","2"},{"B","1"},{"D","1"}]}};
+upgrades(pair_b) ->
+ {[{"A", "1", [{"C", "1", []}]},
+ {"B", "1", [{"D", "1", []}]}
+ ],
+ [{"A", "2", [{"C", "2", []}]},
+ {"B", "2", [{"D", "2", []}]}
+ ],
+ ["A","B","C","D"],
+ {"B", [{"A","1"},{"C","1"},{"B","2"},{"D","2"}]}};
+upgrades(pair_ab) ->
+ {[{"A", "1", [{"C", "1", []}]},
+ {"B", "1", [{"D", "1", []}]}
+ ],
+ [{"A", "2", [{"C", "2", []}]},
+ {"B", "2", [{"D", "2", []}]}
+ ],
+ ["A","B","C","D"],
+ {"A,B", [{"A","2"},{"C","2"},{"B","2"},{"D","2"}]}};
+upgrades(pair_c) ->
+ {[{"A", "1", [{"C", "1", []}]},
+ {"B", "1", [{"D", "1", []}]}
+ ],
+ [{"A", "2", [{"C", "2", []}]},
+ {"B", "2", [{"D", "2", []}]}
+ ],
+ ["A","B","C","D"],
+ {"C", {error, {rebar_prv_upgrade, {transitive_dependency, <<"C">>}}}}};
+upgrades(pair_all) ->
+ {[{"A", "1", [{"C", "1", []}]},
+ {"B", "1", [{"D", "1", []}]}
+ ],
+ [{"A", "2", [{"C", "2", []}]},
+ {"B", "2", [{"D", "2", []}]}
+ ],
+ ["A","B","C","D"],
+ {"", [{"A","2"},{"C","2"},{"B","2"},{"D","2"}]}};
+upgrades(triplet_a) ->
+ {[{"A", "1", [{"D",[]},
+ {"E","3",[]}]},
+ {"B", "1", [{"F","1",[]},
+ {"G",[]}]},
+ {"C", "0", [{"H","3",[]},
+ {"I",[]}]}],
+ [{"A", "1", [{"D",[]},
+ {"E","2",[]}]},
+ {"B", "1", [{"F","1",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H","4",[]},
+ {"I",[]}]}],
+ ["A","C","E","H"],
+ {"A", [{"A","1"}, "D", {"E","2"},
+ {"B","1"}, {"F","1"}, "G",
+ {"C","0"}, {"H","3"}, "I"]}};
+upgrades(triplet_b) ->
+ {[{"A", "1", [{"D",[]},
+ {"E","3",[]}]},
+ {"B", "1", [{"F","1",[]},
+ {"G",[]}]},
+ {"C", "0", [{"H","3",[]},
+ {"I",[]}]}],
+ [{"A", "1", [{"D",[]},
+ {"E","2",[]}]},
+ {"B", "1", [{"F","1",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H","4",[]},
+ {"I",[]}]}],
+ ["A","C","E","H"],
+ {"B", [{"A","1"}, "D", {"E","3"},
+ {"B","1"}, {"F","1"}, "G",
+ {"C","0"}, {"H","3"}, "I"]}};
+upgrades(triplet_c) ->
+ {[{"A", "1", [{"D",[]},
+ {"E","3",[]}]},
+ {"B", "1", [{"F","1",[]},
+ {"G",[]}]},
+ {"C", "0", [{"H","3",[]},
+ {"I",[]}]}],
+ [{"A", "1", [{"D",[]},
+ {"E","2",[]}]},
+ {"B", "1", [{"F","1",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H","4",[]},
+ {"I",[]}]}],
+ ["A","C","E","H"],
+ {"C", [{"A","1"}, "D", {"E","3"},
+ {"B","1"}, {"F","1"}, "G",
+ {"C","1"}, {"H","4"}, "I"]}};
+upgrades(tree_a) ->
+ {[{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]},
+ {"I","2",[]}]}
+ ],
+ [{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]}]}
+ ],
+ ["C"],
+ {"A", [{"A","1"}, "D", "J", "E",
+ {"B","1"}, "F", "G",
+ {"C","1"}, "H", {"I","2"}]}};
+upgrades(tree_b) ->
+ {[{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]},
+ {"I","2",[]}]}
+ ],
+ [{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]}]}
+ ],
+ ["C"],
+ {"B", [{"A","1"}, "D", "J", "E",
+ {"B","1"}, "F", "G",
+ {"C","1"}, "H", {"I","2"}]}};
+upgrades(tree_c) ->
+ {[{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]},
+ {"I","2",[]}]}
+ ],
+ [{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]}]}
+ ],
+ ["C","I"],
+ {"C", [{"A","1"}, "D", "J", "E", {"I","1"},
+ {"B","1"}, "F", "G",
+ {"C","1"}, "H"]}};
+upgrades(tree_c2) ->
+ {[{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]},
+ {"I","2",[]}]}
+ ],
+ [{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[{"K",[]}]},
+ {"I","2",[]}]}
+ ],
+ ["C", "H"],
+ {"C", [{"A","1"}, "D", "J", "E",
+ {"B","1"}, "F", "G",
+ {"C","1"}, "H", {"I", "2"}, "K"]}};
+upgrades(tree_ac) ->
+ {[{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]},
+ {"I","2",[]}]}
+ ],
+ [{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]}]}
+ ],
+ ["C","I"],
+ {"C, A", [{"A","1"}, "D", "J", "E", {"I","1"},
+ {"B","1"}, "F", "G",
+ {"C","1"}, "H"]}};
+upgrades(tree_all) ->
+ {[{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]},
+ {"I","2",[]}]}
+ ],
+ [{"A", "1", [{"D",[{"J",[]}]},
+ {"E",[{"I","1",[]}]}]},
+ {"B", "1", [{"F",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H",[]}]}
+ ],
+ ["C","I"],
+ {"", [{"A","1"}, "D", "J", "E", {"I","1"},
+ {"B","1"}, "F", "G",
+ {"C","1"}, "H"]}};
+upgrades(delete_d) ->
+ {[{"A", "1", [{"B", [{"D", "1", []}]},
+ {"C", [{"D", "2", []}]}]}
+ ],
+ [{"A", "2", [{"B", []},
+ {"C", []}]}
+ ],
+ ["A","B", "C"],
+ %% upgrade vs. new tree
+ {"", [{"A","2"}, "B", "C"]}}.
+
+%% TODO: add a test that verifies that unlocking files and then
+%% running the upgrade code is enough to properly upgrade things.
+
+top_level_deps([]) -> [];
+top_level_deps([{{Name, Vsn, Ref}, _} | Deps]) ->
+ [{list_to_atom(Name), Vsn, Ref} | top_level_deps(Deps)];
+top_level_deps([{{pkg, Name, Vsn, _URL}, _} | Deps]) ->
+ [{list_to_atom(Name), Vsn} | top_level_deps(Deps)].
+
+mock_deps(git, Deps, Upgrades) ->
+ catch mock_git_resource:unmock(),
+ mock_git_resource:mock([{deps, flat_deps(Deps)}, {upgrade, Upgrades}]);
+mock_deps(pkg, Deps, Upgrades) ->
+ catch mock_pkg_resource:unmock(),
+ mock_pkg_resource:mock([{pkgdeps, flat_pkgdeps(Deps)}, {upgrade, Upgrades}]).
+
+flat_deps([]) -> [];
+flat_deps([{{Name,_Vsn,Ref}, Deps} | Rest]) ->
+ [{{Name,vsn_from_ref(Ref)}, top_level_deps(Deps)}]
+ ++
+ flat_deps(Deps)
+ ++
+ flat_deps(Rest).
+
+vsn_from_ref({git, _, {_, Vsn}}) -> Vsn;
+vsn_from_ref({git, _, Vsn}) -> Vsn.
+
+flat_pkgdeps([]) -> [];
+flat_pkgdeps([{{pkg, Name, Vsn, _Url}, Deps} | Rest]) ->
+ [{{iolist_to_binary(Name),iolist_to_binary(Vsn)}, top_level_deps(Deps)}]
+ ++
+ flat_pkgdeps(Deps)
+ ++
+ flat_pkgdeps(Rest).
+
+expand_deps(_, []) -> [];
+expand_deps(git, [{Name, Deps} | Rest]) ->
+ Dep = {Name, ".*", {git, "https://example.org/user/"++Name++".git", "master"}},
+ [{Dep, expand_deps(git, Deps)} | expand_deps(git, Rest)];
+expand_deps(git, [{Name, Vsn, Deps} | Rest]) ->
+ Dep = {Name, Vsn, {git, "https://example.org/user/"++Name++".git", {tag, Vsn}}},
+ [{Dep, expand_deps(git, Deps)} | expand_deps(git, Rest)];
+expand_deps(pkg, [{Name, Deps} | Rest]) ->
+ Dep = {pkg, Name, "0.0.0", "https://example.org/user/"++Name++".tar.gz"},
+ [{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)];
+expand_deps(pkg, [{Name, Vsn, Deps} | Rest]) ->
+ Dep = {pkg, Name, Vsn, "https://example.org/user/"++Name++".tar.gz"},
+ [{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)].
+
+normalize_unlocks({App, Locks}) ->
+ {iolist_to_binary(App),
+ normalize_unlocks_expect(Locks)};
+normalize_unlocks({App, Vsn, Locks}) ->
+ {iolist_to_binary(App), iolist_to_binary(Vsn),
+ normalize_unlocks_expect(Locks)}.
+
+normalize_unlocks_expect({error, Reason}) ->
+ {error, Reason};
+normalize_unlocks_expect([]) ->
+ [];
+normalize_unlocks_expect([{App,Vsn} | Rest]) ->
+ [{dep, App, Vsn},
+ {lock, App, Vsn}
+ | normalize_unlocks_expect(Rest)];
+normalize_unlocks_expect([App | Rest]) ->
+ [{dep, App},
+ {lock, App} | normalize_unlocks_expect(Rest)].
+
+top_a(Config) -> run(Config).
+top_b(Config) -> run(Config).
+top_c(Config) -> run(Config).
+top_d1(Config) -> run(Config).
+top_d2(Config) -> run(Config).
+top_e(Config) -> run(Config).
+
+pair_a(Config) -> run(Config).
+pair_b(Config) -> run(Config).
+pair_ab(Config) -> run(Config).
+pair_c(Config) -> run(Config).
+pair_all(Config) -> run(Config).
+
+triplet_a(Config) -> run(Config).
+triplet_b(Config) -> run(Config).
+triplet_c(Config) -> run(Config).
+
+tree_a(Config) -> run(Config).
+tree_b(Config) -> run(Config).
+tree_c(Config) -> run(Config).
+tree_c2(Config) -> run(Config).
+tree_ac(Config) -> run(Config).
+tree_all(Config) -> run(Config).
+
+delete_d(Config) ->
+ meck:new(rebar_log, [no_link, passthrough]),
+ run(Config),
+ Infos = [{Str, Args}
+ || {_, {rebar_log, log, [info, Str, Args]}, _} <- meck:history(rebar_log)],
+ meck:unload(rebar_log),
+ ?assertNotEqual([],
+ [1 || {"App ~ts is no longer needed and can be deleted.",
+ [<<"D">>]} <- Infos]).
+run(Config) ->
+ apply(?config(mock, Config), []),
+ {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
+ %% Install dependencies before re-mocking for an upgrade
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["lock"], {ok, []}),
+ {App, Unlocks} = ?config(expected, Config),
+ ct:pal("Upgrades: ~p -> ~p", [App, Unlocks]),
+ Expectation = case Unlocks of
+ {error, Term} -> {error, Term};
+ _ -> {ok, Unlocks}
+ end,
+ apply(?config(mock_update, Config), []),
+ NewRebarConf = rebar_test_utils:create_config(?config(apps, Config),
+ [{deps, ?config(next_top_deps, Config)}]),
+ {ok, NewRebarConfig} = file:consult(NewRebarConf),
+ rebar_test_utils:run_and_check(
+ Config, NewRebarConfig, ["upgrade", App], Expectation
+ ).
+