diff options
Diffstat (limited to 'test/rebar_upgrade_SUITE.erl')
-rw-r--r-- | test/rebar_upgrade_SUITE.erl | 475 |
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 + ). + |