%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% -module(fun_SUITE). -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, init_per_suite/1,end_per_suite/1, good_call/1,bad_apply/1,bad_fun_call/1,badarity/1, ext_badarity/1,otp_6061/1,external/1,eep37/1]). %% Internal exports. -export([nothing/0,call_me/1]). -include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,1}}]. all() -> cases(). groups() -> []. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. cases() -> [good_call, bad_apply, bad_fun_call, badarity, ext_badarity, otp_6061, external, eep37]. init_per_testcase(_Case, Config) -> test_lib:interpret(?MODULE), Config. end_per_testcase(_Case, _Config) -> ok. init_per_suite(Config) when is_list(Config) -> test_lib:interpret(?MODULE), true = lists:member(?MODULE, int:interpreted()), Config. end_per_suite(Config) when is_list(Config) -> ok. good_call(Config) when is_list(Config) -> F = fun() -> ok end, ok = F(), FF = fun ?MODULE:nothing/0, ok = FF(), ok. %% Test that the correct EXIT code is returned for all types of bad funs. bad_apply(Config) when is_list(Config) -> bad_apply_fc(42, [0]), bad_apply_fc(xx, [1]), bad_apply_fc({}, [2]), bad_apply_fc({1}, [3]), bad_apply_fc({1,2,3}, [4]), bad_apply_fc({1,2,3}, [5]), bad_apply_fc({1,2,3,4}, [6]), bad_apply_fc({1,2,3,4,5,6}, [7]), bad_apply_fc({1,2,3,4,5}, [8]), bad_apply_badarg({1,2}, [9]), ok. bad_apply_fc(Fun, Args) -> Res = (catch apply(Fun, Args)), erlang:garbage_collect(), erlang:yield(), case Res of {'EXIT',{{badfun,Fun},_Where}} -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]); Other -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]), ct:fail({bad_result,Other}) end. bad_apply_badarg(Fun, Args) -> Res = (catch apply(Fun, Args)), erlang:garbage_collect(), erlang:yield(), case Res of {'EXIT',{{badfun,Fun},_Where}} -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]); Other -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]), ct:fail({bad_result, Other}) end. %% Try directly calling bad funs. bad_fun_call(Config) when is_list(Config) -> bad_call_fc(42), bad_call_fc(xx), bad_call_fc({}), bad_call_fc({1}), bad_call_fc({1,2,3}), bad_call_fc({1,2,3}), bad_call_fc({1,2,3,4}), bad_call_fc({1,2,3,4,5,6}), bad_call_fc({1,2,3,4,5}), bad_call_fc({1,2}), ok. bad_call_fc(Fun) -> Args = [some,stupid,args], Res = (catch Fun(Args)), case Res of {'EXIT',{{badfun,Fun},_Where}} -> ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]); Other -> ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]), ct:fail({bad_result,Other}) end. %% Call and apply valid external funs with wrong number of arguments. badarity(Config) when is_list(Config) -> Fun = fun() -> ok end, Stupid = {stupid,arguments}, Args = [some,{stupid,arguments},here], %% Simple call. Res = (catch Fun(some, Stupid, here)), erlang:garbage_collect(), erlang:yield(), case Res of {'EXIT',{{badarity,{Fun,Args}},[_|_]}} -> ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]); _ -> ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]), ct:fail({bad_result,Res}) end, %% Apply. Res2 = (catch apply(Fun, Args)), erlang:garbage_collect(), erlang:yield(), case Res2 of {'EXIT',{{badarity,{Fun,Args}},[_|_]}} -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]); _ -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]), ct:fail({bad_result,Res2}) end, ok. %% Call and apply valid external funs with wrong number of arguments. ext_badarity(Config) when is_list(Config) -> Fun = fun ?MODULE:nothing/0, Stupid = {stupid,arguments}, Args = [some,{stupid,arguments},here], %% Simple call. Res = (catch Fun(some, Stupid, here)), erlang:garbage_collect(), erlang:yield(), case Res of {'EXIT',{{badarity,{Fun,Args}},_}} -> ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]); _ -> ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]), ct:fail({bad_result,Res}) end, %% Apply. Res2 = (catch apply(Fun, Args)), erlang:garbage_collect(), erlang:yield(), case Res2 of {'EXIT',{{badarity,{Fun,Args}},_}} -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]); _ -> ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]), ct:fail({bad_result,Res2}) end, ok. nothing() -> ok. %% Test handling of fun expression referring to uninterpreted code. otp_6061(Config) when is_list(Config) -> OrigFlag = process_flag(trap_exit, true), Self = self(), Pid = spawn_link(fun() -> test_otp_6061(Self) end), receive working -> ok; not_working -> ct:fail(not_working); {'EXIT', Pid, Reason} -> ct:fail({crash, Reason}) after 5000 -> ct:fail(timeout) end, process_flag(trap_exit, OrigFlag), ok. test_otp_6061(Starter) -> Passes = [2], PassesF = [fun() -> Starter ! not_working end, fun() -> Starter ! working end, fun() -> Starter ! not_working end], lists:foreach(fun(P)->(lists:nth(P,PassesF))() end,Passes). -define(APPLY(M, F, A), (fun(Fun) -> {ok,{a,b}} = Fun({a,b}) end)(fun M:F/A)). -define(APPLY2(M, F, A), (fun(Map) -> Id = fun(I) -> I end, List = [x,y], List = Map(Id, List), {type,external} = erlang:fun_info(Map, type) end)(fun M:F/A)). external(Config) when is_list(Config) -> Mod = id(?MODULE), Func = id(call_me), Arity = id(1), ?APPLY(?MODULE, call_me, 1), ?APPLY(?MODULE, call_me, Arity), ?APPLY(?MODULE, Func, 1), ?APPLY(?MODULE, Func, Arity), ?APPLY(Mod, call_me, 1), ?APPLY(Mod, call_me, Arity), ?APPLY(Mod, Func, 1), ?APPLY(Mod, Func, Arity), ListsMod = id(lists), ListsMap = id(map), ListsArity = id(2), ?APPLY2(lists, map, 2), ?APPLY2(lists, map, ListsArity), ?APPLY2(lists, ListsMap, 2), ?APPLY2(lists, ListsMap, ListsArity), ?APPLY2(ListsMod, map, 2), ?APPLY2(ListsMod, map, ListsArity), ?APPLY2(ListsMod, ListsMap, 2), ?APPLY2(ListsMod, ListsMap, ListsArity), ok. call_me(I) -> {ok,I}. eep37(Config) when is_list(Config) -> F = fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end, Add = fun _(N) -> N + 1 end, UnusedName = fun BlackAdder(N) -> N + 42 end, 720 = F(6), 10 = Add(9), 50 = UnusedName(8), [1,1,2,6,24,120] = lists:map(F, lists:seq(0, 5)), {'EXIT',{{badarity,_},_}} = (catch lists:map(fun G() -> G() end, [1])), {'EXIT',{{badarity,_},_}} = (catch F()), ok. id(I) -> I.