4
%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
20
%%----------------------------------------------------------------------
21
%% Purpose: Verify the application specifics of the Megaco application
22
%%----------------------------------------------------------------------
23
-module(xmerl_appup_test).
27
%-include("megaco_test_lib.hrl").
30
%t() -> megaco_test_lib:t(?MODULE).
31
%t(Case) -> megaco_test_lib:t({?MODULE, Case}).
34
%% Test server callbacks
35
% init_per_testcase(Case, Config) ->
36
% megaco_test_lib:init_per_testcase(Case, Config).
38
% end_per_testcase(Case, Config) ->
39
% megaco_test_lib:end_per_testcase(Case, Config).
41
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
49
init_per_group(_GroupName, Config) ->
52
end_per_group(_GroupName, Config) ->
56
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58
init_per_suite(suite) -> [];
59
init_per_suite(doc) -> [];
60
init_per_suite(Config) when is_list(Config) ->
61
AppFile = file_name(xmerl, ".app"),
62
AppupFile = file_name(xmerl, ".appup"),
63
[{app_file, AppFile}, {appup_file, AppupFile}|Config].
66
file_name(App, Ext) ->
67
LibDir = code:lib_dir(App),
68
filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]).
71
end_per_suite(suite) -> [];
72
end_per_suite(doc) -> [];
73
end_per_suite(Config) when is_list(Config) ->
77
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82
"perform a simple check of the appup file";
83
appup(Config) when is_list(Config) ->
84
AppupFile = key1search(appup_file, Config),
85
AppFile = key1search(app_file, Config),
86
Modules = modules(AppFile),
87
check_appup(AppupFile, Modules).
90
case file:consult(File) of
91
{ok, [{application,xmerl,Info}]} ->
92
case lists:keysearch(modules,1,Info) of
93
{value, {modules, Modules}} ->
96
fail({bad_appinfo, Info})
99
fail({bad_appfile, Error})
103
check_appup(AppupFile, Modules) ->
104
case file:consult(AppupFile) of
105
{ok, [{V, UpFrom, DownTo}]} ->
106
io:format("V= ~p, UpFrom= ~p, DownTo= ~p, Modules= ~p~n",
107
[V, UpFrom, DownTo, Modules]),
108
check_appup(V, UpFrom, DownTo, Modules);
110
fail({bad_appupfile, Else})
114
check_appup(V, UpFrom, DownTo, Modules) ->
116
check_depends(up, UpFrom, Modules),
117
check_depends(down, DownTo, Modules),
121
check_depends(_, [], _) ->
123
check_depends(UpDown, [Dep|Deps], Modules) ->
124
check_depend(UpDown, Dep, Modules),
125
check_depends(UpDown, Deps, Modules).
128
check_depend(up,I={add_application, _App}, Modules) ->
129
d("check_instructions(~w) -> entry with"
131
"~n Modules: ~p", [up,I , Modules]),
133
check_depend(down,I={remove_application, _App}, Modules) ->
134
d("check_instructions(~w) -> entry with"
136
"~n Modules: ~p", [down,I , Modules]),
138
check_depend(UpDown, {V, Instructions}, Modules) ->
139
d("check_instructions(~w) -> entry with"
141
"~n Modules: ~p", [UpDown, V, Modules]),
143
case check_instructions(UpDown,
144
Instructions, Instructions, [], [], Modules) of
148
fail({bad_instructions, Bad, UpDown})
152
check_instructions(_, [], _, Good, Bad, _) ->
153
{lists:reverse(Good), lists:reverse(Bad)};
154
check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) ->
155
d("check_instructions(~w) -> entry with"
156
"~n Instr: ~p", [UpDown,Instr]),
157
case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of
159
check_instructions(UpDown, Instrs, AllInstr,
160
[Instr|Good], Bad, Modules);
162
d("check_instructions(~w) -> bad instruction: "
163
"~n Reason: ~p", [UpDown,Reason]),
164
check_instructions(UpDown, Instrs, AllInstr, Good,
165
[{Instr, Reason}|Bad], Modules)
168
%% A new module is added
169
check_instruction(up, {add_module, Module}, _, Modules)
170
when is_atom(Module) ->
171
d("check_instruction -> entry when up-add_module instruction with"
172
"~n Module: ~p", [Module]),
173
check_module(Module, Modules);
175
%% An old module is re-added
176
check_instruction(down, {add_module, Module}, _, Modules)
177
when is_atom(Module) ->
178
d("check_instruction -> entry when down-add_module instruction with"
179
"~n Module: ~p", [Module]),
180
case (catch check_module(Module, Modules)) of
181
{error, {unknown_module, Module, Modules}} ->
184
local_error({existing_readded_module, Module})
187
%% Removing a module on upgrade:
188
%% - the module has been removed from the app-file.
189
%% - check that no module depends on this (removed) module
190
check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules)
191
when is_atom(Module), is_atom(Pre), is_atom(Post) ->
192
d("check_instruction -> entry when up-remove instruction with"
195
"~n Post: ~p", [Module, Pre, Post]),
196
case (catch check_module(Module, Modules)) of
197
{error, {unknown_module, Module, Modules}} ->
201
local_error({existing_removed_module, Module})
204
%% Removing a module on downgrade: the module exist
206
check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules)
207
when is_atom(Module), is_atom(Pre), is_atom(Post) ->
208
d("check_instruction -> entry when down-remove instruction with"
211
"~n Post: ~p", [Module, Pre, Post]),
212
case (catch check_module(Module, Modules)) of
216
check_no_remove_depends(Module, AllInstr);
217
{error, {unknown_module, Module, Modules}} ->
218
local_error({nonexisting_removed_module, Module})
221
check_instruction(_, {load_module, Module, Pre, Post, Depend},
223
when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) ->
224
d("check_instruction -> entry when load_module instruction with"
228
"~n Depend: ~p", [Module, Pre, Post, Depend]),
229
check_module(Module, Modules),
230
check_module_depend(Module, Depend, Modules),
231
check_module_depend(Module, Depend, updated_modules(AllInstr, [])),
235
check_instruction(_, {update, Module, Change, Pre, Post, Depend},
237
when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) ->
238
d("check_instruction -> entry when update instruction with"
243
"~n Depend: ~p", [Module, Change, Pre, Post, Depend]),
244
check_module(Module, Modules),
245
check_module_depend(Module, Depend, Modules),
246
check_module_depend(Module, Depend, updated_modules(AllInstr, [])),
247
check_change(Change),
251
check_instruction(_, Instr, _AllInstr, _Modules) ->
252
d("check_instruction -> entry when unknown instruction with"
253
"~n Instr: ~p", [Instr]),
254
local_error({error, {unknown_instruction, Instr}}).
257
%% If Module X depends on Module Y, then module Y must have an update
258
%% instruction of some sort (otherwise the depend is faulty).
259
updated_modules([], Modules) ->
260
d("update_modules -> entry when done with"
261
"~n Modules: ~p", [Modules]),
263
updated_modules([Instr |Instrs], Modules) ->
264
d("update_modules -> entry with"
266
"~n Modules: ~p", [Instr,Modules]),
267
Module = instruction_module(Instr),
268
d("update_modules -> Module: ~p", [Module]),
269
updated_modules(Instrs, [Module|Modules]).
271
instruction_module({add_module, Module}) ->
273
instruction_module({remove, {Module, _, _}}) ->
275
instruction_module({load_module, Module, _, _, _}) ->
277
instruction_module({update, Module, _, _, _, _}) ->
279
instruction_module(Instr) ->
280
d("instruction_module -> entry when unknown instruction with"
281
"~n Instr: ~p", [Instr]),
282
local_error({error, {unknown_instruction, Instr}}).
284
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286
check_version(V) when is_list(V) ->
289
local_error({bad_version, V}).
292
check_module(M, Modules) when is_atom(M) ->
293
case lists:member(M,Modules) of
297
local_error({unknown_module, M, Modules})
299
check_module(M, _) ->
300
local_error({bad_module, M}).
303
check_module_depend(M, [], _) when is_atom(M) ->
304
d("check_module_depend -> entry with"
307
check_module_depend(M, Deps, Modules) when is_atom(M), is_list(Deps) ->
308
d("check_module_depend -> entry with"
311
"~n Modules: ~p", [M, Deps, Modules]),
312
case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of
316
local_error({unknown_depend_modules, Unknown})
318
check_module_depend(_M, D, _Modules) ->
319
d("check_module_depend -> entry when bad depend with"
321
local_error({bad_depend, D}).
324
check_no_remove_depends(_Module, []) ->
326
check_no_remove_depends(Module, [Instr|Instrs]) ->
327
check_no_remove_depend(Module, Instr),
328
check_no_remove_depends(Module, Instrs).
330
check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) ->
331
case lists:member(Module, Depend) of
333
local_error({removed_module_in_depend, load_module, Mod, Module});
337
check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) ->
338
case lists:member(Module, Depend) of
340
local_error({removed_module_in_depend, update, Mod, Module});
344
check_no_remove_depend(_, _) ->
348
check_change(soft) ->
350
check_change({advanced, _Something}) ->
352
check_change(Change) ->
353
local_error({bad_change, Change}).
356
check_purge(soft_purge) ->
358
check_purge(brutal_purge) ->
360
check_purge(Purge) ->
361
local_error({bad_purge, Purge}).
365
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
367
local_error(Reason) ->
368
throw({error, Reason}).
371
exit({suite_failed, Reason}).
373
key1search(Key, L) ->
374
case lists:keysearch(Key, 1, L) of
376
fail({not_found, Key, L});
377
{value, {Key, Value}} ->
382
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
388
io:format(F ++ "~n", A);