4
%% Copyright Ericsson AB 2010-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
21
%%----------------------------------------------------------------------
22
%% Purpose: Verify the application specifics of the Wx application
23
%%----------------------------------------------------------------------
24
-module(wx_app_SUITE).
28
-include("wx_test_lib.hrl").
31
t() -> wx_test_lib:t(?MODULE).
32
t(Case) -> wx_test_lib:t({?MODULE, Case}).
34
%% Test server callbacks
35
init_per_testcase(Case, Config0) ->
36
Config1 = wx_test_lib:init_per_testcase(Case, Config0),
39
%% io:format("AppFile: ~n~p~n", [AppFile]),
40
[{app_file, AppFile} | Config1];
45
end_per_testcase(Func,Config) ->
46
wx_test_lib:end_per_testcase(Func, Config).
48
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50
suite() -> [{ct_hooks,[ts_install_cth]}].
53
[fields, modules, exportall, app_depend, undef_funcs].
58
init_per_suite(Config) ->
61
end_per_suite(_Config) ->
64
init_per_group(_GroupName, Config) ->
67
end_per_group(_GroupName, Config) ->
71
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74
LibDir = code:lib_dir(App),
75
File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]),
76
case file:consult(File) of
77
{ok, [{application, App, AppFile}]} ->
80
{error, {invalid_format, Error}}
83
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89
fields(Config) when is_list(Config) ->
90
AppFile = key1search(app_file, Config),
91
Fields = [vsn, description, modules, registered, applications],
92
case check_fields(Fields, AppFile, []) of
96
fail({missing_fields, Missing})
99
check_fields([], _AppFile, Missing) ->
101
check_fields([Field|Fields], AppFile, Missing) ->
102
check_fields(Fields, AppFile, check_field(Field, AppFile, Missing)).
104
check_field(Name, AppFile, Missing) ->
105
io:format("checking field: ~p~n", [Name]),
106
case lists:keymember(Name, 1, AppFile) of
113
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119
modules(Config) when is_list(Config) ->
120
AppFile = key1search(app_file, Config),
121
Mods = key1search(modules, AppFile),
122
EbinList = get_ebin_mods(wx),
123
case missing_modules(Mods, EbinList, []) of
127
throw({error, {missing_modules, Missing}})
129
case extra_modules(Mods, EbinList, []) of
133
throw({error, {extra_modules, Extra}})
137
get_ebin_mods(App) ->
138
LibDir = code:lib_dir(App),
139
EbinDir = filename:join([LibDir,"ebin"]),
140
{ok, Files0} = file:list_dir(EbinDir),
141
Files1 = [lists:reverse(File) || File <- Files0],
142
[list_to_atom(lists:reverse(Name)) || [$m,$a,$e,$b,$.|Name] <- Files1].
144
missing_modules([], _Ebins, Missing) ->
146
missing_modules([Mod|Mods], Ebins, Missing) ->
147
case lists:member(Mod, Ebins) of
149
missing_modules(Mods, Ebins, Missing);
151
io:format("missing module: ~p~n", [Mod]),
152
missing_modules(Mods, Ebins, [Mod|Missing])
156
extra_modules(_Mods, [], Extra) ->
158
extra_modules(Mods, [Mod|Ebins], Extra) ->
159
case lists:member(Mod, Mods) of
161
extra_modules(Mods, Ebins, Extra);
163
io:format("supefluous module: ~p~n", [Mod]),
164
extra_modules(Mods, Ebins, [Mod|Extra])
167
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
173
exportall(Config) when is_list(Config) ->
174
AppFile = key1search(app_file, Config),
175
Mods = key1search(modules, AppFile),
176
check_export_all(Mods).
179
check_export_all([]) ->
181
check_export_all([Mod|Mods]) ->
182
case (catch apply(Mod, module_info, [compile])) of
183
{'EXIT', {undef, _}} ->
184
check_export_all(Mods);
186
case lists:keysearch(options, 1, O) of
188
check_export_all(Mods);
189
{value, {options, List}} ->
190
case lists:member(export_all, List) of
192
throw({error, {export_all, Mod}});
194
check_export_all(Mods)
199
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
205
app_depend(Config) when is_list(Config) ->
206
AppFile = key1search(app_file, Config),
207
Apps = key1search(applications, AppFile),
212
check_apps([App|Apps]) ->
217
throw({error, {missing_app, {App, Error}}})
220
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
222
undef_funcs(suite) ->
226
undef_funcs(Config) when is_list(Config) ->
227
catch test_server:timetrap(timer:minutes(10)),
229
AppFile = key1search(app_file, Config),
230
Mods = key1search(modules, AppFile),
231
Root = code:root_dir(),
232
LibDir = code:lib_dir(App),
233
EbinDir = filename:join([LibDir,"ebin"]),
234
XRefTestName = undef_funcs_make_name(App, xref_test_name),
235
{ok, XRef} = xref:start(XRefTestName),
236
ok = xref:set_default(XRef,
237
[{verbose,false},{warnings,false}]),
238
XRefName = undef_funcs_make_name(App, xref_name),
239
{ok, XRefName} = xref:add_release(XRef, Root, {name,XRefName}),
240
{ok, App} = xref:replace_application(XRef, App, EbinDir),
241
{ok, Undefs} = xref:analyze(XRef, undefined_function_calls),
243
analyze_undefined_function_calls(Undefs, Mods, []).
245
analyze_undefined_function_calls([], _, []) ->
247
analyze_undefined_function_calls([], _, AppUndefs) ->
248
exit({suite_failed, {undefined_function_calls, AppUndefs}});
249
analyze_undefined_function_calls([{{Mod, _F, _A}, _C} = AppUndef|Undefs],
250
AppModules, AppUndefs) ->
251
%% Check that this module is ours
252
case lists:member(Mod,AppModules) of
254
{Calling,Called} = AppUndef,
255
{Mod1,Func1,Ar1} = Calling,
256
{Mod2,Func2,Ar2} = Called,
257
io:format("undefined function call: "
258
"~n ~w:~w/~w calls ~w:~w/~w~n",
259
[Mod1,Func1,Ar1,Mod2,Func2,Ar2]),
260
analyze_undefined_function_calls(Undefs, AppModules,
261
[AppUndef|AppUndefs]);
263
io:format("dropping ~p~n", [Mod]),
264
analyze_undefined_function_calls(Undefs, AppModules, AppUndefs)
267
%% This function is used simply to avoid cut-and-paste errors later...
268
undef_funcs_make_name(App, PostFix) ->
269
list_to_atom(atom_to_list(App) ++ "_" ++ atom_to_list(PostFix)).
272
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
275
exit({suite_failed, Reason}).
277
key1search(Key, L) ->
278
case lists:keysearch(Key, 1, L) of
280
fail({not_found, Key, L});
281
{value, {Key, Value}} ->