1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 1999-2011. 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: %%
   21: -module(fun_SUITE).
   22: 
   23: -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
   24: 	 init_per_testcase/2,end_per_testcase/2,
   25: 	 init_per_suite/1,end_per_suite/1,
   26: 	 good_call/1,bad_apply/1,bad_fun_call/1,badarity/1,
   27: 	 ext_badarity/1,otp_6061/1,external/1]).
   28: 
   29: %% Internal exports.
   30: -export([nothing/0,call_me/1]).
   31: 
   32: -include_lib("test_server/include/test_server.hrl").
   33: 
   34: suite() -> [{ct_hooks,[ts_install_cth]}].
   35: 
   36: all() -> 
   37:     cases().
   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: cases() -> 
   50:     [good_call, bad_apply, bad_fun_call, badarity,
   51:      ext_badarity, otp_6061, external].
   52: 
   53: init_per_testcase(_Case, Config) ->
   54:     test_lib:interpret(?MODULE),
   55:     Dog = test_server:timetrap(?t:minutes(1)),
   56:     [{watchdog,Dog}|Config].
   57: 
   58: end_per_testcase(_Case, Config) ->
   59:     Dog = ?config(watchdog, Config),
   60:     ?t:timetrap_cancel(Dog),
   61:     ok.
   62: 
   63: init_per_suite(Config) when is_list(Config) ->
   64:     ?line test_lib:interpret(?MODULE),
   65:     ?line true = lists:member(?MODULE, int:interpreted()),
   66:     Config.
   67: 
   68: end_per_suite(Config) when is_list(Config) ->
   69:     ok.
   70: 
   71: good_call(Config) when is_list(Config) ->
   72:     ?line F = fun() -> ok end,
   73:     ?line ok = F(),
   74:     ?line FF = fun ?MODULE:nothing/0,
   75:     ?line ok = FF(),
   76:     ok.
   77: 
   78: bad_apply(doc) ->
   79:     "Test that the correct EXIT code is returned for all types of bad funs.";
   80: bad_apply(suite) -> [];
   81: bad_apply(Config) when is_list(Config) ->
   82:     ?line bad_apply_fc(42, [0]),
   83:     ?line bad_apply_fc(xx, [1]),
   84:     ?line bad_apply_fc({}, [2]),
   85:     ?line bad_apply_fc({1}, [3]),
   86:     ?line bad_apply_fc({1,2,3}, [4]),
   87:     ?line bad_apply_fc({1,2,3}, [5]),
   88:     ?line bad_apply_fc({1,2,3,4}, [6]),
   89:     ?line bad_apply_fc({1,2,3,4,5,6}, [7]),
   90:     ?line bad_apply_fc({1,2,3,4,5}, [8]),
   91:     ?line bad_apply_badarg({1,2}, [9]),
   92:     ok.
   93: 
   94: bad_apply_fc(Fun, Args) ->
   95:     Res = (catch apply(Fun, Args)),
   96:     erlang:garbage_collect(),
   97:     erlang:yield(),
   98:     case Res of
   99: 	{'EXIT',{{badfun,Fun},_Where}} ->
  100: 	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]);
  101: 	Other ->
  102: 	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]),
  103: 	    ?t:fail({bad_result,Other})
  104:     end.
  105: 
  106: bad_apply_badarg(Fun, Args) ->
  107:     Res = (catch apply(Fun, Args)),
  108:     erlang:garbage_collect(),
  109:     erlang:yield(),
  110:     case Res of
  111: 	{'EXIT',{{badfun,Fun},_Where}} ->
  112: 	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]);
  113: 	Other ->
  114: 	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]),
  115: 	    ?t:fail({bad_result, Other})
  116:     end.
  117: 
  118: bad_fun_call(doc) ->
  119:     "Try directly calling bad funs.";
  120: bad_fun_call(suite) -> [];
  121: bad_fun_call(Config) when is_list(Config) ->
  122:     ?line bad_call_fc(42),
  123:     ?line bad_call_fc(xx),
  124:     ?line bad_call_fc({}),
  125:     ?line bad_call_fc({1}),
  126:     ?line bad_call_fc({1,2,3}),
  127:     ?line bad_call_fc({1,2,3}),
  128:     ?line bad_call_fc({1,2,3,4}),
  129:     ?line bad_call_fc({1,2,3,4,5,6}),
  130:     ?line bad_call_fc({1,2,3,4,5}),
  131:     ?line bad_call_fc({1,2}),
  132:     ok.
  133: 
  134: bad_call_fc(Fun) ->
  135:     Args = [some,stupid,args],
  136:     Res = (catch Fun(Args)),
  137:     case Res of
  138: 	{'EXIT',{{badfun,Fun},_Where}} ->
  139: 	    ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
  140: 	Other ->
  141: 	    ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
  142: 	    ?t:fail({bad_result,Other})
  143:     end.
  144: 
  145: %% Call and apply valid external funs with wrong number of arguments.
  146: 
  147: badarity(Config) when is_list(Config) ->
  148:     ?line Fun = fun() -> ok end,
  149:     ?line Stupid = {stupid,arguments},
  150:     ?line Args = [some,{stupid,arguments},here],
  151: 
  152:     %% Simple call.
  153: 
  154:     ?line Res = (catch Fun(some, Stupid, here)),
  155:     erlang:garbage_collect(),
  156:     erlang:yield(),
  157:     case Res of
  158: 	{'EXIT',{{badarity,{Fun,Args}},[_|_]}} ->
  159: 	    ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
  160: 	_ ->
  161: 	    ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
  162: 	    ?line ?t:fail({bad_result,Res})
  163:     end,
  164: 
  165:     %% Apply.
  166: 
  167:     ?line Res2 = (catch apply(Fun, Args)),
  168:     erlang:garbage_collect(),
  169:     erlang:yield(),
  170:     case Res2 of
  171: 	{'EXIT',{{badarity,{Fun,Args}},[_|_]}} ->
  172: 	    ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]);
  173: 	_ ->
  174: 	    ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]),
  175: 	    ?line ?t:fail({bad_result,Res2})
  176:     end,
  177:     ok.
  178: 
  179: %% Call and apply valid external funs with wrong number of arguments.
  180: 
  181: ext_badarity(Config) when is_list(Config) ->
  182:     ?line Fun = fun ?MODULE:nothing/0,
  183:     ?line Stupid = {stupid,arguments},
  184:     ?line Args = [some,{stupid,arguments},here],
  185: 
  186:     %% Simple call.
  187: 
  188:     ?line Res = (catch Fun(some, Stupid, here)),
  189:     erlang:garbage_collect(),
  190:     erlang:yield(),
  191:     case Res of
  192: 	{'EXIT',{{badarity,{Fun,Args}},_}} ->
  193: 	    ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
  194: 	_ ->
  195: 	    ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
  196: 	    ?line ?t:fail({bad_result,Res})
  197:     end,
  198: 
  199:     %% Apply.
  200: 
  201:     ?line Res2 = (catch apply(Fun, Args)),
  202:     erlang:garbage_collect(),
  203:     erlang:yield(),
  204:     case Res2 of
  205: 	{'EXIT',{{badarity,{Fun,Args}},_}} ->
  206: 	    ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]);
  207: 	_ ->
  208: 	    ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]),
  209: 	    ?line ?t:fail({bad_result,Res2})
  210:     end,
  211:     ok.
  212: 
  213: nothing() ->
  214:     ok.
  215: 
  216: otp_6061(suite) ->
  217:     [];
  218: otp_6061(doc) ->
  219:     ["Test handling of fun expression referring to uninterpreted code"];
  220: otp_6061(Config) when is_list(Config) ->
  221: 
  222:     ?line OrigFlag = process_flag(trap_exit, true),
  223: 
  224:     ?line Self = self(),
  225:     ?line Pid = spawn_link(fun() -> test_otp_6061(Self) end),
  226: 
  227:     receive
  228: 	working ->
  229: 	    ?line ok;
  230: 	not_working ->
  231: 	    ?line ?t:fail(not_working);
  232: 	{'EXIT', Pid, Reason} ->
  233: 	    ?line ?t:fail({crash, Reason})
  234:     after
  235: 	5000 ->
  236: 	    ?line ?t:fail(timeout)
  237:     end,
  238: 
  239:     ?line process_flag(trap_exit, OrigFlag),
  240: 
  241:     ok.
  242: 
  243: test_otp_6061(Starter) ->
  244:     Passes = [2],
  245:     PassesF = [fun() -> Starter ! not_working end,
  246: 	       fun() -> Starter ! working end,
  247: 	       fun() -> Starter ! not_working end],
  248:     lists:foreach(fun(P)->(lists:nth(P,PassesF))() end,Passes).
  249: 
  250: -define(APPLY(M, F, A), (fun(Fun) -> {ok,{a,b}} = Fun({a,b}) end)(fun M:F/A)).
  251: -define(APPLY2(M, F, A),
  252: 	(fun(Map) ->
  253: 		 Id = fun(I) -> I end,
  254: 		 List = [x,y],
  255: 		 List = Map(Id, List),
  256: 		 {type,external} = erlang:fun_info(Map, type)
  257: 	 end)(fun M:F/A)).
  258: 
  259: external(Config) when is_list(Config) ->
  260:     Mod = id(?MODULE),
  261:     Func = id(call_me),
  262:     Arity = id(1),
  263: 
  264:     ?APPLY(?MODULE, call_me, 1),
  265:     ?APPLY(?MODULE, call_me, Arity),
  266:     ?APPLY(?MODULE, Func, 1),
  267:     ?APPLY(?MODULE, Func, Arity),
  268:     ?APPLY(Mod, call_me, 1),
  269:     ?APPLY(Mod, call_me, Arity),
  270:     ?APPLY(Mod, Func, 1),
  271:     ?APPLY(Mod, Func, Arity),
  272: 
  273:     ListsMod = id(lists),
  274:     ListsMap = id(map),
  275:     ListsArity = id(2),
  276: 
  277:     ?APPLY2(lists, map, 2),
  278:     ?APPLY2(lists, map, ListsArity),
  279:     ?APPLY2(lists, ListsMap, 2),
  280:     ?APPLY2(lists, ListsMap, ListsArity),
  281:     ?APPLY2(ListsMod, map, 2),
  282:     ?APPLY2(ListsMod, map, ListsArity),
  283:     ?APPLY2(ListsMod, ListsMap, 2),
  284:     ?APPLY2(ListsMod, ListsMap, ListsArity),
  285: 
  286:     ok.
  287: 
  288: call_me(I) ->
  289:     {ok,I}.
  290: 
  291: id(I) ->
  292:     I.