1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2007-2021. 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};
316handle_parse({trigger, Desc, Trigger} = Instruction, State)
317  when is_list(Desc) andalso is_function(Trigger) ->
318    {ok, Instruction, State};
319
320handle_parse(Instruction, _State) ->
321    error({invalid_instruction, Instruction}).
322
323
324make_verifier({Tag, No, VerifyFunc} = Verify)
325  when is_atom(Tag) andalso is_integer(No) andalso is_function(VerifyFunc) ->
326    Verify;
327make_verifier({Tag, No, {VMod, VFunc, VArgs}})
328  when is_atom(Tag) andalso is_integer(No) andalso
329       (is_atom(VMod) andalso is_atom(VFunc) andalso is_list(VArgs)) ->
330    VerifyFunc = fun(X) ->
331                         io:format("[megaco_callback ~w] calling ~w:~w with"
332                                   "~n   X: ~p"
333                                   "~n   VArgs: ~w"
334                                   "~n", [Tag, VMod, VFunc, X, VArgs]),
335                         (catch apply(VMod, VFunc, [X|VArgs]))
336                 end,
337    Verify = {Tag, No, VerifyFunc},
338    Verify;
339make_verifier(BadVerifier) ->
340    error({bad_verifier, BadVerifier}).
341
342
343verify_connect_opts([]) ->
344    ok;
345verify_connect_opts([{Key, _}|Opts]) when is_atom(Key) ->
346    verify_connect_opts(Opts);
347verify_connect_opts([H|_]) ->
348    error({bad_opts_list, H}).
349
350%% make_connect_retry_fun1() ->
351%%      fun(Error, _) ->
352%% 	     {false, Error}
353%%      end.
354
355make_connect_retry_fun2() ->
356     fun(Error, noError) ->
357	     Timeout = 250,
358	     sleep(random(Timeout) + 100),
359	     {true, {3, Timeout*2, Error}};
360	(_Error, {0, _Timeout, OriginalError}) ->
361	     {false, OriginalError};
362	(_Error, {N, Timeout, OriginalError}) ->
363	     sleep(random(Timeout) + 100),
364	     {true, {N-1, Timeout*2, OriginalError}}
365     end.
366
367
368%% ----- instruction exececutor -----
369
370handle_exec({debug, Debug}, State) ->
371    p("debug: ~p", [Debug]),
372    put(debug, Debug),
373    {ok, State};
374
375handle_exec({expect_nothing, To}, State) ->
376    p("expect nothing: ~p", [To]),
377    receive
378        Any ->
379            e("received unexpected: "
380              "~n   ~p", [Any]),
381            error({expect_nothing, Any})
382    after To ->
383            p("go nothing (~p) as expected", [To]),
384            {ok, State}
385    end;
386
387handle_exec({megaco_trace, disable}, State) ->
388    p("megaco trace: disable"),
389    megaco:disable_trace(),
390    {ok, State};
391handle_exec({megaco_trace, Level}, State) ->
392    p("megaco trace: enable [~w]", [Level]),
393    megaco:enable_trace(Level, io),
394    {ok, State};
395
396handle_exec(megaco_start, State) ->
397    p("start megaco"),
398    ok = megaco:start(),
399    {ok, State};
400
401handle_exec(megaco_stop, State) ->
402    p("stop megaco"),
403    ok = megaco:stop(),
404    {ok, State};
405
406handle_exec({megaco_start_user, Mid, RecvInfo, Conf}, State) ->
407    p("start megaco user: ~p", [Mid]),
408
409    d("megaco_start_user -> start user"),
410    ok = megaco:start_user(Mid, Conf),
411
412    d("megaco_start_user -> update user info: user_mod"),
413    ok = megaco:update_user_info(Mid, user_mod,  ?MODULE),
414
415    d("megaco_start_user -> update user info: user_args"),
416    ok = megaco:update_user_info(Mid, user_args,  [self()]),
417
418    Port = get_config(port,             RecvInfo),
419    EM   = get_config(encoding_module,  RecvInfo),
420    EC   = get_config(encoding_config,  RecvInfo),
421    TM   = get_config(transport_module, RecvInfo),
422    RH0  = megaco:user_info(Mid, receive_handle),
423
424    RH1  = RH0#megaco_receive_handle{send_mod        = TM,
425				     encoding_mod    = EM,
426				     encoding_config = EC},
427
428    State1 = State#state{mid = Mid, recv_handle = RH1, port = Port},
429    {ok, State1};
430
431handle_exec(megaco_stop_user, #state{mid = Mid} = State)
432  when Mid =/= undefined ->
433    p("stop megaco user: ~p", [Mid]),
434    megaco_cleanup(State),
435    ok = megaco:stop_user(Mid),
436    {ok, State#state{mid = undefined}};
437
438handle_exec(start_transport,
439            #state{recv_handle = #megaco_receive_handle{send_mod = TM}} = State) ->
440    p("start transport ~p", [TM]),
441    try TM:start_transport() of
442	{ok, Sup} ->
443	    d("transport started: Sup: ~p", [Sup]),
444	    {ok, State#state{transport_sup = Sup}};
445	{error, Reason} ->
446	    e("failed starting transport (~w): "
447	      "~n   ~p", [TM, Reason]),
448	    error({failed_starting_transport, TM, Reason});
449	Crap ->
450	    e("failed starting transport (~w): "
451	      "~n   ~p", [TM, Crap]),
452	    error({failed_starting_transport, TM, Crap})
453    catch
454        C:E:S ->
455	    e("failed starting transport (~w) - catched: "
456	      "~n   C: ~p"
457	      "~n   E: ~p"
458	      "~n   S: ~p", [TM, C, E, S]),
459	    error({failed_starting_transport, TM, {E, E, S}})
460    end;
461
462handle_exec({listen, Opts0, MaybeRetry},
463     #state{recv_handle = RH, port = Port, transport_sup = Pid} = State)
464  when RH#megaco_receive_handle.send_mod =:= megaco_tcp ->
465    p("listen(tcp)"),
466    Opts = [{module,         ?DELIVER_MOD},
467	    {port,           Port},
468	    {receive_handle, RH},
469	    {tcp_options,    [{nodelay, true}]} | Opts0],
470    try  handle_exec_listen_tcp(Pid, Opts, MaybeRetry) of
471        ok ->
472            p("listen(tcp) -> ok"),
473            {ok, State};
474        Else ->
475	    e("failed tcp listen: "
476	      "~n   Else: ~p", [Else]),
477            error({tcp_listen_failed, Opts0, Else})
478    catch
479        C:E:S ->
480	    e("failed starting transport (~w) - catched: "
481	      "~n   C: ~p"
482	      "~n   E: ~p"
483	      "~n   S: ~p", [C, E, S]),
484	    error({tc_listen_failed, Opts0, {E, E, S}})
485    end;
486handle_exec({listen, Opts0, _MaybeRetry},
487     #state{recv_handle = RH, port = Port, transport_sup = Pid} = State)
488  when RH#megaco_receive_handle.send_mod =:= megaco_udp ->
489    p("listen(udp) - open"),
490    Opts = [{module, ?DELIVER_MOD}, {port, Port}, {receive_handle, RH}|Opts0],
491    try megaco_udp:open(Pid, Opts) of
492        {ok, _SH, _CtrlPid} ->
493            p("listen(udp) -> ok"),
494            {ok, State};
495        Else ->
496	    e("[listen] failed udp open: "
497	      "~n   Else: ~p", [Else]),
498            error({udp_open, Opts0, Else})
499    catch
500        C:E:S ->
501	    e("[listen] failed udp open - catched: "
502	      "~n   C: ~p"
503	      "~n   E: ~p"
504	      "~n   S: ~p", [C, E, S]),
505            error({udp_open, Opts0, {C, E, S}})
506    end;
507handle_exec({listen, Opts0, _MaybeRetry},
508            #state{recv_handle = RH, port = Port, transport_sup = Pid} = State)
509  when RH#megaco_receive_handle.send_mod =:= megaco_test_generic_transport ->
510    p("listen(generic)"),
511    Opts = [{module, ?DELIVER_MOD}, {port, Port}, {receive_handle, RH}|Opts0],
512    try megaco_test_generic_transport:listen(Pid, Opts) of
513        {ok, _SH, _CtrlPid} ->
514            p("listen(generic) -> ok"),
515            {ok, State};
516        Else ->
517	    e("[listen] failed generic: "
518	      "~n   Else: ~p", [Else]),
519            error({generic_listen, Opts0, Else})
520    catch
521        C:E:S ->
522	    e("[listen] failed generic - catched: "
523	      "~n   C: ~p"
524	      "~n   E: ~p"
525	      "~n   S: ~p", [C, E, S]),
526            error({generic_listen, Opts0, {C, E, S}})
527    end;
528
529handle_exec({connect, Host, Opts0, MaybeRetry},
530     #state{transport_sup = Sup,
531	     recv_handle  = RH,
532	     port         = Port} = State)
533  when RH#megaco_receive_handle.send_mod =:= megaco_tcp ->
534    p("connect(tcp) to ~p:~p", [Host, Port]),
535    PrelMid = preliminary_mid,
536    Opts = [{host,           Host},
537	    {port,           Port},
538	    {receive_handle, RH},
539	    {tcp_options,    [{nodelay, true}]} | Opts0],
540    try handle_exec_connect_tcp(Host, Opts, Sup, MaybeRetry) of
541	{ok, SH, ControlPid} ->
542	    p("connected(tcp): ~p, ~p", [SH, ControlPid]),
543	    megaco_connector_start(RH, PrelMid, SH, ControlPid),
544	    {ok, State#state{send_handle = SH,
545			      ctrl_pid    = ControlPid}};
546	Error ->
547	    e("tcp connect failed: "
548	      "~n   Error: ~p", [Error]),
549	    error({tcp_connect_failed, Host, Opts0, Error})
550    catch
551        C:E:S ->
552	    e("tcp connect failed - catched: "
553	      "~n   C: ~p"
554	      "~n   E: ~p"
555	      "~n   S: ~p", [C, E, S]),
556            error({tcp_connect_failed, Host, Opts0, {C, E, S}})
557    end;
558
559handle_exec({connect, Host, Opts0, _MaybeRetry},
560     #state{transport_sup = Sup,
561	     recv_handle   = RH,
562	     port          = Port} = State)
563  when RH#megaco_receive_handle.send_mod =:= megaco_udp ->
564    p("connect(udp) to ~p", [Host]),
565    PrelMid = preliminary_mid,
566    Opts = [{port, 0}, {receive_handle, RH}|Opts0],
567    d("udp open", []),
568    try megaco_udp:open(Sup, Opts) of
569	{ok, Handle, ControlPid} ->
570	    d("opened(udp): ~p, ~p", [Handle, ControlPid]),
571	    SH = megaco_udp:create_send_handle(Handle, Host, Port),
572	    megaco_connector_start(RH, PrelMid, SH, ControlPid),
573	    {ok, State#state{send_handle = SH,
574			      ctrl_pid    = ControlPid}};
575	Error ->
576	    e("udp connect (open) failed: "
577	      "~n   Error: ~p", [Error]),
578	    error({udp_connect_failed, Host, Opts0, Error})
579    catch
580        C:E:S ->
581	    e("udp connect (open) failed - catched: "
582	      "~n   C: ~p"
583	      "~n   E: ~p"
584	      "~n   S: ~p", [C, E, S]),
585            error({tcp_connect_failed, Host, Opts0, {C, E, S}})
586    end;
587
588handle_exec({connect, Host, Opts0, _MaybeRetry},
589     #state{transport_sup = Sup,
590	     recv_handle   = RH,
591	     port          = Port} = State)
592  when RH#megaco_receive_handle.send_mod =:= megaco_test_generic_transport ->
593    p("connect(generic) to ~p", [Host]),
594    PrelMid = preliminary_mid,
595    Opts = [{host, Host}, {port, Port}, {receive_handle, RH}|Opts0],
596    try megaco_test_generic_transport:connect(Sup, Opts) of
597	{ok, SH, ControlPid} ->
598	    d("connected(generic): ~p, ~p", [SH, ControlPid]),
599	    megaco_connector_start(RH, PrelMid, SH, ControlPid),
600	    {ok, State#state{send_handle = SH,
601			      ctrl_pid    = ControlPid}};
602	Error ->
603	    e("generic connect failed: "
604	      "~n   Error: ~p", [Error]),
605	    error({generic_connect_failed, Host, Opts0, Error})
606    catch
607        C:E:S ->
608	    e("generic connect failed - catched: "
609	      "~n   C: ~p"
610	      "~n   E: ~p"
611	      "~n   S: ~p", [C, E, S]),
612            error({generic_connect_failed, Host, Opts0, {C, E, S}})
613    end;
614
615handle_exec(megaco_connect, State) ->
616    p("expect megaco_connect"),
617    receive
618        {megaco_connect_result, {ok, CH}} ->
619            p("received successful megaco_connect: ~p", [CH]),
620            {ok, State#state{conn_handle = CH}};
621        {megaco_connect_result, Error} ->
622            p("received failed megaco_connect: ~p", [Error]),
623            #state{result = AccRes} = State,
624            {ok, State#state{result = [Error|AccRes]}}
625    end;
626
627handle_exec({megaco_connect, Mid},
628	    #state{recv_handle = RH,
629		   send_handle = SH,
630		   ctrl_pid    = ControlPid} = State) ->
631    p("megaco connect: ~p", [Mid]),
632    megaco_connector_start(RH, Mid, SH, ControlPid),
633    {ok, State};
634
635handle_exec({megaco_user_info, Tag}, #state{mid = Mid, result = AccRes} = State)
636  when Mid /= undefined ->
637    p("megaco user-info: ~w", [Tag]),
638    Val = (catch megaco:user_info(Mid, Tag)),
639    d("megaco_user_info: ~p", [Val]),
640    {ok, State#state{result = [Val|AccRes]}};
641
642handle_exec({megaco_update_user_info, Tag, Val}, #state{mid = Mid} = State)
643  when Mid /= undefined ->
644    p("update megaco user-info: ~w -> ~p", [Tag, Val]),
645    ok = megaco:update_user_info(Mid, Tag, Val),
646    {ok, State};
647
648handle_exec({megaco_conn_info, Tag},
649     #state{conn_handle = CH, result = Res} = State)
650  when CH /= undefined ->
651    p("megaco conn-info: ~w", [Tag]),
652    Val = (catch megaco:conn_info(CH, Tag)),
653    d("megaco_conn_info: ~p", [Val]),
654    {ok, State#state{result = [Val|Res]}};
655
656handle_exec({megaco_update_conn_info, Tag, Val},
657     #state{conn_handle = CH} = State)
658  when CH /= undefined ->
659    p("update megaco conn-info: ~w -> ~p", [Tag, Val]),
660    try megaco:update_conn_info(CH, Tag, Val) of
661        ok ->
662            {ok, State};
663        Error ->
664            e("failed updating connection info: "
665              "~n      Tag:   ~p"
666              "~n      Val:   ~p"
667              "~n      CH:    ~p"
668              "~n      Error: ~p", [Tag, Val, CH, Error]),
669            error({failed_updating_conn_info, Tag, Val, Error})
670    catch
671        C:E:S ->
672            e("failed updating connection info: "
673              "~n      Tag: ~p"
674              "~n      Val: ~p"
675              "~n      CH:  ~p"
676              "~n      C:   ~p"
677              "~n      E:   ~p"
678              "~n      S:   ~p", [Tag, Val, CH, C, E, S]),
679            error({failed_updating_conn_info, Tag, Val, {C, E, S}})
680    end;
681
682handle_exec(megaco_info, #state{result = AccRes} = State) ->
683    p("megaco info", []),
684    Val = (catch megaco:info()),
685    d("megaco_info: ~p", [Val]),
686    {ok, State#state{result = [Val|AccRes]}};
687
688handle_exec({megaco_system_info, Tag, Verify},
689            #state{result = AccRes} = State) ->
690    p("megaco system-info: ~w", [Tag]),
691    Val = (catch megaco:system_info(Tag)),
692    d("megaco system-info: ~p", [Val]),
693    try Verify(Val) of
694	ok ->
695	    {ok, State#state{result = [Val|AccRes]}};
696	Error ->
697            e("verification failed: "
698              "~n      Error: ~p", [Error]),
699	    {error, State#state{result = [Error|AccRes]}}
700    catch
701        C:E:S ->
702            e("verification failed - catched: "
703              "~n      C: ~p"
704              "~n      E: ~p"
705              "~n      S: ~p", [C, E, S]),
706            {error, State#state{result = [{catched, {C, E, S}}|AccRes]}}
707    end;
708
709%% This is either a MG or a MGC which is only connected to one MG
710handle_exec({megaco_call, ARs, Opts}, #state{conn_handle = CH} = State)
711  when CH /= undefined ->
712    p("megaco_call: "
713      "~n      CH:   ~p"
714      "~n      ARs:  ~p"
715      "~n      Opts: ~p", [CH, ARs, Opts]),
716    {_PV, UserReply} = megaco:call(CH, ARs, Opts),
717    d("megaco_call -> UserReply: ~n~p", [UserReply]),
718    {ok, State};
719
720handle_exec({megaco_call, RemoteMid, ARs, Opts}, #state{mid = Mid} = State) ->
721    p("megaco_call: ~p", [RemoteMid]),
722    %% First we have to find the CH for this Mid
723    Conns = megaco:user_info(Mid, connections),
724    {value, {_, CH}} =
725        lists:keysearch(RemoteMid, #megaco_conn_handle.remote_mid, Conns),
726    p("megaco_call: "
727      "~n      CH:   ~p"
728      "~n      ARs:  ~p"
729      "~n      Opts: ~p", [CH, ARs, Opts]),
730    {_PV, UserReply} = megaco:call(CH, ARs, Opts),
731    d("megaco_call -> UserReply: ~n~p", [UserReply]),
732    {ok, State};
733
734%% This is either a MG or a MGC which is only connected to one MG
735handle_exec({megaco_cast, ARs, Opts}, #state{conn_handle = CH,
736                                             result      = AccRes} = State)
737  when CH =/= undefined ->
738    p("megaco_cast: "
739      "~n      CH:  ~p"
740      "~n      ARs: ~p", [CH, ARs]),
741    try megaco:cast(CH, ARs, Opts) of
742        ok ->
743            p("megaco cast ok"),
744            {ok, State};
745        Error ->
746            e("failed sending (cast) message: "
747              "~n      Error: ~p", [Error]),
748            {error, State#state{result = [Error|AccRes]}}
749    catch
750        C:E:S ->
751            e("failed sending (cast) message - catched: "
752              "~n      C: ~p"
753              "~n      E: ~p"
754              "~n      S: ~p", [C, E, S]),
755            {error, State#state{result = [{catched, {C, E, S}}|AccRes]}}
756    end;
757
758handle_exec({megaco_cast, RemoteMid, ARs, Opts},
759            #state{mid    = Mid,
760                   result = AccRes} = State) ->
761    p("megaco_cast with ~p", [RemoteMid]),
762    %% First we have to find the CH for this Mid
763    Conns = megaco:user_info(Mid, connections),
764    {value, {_, CH}} =
765        lists:keysearch(RemoteMid, #megaco_conn_handle.remote_mid, Conns),
766    p("megaco_cast: "
767      "~n      CH:   ~p"
768      "~n      ARs:  ~p"
769      "~n      Opts: ~p", [CH, ARs, Opts]),
770    case megaco:cast(CH, ARs, Opts) of
771        ok ->
772            {ok, State};
773        Error ->
774            e("failed sending (cast) message: "
775              "~n      ~p", [Error]),
776            {error, State#state{result = [Error|AccRes]}}
777    end;
778
779%% Nothing shall happen for atleast Timeout time
780handle_exec({megaco_callback, nocall, Timeout}, State) ->
781    p("expect no megaco_callback for ~w", [Timeout]),
782    receive
783        {handle_megaco_callback, Type, Msg, Pid} ->
784            e("received unexpected megaco callback: ~n~p", [Msg]),
785            #state{result = AccRes} = State,
786            Err = {unexpected_callback, Type, Msg, Pid},
787            {error, State#state{result = [Err|AccRes]}}
788    after Timeout ->
789            p("got no callback (~p) as expected", [Timeout]),
790            {ok, State}
791    end;
792
793handle_exec({megaco_callback, Tag, Verify}, State) when is_function(Verify) ->
794    p("expect megaco_callback ~w", [Tag]),
795    receive
796        {handle_megaco_callback, Type, Msg, Pid} ->
797            d("received megaco callback:"
798              "~n      ~p", [Msg]),
799            try Verify(Msg) of
800                {VRes, Res, Reply} ->
801                    d("megaco_callback [~w] ~w", [Tag, VRes]),
802                    handle_megaco_callback_reply(Pid, Type, Reply),
803                    validate(VRes, Tag, Res, State);
804                {VRes, Delay, Res, Reply} ->
805                    d("megaco_callback [~w] ~w, ~w", [Tag,Delay,VRes]),
806                    handle_megaco_callback_reply(Pid, Type, Delay, Reply),
807                    validate(VRes, Tag, Res, State)
808            catch
809                C:E:S ->
810                    e("megaco callback - verification failed - catched: "
811                      "~n      C: ~p"
812                      "~n      E: ~p"
813                      "~n      S: ~p", [C, E, S]),
814                    error({megaco_callback_verification_failed, Tag, {C, E, S}})
815            end
816    end;
817
818handle_exec({megaco_callback, Tag, {VMod, VFunc, VArgs}}, State)
819  when is_atom(VMod) andalso is_atom(VFunc) andalso is_list(VArgs) ->
820    p("expect megaco_callback ~w", [Tag]),
821    receive
822        {handle_megaco_callback, Type, Msg, Pid} ->
823            d("received megaco callback: ~n~p"
824              "~n   VMod:  ~w"
825              "~n   VFunc: ~w"
826              "~n   VArgs: ~p", [Msg, VMod, VFunc, VArgs]),
827            try apply(VMod, VFunc, [Msg|VArgs]) of
828                {VRes, Res, Reply} ->
829                    d("megaco_callback [~w] ~w",[Tag, VRes]),
830                    handle_megaco_callback_reply(Pid, Type, Reply),
831                    validate(VRes, Tag, Res, State);
832                {VRes, Delay, Res, Reply} ->
833                    d("megaco_callback [~w] ~w, ~w",[Tag,Delay,VRes]),
834                    handle_megaco_callback_reply(Pid, Type, Delay, Reply),
835                    validate(VRes, Tag, Res, State)
836            catch
837                C:E:S ->
838                    e("megaco callback - verification failed - catched: "
839                      "~n      C: ~p"
840                      "~n      E: ~p"
841                      "~n      S: ~p", [C, E, S]),
842                    error({megaco_callback_verification_failed, Tag, {C, E, S}})
843            end
844    end;
845
846handle_exec({megaco_callback, Tag, Verify, Timeout},
847            #state{result = AccRes} = State)
848  when (is_function(Verify) andalso
849	(is_integer(Timeout) andalso (Timeout > 0))) ->
850    p("expect megaco_callback ~w (with ~w)", [Tag, Timeout]),
851    receive
852        {handle_megaco_callback, Type, Msg, Pid} ->
853            d("received megaco callback: ~n~p", [Msg]),
854            try Verify(Msg) of
855                {VRes, Res, Reply} ->
856                    d("megaco_callback [~w] ~w",[Tag,VRes]),
857                    handle_megaco_callback_reply(Pid, Type, Reply),
858                    validate(VRes, Tag, Res, State);
859                {VRes, Delay, Res, Reply} ->
860                    d("megaco_callback [~w] ~w, ~w",[Tag,Delay,VRes]),
861                    handle_megaco_callback_reply(Pid, Type, Delay, Reply),
862                    validate(VRes, Tag, Res, State)
863            catch
864                C:E:S ->
865                    e("megaco callback - verification failed - catched: "
866                      "~n      C: ~p"
867                      "~n      E: ~p"
868                      "~n      S: ~p", [C, E, S]),
869                    error({megaco_callback_verification_failed, Tag, {C, E, S}})
870            end
871    after Timeout ->
872            e("megaco_callback ~w timeout", [Tag]),
873            Err = {callback_timeout, Tag, Timeout},
874            {error, State#state{result = [Err|AccRes]}}
875    end;
876
877handle_exec({megaco_callback, Verifiers}, State) ->
878    p("expect megaco_callback(s)"),
879    megaco_callback_verify(Verifiers, State);
880
881handle_exec({megaco_cancel, Reason}, #state{conn_handle = CH} = State) ->
882    p("megaco_cancel: ~w", [Reason]),
883    case megaco:cancel(CH, Reason) of
884        ok ->
885            {ok, State};
886        Error ->
887            e("failed cancel: ~n~p", [Error]),
888            #state{result = AccRes} = State,
889            {error, State#state{result = [Error|AccRes]}}
890    end;
891
892handle_exec({trigger, Trigger}, State) when is_function(Trigger) ->
893    p("trigger"),
894    (catch Trigger()),
895    {ok, State};
896handle_exec({trigger, Desc, Trigger}, State) when is_function(Trigger) ->
897    p("trigger: ~s", [Desc]),
898    (catch Trigger()),
899    {ok, State};
900
901handle_exec({sleep, To}, State) ->
902    p("sleep ~p", [To]),
903    megaco_test_generator:sleep(To),
904    {ok, State};
905
906handle_exec(BadInstruction, _State) ->
907    error({invalid_instruction, BadInstruction}).
908
909
910%% --- cleanup ---
911
912megaco_cleanup(#state{mid = Mid}) ->
913    Close = fun(CH) -> do_megaco_cleanup(CH) end,
914    Conns =
915	case (catch megaco:user_info(Mid, connections)) of
916	    Connections when is_list(Connections) ->
917		Connections;
918	    _ ->
919		[]
920	end,
921    lists:foreach(Close, Conns).
922
923do_megaco_cleanup(CH) ->
924    case (catch do_megaco_cleanup2(CH)) of
925        ok ->
926            ok;
927        {'EXIT', {no_such_connection, _}} ->
928            ok;
929        {'EXIT', Reason} ->
930            exit(Reason)
931    end.
932
933do_megaco_cleanup2(CH) ->
934    d("do_megaco_cleanup2 -> entry with"
935      "~n   CH: ~p", [CH]),
936    Reason     = {stopped_by_user,self()},
937    Pid        = megaco:conn_info(CH, control_pid),
938    SendMod    = megaco:conn_info(CH, send_mod),
939    SendHandle = megaco:conn_info(CH, send_handle),
940    d("do_megaco_cleanup2 -> disconnect"),
941    megaco:disconnect(CH, Reason),
942    d("do_megaco_cleanup2 -> disconnected, now cancel"),
943    megaco:cancel(CH, Reason),
944    d("do_megaco_cleanup2 -> canceled, now close"),
945    case SendMod of
946        megaco_tcp -> (catch megaco_tcp:close(SendHandle));
947        megaco_udp -> (catch megaco_udp:close(SendHandle));
948        SendMod    -> exit(Pid, Reason)
949    end,
950    ok.
951
952
953%% --- connector ---
954
955megaco_connector_start(RH, PrelMid, SH, ControlPid) ->
956    Self = self(),
957    Fun  = fun() -> megaco_connect(RH, PrelMid, SH, ControlPid, Self) end,
958    erlang:spawn_opt(Fun, [link]).
959
960megaco_connect(RH, PrelMid, SH, ControlPid, Parent) ->
961    Result = megaco:connect(RH, PrelMid, SH, ControlPid),
962    Parent ! {megaco_connect_result, Result},
963    exit(normal).
964
965
966%% --- megaco callback verify ---
967
968%% This is used when a number of callback's is expected, but where
969%% the specific order is unknown.
970megaco_callback_verify([], State) ->
971    d("megaco_callback_verify -> done"),
972    {ok, State};
973megaco_callback_verify(Verifiers0, State0) ->
974    d("megaco_callback_verify -> entry when"
975      "~n   length(Verifiers0): ~w", [length(Verifiers0)]),
976    receive
977        {handle_megaco_callback, Type, Msg, Pid} ->
978            d("megaco_callback_verify -> received megaco callback: ~w"
979	      "~n   Msg: ~p", [Type, Msg]),
980            case megaco_callback_verify(Verifiers0, Type, Msg, Pid, State0) of
981                {ok, Verifiers, State} ->
982                    megaco_callback_verify(Verifiers, State);
983                Error ->
984                    Error
985            end
986    end.
987
988megaco_callback_verify(Verifiers0, Type, Msg, Pid, State0) ->
989    d("megaco_callback_verify -> entry"),
990    Tag = element(1, Msg),
991    d("megaco_callback_verify -> Tag: ~w", [Tag]),
992    case lists:keysearch(Tag, 1, Verifiers0) of
993        {value, {Tag, N, Verify}} when (N > 0) andalso is_function(Verify) ->
994            d("megaco_callback_verify -> N: ~w",[N]),
995            case Verify(Msg) of
996                {VRes, Res, Reply} ->
997                    d("megaco_callback_verify -> VRes: ~w",[VRes]),
998                    handle_megaco_callback_reply(Pid, Type, Reply),
999                    case validate(VRes, Tag, Res, State0) of
1000                        {error, _} = EState ->
1001                            e("megaco_callback_verify -> (1) error", []),
1002                            throw(EState);
1003                        {ok, State} when N > 1 ->
1004                            d("megaco_callback_verify -> (1) validated"),
1005                            Rec = {Tag, N-1, Verify},
1006                            Verifiers =
1007                                lists:keyreplace(Tag, 1, Verifiers0, Rec),
1008                            {ok, Verifiers, State};
1009                        {ok, State} ->
1010                            d("megaco_callback_verify -> (2) validated"),
1011                            Verifiers = lists:keydelete(Tag, 1, Verifiers0),
1012                            {ok, Verifiers, State}
1013                    end;
1014                {VRes, Delay, Res, Reply} ->
1015                    d("megaco_callback_verify -> Delay: ~w, VRes: ~w",
1016                      [Delay,VRes]),
1017                    handle_megaco_callback_reply(Pid, Type, Delay, Reply),
1018                    case validate(VRes, Tag, Res, State0) of
1019                        {error, _} = EState ->
1020                            e("megaco_callback_verify -> (2) error", []),
1021                            throw(EState);
1022                        {ok, State} when N > 1 ->
1023                            d("megaco_callback_verify -> (3) validated"),
1024                            Rec = {Tag, N-1, Verify},
1025                            Verifiers =
1026                                lists:keyreplace(Tag, 1, Verifiers0, Rec),
1027                            {ok, Verifiers, State};
1028                        {ok, State} ->
1029                            d("megaco_callback_verify -> (4) validated"),
1030                            Verifiers = lists:keydelete(Tag, 1, Verifiers0),
1031                            {ok, Verifiers, State}
1032                    end
1033            end;
1034        false ->
1035            e("megaco_callback_verify -> no such tag ~w~n~p",
1036              [Tag, Verifiers0]),
1037            #state{result = Res} = State0,
1038            State = State0#state{result = [{Type, error, Msg}|Res]},
1039            error(State)
1040    end.
1041
1042
1043%% --- validate verify result ---
1044
1045validate(ok, handle_connect = Tag, CH, #state{result = Acc} = S) ->
1046    {ok, S#state{conn_handle = CH, result = [{Tag, ok, CH}|Acc]}};
1047validate(ok, Tag, Res, #state{result = Acc} = S) ->
1048    {ok, S#state{result = [{Tag, ok, Res}|Acc]}};
1049validate(error, Tag, Res, #state{result = Acc} = S) ->
1050    {error, S#state{result = [{Tag, error, Res}|Acc]}}.
1051
1052
1053%% ----- termination -----
1054
1055terminate(normal, #state{result = Result} = _State) ->
1056    d("terminate -> entry when normal with"
1057      "~n   Result: ~p", [Result]),
1058    %% megaco_cleanup(State),
1059    {ok, Result};
1060
1061terminate(Reason, #state{result = Result} = State) ->
1062    d("terminate -> entry with"
1063      "~n   Reason: ~p"
1064      "~n   Result: ~p", [Reason, Result]),
1065    megaco_cleanup(State),
1066    {error, {Reason, Result}}.
1067
1068
1069%%----------------------------------------------------------------------
1070
1071handle_exec_listen_tcp(Sup, Opts, MaybeRetry) ->
1072    handle_exec_listen_tcp(Sup, Opts, MaybeRetry, noError).
1073
1074handle_exec_listen_tcp(Sup, Opts, MaybeRetry, Error0) ->
1075    case (catch megaco_tcp:listen(Sup, Opts)) of
1076        ok ->
1077            ok;
1078	Error1 ->
1079	    case (catch MaybeRetry(Error1, Error0)) of
1080		{true, Error2} ->
1081		    handle_exec_listen_tcp(Sup, Opts, MaybeRetry, Error2);
1082		{false, Error3} ->
1083		    {error, Error3}
1084	    end
1085    end.
1086
1087
1088handle_exec_connect_tcp(Host, Opts, Sup, MaybeRetry)
1089  when is_function(MaybeRetry) ->
1090    handle_exec_connect_tcp(Host, Opts, Sup, MaybeRetry, noError).
1091
1092handle_exec_connect_tcp(Host, Opts, Sup, MaybeRetry, Error0) ->
1093    case (catch megaco_tcp:connect(Sup, Opts)) of
1094	{ok, SH, ControlPid} ->
1095	    d("tcp connected: ~p, ~p", [SH, ControlPid]),
1096	    {ok, SH, ControlPid};
1097	Error1 ->
1098	    case (catch MaybeRetry(Error1, Error0)) of
1099		{true, Error2} ->
1100		    handle_exec_connect_tcp(Host, Opts, Sup,
1101					    MaybeRetry, Error2);
1102		{false, Error3} ->
1103		    {error, Error3}
1104	    end
1105    end.
1106
1107
1108
1109%%----------------------------------------------------------------------
1110%% megaco_user callback functions
1111%%----------------------------------------------------------------------
1112
1113handle_connect(CH, PV, P) ->
1114    Req = {handle_connect, CH, PV},
1115    handle_megaco_callback_call(P, Req).
1116
1117handle_connect(CH, PV, Extra, P) ->
1118    Req = {handle_connect, CH, PV, Extra},
1119    handle_megaco_callback_call(P, Req).
1120
1121handle_disconnect(CH, PV, R, P) ->
1122    Msg   = {handle_disconnect, CH, PV, R},
1123    Reply = ok,
1124    handle_megaco_callback_cast(P, Msg, Reply).
1125
1126handle_syntax_error(RH, PV, ED, P) ->
1127    Req = {handle_syntax_error, RH, PV, ED},
1128    handle_megaco_callback_call(P, Req).
1129
1130handle_syntax_error(RH, PV, ED, Extra, P) ->
1131    Req = {handle_syntax_error, RH, PV, ED, Extra},
1132    handle_megaco_callback_call(P, Req).
1133
1134handle_message_error(CH, PV, ED, P) ->
1135    Msg   = {handle_message_error, CH, PV, ED},
1136    Reply = ok,
1137    handle_megaco_callback_cast(P, Msg, Reply).
1138
1139handle_message_error(CH, PV, ED, Extra, P) ->
1140    Msg   = {handle_message_error, CH, PV, ED, Extra},
1141    Reply = ok,
1142    handle_megaco_callback_cast(P, Msg, Reply).
1143
1144handle_trans_request(CH, PV, AR, P) ->
1145    Req = {handle_trans_request, CH, PV, AR},
1146    handle_megaco_callback_call(P, Req).
1147
1148handle_trans_request(CH, PV, AR, Extra, P) ->
1149    Req = {handle_trans_request, CH, PV, AR, Extra},
1150    handle_megaco_callback_call(P, Req).
1151
1152handle_trans_long_request(CH, PV, RD, P) ->
1153    Req = {handle_trans_long_request, CH, PV, RD},
1154    handle_megaco_callback_call(P, Req).
1155
1156handle_trans_long_request(CH, PV, RD, Extra, P) ->
1157    Req = {handle_trans_long_request, CH, PV, RD, Extra},
1158    handle_megaco_callback_call(P, Req).
1159
1160handle_trans_reply(CH, PV, AR, RD, P) ->
1161    Msg = {handle_trans_reply, CH, PV, AR, RD},
1162    Reply = ok,
1163    handle_megaco_callback_cast(P, Msg, Reply).
1164
1165handle_trans_reply(CH, PV, AR, RD, Extra, P) ->
1166    Msg = {handle_trans_reply, CH, PV, AR, RD, Extra},
1167    Reply = ok,
1168    handle_megaco_callback_cast(P, Msg, Reply).
1169
1170handle_trans_ack(CH, PV, AS, AD, P) ->
1171    Msg = {handle_trans_ack, CH, PV, AS, AD},
1172    Reply = ok,
1173    handle_megaco_callback_cast(P, Msg, Reply).
1174
1175handle_trans_ack(CH, PV, AS, AD, Extra, P) ->
1176    Msg = {handle_trans_ack, CH, PV, AS, AD, Extra},
1177    Reply = ok,
1178    handle_megaco_callback_cast(P, Msg, Reply).
1179
1180handle_unexpected_trans(CH, PV, T, P) ->
1181    Msg = {handle_unexpected_trans, CH, PV, T},
1182    Reply = ok,
1183    handle_megaco_callback_cast(P, Msg, Reply).
1184
1185handle_unexpected_trans(CH, PV, T, Extra, P) ->
1186    Msg = {handle_unexpected_trans, CH, PV, T, Extra},
1187    Reply = ok,
1188    handle_megaco_callback_cast(P, Msg, Reply).
1189
1190handle_trans_request_abort(RH, PV, TransNo, Pid, P) ->
1191    Msg = {handle_trans_request_abort, RH, PV, TransNo, Pid},
1192    Reply = ok,
1193    handle_megaco_callback_cast(P, Msg, Reply).
1194
1195handle_trans_request_abort(RH, PV, TransNo, Pid, Extra, P) ->
1196    Msg = {handle_trans_request_abort, RH, PV, TransNo, Pid, Extra},
1197    Reply = ok,
1198    handle_megaco_callback_cast(P, Msg, Reply).
1199
1200handle_megaco_callback_cast(P, Msg, Reply) ->
1201    d("handle_megaco_callback_cast -> entry with Msg: ~n~p", [Msg]),
1202    P ! {handle_megaco_callback, cast, Msg, self()},
1203    Reply.
1204
1205handle_megaco_callback_call(P, Msg) ->
1206    d("handle_megaco_callback_call -> entry with"
1207      "~n   P:   ~p"
1208      "~n   Msg: ~p", [P, Msg]),
1209    P ! {handle_megaco_callback, call, Msg, self()},
1210    receive
1211        {handle_megaco_callback_reply, Reply} ->
1212            d("handle_megaco_callback_call -> received reply: ~n~p", [Reply]),
1213            Reply;
1214        {handle_megaco_callback_reply, Delay, Reply} when is_integer(Delay) ->
1215            d("handle_megaco_callback_call -> "
1216              "received reply [~w]: "
1217              "~n   ~p", [Delay, Reply]),
1218            sleep(Delay),
1219            d("handle_megaco_callback_call -> deliver reply after delay [~w]",
1220              [Delay]),
1221            Reply;
1222        {'EXIT', Pid, Reason} when (Pid =:= P) ->
1223            d("handle_megaco_callback_call -> "
1224              "received unexpected EXIT signal (from ~p): "
1225              "~n   Reason: ~p", [Pid, Reason]),
1226            exit({unexpected_EXIT_signal, Pid, Reason});
1227        {'EXIT', SomePid, SomeReason} ->
1228            d("handle_megaco_callback_call -> "
1229              "received unexpected EXIT signal from unknown process: "
1230              "~n   Pid:    ~p"
1231              "~n   Reason: ~p", [SomePid, SomeReason]),
1232            exit({unexpected_EXIT_signal, SomePid, SomeReason})
1233    end.
1234
1235
1236handle_megaco_callback_reply(P, call, Reply) ->
1237    P ! {handle_megaco_callback_reply, Reply};
1238handle_megaco_callback_reply(_, _, _) ->
1239    ok.
1240
1241handle_megaco_callback_reply(P, call, Delay, Reply) ->
1242    P ! {handle_megaco_callback_reply, Delay, Reply};
1243handle_megaco_callback_reply(_, _, _, _) ->
1244    ok.
1245
1246
1247%%----------------------------------------------------------------------
1248%% internal utility functions
1249%%----------------------------------------------------------------------
1250
1251random_init() ->
1252    ok.
1253
1254random(N) ->
1255    rand:uniform(N).
1256
1257
1258get_config(Key, Opts) ->
1259    {value, {Key, Val}} = lists:keysearch(Key, 1, Opts),
1260    Val.
1261
1262sleep(X) -> megaco_test_generator:sleep(X).
1263
1264d(F)    -> megaco_test_generator:debug(F).
1265d(F, A) -> megaco_test_generator:debug(F, A).
1266
1267e(F, A) -> megaco_test_generator:error(F, A).
1268
1269p(F      ) -> p("", F, []).
1270p(F,    A) -> p("", F, A).
1271p(P, F, A) -> megaco_test_generator:print(P,    F, A).
1272
1273error(Reason) ->
1274    throw({error, Reason}).
1275
1276