1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1999-2020. 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-module(fun_SUITE).
22
23-export([all/0, suite/0,
24	 bad_apply/1,bad_fun_call/1,badarity/1,ext_badarity/1,
25         bad_arglist/1,
26	 equality/1,ordering/1,
27	 fun_to_port/1,t_phash/1,t_phash2/1,md5/1,
28	 refc/1,refc_ets/1,refc_dist/1,
29	 const_propagation/1,t_arity/1,t_is_function2/1,
30	 t_fun_info/1,t_fun_info_mfa/1,t_fun_to_list/1]).
31
32-export([nothing/0]).
33
34-include_lib("common_test/include/ct.hrl").
35
36suite() ->
37    [{ct_hooks,[ts_install_cth]},
38     {timetrap, {minutes, 1}}].
39
40
41all() ->
42    [bad_apply, bad_fun_call, badarity, ext_badarity,
43     bad_arglist,
44     equality, ordering, fun_to_port, t_phash,
45     t_phash2, md5, refc, refc_ets, refc_dist,
46     const_propagation, t_arity, t_is_function2, t_fun_info,
47     t_fun_info_mfa,t_fun_to_list].
48
49%% Test that the correct EXIT code is returned for all types of bad funs.
50bad_apply(Config) when is_list(Config) ->
51    bad_apply_fc(42, [0]),
52    bad_apply_fc(xx, [1]),
53    bad_apply_fc({}, [2]),
54    bad_apply_fc({1}, [3]),
55    bad_apply_fc({1,2,3}, [4]),
56    bad_apply_fc({1,2,3}, [5]),
57    bad_apply_fc({1,2,3,4}, [6]),
58    bad_apply_fc({1,2,3,4,5,6}, [7]),
59    bad_apply_fc({1,2,3,4,5}, [8]),
60    bad_apply_badarg({1,2}, [9]),
61    ok.
62
63bad_apply_fc(Fun, Args) ->
64    Res = (catch apply(Fun, Args)),
65    erlang:garbage_collect(),
66    erlang:yield(),
67    case Res of
68	{'EXIT',{{badfun,Fun},_Where}} ->
69	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]);
70	Other ->
71	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]),
72	    ct:fail({bad_result,Other})
73    end.
74
75bad_apply_badarg(Fun, Args) ->
76    Res = (catch apply(Fun, Args)),
77    erlang:garbage_collect(),
78    erlang:yield(),
79    case Res of
80	{'EXIT',{{badfun,Fun},_Where}} ->
81	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]);
82	Other ->
83	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]),
84	    ct:fail({bad_result, Other})
85    end.
86
87%% Try directly calling bad funs.
88bad_fun_call(Config) when is_list(Config) ->
89    bad_call_fc(42),
90    bad_call_fc(xx),
91    bad_call_fc({}),
92    bad_call_fc({1}),
93    bad_call_fc({1,2,3}),
94    bad_call_fc({1,2,3}),
95    bad_call_fc({1,2,3,4}),
96    bad_call_fc({1,2,3,4,5,6}),
97    bad_call_fc({1,2,3,4,5}),
98    bad_call_fc({1,2}),
99    ok.
100
101bad_call_fc(Fun) ->
102    Args = [some,stupid,args],
103    Res = (catch Fun(Fun(Args))),
104    case Res of
105	{'EXIT',{{badfun,Fun},_Where}} ->
106	    ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
107	Other ->
108	    ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
109	    ct:fail({bad_result,Other})
110    end.
111
112% Test erlang:apply with non-proper arg-list
113bad_arglist(Config) when is_list(Config) ->
114    Fun = fun(A,B) -> A+B end,
115    {'EXIT', {badarg,_}} = (catch apply(Fun, 17)),
116    {'EXIT', {badarg,_}} = (catch apply(Fun, [17|18])),
117    {'EXIT', {badarg,_}} = (catch apply(Fun, [17,18|19])),
118    {'EXIT', {badarg,_}} = (catch apply(lists,seq, 17)),
119    {'EXIT', {badarg,_}} = (catch apply(lists,seq, [17|18])),
120    {'EXIT', {badarg,_}} = (catch apply(lists,seq, [17,18|19])),
121    ok.
122
123
124%% Call and apply valid funs with wrong number of arguments.
125
126badarity(Config) when is_list(Config) ->
127    Fun = fun() -> ok end,
128    Stupid = {stupid,arguments},
129    Args = [some,{stupid,arguments},here],
130
131    %% Simple call.
132
133    Res = (catch Fun(some, Stupid, here)),
134    erlang:garbage_collect(),
135    erlang:yield(),
136    case Res of
137	{'EXIT',{{badarity,{Fun,Args}},_}} ->
138	    ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
139	_ ->
140	    ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
141	    ct:fail({bad_result,Res})
142    end,
143
144    %% Apply.
145
146    Res2 = (catch apply(Fun, Args)),
147    erlang:garbage_collect(),
148    erlang:yield(),
149    case Res2 of
150	{'EXIT',{{badarity,{Fun,Args}},_}} ->
151	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]);
152	_ ->
153	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]),
154	    ct:fail({bad_result,Res2})
155    end,
156    ok.
157
158%% Call and apply valid external funs with wrong number of arguments.
159
160ext_badarity(Config) when is_list(Config) ->
161    Fun = fun ?MODULE:nothing/0,
162    Stupid = {stupid,arguments},
163    Args = [some,{stupid,arguments},here],
164
165    %% Simple call.
166
167    Res = (catch Fun(some, Stupid, here)),
168    erlang:garbage_collect(),
169    erlang:yield(),
170    case Res of
171	{'EXIT',{{badarity,{Fun,Args}},_}} ->
172	    ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
173	_ ->
174	    ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
175	    ct:fail({bad_result,Res})
176    end,
177
178    %% Apply.
179
180    Res2 = (catch apply(Fun, Args)),
181    erlang:garbage_collect(),
182    erlang:yield(),
183    case Res2 of
184	{'EXIT',{{badarity,{Fun,Args}},_}} ->
185	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]);
186	_ ->
187	    ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]),
188	    ct:fail({bad_result,Res2})
189    end,
190    ok.
191
192nothing() ->
193    ok.
194
195%% Test equality of funs.
196
197equality(Config) when is_list(Config) ->
198    F0 = fun() -> 1 end,
199    F0_copy = copy_term(F0),
200    true = eq(F0, F0),
201    true = eq(F0, F0_copy),
202
203    %% Compare different arities.
204    F1 = fun(X) -> X + 1 end,
205    true = eq(F1, F1),
206    false = eq(F0, F1),
207    false = eq(F0_copy, F1),
208
209    %% Compare different environments.
210    G1 = make_fun(1),
211    G2 = make_fun(2),
212    true = eq(G1, G1),
213    true = eq(G2, G2),
214    false = eq(G1, G2),
215    false = eq(G2, G1),
216    G1_copy = copy_term(G1),
217    true = eq(G1, G1_copy),
218
219    %% Compare fun with binaries.
220    B = list_to_binary([7,8,9]),
221    false = eq(B, G1),
222    false = eq(G1, B),
223
224    %% Compare external funs.
225    FF0 = fun aa:blurf/0,
226    FF0_copy = copy_term(FF0),
227    FF1 = fun erlang:abs/0,
228    FF2 = fun erlang:exit/1,
229    FF3 = fun erlang:exit/2,
230    FF4 = fun z:ff/0,
231
232    true = eq(FF0, FF0),
233    true = eq(FF0, FF0_copy),
234    true = eq(FF1, FF1),
235    true = eq(FF2, FF2),
236    true = eq(FF3, FF3),
237    true = eq(FF4, FF4),
238    false = eq(FF0, FF1),
239    false = eq(FF0, FF2),
240    false = eq(FF0, FF3),
241    false = eq(FF0, FF4),
242    false = eq(FF1, FF0),
243    false = eq(FF1, FF2),
244    false = eq(FF1, FF3),
245    false = eq(FF1, FF4),
246    false = eq(FF2, FF3),
247    false = eq(FF2, FF4),
248    false = eq(FF3, FF4),
249
250    %% EEP37
251    H1 = fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end,
252    H2 = fun Pow(N, M) when M > 0 -> N * Pow(N, M - 1); Pow(_, 0) -> 1 end,
253    H1_copy = copy_term(H1),
254
255    true = eq(H1, H1),
256    true = eq(H1, H1_copy),
257    true = eq(H2, H2),
258    false = eq(H1, H2),
259
260    ok.
261
262eq(X, X) -> true;
263eq(_, _) -> false.
264
265copy_term(Term) ->
266    binary_to_term(term_to_binary(Term)).
267
268make_fun(X) ->
269    fun() -> X end.
270
271%% Tests ordering of funs.
272ordering(Config) when is_list(Config) ->
273    F1 = make_fun(1, 2),
274    F1_copy = copy_term(F1),
275    F2 = make_fun(1, 3),
276    F3 = make_fun(3, 4),
277
278    FF0 = fun aa:blurf/0,
279    FF1 = fun erlang:abs/0,
280    FF2 = fun erlang:exit/1,
281    FF3 = fun erlang:exit/2,
282    FF4 = fun z:ff/0,
283
284    true = FF0 < FF1,
285    true = FF1 < FF2,
286    true = FF2 < FF3,
287    true = FF3 < FF4,
288
289    true = FF0 > F1,
290    true = FF0 > F2,
291    true = FF0 > F3,
292    true = FF4 > F1,
293    true = FF4 > F2,
294    true = FF4 > F3,
295
296    true = F1 == F1,
297    true = F1 == F1_copy,
298    true = F1 /= F2,
299
300    true = F1 < F2,
301    true = F2 > F1,
302    true = F2 < F3,
303    true = F3 > F2,
304
305    false = F1 > F2,
306    false = F2 > F3,
307
308    %% Compare with binaries.
309
310    B = list_to_binary([7,8,9,10]),
311    false = B == F1,
312    false = F1 == B,
313
314    true = F1 < B,
315    true = B > F2,
316
317    false = F1 > B,
318    false = B < F2,
319
320    false = F1 >= B,
321    false = B =< F2,
322
323    %% Compare module funs with binaries.
324    false = B == FF1,
325    false = FF1 == B,
326
327    true = FF1 < B,
328    true = B > FF2,
329
330    false = FF1 > B,
331    false = B < FF2,
332
333    false = FF1 >= B,
334    false = B =< FF2,
335
336    %% Create a port and ref.
337
338    Path = proplists:get_value(priv_dir, Config),
339    AFile = filename:join(Path, "vanilla_file"),
340    P = open_port(AFile, [out]),
341    R = make_ref(),
342
343    %% Compare funs with ports and refs.
344
345    true = R < F3,
346    true = F3 > R,
347    true = F3 < P,
348    true = P > F3,
349
350    true = R =< F3,
351    true = F3 >= R,
352    true = F3 =< P,
353    true = P >= F3,
354
355    false = R > F3,
356    false = F3 < R,
357    false = F3 > P,
358    false = P < F3,
359
360    %% Compare funs with conses and nils.
361
362    true = F1 < [a],
363    true = F1 < [],
364    true = [a,b] > F1,
365    true = [] > F1,
366
367    false = [1] < F1,
368    false = [] < F1,
369    false = F1 > [2],
370    false = F1 > [],
371
372    false = [1] =< F1,
373    false = [] =< F1,
374    false = F1 >= [2],
375    false = F1 >= [],
376
377    %% Compare module funs with conses and nils.
378
379    true = FF1 < [a],
380    true = FF1 < [],
381    true = [a,b] > FF1,
382    true = [] > FF1,
383
384    false = [1] < FF1,
385    false = [] < FF1,
386    false = FF1 > [2],
387    false = FF1 > [],
388
389    false = [1] =< FF1,
390    false = [] =< FF1,
391    false = FF1 >= [2],
392    false = FF1 >= [],
393    ok.
394
395make_fun(X, Y) ->
396    fun(A) -> A*X+Y end.
397
398%% Try sending funs to ports (should fail).
399fun_to_port(Config) when is_list(Config) ->
400    fun_to_port(Config, xxx),
401    fun_to_port(Config, fun() -> 42 end),
402    fun_to_port(Config, [fun() -> 43 end]),
403    fun_to_port(Config, [1,fun() -> 44 end]),
404    fun_to_port(Config, [0,1|fun() -> 45 end]),
405    B64K = build_io_list(65536),
406    fun_to_port(Config, [B64K,fun() -> 45 end]),
407    fun_to_port(Config, [B64K|fun() -> 45 end]),
408    ok.
409
410fun_to_port(Config, IoList) ->
411    Path = proplists:get_value(priv_dir, Config),
412    AFile = filename:join(Path, "vanilla_file"),
413    Port = open_port(AFile, [out]),
414    case catch port_command(Port, IoList) of
415	{'EXIT',{badarg,_}} -> ok;
416	Other -> ct:fail({unexpected_retval,Other})
417    end.
418
419build_io_list(0) -> [];
420build_io_list(1) -> [7];
421build_io_list(N) ->
422    L = build_io_list(N div 2),
423    case N rem 2 of
424	0 -> [L|L];
425	1 -> [7,L|L]
426    end.
427
428%% Test the phash/2 BIF on funs.
429t_phash(Config) when is_list(Config) ->
430    F1 = fun(_X) -> 1 end,
431    F2 = fun(_X) -> 2 end,
432    true = phash(F1) /= phash(F2),
433
434    G1 = make_fun(1, 2, 3),
435    G2 = make_fun(1, 2, 3),
436    G3 = make_fun(1, 2, 4),
437    true = phash(G1) == phash(G2),
438    true = phash(G2) /= phash(G3),
439
440    FF0 = fun erlang:abs/1,
441    FF1 = fun erlang:exit/1,
442    FF2 = fun erlang:exit/2,
443    FF3 = fun blurf:exit/2,
444    true = phash(FF0) =/= phash(FF1),
445    true = phash(FF0) =/= phash(FF2),
446    true = phash(FF0) =/= phash(FF3),
447    true = phash(FF1) =/= phash(FF2),
448    true = phash(FF1) =/= phash(FF3),
449    true = phash(FF2) =/= phash(FF3),
450    ok.
451
452phash(Term) ->
453    erlang:phash(Term, 16#7ffffff).
454
455%% Test the phash2/2 BIF on funs.
456t_phash2(Config) when is_list(Config) ->
457    F1 = fun(_X) -> 1 end,
458    F2 = fun(_X) -> 2 end,
459    true = phash2(F1) /= phash2(F2),
460
461    G1 = make_fun(1, 2, 3),
462    G2 = make_fun(1, 2, 3),
463    G3 = make_fun(1, 2, 4),
464    true = phash2(G1) == phash2(G2),
465    true = phash2(G2) /= phash2(G3),
466
467    FF0 = fun erlang:abs/1,
468    FF1 = fun erlang:exit/1,
469    FF2 = fun erlang:exit/2,
470    FF3 = fun blurf:exit/2,
471    true = phash2(FF0) =/= phash2(FF1),
472    true = phash2(FF0) =/= phash2(FF2),
473    true = phash2(FF0) =/= phash2(FF3),
474    true = phash2(FF1) =/= phash2(FF2),
475    true = phash2(FF1) =/= phash2(FF3),
476    true = phash2(FF2) =/= phash2(FF3),
477
478    ok.
479
480phash2(Term) ->
481    erlang:phash2(Term, 16#7ffffff).
482
483make_fun(X, Y, Z) ->
484    fun() -> {X,Y,Z} end.
485
486%% Test that MD5 bifs reject funs properly.
487md5(Config) when is_list(Config) ->
488    _ = size(erlang:md5_init()),
489
490    %% Try funs in the i/o list.
491    bad_md5(fun(_X) -> 42 end),
492    bad_md5([fun(_X) -> 43 end]),
493    bad_md5([1,fun(_X) -> 44 end]),
494    bad_md5([1|fun(_X) -> 45 end]),
495    B64K = build_io_list(65536),
496    bad_md5([B64K,fun(_X) -> 46 end]),
497    bad_md5([B64K|fun(_X) -> 46 end]),
498    ok.
499
500bad_md5(Bad) ->
501    {'EXIT',{badarg,_}} = (catch erlang:md5(Bad)).
502
503refc(Config) when is_list(Config) ->
504    F1 = fun_factory(2),
505    {refc,2} = erlang:fun_info(F1, refc),
506    F2 = fun_factory(42),
507    {refc,3} = erlang:fun_info(F1, refc),
508
509    process_flag(trap_exit, true),
510    Pid = spawn_link(fun() -> {refc,4} = erlang:fun_info(F1, refc) end),
511    receive
512	{'EXIT',Pid,normal} -> ok;
513	Other -> ct:fail({unexpected,Other})
514    end,
515    process_flag(trap_exit, false),
516    %% Wait to make sure that the process has terminated completely.
517    receive after 1 -> ok end,
518    {refc,3} = erlang:fun_info(F1, refc),
519
520    %% Garbage collect. Only the F2 fun will be left.
521    7 = F1(5),
522    true = erlang:garbage_collect(),
523    40 = F2(-2),
524    {refc,2} = erlang:fun_info(F2, refc),
525    ok.
526
527fun_factory(Const) ->
528    fun(X) -> X + Const end.
529
530refc_ets(Config) when is_list(Config) ->
531    F = fun(X) -> X + 33 end,
532    {refc,2} = erlang:fun_info(F, refc),
533
534    refc_ets_set(F, [set]),
535    refc_ets_set(F, [ordered_set]),
536    refc_ets_bag(F, [bag]),
537    refc_ets_bag(F, [duplicate_bag]),
538    ok.
539
540refc_ets_set(F1, Options) ->
541    io:format("~p", [Options]),
542    Tab = ets:new(kalle, Options),
543    true = ets:insert(Tab, {a_key,F1}),
544    3 = fun_refc(F1),
545    [{a_key,F3}] = ets:lookup(Tab, a_key),
546    4 = fun_refc(F1),
547    true = ets:insert(Tab, {a_key,not_a_fun}),
548    3 = fun_refc(F1),
549    true = ets:insert(Tab, {another_key,F1}),
550    4 = fun_refc(F1),
551    true = ets:delete(Tab),
552    3 = fun_refc(F1),
553    10 = F3(-23),
554    true = erlang:garbage_collect(),
555    2 = fun_refc(F1),
556    ok.
557
558refc_ets_bag(F1, Options) ->
559    io:format("~p", [Options]),
560    Tab = ets:new(kalle, Options),
561    true = ets:insert(Tab, {a_key,F1}),
562    3 = fun_refc(F1),
563    [{a_key,F3}] = ets:lookup(Tab, a_key),
564    4 = fun_refc(F1),
565    true = ets:insert(Tab, {a_key,not_a_fun}),
566    4 = fun_refc(F1),
567    true = ets:insert(Tab, {another_key,F1}),
568    5 = fun_refc(F1),
569    true = ets:delete(Tab),
570    3 = fun_refc(F1),
571    10 = F3(-23),
572    true = erlang:garbage_collect(),
573    2 = fun_refc(F1),
574    ok.
575
576refc_dist(Config) when is_list(Config) ->
577    {ok,Node} = start_node(fun_SUITE_refc_dist),
578    process_flag(trap_exit, true),
579    Pid = spawn_link(Node, fun() -> receive
580                                        Fun when is_function(Fun) ->
581                                            3 = fun_refc(Fun),
582                                            exit({normal,Fun}) end
583                           end),
584    F = fun() -> 42 end,
585    2 = fun_refc(F),
586    Pid ! F,
587    F2 = receive
588	     {'EXIT',Pid,{normal,Fun}} -> Fun;
589	     Other -> ct:fail({unexpected,Other})
590	 end,
591    %% dist.c:net_mess2 have a reference to Fun for a while since
592    %% Fun is passed in an exit signal. Wait until it is gone.
593    wait_until(fun () -> 4 =/= fun_refc(F2) end),
594    3 = fun_refc(F2),
595    true = erlang:garbage_collect(),
596    2 = fun_refc(F),
597    refc_dist_send(Node, F),
598    test_server:stop_node(Node).
599
600refc_dist_send(Node, F) ->
601    Pid = spawn_link(Node, fun() -> receive
602                                        {To,Fun} when is_function(Fun) ->
603                                            wait_until(fun () ->
604                                                               3 =:= fun_refc(Fun)
605                                                       end),
606                                            To ! Fun
607                                    end
608                           end),
609    2 = fun_refc(F),
610    Pid ! {self(),F},
611    F2 = receive
612	     Fun when is_function(Fun) -> Fun;
613	     Other -> ct:fail({unexpected,Other})
614	 end,
615    receive {'EXIT',Pid,normal} -> ok end,
616    %% No reference from dist.c:net_mess2 since Fun is passed
617    %% in an ordinary message.
618    3 = fun_refc(F),
619    3 = fun_refc(F2),
620    refc_dist_reg_send(Node, F).
621
622refc_dist_reg_send(Node, F) ->
623    true = erlang:garbage_collect(),
624    2 = fun_refc(F),
625    Ref = make_ref(),
626    Me = self(),
627    Pid = spawn_link(Node, fun() ->
628                                   true = register(my_fun_tester, self()),
629                                   Me ! Ref,
630                                   receive
631                                       {Me,Fun} when is_function(Fun) ->
632                                           3 = fun_refc(Fun),
633                                           Me ! Fun
634                                   end
635                           end),
636    erlang:yield(),
637    2 = fun_refc(F),
638    receive Ref -> ok end,
639    {my_fun_tester,Node} ! {self(),F},
640    F2 = receive
641	     Fun when is_function(Fun) -> Fun;
642	     Other -> ct:fail({unexpected,Other})
643	 end,
644    receive {'EXIT',Pid,normal} -> ok end,
645
646    3 = fun_refc(F),
647    3 = fun_refc(F2),
648    ok.
649
650fun_refc(F) ->
651    {refc,Count} = erlang:fun_info(F, refc),
652    Count.
653
654const_propagation(Config) when is_list(Config) ->
655    Fun1 = fun start_node/1,
656    2 = fun_refc(Fun1),
657    Fun2 = Fun1,
658    my_cmp({Fun1,Fun2}),
659
660    Fun3 = fun() -> ok end,
661    2 = fun_refc(Fun3),
662    Fun4 = Fun3,
663    my_cmp({Fun3,Fun4}),
664    ok.
665
666my_cmp({Fun,Fun}) -> ok;
667my_cmp({Fun1,Fun2}) ->
668    io:format("Fun1: ~p", [erlang:fun_info(Fun1)]),
669    io:format("Fun2: ~p", [erlang:fun_info(Fun2)]),
670    ct:fail(no_match).
671
672t_arity(Config) when is_list(Config) ->
673    0 = fun_arity(fun() -> ok end),
674    0 = fun_arity(fun() -> Config end),
675    1 = fun_arity(fun(X) -> X+1 end),
676    1 = fun_arity(fun(X) -> Config =:= X end),
677    A = id(42),
678
679    %% Test that the arity is transferred properly.
680    process_flag(trap_exit, true),
681    {ok,Node} = start_node(fun_test_arity),
682    hello_world = spawn_call(Node, fun() -> hello_world end),
683    0 = spawn_call(Node, fun(X) -> X end),
684    42 = spawn_call(Node, fun(_X) -> A end),
685    43 = spawn_call(Node, fun(X, Y) -> A+X+Y end),
686    1 = spawn_call(Node, fun(X, Y) -> X+Y end),
687    45 = spawn_call(Node, fun(X, Y, Z) -> A+X+Y+Z end),
688    test_server:stop_node(Node),
689    ok.
690
691t_is_function2(Config) when is_list(Config) ->
692    false = is_function(id({a,b}), 0),
693    false = is_function(id({a,b}), 234343434333433433),
694    true = is_function(fun() -> ok end, 0),
695    true = is_function(fun(_) -> ok end, 1),
696    false = is_function(fun(_) -> ok end, 0),
697
698    true = is_function(fun erlang:abs/1, 1),
699    true = is_function(fun erlang:abs/99, 99),
700    false = is_function(fun erlang:abs/1, 0),
701    false = is_function(fun erlang:abs/99, 0),
702
703    false = is_function(id(self()), 0),
704    false = is_function(id({a,b,c}), 0),
705    false = is_function(id({a}), 0),
706    false = is_function(id([a,b,c]), 0),
707
708    %% Bad arity argument.
709    bad_arity(a),
710    bad_arity(-1),
711    bad_arity(-9738974938734938793873498378),
712    bad_arity([]),
713    bad_arity(fun() -> ok end),
714    bad_arity({}),
715    bad_arity({a,b}),
716    bad_arity(self()),
717
718    %% Bad arity argument in guard test.
719    Fun = fun erlang:abs/1,
720    ok = if
721             is_function(Fun, -1) -> error;
722             is_function(Fun, 256) -> error;
723             is_function(Fun, a) -> error;
724             is_function(Fun, Fun) -> error;
725             true -> ok
726         end,
727    ok.
728
729bad_arity(A) ->
730    {'EXIT',_} = (catch is_function(fun() -> ok end, A)),
731    {'EXIT',_} = (catch is_function(no_fun, A)),
732    ok.
733
734t_fun_info(Config) when is_list(Config) ->
735    F = fun t_fun_info/1,
736    try F(blurf) of
737	      FAny ->
738		  ct:fail("should fail; returned ~p\n", [FAny])
739	  catch
740	      error:function_clause -> ok
741	  end,
742    {module,?MODULE} = erlang:fun_info(F, module),
743    case erlang:fun_info(F, name) of
744	      undefined ->
745		  ct:fail(no_fun_info);
746	      _ -> ok
747	  end,
748    {arity,1} = erlang:fun_info(F, arity),
749    {env,[]} = erlang:fun_info(F, env),
750    verify_not_undef(F, index),
751    verify_not_undef(F, uniq),
752    verify_not_undef(F, new_index),
753    verify_not_undef(F, new_uniq),
754    verify_not_undef(F, refc),
755    {'EXIT',_} = (catch erlang:fun_info(F, blurf)),
756
757    %% Module fun.
758    FF = fun ?MODULE:t_fun_info/1,
759    try FF(blurf) of
760	      FFAny ->
761		  ct:fail("should fail; returned ~p\n", [FFAny])
762	  catch
763	      error:function_clause -> ok
764	  end,
765
766    {module,?MODULE} = erlang:fun_info(FF, module),
767    {name,t_fun_info} = erlang:fun_info(FF, name),
768    {arity,1} = erlang:fun_info(FF, arity),
769    {env,[]} = erlang:fun_info(FF, env),
770    verify_undef(FF, index),
771    verify_undef(FF, uniq),
772    verify_undef(FF, new_index),
773    verify_undef(FF, new_uniq),
774    verify_undef(FF, refc),
775    {'EXIT',_} = (catch erlang:fun_info(FF, blurf)),
776
777    %% Not fun.
778    bad_info(abc),
779    bad_info(42),
780    bad_info({fun erlang:list_to_integer/1}),
781    bad_info([42]),
782    bad_info([]),
783    bad_info(self()),
784    bad_info(<<>>),
785    bad_info(<<1,2>>),
786    ok.
787
788t_fun_info_mfa(Config) when is_list(Config) ->
789    Fun1 = fun spawn_call/2,
790    {module,M1}  = erlang:fun_info(Fun1, module),
791    {name,F1}    = erlang:fun_info(Fun1, name),
792    {arity,A1}   = erlang:fun_info(Fun1, arity),
793    {M1,F1,A1=2} = erlang:fun_info_mfa(Fun1),
794    %% Module fun.
795    Fun2 = fun ?MODULE:t_fun_info/1,
796    {module,M2}  = erlang:fun_info(Fun2, module),
797    {name,F2}    = erlang:fun_info(Fun2, name),
798    {arity,A2}   = erlang:fun_info(Fun2, arity),
799    {M2,F2,A2=1} = erlang:fun_info_mfa(Fun2),
800
801    %% Not fun.
802    {'EXIT',_} = (catch erlang:fun_info_mfa(id(d))),
803    ok.
804
805t_fun_to_list(Config) when is_list(Config) ->
806    "fun a:b/1" = erlang:fun_to_list(fun a:b/1),
807    "fun 'a-esc':'b-esc'/1" = erlang:fun_to_list(fun 'a-esc':'b-esc'/1),
808    "fun 'a-esc':b/1" = erlang:fun_to_list(fun 'a-esc':b/1),
809    "fun a:'b-esc'/1" = erlang:fun_to_list(fun a:'b-esc'/1),
810    ok.
811
812bad_info(Term) ->
813    try	erlang:fun_info(Term, module) of
814	Any ->
815	    ict:fail("should fail; returned ~p\n", [Any])
816    catch
817	error:badarg -> ok
818    end.
819
820verify_undef(Fun, Tag) ->
821    {Tag,undefined} = erlang:fun_info(Fun, Tag).
822
823verify_not_undef(Fun, Tag) ->
824    case erlang:fun_info(Fun, Tag) of
825	{Tag,undefined} ->
826	    ct:fail("tag ~w not defined in fun_info", [Tag]);
827	{Tag,_} -> ok
828    end.
829
830id(X) ->
831    X.
832
833spawn_call(Node, AFun) ->
834    Parent = self(),
835    Init = erlang:whereis(init),
836    Pid = spawn_link(Node,
837		     fun() ->
838			     receive
839				 {Fun,Fun,Fun} when is_function(Fun) ->
840				     Arity = fun_arity(Fun),
841				     Args = case Arity of
842						0 -> [];
843						_ -> lists:seq(0, Arity-1)
844					    end,
845				     Res = apply(Fun, Args),
846                     case erlang:fun_info(Fun, pid) of
847                        {pid,Init} -> Parent ! {result,Res};
848                        {pid,Creator} -> Creator ! {result,Res}
849                     end
850			     end
851		     end),
852    Pid ! {AFun,AFun,AFun},
853    Res = receive
854	      {result,R} -> R;
855	      Other -> ct:fail({bad_message,Other})
856	  after 10000 ->
857		  ct:fail(timeout_waiting_for_result)
858	  end,
859    receive
860	{'EXIT',Pid,normal} -> ok;
861	Other2 -> ct:fail({bad_message_waiting_for_exit,Other2})
862    after 10000 ->
863	    ct:fail(timeout_waiting_for_exit)
864    end,
865    Res.
866
867fun_arity(F) ->
868    {arity,Arity} = erlang:fun_info(F, arity),
869    Arity.
870
871start_node(Name) ->
872    Pa = filename:dirname(code:which(?MODULE)),
873    Cookie = atom_to_list(erlang:get_cookie()),
874    test_server:start_node(Name, slave,
875			   [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]).
876
877wait_until(Fun) ->
878    case catch Fun() of
879	true -> ok;
880	_ -> receive after 100 -> wait_until(Fun) end
881    end.
882