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