1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2007-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%% 22%%---------------------------------------------------------------------- 23%% Purpose: megaco sequence generator for the megaco test suite 24%%---------------------------------------------------------------------- 25 26-module(megaco_test_megaco_generator). 27 28-behaviour(megaco_test_generator). 29 30-compile({no_auto_import,[error/1]}). 31 32%% API 33-export([ 34 start_link/1, start_link/2, 35 stop/1, 36 exec/2, exec/3 37 ]). 38 39%% genarator behaviour callback exports 40-export([ 41 init/1, 42 handle_parse/2, 43 handle_exec/2, 44 terminate/2 45 ]). 46 47%% Megaco callback api 48-export([ 49 handle_connect/3, handle_connect/4, 50 handle_disconnect/4, 51 handle_syntax_error/4, handle_syntax_error/5, 52 handle_message_error/4, handle_message_error/5, 53 handle_trans_request/4, handle_trans_request/5, 54 handle_trans_long_request/4, handle_trans_long_request/5, 55 handle_trans_reply/5, handle_trans_reply/6, 56 handle_trans_ack/5, handle_trans_ack/6, 57 handle_trans_request_abort/5, handle_trans_request_abort/6, 58 handle_unexpected_trans/4, handle_unexpected_trans/5 59 ]). 60 61 62%%---------------------------------------------------------------------- 63 64-include_lib("megaco/include/megaco.hrl"). 65 66 67%%---------------------------------------------------------------------- 68 69-define(DELIVER_MOD, megaco_test_deliver). 70 71 72%%---------------------------------------------------------------------- 73 74-record(state, 75 { 76 mid, 77 recv_handle, 78 port, 79 send_handle, 80 conn_handle, 81 82 transport_sup, 83 ctrl_pid, 84 85 result = [] % Accumulated results from verification 86 }). 87 88 89%%---------------------------------------------------------------------- 90%% API 91%%---------------------------------------------------------------------- 92 93start_link(Name) -> 94 megaco_test_generator:start_link(?MODULE, [], Name). 95 96start_link(Name, Node) -> 97 megaco_test_generator:start_link(?MODULE, [], Name, Node). 98 99stop(Server) -> 100 megaco_test_generator:stop(Server). 101 102exec(Server, Instructions) when is_list(Instructions) -> 103 megaco_test_generator:exec(Server, Instructions). 104 105exec(Server, Instructions, Timeout) when is_list(Instructions) -> 106 megaco_test_generator:exec(Server, Instructions, Timeout). 107 108 109%%---------------------------------------------------------------------- 110%% generator callback functions 111%%---------------------------------------------------------------------- 112 113init([]) -> 114 random_init(), 115 {ok, #state{}}. 116 117 118%% ----- instruction parser ----- 119 120handle_parse({debug, Debug} = Instruction, State) 121 when is_boolean(Debug) -> 122 {ok, Instruction, State}; 123 124handle_parse({expect_nothing, To} = Instruction, State) 125 when is_integer(To) andalso (To > 0) -> 126 {ok, Instruction, State}; 127 128handle_parse({megaco_trace, Level} = Instruction, State) 129 when (Level =:= disable) orelse 130 (Level =:= max) orelse 131 (Level =:= min) orelse 132 is_integer(Level) -> 133 {ok, Instruction, State}; 134 135handle_parse({sleep, To} = Instruction, State) 136 when is_integer(To) andalso (To > 0) -> 137 {ok, Instruction, State}; 138 139handle_parse(megaco_start = Instruction, State) -> 140 {ok, Instruction, State}; 141 142handle_parse(megaco_stop = Instruction, State) -> 143 {ok, Instruction, State}; 144 145handle_parse({megaco_start_user, _Mid, _RecvInfo, Conf} = Instruction, State) 146 when is_list(Conf) -> 147 {ok, Instruction, State}; 148 149handle_parse(megaco_stop_user = Instruction, State) -> 150 {ok, Instruction, State}; 151 152handle_parse(megaco_info = Instruction, State) -> 153 {ok, Instruction, State}; 154 155handle_parse(megaco_system_info, State) -> 156 Verify = fun(_) -> ok end, 157 Instruction = {megaco_system_info, internal_system_info_tag, Verify}, 158 {ok, Instruction, State}; 159 160handle_parse({megaco_system_info, Tag}, State) 161 when is_atom(Tag) -> 162 Verify = fun(_) -> ok end, 163 Instruction = {megaco_system_info, Tag, Verify}, 164 {ok, Instruction, State}; 165 166handle_parse({megaco_system_info, Tag, Verify} = Instruction, State) 167 when is_atom(Tag) andalso is_function(Verify) -> 168 {ok, Instruction, State}; 169 170handle_parse({megaco_user_info, Tag} = Instruction, State) 171 when is_atom(Tag) -> 172 {ok, Instruction, State}; 173 174handle_parse({megaco_update_user_info, Tag, _Val} = Instruction, State) 175 when is_atom(Tag) -> 176 {ok, Instruction, State}; 177 178handle_parse({megaco_conn_info, Tag} = Instruction, State) 179 when is_atom(Tag) -> 180 {ok, Instruction, State}; 181 182handle_parse({megaco_update_conn_info, Tag, _Val} = Instruction, State) 183 when is_atom(Tag) -> 184 {ok, Instruction, State}; 185 186handle_parse(start_transport = Instruction, State) -> 187 {ok, Instruction, State}; 188 189handle_parse(listen = _Instruction, State) -> 190 MeybeRetry = make_connect_retry_fun2(), 191 Instruction = {listen, [], MeybeRetry}, 192 {ok, Instruction, State}; 193 194handle_parse({listen, Opts} = _Instruction, State) 195 when is_list(Opts) -> 196 MeybeRetry = make_connect_retry_fun2(), 197 Instruction = {listen, Opts, MeybeRetry}, 198 {ok, Instruction, State}; 199 200handle_parse({listen, Opts, MeybeRetry} = Instruction, State) 201 when is_list(Opts) andalso is_function(MeybeRetry) -> 202 {ok, Instruction, State}; 203 204handle_parse(connect = _Instruction, State) -> 205 case inet:gethostname() of 206 {ok, LocalHost} -> 207 MeybeRetry = make_connect_retry_fun2(), 208 Instruction = {connect, LocalHost, [], MeybeRetry}, 209 {ok, Instruction, State}; 210 Error -> 211 Error 212 end; 213 214handle_parse({connect, Opts} = _Instruction, State) 215 when is_list(Opts) -> 216 verify_connect_opts(Opts), 217 case inet:gethostname() of 218 {ok, LocalHost} -> 219 MeybeRetry = make_connect_retry_fun2(), 220 Instruction = {connect, LocalHost, Opts, MeybeRetry}, 221 {ok, Instruction, State}; 222 Error -> 223 Error 224 end; 225 226handle_parse({connect, Host} = _Instruction, State) 227 when is_atom(Host) -> 228 MeybeRetry = make_connect_retry_fun2(), 229 Instruction = {connect, Host, [], MeybeRetry}, 230 {ok, Instruction, State}; 231 232handle_parse({connect, Host, Opts} = _Instruction, State) 233 when (is_atom(Host) orelse is_list(Host)) andalso is_list(Opts) -> 234 verify_connect_opts(Opts), 235 MeybeRetry = make_connect_retry_fun2(), 236 Instruction = {connect, Host, Opts, MeybeRetry}, 237 {ok, Instruction, State}; 238 239handle_parse({connect, Host, Opts, MeybeRetry} = Instruction, State) 240 when (is_atom(Host) orelse is_list(Host)) andalso 241 is_list(Opts) andalso 242 is_function(MeybeRetry) -> 243 verify_connect_opts(Opts), 244 {ok, Instruction, State}; 245 246handle_parse(disconnect = Instruction, State) -> 247 {ok, Instruction, State}; 248 249handle_parse(megaco_connect = Instruction, State) -> 250 {ok, Instruction, State}; 251 252handle_parse({megaco_connect, _} = Instruction, State) -> 253 {ok, Instruction, State}; 254 255handle_parse(megaco_disconnect = Instruction, State) -> 256 {ok, Instruction, State}; 257 258handle_parse({megaco_disconnect, _Reason} = Instruction, State) -> 259 {ok, Instruction, State}; 260 261handle_parse({megaco_call, ARs, Opts} = Instruction, State) 262 when (is_list(ARs) orelse is_binary(ARs)) andalso is_list(Opts) -> 263 {ok, Instruction, State}; 264 265handle_parse({megaco_call, _Mid, ARs, Opts} = Instruction, State) 266 when (is_list(ARs) orelse is_binary(ARs)) andalso is_list(Opts) -> 267 {ok, Instruction, State}; 268 269handle_parse({megaco_cast, ARs, Opts} = Instruction, State) 270 when (is_list(ARs) orelse is_binary(ARs)) andalso is_list(Opts) -> 271 {ok, Instruction, State}; 272 273handle_parse({megaco_cast, _Mid, ARs, Opts} = Instruction, State) 274 when (is_list(ARs) orelse is_binary(ARs)) andalso is_list(Opts) -> 275 {ok, Instruction, State}; 276 277handle_parse({megaco_cancel, _Reason} = Instruction, State) -> 278 {ok, Instruction, State}; 279 280handle_parse({megaco_callback, Tag, TimeoutOrVerify} = Instruction, State) 281 when (is_atom(Tag) andalso 282 ((is_integer(TimeoutOrVerify) andalso 283 (TimeoutOrVerify > 0)) orelse 284 is_function(TimeoutOrVerify))) -> 285 {ok, Instruction, State}; 286 287handle_parse({megaco_callback, Tag, Verify, Timeout} = Instruction, State) 288 when (is_atom(Tag) andalso 289 is_function(Verify) andalso 290 (is_integer(Timeout) andalso (Timeout > 0))) -> 291 {ok, Instruction, State}; 292 293handle_parse({megaco_callback, Tag, {VMod, VFunc, VArgs}} = _Instruction, 294 State) 295 when (is_atom(Tag) andalso 296 (is_atom(VMod) andalso is_atom(VFunc) andalso is_list(VArgs))) -> 297 Verify = fun(X) -> 298 io:format("[megaco_callback ~w] calling ~w:~w with" 299 "~n X: ~p" 300 "~n VArgs: ~w" 301 "~n", [Tag, VMod, VFunc, X, VArgs]), 302 (catch apply(VMod, VFunc, [X|VArgs])) 303 end, 304 Instruction = {megaco_callback, Tag, Verify}, 305 {ok, Instruction, State}; 306 307handle_parse({megaco_callback, Verifiers0} = _Instruction, State) 308 when is_list(Verifiers0) -> 309 Verifiers = [make_verifier(Verifier) || Verifier <- Verifiers0], 310 Instruction = {megaco_callback, Verifiers}, 311 {ok, Instruction, State}; 312 313handle_parse({trigger, Trigger} = Instruction, State) 314 when is_function(Trigger) -> 315 {ok, Instruction, State}; 316 317handle_parse(Instruction, _State) -> 318 error({invalid_instruction, Instruction}). 319 320 321make_verifier({Tag, No, VerifyFunc} = Verify) 322 when is_atom(Tag) andalso is_integer(No) andalso is_function(VerifyFunc) -> 323 Verify; 324make_verifier({Tag, No, {VMod, VFunc, VArgs}}) 325 when is_atom(Tag) andalso is_integer(No) andalso 326 (is_atom(VMod) andalso is_atom(VFunc) andalso is_list(VArgs)) -> 327 VerifyFunc = fun(X) -> 328 io:format("[megaco_callback ~w] calling ~w:~w with" 329 "~n X: ~p" 330 "~n VArgs: ~w" 331 "~n", [Tag, VMod, VFunc, X, VArgs]), 332 (catch apply(VMod, VFunc, [X|VArgs])) 333 end, 334 Verify = {Tag, No, VerifyFunc}, 335 Verify; 336make_verifier(BadVerifier) -> 337 error({bad_verifier, BadVerifier}). 338 339 340verify_connect_opts([]) -> 341 ok; 342verify_connect_opts([{Key, _}|Opts]) when is_atom(Key) -> 343 verify_connect_opts(Opts); 344verify_connect_opts([H|_]) -> 345 error({bad_opts_list, H}). 346 347%% make_connect_retry_fun1() -> 348%% fun(Error, _) -> 349%% {false, Error} 350%% end. 351 352make_connect_retry_fun2() -> 353 fun(Error, noError) -> 354 Timeout = 250, 355 sleep(random(Timeout) + 100), 356 {true, {3, Timeout*2, Error}}; 357 (_Error, {0, _Timeout, OriginalError}) -> 358 {false, OriginalError}; 359 (_Error, {N, Timeout, OriginalError}) -> 360 sleep(random(Timeout) + 100), 361 {true, {N-1, Timeout*2, OriginalError}} 362 end. 363 364 365%% ----- instruction exececutor ----- 366 367handle_exec({debug, Debug}, State) -> 368 p("debug: ~p", [Debug]), 369 put(debug, Debug), 370 {ok, State}; 371 372handle_exec({expect_nothing, To}, State) -> 373 p("expect nothing: ~p", [To]), 374 receive 375 Any -> 376 e("received unexpected: " 377 "~n ~p", [Any]), 378 error({expect_nothing, Any}) 379 after To -> 380 p("go nothing (~p) as expected", [To]), 381 {ok, State} 382 end; 383 384handle_exec({megaco_trace, disable}, State) -> 385 p("megaco trace: disable"), 386 megaco:disable_trace(), 387 {ok, State}; 388handle_exec({megaco_trace, Level}, State) -> 389 p("megaco trace: enable [~w]", [Level]), 390 megaco:enable_trace(Level, io), 391 {ok, State}; 392 393handle_exec(megaco_start, State) -> 394 p("start megaco"), 395 ok = megaco:start(), 396 {ok, State}; 397 398handle_exec(megaco_stop, State) -> 399 p("stop megaco"), 400 ok = megaco:stop(), 401 {ok, State}; 402 403handle_exec({megaco_start_user, Mid, RecvInfo, Conf}, State) -> 404 p("start megaco user: ~p", [Mid]), 405 406 d("megaco_start_user -> start user"), 407 ok = megaco:start_user(Mid, Conf), 408 409 d("megaco_start_user -> update user info: user_mod"), 410 ok = megaco:update_user_info(Mid, user_mod, ?MODULE), 411 412 d("megaco_start_user -> update user info: user_args"), 413 ok = megaco:update_user_info(Mid, user_args, [self()]), 414 415 Port = get_config(port, RecvInfo), 416 EM = get_config(encoding_module, RecvInfo), 417 EC = get_config(encoding_config, RecvInfo), 418 TM = get_config(transport_module, RecvInfo), 419 RH0 = megaco:user_info(Mid, receive_handle), 420 421 RH1 = RH0#megaco_receive_handle{send_mod = TM, 422 encoding_mod = EM, 423 encoding_config = EC}, 424 425 State1 = State#state{mid = Mid, recv_handle = RH1, port = Port}, 426 {ok, State1}; 427 428handle_exec(megaco_stop_user, #state{mid = Mid} = State) 429 when Mid /= undefined -> 430 p("stop megaco user: ~p", [Mid]), 431 megaco_cleanup(State), 432 ok = megaco:stop_user(Mid), 433 {ok, State#state{mid = undefined}}; 434 435handle_exec(start_transport, 436 #state{recv_handle = #megaco_receive_handle{send_mod = TM}} = State) -> 437 p("start transport ~p", [TM]), 438 case (catch TM:start_transport()) of 439 {ok, Sup} -> 440 d("transport started: Sup: ~p", [Sup]), 441 {ok, State#state{transport_sup = Sup}}; 442 {error, Reason} -> 443 e("failed starting transport (~w): " 444 "~n ~p", [TM, Reason]), 445 error({failed_starting_transport, TM, Reason}); 446 Crap -> 447 e("failed starting transport (~w): " 448 "~n ~p", [TM, Crap]), 449 error({failed_starting_transport, TM, Crap}) 450 end; 451 452handle_exec({listen, Opts0, MaybeRetry}, 453 #state{recv_handle = RH, port = Port, transport_sup = Pid} = State) 454 when RH#megaco_receive_handle.send_mod =:= megaco_tcp -> 455 p("listen(tcp)"), 456 Opts = [{module, ?DELIVER_MOD}, 457 {port, Port}, 458 {receive_handle, RH}, 459 {tcp_options, [{nodelay, true}]} | Opts0], 460 case (catch handle_exec_listen_tcp(Pid, Opts, MaybeRetry)) of 461 ok -> 462 {ok, State}; 463 Else -> 464 error({tcp_listen_failed, Opts0, Else}) 465 end; 466handle_exec({listen, Opts0, _MaybeRetry}, 467 #state{recv_handle = RH, port = Port, transport_sup = Pid} = State) 468 when RH#megaco_receive_handle.send_mod =:= megaco_udp -> 469 p("listen(udp) - open"), 470 Opts = [{module, ?DELIVER_MOD}, {port, Port}, {receive_handle, RH}|Opts0], 471 case (catch megaco_udp:open(Pid, Opts)) of 472 {ok, _SH, _CtrlPid} -> 473 {ok, State}; 474 Else -> 475 error({udp_open, Opts0, Else}) 476 end; 477handle_exec({listen, Opts0, _MaybeRetry}, 478 #state{recv_handle = RH, port = Port, transport_sup = Pid} = State) 479 when RH#megaco_receive_handle.send_mod =:= megaco_test_generic_transport -> 480 p("listen(generic)"), 481 Opts = [{module, ?DELIVER_MOD}, {port, Port}, {receive_handle, RH}|Opts0], 482 case (catch megaco_test_generic_transport:listen(Pid, Opts)) of 483 {ok, _SH, _CtrlPid} -> 484 {ok, State}; 485 Else -> 486 error({udp_open, Opts0, Else}) 487 end; 488 489handle_exec({connect, Host, Opts0, MaybeRetry}, 490 #state{transport_sup = Sup, 491 recv_handle = RH, 492 port = Port} = State) 493 when RH#megaco_receive_handle.send_mod =:= megaco_tcp -> 494 p("connect(tcp) to ~p:~p", [Host, Port]), 495 PrelMid = preliminary_mid, 496 Opts = [{host, Host}, 497 {port, Port}, 498 {receive_handle, RH}, 499 {tcp_options, [{nodelay, true}]} | Opts0], 500 case (catch handle_exec_connect_tcp(Host, Opts, Sup, MaybeRetry)) of 501 {ok, SH, ControlPid} -> 502 d("connected(tcp): ~p, ~p", [SH, ControlPid]), 503 megaco_connector_start(RH, PrelMid, SH, ControlPid), 504 {ok, State#state{send_handle = SH, 505 ctrl_pid = ControlPid}}; 506 Error -> 507 error({tcp_connect_failed, Host, Opts0, Error}) 508 end; 509 510handle_exec({connect, Host, Opts0, _MaybeRetry}, 511 #state{transport_sup = Sup, 512 recv_handle = RH, 513 port = Port} = State) 514 when RH#megaco_receive_handle.send_mod =:= megaco_udp -> 515 p("connect(udp) to ~p", [Host]), 516 PrelMid = preliminary_mid, 517 Opts = [{port, 0}, {receive_handle, RH}|Opts0], 518 d("udp open", []), 519 case (catch megaco_udp:open(Sup, Opts)) of 520 {ok, Handle, ControlPid} -> 521 d("opened(udp): ~p, ~p", [Handle, ControlPid]), 522 SH = megaco_udp:create_send_handle(Handle, Host, Port), 523 megaco_connector_start(RH, PrelMid, SH, ControlPid), 524 {ok, State#state{send_handle = SH, 525 ctrl_pid = ControlPid}}; 526 Error -> 527 error({udp_connect_failed, Host, Opts0, Error}) 528 end; 529 530handle_exec({connect, Host, Opts0, _MaybeRetry}, 531 #state{transport_sup = Sup, 532 recv_handle = RH, 533 port = Port} = State) 534 when RH#megaco_receive_handle.send_mod =:= megaco_test_generic_transport -> 535 p("connect(generic) to ~p", [Host]), 536 PrelMid = preliminary_mid, 537 Opts = [{host, Host}, {port, Port}, {receive_handle, RH}|Opts0], 538 case (catch megaco_test_generic_transport:connect(Sup, Opts)) of 539 {ok, SH, ControlPid} -> 540 d("connected(generic): ~p, ~p", [SH, ControlPid]), 541 megaco_connector_start(RH, PrelMid, SH, ControlPid), 542 {ok, State#state{send_handle = SH, 543 ctrl_pid = ControlPid}}; 544 Error -> 545 error({generic_connect_failed, Host, Opts0, Error}) 546 end; 547 548handle_exec(megaco_connect, State) -> 549 p("expect megaco_connect"), 550 receive 551 {megaco_connect_result, {ok, CH}} -> 552 p("received successful megaco_connect: ~p", [CH]), 553 {ok, State#state{conn_handle = CH}}; 554 {megaco_connect_result, Error} -> 555 p("received failed megaco_connect: ~p", [Error]), 556 #state{result = Res} = State, 557 {ok, State#state{result = [Error|Res]}} 558 end; 559 560handle_exec({megaco_connect, Mid}, 561 #state{recv_handle = RH, 562 send_handle = SH, 563 ctrl_pid = ControlPid} = State) -> 564 p("megaco connect: ~p", [Mid]), 565 megaco_connector_start(RH, Mid, SH, ControlPid), 566 {ok, State}; 567 568handle_exec({megaco_user_info, Tag}, #state{mid = Mid, result = Res} = State) 569 when Mid /= undefined -> 570 p("megaco user-info: ~w", [Tag]), 571 Val = (catch megaco:user_info(Mid, Tag)), 572 d("megaco_user_info: ~p", [Val]), 573 {ok, State#state{result = [Val|Res]}}; 574 575handle_exec({megaco_update_user_info, Tag, Val}, #state{mid = Mid} = State) 576 when Mid /= undefined -> 577 p("update megaco user-info: ~w -> ~p", [Tag, Val]), 578 ok = megaco:update_user_info(Mid, Tag, Val), 579 {ok, State}; 580 581handle_exec({megaco_conn_info, Tag}, 582 #state{conn_handle = CH, result = Res} = State) 583 when CH /= undefined -> 584 p("megaco conn-info: ~w", [Tag]), 585 Val = (catch megaco:conn_info(CH, Tag)), 586 d("megaco_conn_info: ~p", [Val]), 587 {ok, State#state{result = [Val|Res]}}; 588 589handle_exec({megaco_update_conn_info, Tag, Val}, 590 #state{conn_handle = CH} = State) 591 when CH /= undefined -> 592 p("update megaco conn-info: ~w -> ~p", [Tag, Val]), 593 case megaco:update_conn_info(CH, Tag, Val) of 594 ok -> 595 {ok, State}; 596 Error -> 597 error({failed_updating_conn_info, Tag, Val, Error}) 598 end; 599 600handle_exec(megaco_info, #state{result = Res} = State) -> 601 p("megaco info", []), 602 Val = (catch megaco:info()), 603 d("megaco_info: ~p", [Val]), 604 {ok, State#state{result = [Val|Res]}}; 605 606handle_exec({megaco_system_info, Tag, Verify}, #state{result = Res} = State) -> 607 p("megaco system-info: ~w", [Tag]), 608 Val = (catch megaco:system_info(Tag)), 609 d("megaco system-info: ~p", [Val]), 610 case Verify(Val) of 611 ok -> 612 {ok, State#state{result = [Val|Res]}}; 613 Error -> 614 {error, State#state{result = [Error|Res]}} 615 end; 616 617%% This is either a MG or a MGC which is only connected to one MG 618handle_exec({megaco_call, ARs, Opts}, #state{conn_handle = CH} = State) 619 when CH /= undefined -> 620 p("megaco_call: " 621 "~n CH: ~p" 622 "~n ARs: ~p" 623 "~n Opts: ~p", [CH, ARs, Opts]), 624 {_PV, UserReply} = megaco:call(CH, ARs, Opts), 625 d("megaco_call -> UserReply: ~n~p", [UserReply]), 626 {ok, State}; 627 628handle_exec({megaco_call, RemoteMid, ARs, Opts}, #state{mid = Mid} = State) -> 629 p("megaco_call: ~p", [RemoteMid]), 630 %% First we have to find the CH for this Mid 631 Conns = megaco:user_info(Mid, connections), 632 {value, {_, CH}} = 633 lists:keysearch(RemoteMid, #megaco_conn_handle.remote_mid, Conns), 634 p("megaco_call: " 635 "~n CH: ~p" 636 "~n ARs: ~p" 637 "~n Opts: ~p", [CH, ARs, Opts]), 638 {_PV, UserReply} = megaco:call(CH, ARs, Opts), 639 d("megaco_call -> UserReply: ~n~p", [UserReply]), 640 {ok, State}; 641 642%% This is either a MG or a MGC which is only connected to one MG 643handle_exec({megaco_cast, ARs, Opts}, #state{conn_handle = CH} = State) 644 when CH =/= undefined -> 645 p("megaco_cast: " 646 "~n CH: ~p" 647 "~n ARs: ~p", [CH, ARs]), 648 case megaco:cast(CH, ARs, Opts) of 649 ok -> 650 {ok, State}; 651 Error -> 652 e("failed sending (cast) message: ~n~p", [Error]), 653 #state{result = Acc} = State, 654 {error, State#state{result = [Error|Acc]}} 655 end; 656 657handle_exec({megaco_cast, RemoteMid, ARs, Opts}, #state{mid = Mid} = State) -> 658 p("megaco_cast with ~p", [RemoteMid]), 659 %% First we have to find the CH for this Mid 660 Conns = megaco:user_info(Mid, connections), 661 {value, {_, CH}} = 662 lists:keysearch(RemoteMid, #megaco_conn_handle.remote_mid, Conns), 663 p("megaco_cast: " 664 "~n CH: ~p" 665 "~n ARs: ~p" 666 "~n Opts: ~p", [CH, ARs, Opts]), 667 case megaco:cast(CH, ARs, Opts) of 668 ok -> 669 {ok, State}; 670 Error -> 671 e("failed sending (cast) message: " 672 "~n ~p", [Error]), 673 #state{result = Acc} = State, 674 {error, State#state{result = [Error|Acc]}} 675 end; 676 677%% Nothing shall happen for atleast Timeout time 678handle_exec({megaco_callback, nocall, Timeout}, State) -> 679 p("expect no megaco_callback for ~w", [Timeout]), 680 receive 681 {handle_megaco_callback, Type, Msg, Pid} -> 682 e("received unexpected megaco callback: ~n~p", [Msg]), 683 #state{result = Res} = State, 684 Err = {unexpected_callback, Type, Msg, Pid}, 685 {error, State#state{result = [Err|Res]}} 686 after Timeout -> 687 p("got no callback (~p) as expected", [Timeout]), 688 {ok, State} 689 end; 690 691handle_exec({megaco_callback, Tag, Verify}, State) when is_function(Verify) -> 692 p("expect megaco_callback ~w", [Tag]), 693 receive 694 {handle_megaco_callback, Type, Msg, Pid} -> 695 d("received megaco callback:" 696 "~n ~p", [Msg]), 697 case Verify(Msg) of 698 {VRes, Res, Reply} -> 699 d("megaco_callback [~w] ~w", [Tag, VRes]), 700 handle_megaco_callback_reply(Pid, Type, Reply), 701 validate(VRes, Tag, Res, State); 702 {VRes, Delay, Res, Reply} -> 703 d("megaco_callback [~w] ~w, ~w", [Tag,Delay,VRes]), 704 handle_megaco_callback_reply(Pid, Type, Delay, Reply), 705 validate(VRes, Tag, Res, State) 706 end 707 end; 708 709handle_exec({megaco_callback, Tag, {VMod, VFunc, VArgs}}, State) 710 when is_atom(VMod) andalso is_atom(VFunc) andalso is_list(VArgs) -> 711 p("expect megaco_callback ~w", [Tag]), 712 receive 713 {handle_megaco_callback, Type, Msg, Pid} -> 714 d("received megaco callback: ~n~p" 715 "~n VMod: ~w" 716 "~n VFunc: ~w" 717 "~n VArgs: ~p", [Msg, VMod, VFunc, VArgs]), 718 case apply(VMod, VFunc, [Msg|VArgs]) of 719 {VRes, Res, Reply} -> 720 d("megaco_callback [~w] ~w",[Tag, VRes]), 721 handle_megaco_callback_reply(Pid, Type, Reply), 722 validate(VRes, Tag, Res, State); 723 {VRes, Delay, Res, Reply} -> 724 d("megaco_callback [~w] ~w, ~w",[Tag,Delay,VRes]), 725 handle_megaco_callback_reply(Pid, Type, Delay, Reply), 726 validate(VRes, Tag, Res, State) 727 end 728 end; 729 730handle_exec({megaco_callback, Tag, Verify, Timeout}, State) 731 when (is_function(Verify) andalso 732 (is_integer(Timeout) andalso (Timeout > 0))) -> 733 p("expect megaco_callback ~w (with ~w)", [Tag, Timeout]), 734 receive 735 {handle_megaco_callback, Type, Msg, Pid} -> 736 d("received megaco callback: ~n~p", [Msg]), 737 case Verify(Msg) of 738 {VRes, Res, Reply} -> 739 d("megaco_callback [~w] ~w",[Tag,VRes]), 740 handle_megaco_callback_reply(Pid, Type, Reply), 741 validate(VRes, Tag, Res, State); 742 {VRes, Delay, Res, Reply} -> 743 d("megaco_callback [~w] ~w, ~w",[Tag,Delay,VRes]), 744 handle_megaco_callback_reply(Pid, Type, Delay, Reply), 745 validate(VRes, Tag, Res, State) 746 end 747 after Timeout -> 748 e("megaco_callback ~w timeout", [Tag]), 749 #state{result = Res} = State, 750 Err = {callback_timeout, Tag, Timeout}, 751 {error, State#state{result = [Err|Res]}} 752 end; 753 754handle_exec({megaco_callback, Verifiers}, State) -> 755 p("expect megaco_callback(s)"), 756 megaco_callback_verify(Verifiers, State); 757 758handle_exec({megaco_cancel, Reason}, #state{conn_handle = CH} = State) -> 759 p("megaco_cancel: ~w", [Reason]), 760 case megaco:cancel(CH, Reason) of 761 ok -> 762 {ok, State}; 763 Error -> 764 e("failed cancel: ~n~p", [Error]), 765 #state{result = Acc} = State, 766 {error, State#state{result = [Error|Acc]}} 767 end; 768 769handle_exec({trigger, Trigger}, State) when is_function(Trigger) -> 770 p("trigger"), 771 (catch Trigger()), 772 {ok, State}; 773 774handle_exec({sleep, To}, State) -> 775 p("sleep ~p", [To]), 776 megaco_test_generator:sleep(To), 777 {ok, State}; 778 779handle_exec(BadInstruction, _State) -> 780 error({invalid_instruction, BadInstruction}). 781 782 783%% --- cleanup --- 784 785megaco_cleanup(#state{mid = Mid}) -> 786 Close = fun(CH) -> do_megaco_cleanup(CH) end, 787 Conns = 788 case (catch megaco:user_info(Mid, connections)) of 789 Connections when is_list(Connections) -> 790 Connections; 791 _ -> 792 [] 793 end, 794 lists:foreach(Close, Conns). 795 796do_megaco_cleanup(CH) -> 797 case (catch do_megaco_cleanup2(CH)) of 798 ok -> 799 ok; 800 {'EXIT', {no_such_connection, _}} -> 801 ok; 802 {'EXIT', Reason} -> 803 exit(Reason) 804 end. 805 806do_megaco_cleanup2(CH) -> 807 d("do_megaco_cleanup2 -> entry with" 808 "~n CH: ~p", [CH]), 809 Reason = {stopped_by_user,self()}, 810 Pid = megaco:conn_info(CH, control_pid), 811 SendMod = megaco:conn_info(CH, send_mod), 812 SendHandle = megaco:conn_info(CH, send_handle), 813 d("do_megaco_cleanup2 -> disconnect"), 814 megaco:disconnect(CH, Reason), 815 d("do_megaco_cleanup2 -> disconnected, now cancel"), 816 megaco:cancel(CH, Reason), 817 d("do_megaco_cleanup2 -> canceled, now close"), 818 case SendMod of 819 megaco_tcp -> (catch megaco_tcp:close(SendHandle)); 820 megaco_udp -> (catch megaco_udp:close(SendHandle)); 821 SendMod -> exit(Pid, Reason) 822 end, 823 ok. 824 825 826%% --- connector --- 827 828megaco_connector_start(RH, PrelMid, SH, ControlPid) -> 829 Self = self(), 830 Fun = fun() -> megaco_connect(RH, PrelMid, SH, ControlPid, Self) end, 831 erlang:spawn_opt(Fun, [link]). 832 833megaco_connect(RH, PrelMid, SH, ControlPid, Parent) -> 834 Result = megaco:connect(RH, PrelMid, SH, ControlPid), 835 Parent ! {megaco_connect_result, Result}, 836 exit(normal). 837 838 839%% --- megaco callback verify --- 840 841%% This is used when a number of callback's is expected, but where 842%% the specific order is unknown. 843megaco_callback_verify([], State) -> 844 d("megaco_callback_verify -> done"), 845 {ok, State}; 846megaco_callback_verify(Verifiers0, State0) -> 847 d("megaco_callback_verify -> entry when" 848 "~n length(Verifiers0): ~w", [length(Verifiers0)]), 849 receive 850 {handle_megaco_callback, Type, Msg, Pid} -> 851 d("megaco_callback_verify -> received megaco callback: ~w" 852 "~n Msg: ~p", [Type, Msg]), 853 case megaco_callback_verify(Verifiers0, Type, Msg, Pid, State0) of 854 {ok, Verifiers, State} -> 855 megaco_callback_verify(Verifiers, State); 856 Error -> 857 Error 858 end 859 end. 860 861megaco_callback_verify(Verifiers0, Type, Msg, Pid, State0) -> 862 d("megaco_callback_verify -> entry"), 863 Tag = element(1, Msg), 864 d("megaco_callback_verify -> Tag: ~w", [Tag]), 865 case lists:keysearch(Tag, 1, Verifiers0) of 866 {value, {Tag, N, Verify}} when (N > 0) andalso is_function(Verify) -> 867 d("megaco_callback_verify -> N: ~w",[N]), 868 case Verify(Msg) of 869 {VRes, Res, Reply} -> 870 d("megaco_callback_verify -> VRes: ~w",[VRes]), 871 handle_megaco_callback_reply(Pid, Type, Reply), 872 case validate(VRes, Tag, Res, State0) of 873 {error, _} = EState -> 874 e("megaco_callback_verify -> (1) error", []), 875 throw(EState); 876 {ok, State} when N > 1 -> 877 d("megaco_callback_verify -> (1) validated"), 878 Rec = {Tag, N-1, Verify}, 879 Verifiers = 880 lists:keyreplace(Tag, 1, Verifiers0, Rec), 881 {ok, Verifiers, State}; 882 {ok, State} -> 883 d("megaco_callback_verify -> (2) validated"), 884 Verifiers = lists:keydelete(Tag, 1, Verifiers0), 885 {ok, Verifiers, State} 886 end; 887 {VRes, Delay, Res, Reply} -> 888 d("megaco_callback_verify -> Delay: ~w, VRes: ~w", 889 [Delay,VRes]), 890 handle_megaco_callback_reply(Pid, Type, Delay, Reply), 891 case validate(VRes, Tag, Res, State0) of 892 {error, _} = EState -> 893 e("megaco_callback_verify -> (2) error", []), 894 throw(EState); 895 {ok, State} when N > 1 -> 896 d("megaco_callback_verify -> (3) validated"), 897 Rec = {Tag, N-1, Verify}, 898 Verifiers = 899 lists:keyreplace(Tag, 1, Verifiers0, Rec), 900 {ok, Verifiers, State}; 901 {ok, State} -> 902 d("megaco_callback_verify -> (4) validated"), 903 Verifiers = lists:keydelete(Tag, 1, Verifiers0), 904 {ok, Verifiers, State} 905 end 906 end; 907 false -> 908 e("megaco_callback_verify -> no such tag ~w~n~p", 909 [Tag, Verifiers0]), 910 #state{result = Res} = State0, 911 State = State0#state{result = [{Type, error, Msg}|Res]}, 912 error(State) 913 end. 914 915 916%% --- validate verify result --- 917 918validate(ok, handle_connect = Tag, CH, #state{result = Acc} = S) -> 919 {ok, S#state{conn_handle = CH, result = [{Tag, ok, CH}|Acc]}}; 920validate(ok, Tag, Res, #state{result = Acc} = S) -> 921 {ok, S#state{result = [{Tag, ok, Res}|Acc]}}; 922validate(error, Tag, Res, #state{result = Acc} = S) -> 923 {error, S#state{result = [{Tag, error, Res}|Acc]}}. 924 925 926%% ----- termination ----- 927 928terminate(normal, #state{result = Result} = _State) -> 929 d("terminate -> entry when normal with" 930 "~n Result: ~p", [Result]), 931 %% megaco_cleanup(State), 932 {ok, Result}; 933 934terminate(Reason, #state{result = Result} = State) -> 935 d("terminate -> entry with" 936 "~n Reason: ~p" 937 "~n Result: ~p", [Reason, Result]), 938 megaco_cleanup(State), 939 {error, {Reason, Result}}. 940 941 942%%---------------------------------------------------------------------- 943 944handle_exec_listen_tcp(Sup, Opts, MaybeRetry) -> 945 handle_exec_listen_tcp(Sup, Opts, MaybeRetry, noError). 946 947handle_exec_listen_tcp(Sup, Opts, MaybeRetry, Error0) -> 948 case (catch megaco_tcp:listen(Sup, Opts)) of 949 ok -> 950 ok; 951 Error1 -> 952 case (catch MaybeRetry(Error1, Error0)) of 953 {true, Error2} -> 954 handle_exec_listen_tcp(Sup, Opts, MaybeRetry, Error2); 955 {false, Error3} -> 956 {error, Error3} 957 end 958 end. 959 960 961handle_exec_connect_tcp(Host, Opts, Sup, MaybeRetry) 962 when is_function(MaybeRetry) -> 963 handle_exec_connect_tcp(Host, Opts, Sup, MaybeRetry, noError). 964 965handle_exec_connect_tcp(Host, Opts, Sup, MaybeRetry, Error0) -> 966 case (catch megaco_tcp:connect(Sup, Opts)) of 967 {ok, SH, ControlPid} -> 968 d("tcp connected: ~p, ~p", [SH, ControlPid]), 969 {ok, SH, ControlPid}; 970 Error1 -> 971 case (catch MaybeRetry(Error1, Error0)) of 972 {true, Error2} -> 973 handle_exec_connect_tcp(Host, Opts, Sup, 974 MaybeRetry, Error2); 975 {false, Error3} -> 976 {error, Error3} 977 end 978 end. 979 980 981 982%%---------------------------------------------------------------------- 983%% megaco_user callback functions 984%%---------------------------------------------------------------------- 985 986handle_connect(CH, PV, P) -> 987 Req = {handle_connect, CH, PV}, 988 handle_megaco_callback_call(P, Req). 989 990handle_connect(CH, PV, Extra, P) -> 991 Req = {handle_connect, CH, PV, Extra}, 992 handle_megaco_callback_call(P, Req). 993 994handle_disconnect(CH, PV, R, P) -> 995 Msg = {handle_disconnect, CH, PV, R}, 996 Reply = ok, 997 handle_megaco_callback_cast(P, Msg, Reply). 998 999handle_syntax_error(RH, PV, ED, P) -> 1000 Req = {handle_syntax_error, RH, PV, ED}, 1001 handle_megaco_callback_call(P, Req). 1002 1003handle_syntax_error(RH, PV, ED, Extra, P) -> 1004 Req = {handle_syntax_error, RH, PV, ED, Extra}, 1005 handle_megaco_callback_call(P, Req). 1006 1007handle_message_error(CH, PV, ED, P) -> 1008 Msg = {handle_message_error, CH, PV, ED}, 1009 Reply = ok, 1010 handle_megaco_callback_cast(P, Msg, Reply). 1011 1012handle_message_error(CH, PV, ED, Extra, P) -> 1013 Msg = {handle_message_error, CH, PV, ED, Extra}, 1014 Reply = ok, 1015 handle_megaco_callback_cast(P, Msg, Reply). 1016 1017handle_trans_request(CH, PV, AR, P) -> 1018 Req = {handle_trans_request, CH, PV, AR}, 1019 handle_megaco_callback_call(P, Req). 1020 1021handle_trans_request(CH, PV, AR, Extra, P) -> 1022 Req = {handle_trans_request, CH, PV, AR, Extra}, 1023 handle_megaco_callback_call(P, Req). 1024 1025handle_trans_long_request(CH, PV, RD, P) -> 1026 Req = {handle_trans_long_request, CH, PV, RD}, 1027 handle_megaco_callback_call(P, Req). 1028 1029handle_trans_long_request(CH, PV, RD, Extra, P) -> 1030 Req = {handle_trans_long_request, CH, PV, RD, Extra}, 1031 handle_megaco_callback_call(P, Req). 1032 1033handle_trans_reply(CH, PV, AR, RD, P) -> 1034 Msg = {handle_trans_reply, CH, PV, AR, RD}, 1035 Reply = ok, 1036 handle_megaco_callback_cast(P, Msg, Reply). 1037 1038handle_trans_reply(CH, PV, AR, RD, Extra, P) -> 1039 Msg = {handle_trans_reply, CH, PV, AR, RD, Extra}, 1040 Reply = ok, 1041 handle_megaco_callback_cast(P, Msg, Reply). 1042 1043handle_trans_ack(CH, PV, AS, AD, P) -> 1044 Msg = {handle_trans_ack, CH, PV, AS, AD}, 1045 Reply = ok, 1046 handle_megaco_callback_cast(P, Msg, Reply). 1047 1048handle_trans_ack(CH, PV, AS, AD, Extra, P) -> 1049 Msg = {handle_trans_ack, CH, PV, AS, AD, Extra}, 1050 Reply = ok, 1051 handle_megaco_callback_cast(P, Msg, Reply). 1052 1053handle_unexpected_trans(CH, PV, T, P) -> 1054 Msg = {handle_unexpected_trans, CH, PV, T}, 1055 Reply = ok, 1056 handle_megaco_callback_cast(P, Msg, Reply). 1057 1058handle_unexpected_trans(CH, PV, T, Extra, P) -> 1059 Msg = {handle_unexpected_trans, CH, PV, T, Extra}, 1060 Reply = ok, 1061 handle_megaco_callback_cast(P, Msg, Reply). 1062 1063handle_trans_request_abort(RH, PV, TransNo, Pid, P) -> 1064 Msg = {handle_trans_request_abort, RH, PV, TransNo, Pid}, 1065 Reply = ok, 1066 handle_megaco_callback_cast(P, Msg, Reply). 1067 1068handle_trans_request_abort(RH, PV, TransNo, Pid, Extra, P) -> 1069 Msg = {handle_trans_request_abort, RH, PV, TransNo, Pid, Extra}, 1070 Reply = ok, 1071 handle_megaco_callback_cast(P, Msg, Reply). 1072 1073handle_megaco_callback_cast(P, Msg, Reply) -> 1074 d("handle_megaco_callback_cast -> entry with Msg: ~n~p", [Msg]), 1075 P ! {handle_megaco_callback, cast, Msg, self()}, 1076 Reply. 1077 1078handle_megaco_callback_call(P, Msg) -> 1079 d("handle_megaco_callback_call -> entry with" 1080 "~n P: ~p" 1081 "~n Msg: ~p", [P, Msg]), 1082 P ! {handle_megaco_callback, call, Msg, self()}, 1083 receive 1084 {handle_megaco_callback_reply, Reply} -> 1085 d("handle_megaco_callback_call -> received reply: ~n~p", [Reply]), 1086 Reply; 1087 {handle_megaco_callback_reply, Delay, Reply} when is_integer(Delay) -> 1088 d("handle_megaco_callback_call -> " 1089 "received reply [~w]: " 1090 "~n ~p", [Delay, Reply]), 1091 sleep(Delay), 1092 d("handle_megaco_callback_call -> deliver reply after delay [~w]", 1093 [Delay]), 1094 Reply; 1095 {'EXIT', Pid, Reason} when (Pid =:= P) -> 1096 d("handle_megaco_callback_call -> " 1097 "received unexpected EXIT signal (from ~p): " 1098 "~n Reason: ~p", [Pid, Reason]), 1099 exit({unexpected_EXIT_signal, Pid, Reason}); 1100 {'EXIT', SomePid, SomeReason} -> 1101 d("handle_megaco_callback_call -> " 1102 "received unexpected EXIT signal from unknown process: " 1103 "~n Pid: ~p" 1104 "~n Reason: ~p", [SomePid, SomeReason]), 1105 exit({unexpected_EXIT_signal, SomePid, SomeReason}) 1106 end. 1107 1108 1109handle_megaco_callback_reply(P, call, Reply) -> 1110 P ! {handle_megaco_callback_reply, Reply}; 1111handle_megaco_callback_reply(_, _, _) -> 1112 ok. 1113 1114handle_megaco_callback_reply(P, call, Delay, Reply) -> 1115 P ! {handle_megaco_callback_reply, Delay, Reply}; 1116handle_megaco_callback_reply(_, _, _, _) -> 1117 ok. 1118 1119 1120%%---------------------------------------------------------------------- 1121%% internal utility functions 1122%%---------------------------------------------------------------------- 1123 1124random_init() -> 1125 ok. 1126 1127random(N) -> 1128 rand:uniform(N). 1129 1130 1131get_config(Key, Opts) -> 1132 {value, {Key, Val}} = lists:keysearch(Key, 1, Opts), 1133 Val. 1134 1135sleep(X) -> megaco_test_generator:sleep(X). 1136 1137d(F) -> megaco_test_generator:debug(F). 1138d(F, A) -> megaco_test_generator:debug(F, A). 1139 1140e(F, A) -> megaco_test_generator:error(F, A). 1141 1142p(F ) -> p("", F, []). 1143p(F, A) -> p("", F, A). 1144p(P, F, A) -> megaco_test_generator:print(P, F, A). 1145 1146error(Reason) -> 1147 throw({error, Reason}). 1148 1149