1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 2000-2013. All Rights Reserved.
    5: %%
    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/.
   11: %%
   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
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: -module(otp_SUITE).
   21: 
   22: -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
   23: 	 init_per_suite/1,end_per_suite/1]).
   24: -export([undefined_functions/1,deprecated_not_in_obsolete/1,
   25: 	 obsolete_but_not_deprecated/1,call_to_deprecated/1,
   26:          call_to_size_1/1,strong_components/1]).
   27: 
   28: -include_lib("test_server/include/test_server.hrl").
   29: 
   30: -import(lists, [filter/2,foldl/3,foreach/2]).
   31: 
   32: suite() -> [{ct_hooks,[ts_install_cth]}].
   33: 
   34: all() -> 
   35:     [undefined_functions, deprecated_not_in_obsolete,
   36:      obsolete_but_not_deprecated, call_to_deprecated,
   37:      call_to_size_1, strong_components].
   38: 
   39: groups() -> 
   40:     [].
   41: 
   42: init_per_group(_GroupName, Config) ->
   43:     Config.
   44: 
   45: end_per_group(_GroupName, Config) ->
   46:     Config.
   47: 
   48: 
   49: init_per_suite(Config) ->
   50:     Dog = test_server:timetrap(?t:minutes(10)),
   51:     Root = code:root_dir(),
   52:     Server = daily_xref,
   53:     ?line xref:start(Server),
   54:     ?line xref:set_default(Server, [{verbose,false},
   55:                                     {warnings,false},
   56:                                     {builtins,true}]),
   57:     ?line {ok,_Relname} = xref:add_release(Server, Root, {name,otp}),
   58: 
   59:     %% If we are running the tests in the source tree, the ERTS application
   60:     %% is not in the code path. We must add it explicitly.
   61:     case code:lib_dir(erts) of
   62: 	{error,bad_name} ->
   63: 	    Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]),
   64: 	    ?line {ok,_} = xref:add_directory(Server, Erts, []);
   65: 	_ ->
   66: 	    ok
   67:     end,
   68: 	    
   69:     ?line ?t:timetrap_cancel(Dog),
   70:     [{xref_server,Server}|Config].
   71: 
   72: end_per_suite(Config) ->
   73:     Server = ?config(xref_server, Config),
   74:     catch xref:stop(Server),
   75:     Config.
   76: 
   77: undefined_functions(Config) when is_list(Config) ->
   78:     Server = ?config(xref_server, Config),
   79: 
   80:     %% Exclude calls from generated modules in the SSL application.
   81:     ExcludeFrom = "SSL-PKIX|PKIX.*|ssl_pkix_oid",
   82:     ?line UndefS = xref_base:analysis(undefined_function_calls),
   83:     ?line Q = io_lib:format("Undef = ~s,"
   84: 		      "ExcludedFrom = ~p:_/_,"
   85: 		      "Undef - Undef | ExcludedFrom", 
   86: 		      [UndefS,ExcludeFrom]),
   87:     {ok,Undef0} = xref:q(Server, lists:flatten(Q)),
   88:     Undef1 = hipe_filter(Undef0),
   89:     Undef2 = ssl_crypto_filter(Undef1),
   90:     Undef3 = edoc_filter(Undef2),
   91:     Undef4 = eunit_filter(Undef3),
   92:     Undef5 = dialyzer_filter(Undef4),
   93:     Undef6 = wx_filter(Undef5),
   94:     Undef  = gs_filter(Undef6),
   95: 
   96:     case Undef of
   97: 	[] -> ok;
   98: 	_ ->
   99: 	    Fd = open_log(Config, "undefined_functions"),
  100: 	    foreach(fun ({MFA1,MFA2}) ->
  101: 			    io:format("~s calls undefined ~s",
  102: 				      [format_mfa(Server, MFA1),
  103: 				       format_mfa(MFA2)]),
  104: 			    io:format(Fd, "~s ~s\n",
  105: 				      [format_mfa(Server, MFA1),
  106: 				       format_mfa(MFA2)])
  107: 		    end, Undef),
  108: 	    close_log(Fd),
  109: 	    ?line ?t:fail({length(Undef),undefined_functions_in_otp})
  110:     end.
  111: 
  112: hipe_filter(Undef) ->
  113:     case erlang:system_info(hipe_architecture) of
  114: 	undefined ->
  115: 	    filter(fun ({_,{hipe_bifs,_,_}}) -> false;
  116: 		       ({_,{hipe,_,_}}) -> false;
  117: 		       ({_,{hipe_consttab,_,_}}) -> false;
  118: 		       ({_,{hipe_converters,_,_}}) -> false;
  119: 		       ({{code,_,_},{Mod,_,_}}) ->
  120: 			   not is_hipe_module(Mod);
  121: 		       ({{code_server,_,_},{Mod,_,_}}) ->
  122: 			   not is_hipe_module(Mod);
  123: 		       ({{compile,_,_},{Mod,_,_}}) ->
  124: 			   not is_hipe_module(Mod);
  125: 		       ({{hipe,_,_},{Mod,_,_}}) ->
  126: 			   %% See comment for the next clause.
  127: 			   not is_hipe_module(Mod);
  128: 		       ({{cerl_to_icode,translate_flags1,2},
  129: 			 {hipe_rtl_arch,endianess,0}}) ->
  130: 			   false;
  131: 		       ({{Caller,_,_},{Callee,_,_}}) ->
  132: 			   %% Part of the hipe application is here
  133: 			   %% for the sake of Dialyzer. There are many
  134: 			   %% undefined calls within the hipe application.
  135: 			   not is_hipe_module(Caller) orelse
  136: 			       not is_hipe_module(Callee);
  137: 		       (_) -> true
  138: 		   end, Undef);
  139: 	_Arch ->
  140: 	    filter(fun ({{Mod,_,_},{hipe_bifs,write_u64,2}}) ->
  141: 			   %% Unavailable except in 64 bit AMD. Ignore it.
  142: 			   not is_hipe_module(Mod);
  143: 		       (_) -> true
  144: 		   end, Undef)
  145:     end.
  146: 
  147: is_hipe_module(Mod) ->
  148:     case atom_to_list(Mod) of
  149: 	"hipe_"++_ -> true;
  150: 	_ -> false
  151:     end.
  152: 
  153: ssl_crypto_filter(Undef) ->
  154:     case {app_exists(crypto),app_exists(ssl)} of
  155: 	{false,false} ->
  156: 	    filter(fun({_,{ssl,_,_}}) -> false;
  157: 		      ({_,{crypto,_,_}}) -> false;
  158: 		      ({_,{ssh,_,_}}) -> false;
  159: 		      ({_,{ssh_connection,_,_}}) -> false;
  160: 		      ({_,{ssh_sftp,_,_}}) -> false;
  161: 		      (_) -> true
  162: 		   end, Undef);
  163: 	{_,_} -> Undef
  164:     end.
  165: 
  166: edoc_filter(Undef) ->
  167:     %% Filter away function call that is catched.
  168:     filter(fun({{edoc_lib,uri_get_http,1},{http,request_sync,2}}) -> false;
  169: 	      (_) -> true
  170: 	   end, Undef).
  171: 
  172: eunit_filter(Undef) ->
  173:     filter(fun({{eunit_test,wrapper_test_exported_,0},
  174: 		{eunit_test,nonexisting_function,0}}) -> false;
  175: 	      (_) -> true
  176: 	   end, Undef).
  177: 
  178: dialyzer_filter(Undef) ->
  179:     case app_exists(dialyzer) of
  180: 	false ->
  181: 	    filter(fun({_,{dialyzer_callgraph,_,_}}) -> false;
  182: 		      ({_,{dialyzer_codeserver,_,_}}) -> false;
  183: 		      ({_,{dialyzer_contracts,_,_}}) -> false;
  184: 		      ({_,{dialyzer_cl_parse,_,_}}) -> false;
  185: 		      ({_,{dialyzer_timing,_,_}}) -> false;
  186: 		      ({_,{dialyzer_plt,_,_}}) -> false;
  187: 		      ({_,{dialyzer_succ_typings,_,_}}) -> false;
  188: 		      ({_,{dialyzer_utils,_,_}}) -> false;
  189: 		      (_) -> true
  190: 		   end, Undef);
  191: 	_ -> Undef
  192:     end.
  193: 
  194: wx_filter(Undef) ->
  195:     case app_exists(wx) of
  196: 	false ->
  197: 	    filter(fun({_,{MaybeWxModule,_,_}}) ->
  198: 			   case atom_to_list(MaybeWxModule) of
  199: 			       "wx"++_ -> false;
  200: 			       _ -> true
  201: 			   end
  202: 		   end, Undef);
  203: 	_ -> Undef
  204:     end.
  205: 				   
  206: gs_filter(Undef) ->
  207:     case code:lib_dir(gs) of
  208: 	{error,bad_name} ->
  209: 	    filter(fun({_,{gs,_,_}}) -> false;
  210: 		      ({_,{gse,_,_}}) -> false;
  211:                       ({_,{tool_utils,_,_}}) -> false;
  212: 		      (_) -> true
  213: 		   end, Undef);
  214: 	_ -> Undef
  215:     end.
  216: 
  217: deprecated_not_in_obsolete(Config) when is_list(Config) ->
  218:     ?line Server = ?config(xref_server, Config),
  219:     ?line {ok,DeprecatedFunctions} = xref:q(Server, "DF"),
  220: 
  221:     ?line L = foldl(fun({M,F,A}=MFA, Acc) ->
  222: 			    case otp_internal:obsolete(M, F, A) of
  223: 				no -> [MFA|Acc];
  224: 				_ -> Acc
  225: 			    end
  226: 		    end, [], DeprecatedFunctions),
  227:     case L of
  228: 	[] -> ok;
  229: 	_ ->
  230: 	    io:put_chars("The following functions have -deprecated() attributes,\n"
  231: 			 "but are not listed in otp_internal:obsolete/3.\n"),
  232: 	    print_mfas(group_leader(), Server, L),
  233: 	    Fd = open_log(Config, "deprecated_not_obsolete"),
  234: 	    print_mfas(Fd, Server, L),
  235: 	    close_log(Fd),
  236: 	    ?line ?t:fail({length(L),deprecated_but_not_obsolete})
  237:     end.
  238: 
  239: obsolete_but_not_deprecated(Config) when is_list(Config) ->
  240:     ?line Server = ?config(xref_server, Config),
  241:     ?line {ok,NotDeprecated} = xref:q(Server, "X - DF"),
  242: 
  243:     ?line L = foldl(fun({M,F,A}=MFA, Acc) ->
  244: 			    case otp_internal:obsolete(M, F, A) of
  245: 				no -> Acc;
  246: 				_ -> [MFA|Acc]
  247: 			    end
  248: 		    end, [], NotDeprecated),
  249: 
  250:     case L of
  251: 	[] -> ok;
  252: 	_ ->
  253: 	    io:put_chars("The following functions are listed "
  254: 			 "in otp_internal:obsolete/3,\n"
  255: 			 "but don't have -deprecated() attributes.\n"),
  256: 	    print_mfas(group_leader(), Server, L),
  257: 	    Fd = open_log(Config, "obsolete_not_deprecated"),
  258: 	    print_mfas(Fd, Server, L),
  259: 	    close_log(Fd),
  260: 	    ?line ?t:fail({length(L),obsolete_but_not_deprecated})
  261:     end.
  262:     
  263: call_to_deprecated(Config) when is_list(Config) ->
  264:     Server = ?config(xref_server, Config),
  265:     ?line {ok,DeprecatedCalls} = xref:q(Server, "strict(E || DF)"),
  266:     foreach(fun ({MFA1,MFA2}) ->
  267: 		    io:format("~s calls deprecated ~s",
  268: 			      [format_mfa(MFA1),format_mfa(MFA2)])
  269: 	    end, DeprecatedCalls),
  270:     {comment,integer_to_list(length(DeprecatedCalls))++" calls to deprecated functions"}.
  271: 
  272: call_to_size_1(Config) when is_list(Config) ->
  273:     Server = ?config(xref_server, Config),
  274: 
  275:     %% Applications that do not call erlang:size/1:
  276:     Apps = [asn1,compiler,debugger,kernel,observer,parsetools,
  277: 	    runtime_tools,stdlib,tools,webtool],
  278: 
  279:     Fs = [{erlang,size,1}],
  280: 
  281:     Q1 = io_lib:format("E || ~p : Fun", [Fs]),
  282:     ?line {ok,AllCallsToSize1} = xref:q(Server, lists:flatten(Q1)),
  283: 
  284:     Q2 = io_lib:format("E | ~p : App || ~p : Fun", [Apps,Fs]),
  285:     ?line {ok,CallsToSize1} = xref:q(Server, lists:flatten(Q2)),
  286: 
  287:     case CallsToSize1 of
  288: 	[] -> 
  289:             ok;
  290: 	_ ->
  291:             io:format("These calls cause an error:~n"),
  292: 	    foreach(fun ({MFA1,MFA2}) ->
  293: 			    io:format("~s calls soon to be deprecated ~s",
  294: 				      [format_mfa(MFA1),format_mfa(MFA2)])
  295: 		    end, CallsToSize1)
  296:     end,
  297: 
  298:     %% Enumerate calls to erlang:size/1 from other applications than
  299:     %% the ones known not to call erlang:size/1:
  300:     case AllCallsToSize1--CallsToSize1 of
  301:         [] ->
  302:             ok;
  303:         Calls ->
  304:             io:format("~n~nThese calls do not cause an error (yet):~n"),
  305:             foreach(fun ({MFA1,MFA2}) ->
  306:                             io:format("~s calls soon to be deprecated ~s",
  307:                                       [format_mfa(MFA1),format_mfa(MFA2)])
  308:                     end, Calls)
  309:     end,
  310:     case CallsToSize1 of
  311: 	[] -> 
  312:             ok;
  313: 	_ ->
  314: 	    ?line ?t:fail({length(CallsToSize1),calls_to_size_1})
  315:     end.
  316: 
  317: strong_components(Config) when is_list(Config) ->
  318:     Server = ?config(xref_server, Config),
  319:     ?line {ok,Cs} = xref:q(Server, "components AE"),
  320:     io:format("\n\nStrong components:\n\n~p\n", [Cs]),
  321:     ok.
  322: 
  323: %%%
  324: %%% Common help functions.
  325: %%%
  326:     
  327: print_mfas(Fd, Server, MFAs) ->
  328:     [io:format(Fd, "~s\n", [format_mfa(Server, MFA)]) || MFA <- MFAs],
  329:     ok.
  330: 
  331: format_mfa({M,F,A}) ->
  332:     lists:flatten(io_lib:format("~s:~s/~p", [M,F,A])).
  333: 
  334: format_mfa(Server, MFA) ->
  335:     MFAString = format_mfa(MFA),
  336:     AQ = "(App)" ++ MFAString,
  337:     AppPrefix = case xref:q(Server, AQ) of
  338: 		    {ok,[App]} -> "[" ++ atom_to_list(App) ++ "]";
  339: 		    _ -> ""
  340: 		end,
  341:     AppPrefix ++ MFAString.
  342: 
  343: open_log(Config, Name) ->
  344:     PrivDir = ?config(priv_dir, Config),
  345:     RunDir = filename:dirname(filename:dirname(PrivDir)),
  346:     Path = filename:join(RunDir, "system_"++Name++".log"),
  347:     {ok,Fd} = file:open(Path, [write]),
  348:     Fd.
  349: 
  350: close_log(Fd) ->
  351:     ok = file:close(Fd).
  352: 
  353: app_exists(AppAtom) ->
  354:     case code:lib_dir(AppAtom) of
  355: 	{error,bad_name} ->
  356: 	    false;
  357: 	Path ->
  358: 	    case file:read_file_info(filename:join(Path,"ebin")) of
  359: 		{ok,_} ->
  360: 		    true;
  361: 		_ ->
  362: 		    false
  363: 	    end
  364:     end.