1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1999-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-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]). 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]. 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 {refc,3} = erlang:fun_info(F1, refc), 517 518 %% Garbage collect. Only the F2 fun will be left. 519 7 = F1(5), 520 true = erlang:garbage_collect(), 521 40 = F2(-2), 522 {refc,2} = erlang:fun_info(F2, refc), 523 ok. 524 525fun_factory(Const) -> 526 fun(X) -> X + Const end. 527 528refc_ets(Config) when is_list(Config) -> 529 F = fun(X) -> X + 33 end, 530 {refc,2} = erlang:fun_info(F, refc), 531 532 refc_ets_set(F, [set]), 533 refc_ets_set(F, [ordered_set]), 534 refc_ets_bag(F, [bag]), 535 refc_ets_bag(F, [duplicate_bag]), 536 ok. 537 538refc_ets_set(F1, Options) -> 539 io:format("~p", [Options]), 540 Tab = ets:new(kalle, Options), 541 true = ets:insert(Tab, {a_key,F1}), 542 3 = fun_refc(F1), 543 [{a_key,F3}] = ets:lookup(Tab, a_key), 544 4 = fun_refc(F1), 545 true = ets:insert(Tab, {a_key,not_a_fun}), 546 3 = fun_refc(F1), 547 true = ets:insert(Tab, {another_key,F1}), 548 4 = fun_refc(F1), 549 true = ets:delete(Tab), 550 3 = fun_refc(F1), 551 10 = F3(-23), 552 true = erlang:garbage_collect(), 553 2 = fun_refc(F1), 554 ok. 555 556refc_ets_bag(F1, Options) -> 557 io:format("~p", [Options]), 558 Tab = ets:new(kalle, Options), 559 true = ets:insert(Tab, {a_key,F1}), 560 3 = fun_refc(F1), 561 [{a_key,F3}] = ets:lookup(Tab, a_key), 562 4 = fun_refc(F1), 563 true = ets:insert(Tab, {a_key,not_a_fun}), 564 4 = fun_refc(F1), 565 true = ets:insert(Tab, {another_key,F1}), 566 5 = fun_refc(F1), 567 true = ets:delete(Tab), 568 3 = fun_refc(F1), 569 10 = F3(-23), 570 true = erlang:garbage_collect(), 571 2 = fun_refc(F1), 572 ok. 573 574refc_dist(Config) when is_list(Config) -> 575 {ok,Node} = start_node(fun_SUITE_refc_dist), 576 process_flag(trap_exit, true), 577 Pid = spawn_link(Node, fun() -> receive 578 Fun when is_function(Fun) -> 579 2 = fun_refc(Fun), 580 exit({normal,Fun}) end 581 end), 582 F = fun() -> 42 end, 583 2 = fun_refc(F), 584 Pid ! F, 585 F2 = receive 586 {'EXIT',Pid,{normal,Fun}} -> Fun; 587 Other -> ct:fail({unexpected,Other}) 588 end, 589 %% dist.c:net_mess2 have a reference to Fun for a while since 590 %% Fun is passed in an exit signal. Wait until it is gone. 591 wait_until(fun () -> 4 =/= fun_refc(F2) end), 592 3 = fun_refc(F2), 593 true = erlang:garbage_collect(), 594 2 = fun_refc(F), 595 refc_dist_send(Node, F). 596 597refc_dist_send(Node, F) -> 598 Pid = spawn_link(Node, fun() -> receive 599 {To,Fun} when is_function(Fun) -> 600 wait_until(fun () -> 601 2 =:= fun_refc(Fun) 602 end), 603 To ! Fun 604 end 605 end), 606 2 = fun_refc(F), 607 Pid ! {self(),F}, 608 F2 = receive 609 Fun when is_function(Fun) -> Fun; 610 Other -> ct:fail({unexpected,Other}) 611 end, 612 receive {'EXIT',Pid,normal} -> ok end, 613 %% No reference from dist.c:net_mess2 since Fun is passed 614 %% in an ordinary message. 615 3 = fun_refc(F), 616 3 = fun_refc(F2), 617 refc_dist_reg_send(Node, F). 618 619refc_dist_reg_send(Node, F) -> 620 true = erlang:garbage_collect(), 621 2 = fun_refc(F), 622 Ref = make_ref(), 623 Me = self(), 624 Pid = spawn_link(Node, fun() -> 625 true = register(my_fun_tester, self()), 626 Me ! Ref, 627 receive 628 {Me,Fun} when is_function(Fun) -> 629 2 = fun_refc(Fun), 630 Me ! Fun 631 end 632 end), 633 erlang:yield(), 634 2 = fun_refc(F), 635 receive Ref -> ok end, 636 {my_fun_tester,Node} ! {self(),F}, 637 F2 = receive 638 Fun when is_function(Fun) -> Fun; 639 Other -> ct:fail({unexpected,Other}) 640 end, 641 receive {'EXIT',Pid,normal} -> ok end, 642 643 3 = fun_refc(F), 644 3 = fun_refc(F2), 645 ok. 646 647fun_refc(F) -> 648 {refc,Count} = erlang:fun_info(F, refc), 649 Count. 650 651const_propagation(Config) when is_list(Config) -> 652 Fun1 = fun start_node/1, 653 2 = fun_refc(Fun1), 654 Fun2 = Fun1, 655 my_cmp({Fun1,Fun2}), 656 657 Fun3 = fun() -> ok end, 658 2 = fun_refc(Fun3), 659 Fun4 = Fun3, 660 my_cmp({Fun3,Fun4}), 661 ok. 662 663my_cmp({Fun,Fun}) -> ok; 664my_cmp({Fun1,Fun2}) -> 665 io:format("Fun1: ~p", [erlang:fun_info(Fun1)]), 666 io:format("Fun2: ~p", [erlang:fun_info(Fun2)]), 667 ct:fail(no_match). 668 669t_arity(Config) when is_list(Config) -> 670 0 = fun_arity(fun() -> ok end), 671 0 = fun_arity(fun() -> Config end), 672 1 = fun_arity(fun(X) -> X+1 end), 673 1 = fun_arity(fun(X) -> Config =:= X end), 674 A = id(42), 675 676 %% Test that the arity is transferred properly. 677 process_flag(trap_exit, true), 678 {ok,Node} = start_node(fun_test_arity), 679 hello_world = spawn_call(Node, fun() -> hello_world end), 680 0 = spawn_call(Node, fun(X) -> X end), 681 42 = spawn_call(Node, fun(_X) -> A end), 682 43 = spawn_call(Node, fun(X, Y) -> A+X+Y end), 683 1 = spawn_call(Node, fun(X, Y) -> X+Y end), 684 45 = spawn_call(Node, fun(X, Y, Z) -> A+X+Y+Z end), 685 ok. 686 687t_is_function2(Config) when is_list(Config) -> 688 false = is_function(id({a,b}), 0), 689 false = is_function(id({a,b}), 234343434333433433), 690 true = is_function(fun() -> ok end, 0), 691 true = is_function(fun(_) -> ok end, 1), 692 false = is_function(fun(_) -> ok end, 0), 693 694 true = is_function(fun erlang:abs/1, 1), 695 true = is_function(fun erlang:abs/99, 99), 696 false = is_function(fun erlang:abs/1, 0), 697 false = is_function(fun erlang:abs/99, 0), 698 699 false = is_function(id(self()), 0), 700 false = is_function(id({a,b,c}), 0), 701 false = is_function(id({a}), 0), 702 false = is_function(id([a,b,c]), 0), 703 704 %% Bad arity argument. 705 bad_arity(a), 706 bad_arity(-1), 707 bad_arity(-9738974938734938793873498378), 708 bad_arity([]), 709 bad_arity(fun() -> ok end), 710 bad_arity({}), 711 bad_arity({a,b}), 712 bad_arity(self()), 713 ok. 714 715bad_arity(A) -> 716 {'EXIT',_} = (catch is_function(fun() -> ok end, A)), 717 {'EXIT',_} = (catch is_function(no_fun, A)), 718 ok. 719 720t_fun_info(Config) when is_list(Config) -> 721 F = fun t_fun_info/1, 722 try F(blurf) of 723 FAny -> 724 ct:fail("should fail; returned ~p\n", [FAny]) 725 catch 726 error:function_clause -> ok 727 end, 728 {module,?MODULE} = erlang:fun_info(F, module), 729 case erlang:fun_info(F, name) of 730 undefined -> 731 ct:fail(no_fun_info); 732 _ -> ok 733 end, 734 {arity,1} = erlang:fun_info(F, arity), 735 {env,[]} = erlang:fun_info(F, env), 736 verify_not_undef(F, index), 737 verify_not_undef(F, uniq), 738 verify_not_undef(F, new_index), 739 verify_not_undef(F, new_uniq), 740 verify_not_undef(F, refc), 741 {'EXIT',_} = (catch erlang:fun_info(F, blurf)), 742 743 %% Module fun. 744 FF = fun ?MODULE:t_fun_info/1, 745 try FF(blurf) of 746 FFAny -> 747 ct:fail("should fail; returned ~p\n", [FFAny]) 748 catch 749 error:function_clause -> ok 750 end, 751 752 {module,?MODULE} = erlang:fun_info(FF, module), 753 {name,t_fun_info} = erlang:fun_info(FF, name), 754 {arity,1} = erlang:fun_info(FF, arity), 755 {env,[]} = erlang:fun_info(FF, env), 756 verify_undef(FF, index), 757 verify_undef(FF, uniq), 758 verify_undef(FF, new_index), 759 verify_undef(FF, new_uniq), 760 verify_undef(FF, refc), 761 {'EXIT',_} = (catch erlang:fun_info(FF, blurf)), 762 763 %% Not fun. 764 bad_info(abc), 765 bad_info(42), 766 bad_info({fun erlang:list_to_integer/1}), 767 bad_info([42]), 768 bad_info([]), 769 bad_info(self()), 770 bad_info(<<>>), 771 bad_info(<<1,2>>), 772 ok. 773 774t_fun_info_mfa(Config) when is_list(Config) -> 775 Fun1 = fun spawn_call/2, 776 {module,M1} = erlang:fun_info(Fun1, module), 777 {name,F1} = erlang:fun_info(Fun1, name), 778 {arity,A1} = erlang:fun_info(Fun1, arity), 779 {M1,F1,A1=2} = erlang:fun_info_mfa(Fun1), 780 %% Module fun. 781 Fun2 = fun ?MODULE:t_fun_info/1, 782 {module,M2} = erlang:fun_info(Fun2, module), 783 {name,F2} = erlang:fun_info(Fun2, name), 784 {arity,A2} = erlang:fun_info(Fun2, arity), 785 {M2,F2,A2=1} = erlang:fun_info_mfa(Fun2), 786 787 %% Not fun. 788 {'EXIT',_} = (catch erlang:fun_info_mfa(id(d))), 789 ok. 790 791 792bad_info(Term) -> 793 try erlang:fun_info(Term, module) of 794 Any -> 795 ict:fail("should fail; returned ~p\n", [Any]) 796 catch 797 error:badarg -> ok 798 end. 799 800verify_undef(Fun, Tag) -> 801 {Tag,undefined} = erlang:fun_info(Fun, Tag). 802 803verify_not_undef(Fun, Tag) -> 804 case erlang:fun_info(Fun, Tag) of 805 {Tag,undefined} -> 806 ct:fail("tag ~w not defined in fun_info", [Tag]); 807 {Tag,_} -> ok 808 end. 809 810id(X) -> 811 X. 812 813spawn_call(Node, AFun) -> 814 Pid = spawn_link(Node, 815 fun() -> 816 receive 817 {Fun,Fun,Fun} when is_function(Fun) -> 818 Arity = fun_arity(Fun), 819 Args = case Arity of 820 0 -> []; 821 _ -> lists:seq(0, Arity-1) 822 end, 823 Res = apply(Fun, Args), 824 {pid,Creator} = erlang:fun_info(Fun, pid), 825 Creator ! {result,Res} 826 end 827 end), 828 Pid ! {AFun,AFun,AFun}, 829 Res = receive 830 {result,R} -> R; 831 Other -> ct:fail({bad_message,Other}) 832 after 10000 -> 833 ct:fail(timeout_waiting_for_result) 834 end, 835 receive 836 {'EXIT',Pid,normal} -> ok; 837 Other2 -> ct:fail({bad_message_waiting_for_exit,Other2}) 838 after 10000 -> 839 ct:fail(timeout_waiting_for_exit) 840 end, 841 Res. 842 843fun_arity(F) -> 844 {arity,Arity} = erlang:fun_info(F, arity), 845 Arity. 846 847start_node(Name) -> 848 Pa = filename:dirname(code:which(?MODULE)), 849 Cookie = atom_to_list(erlang:get_cookie()), 850 test_server:start_node(Name, slave, 851 [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]). 852 853wait_until(Fun) -> 854 case catch Fun() of 855 true -> ok; 856 _ -> receive after 100 -> wait_until(Fun) end 857 end. 858