summaryrefslogtreecommitdiff
path: root/test/rebar_upgrade_SUITE.erl
blob: 5915d0d1b421220d60e7749a6b5a25f74342829a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
-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, pair, triplet, tree]},
     {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, Unlocks} = upgrades(Case),
    Expanded = expand_deps(DepsType, Deps),
    [{unlocks, normalize_unlocks(Unlocks)},
     {mock, fun() -> mock_deps(DepsType, Expanded, []) end}
     | setup_project(Case, Config, Expanded)].

end_per_testcase(_, Config) ->
    meck:unload(),
    Config.

setup_project(Case, Config0, Deps) ->
    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} | Config].


upgrades(top) ->
    {[{"A", [{"B", [{"D", "1", []}]},
             {"C", [{"D", "2", []}]}]}
     ],
     %% upgrade vs. locked after upgrade
     [{"A", []},
      {"B", {error, transitive_dependency}},
      {"C", {error, transitive_dependency}},
      {"D", "1", {error, transitive_dependency}},
      {"D", "2", {error, unknown_dependency}}]};
upgrades(pair) ->
    {[{"A", [{"C", []}]},
      {"B", [{"D", []}]}],
     [{"A", ["B"]},
      {"B", ["A"]},
      {"C", {error, transitive_dependency}},
      {"D", {error, transitive_dependency}}]};
upgrades(triplet) ->
    {[{"A", [{"D",[]},
             {"E",[]}]},
      {"B", [{"F",[]},
             {"G",[]}]},
      {"C", [{"H",[]},
             {"I",[]}]}],
     [{"A", ["B","C"]},
      {"B", ["A","C"]},
      {"C", ["A","B"]},
      {"D", {error, transitive_dependency}},
      %% ...
      {"I", {error, transitive_dependency}}]};
upgrades(tree) ->
    {[{"A", [{"D",[{"J",[]}]},
             {"E",[{"K",[]}]}]},
      {"B", [{"F",[]},
             {"G",[]}]},
      {"C", [{"H",[]},
             {"I",[]}]}],
     [{"A", ["B","C"]},
      {"B", ["A","C"]},
      {"C", ["A","B"]},
      {"D", {error, transitive_dependency}},
      %% ...
      {"K", {error, transitive_dependency}}]}.

%% 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) ->
    mock_git_resource:mock([{deps, flat_deps(Deps)}, {upgrade, Upgrades}]);
mock_deps(pkg, Deps, Upgrades) ->
    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([]) -> [];
normalize_unlocks([{App, Locks} | Rest]) ->
    [{iolist_to_binary(App),
      normalize_unlocks_expect(Locks)} | normalize_unlocks(Rest)];
normalize_unlocks([{App, Vsn, Locks} | Rest]) ->
    [{iolist_to_binary(App), iolist_to_binary(Vsn),
      normalize_unlocks_expect(Locks)} | normalize_unlocks(Rest)].

normalize_unlocks_expect({error, Reason}) ->
    {error, Reason};
normalize_unlocks_expect([]) ->
    [];
normalize_unlocks_expect([{App,Vsn} | Rest]) ->
    [{iolist_to_binary(App), iolist_to_binary(Vsn)}
     | normalize_unlocks_expect(Rest)];
normalize_unlocks_expect([App | Rest]) ->
    [iolist_to_binary(App) | normalize_unlocks_expect(Rest)].

top(Config) -> run(Config).
pair(Config) -> run(Config).
triplet(Config) -> run(Config).
tree(Config) -> run(Config).

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, []}),
    Unlocks = ?config(unlocks, Config),
    [begin
        ct:pal("Unlocks: ~p -> ~p", [App, Unlocked]),
        Expectation = case Unlocked of
            Tuple when is_tuple(Tuple) -> Tuple;
            _ -> {unlocked, Unlocked}
        end,
        rebar_test_utils:run_and_check(
            Config, RebarConfig, ["upgrade", App], Expectation
        )
     end || {App, Unlocked} <- Unlocks].