1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2003-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: Verify the application specifics of the Megaco application
24%%----------------------------------------------------------------------
25-module(megaco_mreq_SUITE).
26
27-export([
28 	 suite/0, all/0, groups/0,
29         init_per_suite/1, end_per_suite/1,
30         init_per_group/2, end_per_group/2,
31         init_per_testcase/2, end_per_testcase/2,
32
33         req_and_rep/1,
34         req_and_pending/1,
35         req_and_cancel/1
36
37        ]).
38
39-include_lib("common_test/include/ct.hrl").
40-include_lib("megaco/include/megaco.hrl").
41-include_lib("megaco/include/megaco_message_v1.hrl").
42-include("megaco_test_lib.hrl").
43
44-define(TEST_VERBOSITY, debug).
45-define(MGC_VERBOSITY,  debug).
46-define(MG_VERBOSITY,   debug).
47
48-define(LOAD_COUNTER_START, 10).
49-define(A4444, ["11111111", "00000000", "00000000"]).
50-define(A4445, ["11111111", "00000000", "11111111"]).
51-define(A5555, ["11111111", "11111111", "00000000"]).
52-define(A5556, ["11111111", "11111111", "11111111"]).
53
54-define(MGC_START(Pid, Mid, ET, Conf, Verb),
55	megaco_test_mgc:start(Pid, Mid, ET, Conf, Verb)).
56-define(MGC_STOP(Pid), megaco_test_mgc:stop(Pid)).
57-define(MGC_GET_STATS(Pid, No), megaco_test_mgc:get_stats(Pid, No)).
58-define(MGC_RESET_STATS(Pid), megaco_test_mgc:reset_stats(Pid)).
59-define(MGC_REQ_DISC(Pid,To), megaco_test_mgc:request_discard(Pid,To)).
60-define(MGC_REQ_PEND(Pid,To), megaco_test_mgc:request_pending(Pid,To)).
61-define(MGC_REQ_HAND(Pid), megaco_test_mgc:request_handle(Pid)).
62
63-define(MG_START(Pid, Mid, Enc, Transp, Conf, Verb),
64	megaco_test_mg:start(Pid, Mid, Enc, Transp, Conf, Verb)).
65-define(MG_STOP(Pid),                megaco_test_mg:stop(Pid)).
66-define(MG_GET_STATS(Pid),           megaco_test_mg:get_stats(Pid)).
67-define(MG_RESET_STATS(Pid),         megaco_test_mg:reset_stats(Pid)).
68-define(MG_SERV_CHANGE(Pid),         megaco_test_mg:service_change(Pid)).
69-define(MG_NOTIF_RAR(Pid),           megaco_test_mg:notify_request_and_reply(Pid)).
70-define(MG_NOTIF_REQ(Pid),           megaco_test_mg:notify_request(Pid)).
71-define(MG_NOTIF_AR(Pid),            megaco_test_mg:await_notify_reply(Pid)).
72-define(MG_CANCEL(Pid,R),            megaco_test_mg:cancel_request(Pid,R)).
73-define(MG_APPLY_LOAD(Pid,CntStart), megaco_test_mg:apply_load(Pid,CntStart)).
74
75
76%%======================================================================
77%% Common Test interface functions
78%%======================================================================
79
80suite() ->
81    [{ct_hooks, [ts_install_cth]}].
82
83all() ->
84    %% This is a temporary messure to ensure that we can
85    %% test the socket backend without effecting *all*
86    %% applications on *all* machines.
87    %% This flag is set only for *one* host.
88    case ?TEST_INET_BACKENDS() of
89        true ->
90            [
91             {group, inet_backend_default},
92             {group, inet_backend_inet},
93             {group, inet_backend_socket}
94            ];
95        _ ->
96            [
97             {group, inet_backend_default}
98            ]
99    end.
100
101groups() ->
102    [
103     {inet_backend_default, [], inet_backend_default_cases()},
104     {inet_backend_inet,    [], inet_backend_inet_cases()},
105     {inet_backend_socket,  [], inet_backend_socket_cases()},
106
107     {all,                  [], all_cases()}
108    ].
109
110inet_backend_default_cases() ->
111    [{all, [], all_cases()}].
112
113inet_backend_inet_cases() ->
114    [{all, [], all_cases()}].
115
116inet_backend_socket_cases() ->
117    [{all, [], all_cases()}].
118
119all_cases() ->
120    [
121     req_and_rep,
122     req_and_pending,
123     req_and_cancel
124    ].
125
126
127
128%%
129%% -----
130%%
131
132init_per_suite(suite) ->
133    [];
134init_per_suite(doc) ->
135    [];
136init_per_suite(Config0) when is_list(Config0) ->
137
138    ?ANNOUNCE_SUITE_INIT(),
139
140    p("init_per_suite -> entry with"
141      "~n      Config: ~p"
142      "~n      Nodes:  ~p", [Config0, erlang:nodes()]),
143
144    case ?LIB:init_per_suite(Config0) of
145        {skip, _} = SKIP ->
146            SKIP;
147
148        Config1 when is_list(Config1) ->
149
150            %% We need a (local) monitor on this node also
151            megaco_test_sys_monitor:start(),
152
153            p("init_per_suite -> end when"
154              "~n      Config: ~p"
155              "~n      Nodes:  ~p", [Config1, erlang:nodes()]),
156
157            Config1
158    end.
159
160end_per_suite(suite) -> [];
161end_per_suite(doc) -> [];
162end_per_suite(Config0) when is_list(Config0) ->
163
164    p("end_per_suite -> entry with"
165      "~n      Config: ~p"
166      "~n      Nodes:  ~p", [Config0, erlang:nodes()]),
167
168    megaco_test_sys_monitor:stop(),
169    Config1 = ?LIB:end_per_suite(Config0),
170
171    p("end_per_suite -> end when"
172      "~n      Nodes:  ~p", [erlang:nodes()]),
173
174    Config1.
175
176
177%%
178%% -----
179%%
180
181init_per_group(inet_backend_default = Group, Config) ->
182    ?ANNOUNCE_GROUP_INIT(Group),
183    [{socket_create_opts, []} | Config];
184init_per_group(inet_backend_inet = Group, Config) ->
185    ?ANNOUNCE_GROUP_INIT(Group),
186    case ?EXPLICIT_INET_BACKEND() of
187        true ->
188            %% The environment trumps us,
189            %% so only the default group should be run!
190            {skip, "explicit inet backend"};
191        false ->
192            [{socket_create_opts, [{inet_backend, inet}]} | Config]
193    end;
194init_per_group(inet_backend_socket = Group, Config) ->
195    ?ANNOUNCE_GROUP_INIT(Group),
196    case ?EXPLICIT_INET_BACKEND() of
197        true ->
198            %% The environment trumps us,
199            %% so only the default group should be run!
200            {skip, "explicit inet backend"};
201        false ->
202            [{socket_create_opts, [{inet_backend, socket}]} | Config]
203    end;
204init_per_group(Group, Config) ->
205    ?ANNOUNCE_GROUP_INIT(Group),
206    Config.
207
208end_per_group(_Group, Config) ->
209    Config.
210
211
212
213init_per_testcase(Case, Config) ->
214    process_flag(trap_exit, true),
215
216    p("init_per_suite -> entry with"
217      "~n      Config: ~p"
218      "~n      Nodes:  ~p", [Config, erlang:nodes()]),
219
220    megaco_test_global_sys_monitor:reset_events(),
221    megaco_test_lib:init_per_testcase(Case, Config).
222
223end_per_testcase(Case, Config) ->
224    process_flag(trap_exit, false),
225
226    p("end_per_suite -> entry with"
227      "~n      Config: ~p"
228      "~n      Nodes:  ~p", [Config, erlang:nodes()]),
229
230    p("system events during test: "
231      "~n   ~p", [megaco_test_global_sys_monitor:events()]),
232
233    megaco_test_lib:end_per_testcase(Case, Config).
234
235
236
237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
238
239req_and_rep(suite) ->
240    [];
241req_and_rep(doc) ->
242    [];
243req_and_rep(Config) when is_list(Config) ->
244    Pre = fun() ->
245                  MgcNode = make_node_name(mgc),
246                  Mg1Node = make_node_name(mg1),
247                  Mg2Node = make_node_name(mg2),
248                  Mg3Node = make_node_name(mg3),
249                  Mg4Node = make_node_name(mg4),
250                  d("try start nodes: "
251                    "~n      MgcNode: ~p"
252                    "~n      Mg1Node: ~p"
253                    "~n      Mg2Node: ~p"
254                    "~n      Mg3Node: ~p"
255                    "~n      Mg4Node: ~p",
256                    [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]),
257                  Nodes = [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node],
258                  ok    = ?START_NODES(Nodes),
259                  Nodes
260          end,
261    Case = fun(X) ->
262                   SCO = ?config(socket_create_opts, Config),
263                   do_req_and_rep(SCO, X)
264           end,
265    Post = fun(Nodes) ->
266                   d("stop nodes (in the reverse order):"
267                     "~n       ~p", [Nodes]),
268                   ?STOP_NODES(lists:reverse(Nodes))
269           end,
270    try_tc(req_and_rep, Pre, Case, Post).
271
272do_req_and_rep(SCO, [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]) ->
273    %% Start the MGC and MGs
274    i("start the MGC"),
275    ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}],
276    {ok, Mgc} =
277	?MGC_START(MgcNode, {deviceName, "ctrl"}, ET, SCO, ?MGC_VERBOSITY),
278
279    i("start and connect the MGs"),
280    MgConf0 = [{Mg1Node, "mg1", text,   tcp, ?MG_VERBOSITY},
281	       {Mg2Node, "mg2", text,   udp, ?MG_VERBOSITY},
282	       {Mg3Node, "mg3", binary, tcp, ?MG_VERBOSITY},
283	       {Mg4Node, "mg4", binary, udp, ?MG_VERBOSITY}],
284    MgConf = rar_connect_mg(SCO, MgConf0, []),
285
286    %% Collect the (initial) MGs statistics
287    Stats1 = rar_get_mg_stats(MgConf, []),
288    d("stats for the MGs: "
289      "~n      ~p", [Stats1]),
290
291    %% Collect and check the MGC statistics
292    i("collect and check the MGC stats"),
293    {ok, MgcStats1} = ?MGC_GET_STATS(Mgc, 1),
294    d("stats (1) for Mgc: "
295      "~n      ~p"
296      "~n", [MgcStats1]),
297
298
299    sleep(1000),
300
301
302    %% And apply some load
303    i("apply traffic load"),
304    ok = rar_apply_load(MgConf),
305
306    %% Await completion of load part and the collect traffic
307    i("await load completion"),
308    ok = rar_await_load_complete(MgConf),
309
310
311    sleep(1000),
312
313
314    i("collect the MGs statistics"),
315    Stats2 = rar_get_mg_stats(MgConf, []),
316    d("stats for MGs: "
317      "~n      ~p", [Stats2]),
318
319    i("collect the MGC statistics"),
320    {ok, MgcStats2} = ?MGC_GET_STATS(Mgc, 1),
321    d("stats (1) for Mgc: "
322      "~n      ~p"
323      "~n", [MgcStats2]),
324
325
326    sleep(1000),
327
328
329    %% Reset counters
330    i("reset the MGs statistics"),
331    rar_reset_mg_stats(MgConf),
332    Stats3 = rar_get_mg_stats(MgConf, []),
333    d("stats for the MGs: "
334      "~n      ~p", [Stats3]),
335
336    i("reset the MGC statistics"),
337    rar_reset_mgc_stats(Mgc),
338    {ok, MgcStats3} = ?MGC_GET_STATS(Mgc, 1),
339    d("stats (1) for Mgc: "
340      "~n      ~p"
341      "~n", [MgcStats3]),
342
343
344    sleep(1000),
345
346
347    %% Tell MGs to stop
348    i("stop the MGs"),
349    rar_stop_mg(MgConf),
350
351
352    sleep(1000),
353
354
355    %% Collect the statistics
356    i("collect the MGC statistics"),
357    {ok, MgcStats4} = ?MGC_GET_STATS(Mgc, 1),
358    d("stats (1) for Mgc: "
359      "~n      ~p", [MgcStats4]),
360    {ok, MgcStats5} = ?MGC_GET_STATS(Mgc, 2),
361    d("stats (2) for Mgc: "
362      "~n      ~p"
363      "~n", [MgcStats5]),
364
365    %% Tell Mgc to stop
366    i("stop the MGC"),
367    ?MGC_STOP(Mgc),
368
369    i("done", []),
370    ok.
371
372
373rar_connect_mg(_, [], Acc) ->
374    lists:reverse(Acc);
375rar_connect_mg(SCO, [{Node, Name, Coding, Trans, Verb}|Mg], Acc) ->
376    Pid = rar_connect_mg(SCO, Node, Name, Coding, Trans, Verb),
377    rar_connect_mg(SCO, Mg, [{Name, Pid}|Acc]).
378
379rar_connect_mg(SCO, Node, Name, Coding, Trans, Verb) ->
380    Mid = {deviceName, Name},
381    {ok, Pid} = ?MG_START(Node, Mid, Coding, Trans, SCO, Verb),
382
383    %% Ask the MGs to do a service change
384    Res = ?MG_SERV_CHANGE(Pid),
385    d("rar_connect_mg -> (~s) service change result: ~p", [Name, Res]),
386
387    Pid.
388
389
390rar_stop_mg(MGs) ->
391    [?MG_STOP(Pid) || {_Name, Pid} <- MGs].
392
393
394rar_get_mg_stats([], Acc) ->
395    lists:reverse(Acc);
396rar_get_mg_stats([{Name, Pid}|Mgs], Acc) ->
397    {ok, Stats} = ?MG_GET_STATS(Pid),
398    d("rar_get_mg_stats -> stats for ~s: "
399      "~n      ~p"
400      "~n", [Name, Stats]),
401    rar_get_mg_stats(Mgs, [{Name, Stats}|Acc]).
402
403
404rar_apply_load([]) ->
405    ok;
406rar_apply_load([{_, MG}|MGs]) ->
407    ?MG_APPLY_LOAD(MG,?LOAD_COUNTER_START),
408    rar_apply_load(MGs).
409
410
411rar_reset_mg_stats([]) ->
412    ok;
413rar_reset_mg_stats([{Name, Pid}|MGs]) ->
414    d("rar_reset_mg_stats -> resetting ~s", [Name]),
415    ?MG_RESET_STATS(Pid),
416    rar_reset_mg_stats(MGs).
417
418rar_reset_mgc_stats(Mgc) ->
419    d("rar_reset_mgc_stats -> resetting ~p", [Mgc]),
420    ?MGC_RESET_STATS(Mgc).
421
422
423rar_await_load_complete([]) ->
424    ok;
425rar_await_load_complete(MGs0) ->
426    receive
427	{load_complete, Pid} ->
428	    d("received load_complete from ~p", [Pid]),
429	    MGs1 = lists:keydelete(Pid, 2, MGs0),
430	    rar_await_load_complete(lists:delete(Pid, MGs1));
431	{'EXIT', Pid, Reason} ->
432	    e("exit signal from ~p: ~p", [Pid, Reason]),
433	    case lists:keymember(Pid, 2, MGs0) of
434		true ->
435		    exit({mg_exit, Pid, Reason});
436		false ->
437		    MGs1 = lists:keydelete(Pid, 2, MGs0),
438		    rar_await_load_complete(lists:delete(Pid, MGs1))
439	    end
440    end.
441
442
443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444
445req_and_pending(suite) ->
446    [];
447req_and_pending(doc) ->
448    [];
449req_and_pending(Config) when is_list(Config) ->
450    Pre = fun() ->
451                  MgcNode = make_node_name(mgc),
452                  MgNode  = make_node_name(mg),
453                  d("try starting nodes: "
454                    "~n      MgcNode: ~p"
455                    "~n      MgNode:  ~p", [MgcNode, MgNode]),
456                  Nodes = [MgcNode, MgNode],
457                  ok    = ?START_NODES(Nodes),
458                  Nodes
459          end,
460    Case = fun(X) ->
461                   SCO = ?config(socket_create_opts, Config),
462                   do_req_and_pending(SCO, X)
463           end,
464    Post = fun(Nodes) ->
465                   d("stop nodes (in the reverse order):"
466                     "~n       ~p", [Nodes]),
467                   ?STOP_NODES(lists:reverse(Nodes))
468           end,
469    try_tc(req_and_pending, Pre, Case, Post).
470
471do_req_and_pending(SCO, [MgcNode, MgNode]) ->
472
473    %% Start the MGC and MGs
474    i("try start the MGC"),
475    ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}],
476    {ok, Mgc} =
477	?MGC_START(MgcNode, {deviceName, "ctrl"}, ET, SCO, ?MGC_VERBOSITY),
478
479    i("try start the MG"),
480    {ok, Mg} =
481	?MG_START(MgNode, {deviceName, "mg"}, text, tcp, SCO, ?MG_VERBOSITY),
482
483    i("connect MG (to MFC)"),
484    Res1 = ?MG_SERV_CHANGE(Mg),
485    d("service change result: ~p", [Res1]),
486
487    sleep(1000),
488
489    i("[MGC] change request action to pending"),
490    {ok, _} = ?MGC_REQ_PEND(Mgc, 3500),
491
492    i("[MG] send notify request"),
493    {ok, Res2} = ?MG_NOTIF_RAR(Mg),
494    d("notify reply: ~p", [Res2]),
495
496    sleep(1000),
497
498    %% Tell MG to stop
499    i("stop the MG"),
500    ?MG_STOP(Mg),
501
502    %% Tell Mgc to stop
503    i("stop the MGC"),
504    ?MGC_STOP(Mgc),
505
506    i("done", []),
507    ok.
508
509
510
511%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512
513req_and_cancel(suite) ->
514    [];
515req_and_cancel(doc) ->
516    [];
517req_and_cancel(Config) when is_list(Config) ->
518    Pre = fun() ->
519                  MgcNode = make_node_name(mgc),
520                  MgNode  = make_node_name(mg),
521                  d("try start nodes: "
522                    "~n      MgcNode: ~p"
523                    "~n      MgNode:  ~p",
524                    [MgcNode, MgNode]),
525                  Nodes = [MgcNode, MgNode],
526                  ok    = ?START_NODES(Nodes),
527                  Nodes
528          end,
529    Case = fun(X) ->
530                   SCO = ?config(socket_create_opts, Config),
531                   do_req_and_cancel(SCO, X)
532           end,
533    Post = fun(Nodes) ->
534                   d("stop nodes (in the reverse order):"
535                     "~n       ~p", [Nodes]),
536                   ?STOP_NODES(lists:reverse(Nodes))
537           end,
538    try_tc(req_and_cancel, Pre, Case, Post).
539
540do_req_and_cancel(SCO, [MgcNode, MgNode]) ->
541    %% Start the MGC and MGs
542    i("start the MGC"),
543    ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}],
544    {ok, Mgc} =
545	?MGC_START(MgcNode, {deviceName, "ctrl"}, ET, SCO, ?MGC_VERBOSITY),
546
547    i("start the MG"),
548    {ok, Mg} =
549	?MG_START(MgNode, {deviceName, "mg"}, text, tcp, SCO, ?MG_VERBOSITY),
550
551    i("connect the MG"),
552    Res1 = ?MG_SERV_CHANGE(Mg),
553    d("service change result: ~p", [Res1]),
554
555
556    sleep(1000),
557
558    i("change request action to pending"),
559    {ok, _} = ?MGC_REQ_DISC(Mgc,5000),
560
561    i("send notify request"),
562    ?MG_NOTIF_REQ(Mg),
563
564    d("wait some to get it going",[]),
565    sleep(1000),
566
567    i("now cancel the notify request"),
568    ok = ?MG_CANCEL(Mg, req_and_cancel),
569
570    i("now await the notify request result"),
571    Res2 = ?MG_NOTIF_AR(Mg),
572    rac_analyze_result(Res2),
573
574
575    %% Tell MGs to stop
576    i("stop the MG"),
577    ?MG_STOP(Mg),
578
579    %% Tell Mgc to stop
580    i("stop the MGC"),
581    ?MGC_STOP(Mgc),
582
583    i("done", []),
584    ok.
585
586
587rac_analyze_result({ok, {_PV,Res}}) ->
588    i("rac_analyze_result -> notify request result:"
589      "~n      ~p", [Res]),
590    rac_analyze_result2(Res);
591rac_analyze_result(Unexpected) ->
592    e("rac_analyze_result -> unexpected result: "
593      "~n      ~p", [Unexpected]),
594    exit({unexpected_result, Unexpected}).
595
596rac_analyze_result2({error,{user_cancel, req_and_cancel}}) ->
597    ok;
598rac_analyze_result2([]) ->
599    ok;
600rac_analyze_result2([{error,{user_cancel, req_and_cancel}}|Res]) ->
601    rac_analyze_result2(Res);
602rac_analyze_result2([Unknown|_Res]) ->
603    e("rac_analyze_result2 -> unexpected result: "
604      "~n      ~p", [Unknown]),
605    exit({unknown_result, Unknown}).
606
607
608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609
610make_node_name(Name) ->
611    case string:tokens(atom_to_list(node()), [$@]) of
612	[_,Host] ->
613	    list_to_atom(lists:concat([atom_to_list(Name) ++ "@" ++ Host]));
614	_ ->
615	    exit("Test node must be started with '-sname'")
616     end.
617
618
619%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
620
621sleep(X) ->
622    receive after X -> ok end.
623
624
625%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
626
627try_tc(TCName, Pre, Case, Post) ->
628    try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post).
629
630try_tc(TCName, Name, Verbosity, Pre, Case, Post) ->
631    ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post).
632
633
634
635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
636
637p(F, A) ->
638    io:format("*** [~s] ~p ***"
639	      "~n   " ++ F ++ "~n",
640	      [?FTS(), self() | A]).
641
642%% e(F) ->
643%%     i(F, []).
644
645e(F, A) ->
646    print(info, get(verbosity), "ERROR", get(tc), F, A).
647
648
649i(F) ->
650    i(F, []).
651
652i(F, A) ->
653    print(info, get(verbosity), "INFO", get(tc), F, A).
654
655
656%% d(F) ->
657%%     d(F, []).
658
659d(F, A) ->
660    print(debug, get(verbosity), "DBG", get(tc), F, A).
661
662
663printable(_, debug)   -> true;
664printable(info, info) -> true;
665printable(_,_)        -> false.
666
667print(Severity, Verbosity, P, TC, F, A) ->
668    print(printable(Severity,Verbosity), P, TC, F, A).
669
670print(true, P, TC, F, A) ->
671    io:format("*** [~s] [~s] ~p ~s:~w ***"
672	      "~n   " ++ F ++ "~n",
673	      [?FTS(), P, self(), get(sname), TC | A]);
674print(_, _, _, _, _) ->
675    ok.
676
677