1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2010-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22%%---------------------------------------------------------------------- 23%% Purpose: Verify the application specifics of the Reltool application 24%%---------------------------------------------------------------------- 25-module(reltool_app_SUITE). 26 27-compile([export_all, nowarn_export_all]). 28 29-include("reltool_test_lib.hrl"). 30-include_lib("common_test/include/ct.hrl"). 31 32 33t() -> reltool_test_lib:t(?MODULE). 34t(Case) -> reltool_test_lib:t({?MODULE, Case}). 35 36%% Test server callbacks 37init_per_suite(Config) -> 38 Config2 = reltool_test_lib:init_per_suite(Config), 39 case is_app(reltool) of 40 {ok, AppFile} -> 41 %% io:format("AppFile: ~n~p~n", [AppFile]), 42 [{app_file, AppFile} | Config2]; 43 {error, Reason} -> 44 fail(Reason) 45 end. 46 47end_per_suite(Config) -> 48 reltool_test_lib:end_per_suite(Config). 49 50init_per_testcase(undef_funcs=Case, Config) -> 51 case test_server:is_debug() of 52 true -> 53 {skip,"Debug-compiled emulator -- far too slow"}; 54 false -> 55 Config2 = [{tc_timeout, timer:minutes(10)} | Config], 56 reltool_test_lib:init_per_testcase(Case, Config2) 57 end; 58init_per_testcase(Case, Config) -> 59 reltool_test_lib:init_per_testcase(Case, Config). 60 61end_per_testcase(Func,Config) -> 62 reltool_test_lib:end_per_testcase(Func,Config). 63 64%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 65 66suite() -> [{ct_hooks,[ts_install_cth]}]. 67 68all() -> 69 [fields, modules, export_all, app_depend, undef_funcs, appup]. 70 71groups() -> 72 []. 73 74init_per_group(_GroupName, Config) -> 75 Config. 76 77end_per_group(_GroupName, Config) -> 78 Config. 79 80 81%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 82 83is_app(App) -> 84 LibDir = code:lib_dir(App), 85 File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]), 86 case file:consult(File) of 87 {ok, [{application, App, AppFile}]} -> 88 {ok, AppFile}; 89 {error, {LineNo, Mod, Code}} -> 90 IoList = lists:concat([File, ":", LineNo, ": ", 91 Mod:format_error(Code)]), 92 {error, list_to_atom(lists:flatten(IoList))} 93 end. 94 95%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 96 97fields(suite) -> 98 []; 99fields(doc) -> 100 []; 101fields(Config) when is_list(Config) -> 102 AppFile = key1search(app_file, Config), 103 Fields = [vsn, description, modules, registered, applications], 104 case check_fields(Fields, AppFile, []) of 105 [] -> 106 ok; 107 Missing -> 108 fail({missing_fields, Missing}) 109 end. 110 111check_fields([], _AppFile, Missing) -> 112 Missing; 113check_fields([Field|Fields], AppFile, Missing) -> 114 check_fields(Fields, AppFile, check_field(Field, AppFile, Missing)). 115 116check_field(Name, AppFile, Missing) -> 117 io:format("checking field: ~p~n", [Name]), 118 case lists:keymember(Name, 1, AppFile) of 119 true -> 120 Missing; 121 false -> 122 [Name|Missing] 123 end. 124 125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 126 127modules(suite) -> 128 []; 129modules(doc) -> 130 []; 131modules(Config) when is_list(Config) -> 132 AppFile = key1search(app_file, Config), 133 Mods = key1search(modules, AppFile), 134 EbinList = get_ebin_mods(reltool), 135 case missing_modules(Mods, EbinList, []) of 136 [] -> 137 ok; 138 Missing -> 139 throw({error, {missing_modules, Missing}}) 140 end, 141 case extra_modules(Mods, EbinList, []) of 142 [] -> 143 ok; 144 Extra -> 145 throw({error, {extra_modules, Extra}}) 146 end, 147 {ok, Mods}. 148 149get_ebin_mods(App) -> 150 LibDir = code:lib_dir(App), 151 EbinDir = filename:join([LibDir,"ebin"]), 152 {ok, Files0} = file:list_dir(EbinDir), 153 Files1 = [lists:reverse(File) || File <- Files0], 154 [list_to_atom(lists:reverse(Name)) || [$m,$a,$e,$b,$.|Name] <- Files1]. 155 156missing_modules([], _Ebins, Missing) -> 157 Missing; 158missing_modules([Mod|Mods], Ebins, Missing) -> 159 case lists:member(Mod, Ebins) of 160 true -> 161 missing_modules(Mods, Ebins, Missing); 162 false -> 163 io:format("missing module: ~p~n", [Mod]), 164 missing_modules(Mods, Ebins, [Mod|Missing]) 165 end. 166 167 168extra_modules(_Mods, [], Extra) -> 169 Extra; 170extra_modules(Mods, [Mod|Ebins], Extra) -> 171 case lists:member(Mod, Mods) of 172 true -> 173 extra_modules(Mods, Ebins, Extra); 174 false -> 175 io:format("supefluous module: ~p~n", [Mod]), 176 extra_modules(Mods, Ebins, [Mod|Extra]) 177 end. 178 179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 180 181export_all(suite) -> 182 []; 183export_all(doc) -> 184 []; 185export_all(Config) when is_list(Config) -> 186 AppFile = key1search(app_file, Config), 187 Mods = key1search(modules, AppFile), 188 check_export_all(Mods). 189 190 191check_export_all([]) -> 192 ok; 193check_export_all([Mod|Mods]) -> 194 case (catch apply(Mod, module_info, [compile])) of 195 {'EXIT', {undef, _}} -> 196 check_export_all(Mods); 197 O -> 198 case lists:keysearch(options, 1, O) of 199 false -> 200 check_export_all(Mods); 201 {value, {options, List}} -> 202 case lists:member(export_all, List) of 203 true -> 204 throw({error, {export_all, Mod}}); 205 false -> 206 check_export_all(Mods) 207 end 208 end 209 end. 210 211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 212 213app_depend(suite) -> 214 []; 215app_depend(doc) -> 216 []; 217app_depend(Config) when is_list(Config) -> 218 AppFile = key1search(app_file, Config), 219 Apps = key1search(applications, AppFile), 220 check_apps(Apps). 221 222check_apps([]) -> 223 ok; 224check_apps([App|Apps]) -> 225 case is_app(App) of 226 {ok, _} -> 227 check_apps(Apps); 228 Error -> 229 throw({error, {missing_app, {App, Error}}}) 230 end. 231 232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 233 234undef_funcs(suite) -> 235 []; 236undef_funcs(doc) -> 237 []; 238undef_funcs(Config) when is_list(Config) -> 239 App = reltool, 240 AppFile = key1search(app_file, Config), 241 Mods = key1search(modules, AppFile), 242 Root = code:root_dir(), 243 LibDir = code:lib_dir(App), 244 EbinDir = filename:join([LibDir,"ebin"]), 245 XRefTestName = undef_funcs_make_name(App, xref_test_name), 246 {ok, XRef} = xref:start(XRefTestName), 247 ok = xref:set_default(XRef, 248 [{verbose,false},{warnings,false}]), 249 XRefName = undef_funcs_make_name(App, xref_name), 250 {ok, XRefName} = xref:add_release(XRef, Root, {name,XRefName}), 251 {ok, App} = xref:replace_application(XRef, App, EbinDir), 252 {ok, Undefs} = xref:analyze(XRef, undefined_function_calls), 253 xref:stop(XRef), 254 analyze_undefined_function_calls(Undefs, Mods, []). 255 256analyze_undefined_function_calls([], _, []) -> 257 ok; 258analyze_undefined_function_calls([], _, AppUndefs) -> 259 exit({suite_failed, {undefined_function_calls, AppUndefs}}); 260analyze_undefined_function_calls([{{Mod, _F, _A}, _C} = AppUndef|Undefs], 261 AppModules, AppUndefs) -> 262 %% Check that this module is our's 263 case lists:member(Mod,AppModules) of 264 true -> 265 {Calling,Called} = AppUndef, 266 {Mod1,Func1,Ar1} = Calling, 267 {Mod2,Func2,Ar2} = Called, 268 io:format("undefined function call: " 269 "~n ~w:~w/~w calls ~w:~w/~w~n", 270 [Mod1,Func1,Ar1,Mod2,Func2,Ar2]), 271 analyze_undefined_function_calls(Undefs, AppModules, 272 [AppUndef|AppUndefs]); 273 false -> 274 io:format("dropping ~p~n", [Mod]), 275 analyze_undefined_function_calls(Undefs, AppModules, AppUndefs) 276 end. 277 278%% This function is used simply to avoid cut-and-paste errors later... 279undef_funcs_make_name(App, PostFix) -> 280 list_to_atom(atom_to_list(App) ++ "_" ++ atom_to_list(PostFix)). 281 282 283%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 284 285fail(Reason) -> 286 exit({suite_failed, Reason}). 287 288key1search(Key, L) -> 289 case lists:keysearch(Key, 1, L) of 290 false -> 291 fail({not_found, Key, L}); 292 {value, {Key, Value}} -> 293 Value 294 end. 295 296%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 297 298%% Test that the reltool appup file is ok 299appup(Config) when is_list(Config) -> 300 ok = test_server:appup_test(reltool). 301