1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2020-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:
24%%----------------------------------------------------------------------
25-module(megaco_test_command_handler).
26
27%%----------------------------------------------------------------------
28%% Include files
29%%----------------------------------------------------------------------
30-include("megaco_test_lib.hrl").
31
32
33%%----------------------------------------------------------------------
34%% External exports
35%%----------------------------------------------------------------------
36-export([
37	 start/4,
38	 await_completion/2,
39
40	 print/1, print/2
41	]).
42
43
44%%----------------------------------------------------------------------
45%% Macros
46%%----------------------------------------------------------------------
47
48%%----------------------------------------------------------------------
49%% Records
50%%----------------------------------------------------------------------
51
52
53%%----------------------------------------------------------------------
54%% Exported functions
55%%----------------------------------------------------------------------
56
57start(Node, Commands, InitialState, ShortName)
58  when is_atom(Node) andalso
59       is_list(Commands) andalso
60       is_map(InitialState) andalso
61       is_list(ShortName) ->
62    Fun = fun() ->
63		  put(sname, ShortName),
64		  process_flag(trap_exit, true),
65		  Result = command_handler(Commands, InitialState),
66		  print("command handler terminated with: "
67			"~n      ~p", [Result]),
68		  exit(Result)
69	  end,
70    erlang:spawn_link(Node, Fun).
71
72command_handler([], State) ->
73    print("command_handler -> entry when done with"
74	  "~n      State: ~p", [State]),
75    {ok, State};
76command_handler([#{id   := Id,
77		   desc := Desc,
78		   cmd  := Cmd}|Commands], State)
79  when is_list(Desc) andalso is_function(Cmd, 1) ->
80    print("command_handler -> [~w] ~s", [Id, Desc]),
81    try Cmd(State) of
82	{ok, NewState} ->
83	    print("command_handler -> [~w] cmd ok", [Id]),
84	    command_handler(Commands, NewState);
85	{skip, _} = SKIP ->
86	    print("command_handler -> [~w] cmd skip (returned)", [Id]),
87	    SKIP;
88	{error, Reason} ->
89	    print("command_handler -> [~w] cmd error: "
90		  "~n   Reason: ~p", [Id, Reason]),
91	    {error, {cmd_error, Reason}}
92
93    catch
94	throw:{skip, _} = SKIP:_ ->
95	    print("command_handler -> [~w] cmd skip (throw)", [Id]),
96	    SKIP;
97	exit:{skip, _} = SKIP:_ ->
98	    print("command_handler -> [~w] cmd skip (exit)", [Id]),
99	    SKIP;
100	C:E:S ->
101	    print("command_handler -> [~w] cmd failure:"
102		  "~n   C: ~p"
103		  "~n   E: ~p"
104		  "~n   S: ~p", [Id, C, E, S]),
105	    {error, {cmd_failure, {C, E, S}}}
106    end.
107
108
109%% --- Await completion of one or more command handler(s)
110
111await_completion(Pids, Timeout) ->
112    await_completion(Pids, [], [], Timeout).
113
114await_completion([], [], _Good, _Timeout) ->
115    print("await_completion -> entry when done (success)"),
116    ok;
117await_completion([], Bad, Good, _Timeout) ->
118    print("await_completion -> entry when done with bad result: "
119	  "~n      Bad:  ~p"
120	  "~n      Good: ~p", [Bad, Good]),
121    {error, Bad, Good};
122await_completion(Pids, Bad, Good, Timeout) ->
123    print("await_completion -> entry when waiting for"
124	  "~n      Pids:    ~p"
125	  "~n      Bad:     ~p"
126	  "~n      Good:    ~p"
127	  "~n      Timeout: ~p", [Pids, Bad, Good, Timeout]),
128    Begin = ms(),
129    receive
130	{'EXIT', Pid, {ok, FinalState}} ->
131	    case lists:delete(Pid, Pids) of
132		Pids ->
133		    print("await_completion -> "
134			  "received ok EXIT signal from unknown ~p", [Pid]),
135		    await_completion(Pids, Bad, Good,
136				     Timeout - (ms() - Begin));
137		Pids2 ->
138		    print("await_completion -> success from  ~p", [Pid]),
139		    await_completion(Pids2,
140				     Bad,
141				     [{Pid, FinalState}|Good],
142				     Timeout - (ms() - Begin))
143	    end;
144
145	{'EXIT', Pid, {error, Reason}} ->
146	    case lists:delete(Pid, Pids) of
147		Pids ->
148		    print("await_completion -> "
149			  "received error EXIT signal from unknown ~p", [Pid]),
150		    await_completion(Pids, Bad, Good,
151				     Timeout - (ms() - Begin));
152		Pids2 ->
153		    print("await_completion -> failure from ~p", [Pid]),
154		    await_completion(Pids2,
155				     [{Pid, Reason}|Bad],
156				     Good,
157				     Timeout - (ms() - Begin))
158	    end;
159
160	{'EXIT', Pid, {skip, Reason}} ->
161	    print("await_completion -> skip (exit) from ~p:"
162		  "      ~p", [Pid, Reason]),
163	    ?SKIP(Reason)
164
165    after Timeout ->
166	    print("await_completion -> timeout"),
167	    exit({timeout, Pids})
168    end.
169
170
171
172%% ------- Misc functions --------
173
174print(F) ->
175    print(F, []).
176
177print(F, A) ->
178    print(get(sname), F, A).
179
180print(N, F, A) when is_list(N) ->
181    io:format("*** [~s] ~p ~s ***"
182	      "~n   " ++ F ++ "~n",
183	      [?FTS(), self(), N | A]);
184print(_N, F, A) ->
185    io:format("*** [~s] ~p *** "
186	      "~n   " ++ F ++ "~n",
187	      [?FTS(), self() | A]).
188
189
190ms() ->
191    erlang:monotonic_time(milli_seconds).
192
193
194