4
%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
20
-module(diameter_util).
40
%% common_test-specific
41
-export([write_priv/3,
45
-define(L, atom_to_list).
47
%% ---------------------------------------------------------------------------
50
%% Extract info from the app/appup file (presumably) of the named
54
when is_atom(Name), is_atom(Suf) ->
55
case code:lib_dir(Name, ebin) of
56
{error = E, Reason} ->
59
consult(filename:join([Dir, ?L(Name) ++ "." ++ ?L(Suf)]))
63
case file:consult(Path) of
67
{error, {Path, Reason}}
69
%% Name/Path in the return value distinguish the errors and allow for
72
%% ---------------------------------------------------------------------------
75
%% Evaluate functions in parallel and return a list of those that
76
%% failed to return. The fun takes a boolean (did the function return
77
%% or not), the function that was evaluated, the return value or exit
78
%% reason and the prevailing accumulator.
81
fold(fun cons/4, [], L).
83
cons(true, _, _, Acc) ->
85
cons(false, F, RC, Acc) ->
88
%% ---------------------------------------------------------------------------
91
%% Parallel fold. Results are folded in the order received.
94
when is_function(Fun, 4) ->
96
%% Spawn a middleman to collect down messages from processes
97
%% spawned for each function so as not to assume that all DOWN
99
MRef = run1([fun fold/4, Ref, Fun, Acc0, L], Ref),
100
{Ref, RC} = down(MRef),
103
fold(Ref, Fun, Acc0, L) ->
104
recv(run(Ref, L), Ref, Fun, Acc0).
107
[{run1(F, Ref), F} || F <- L].
110
{_, MRef} = spawn_monitor(fun() -> exit({Ref, eval(F)}) end),
113
recv([], _, _, Acc) ->
115
recv(L, Ref, Fun, Acc) ->
117
{MRef, F} = lists:keyfind(MRef, 1, L),
118
recv(lists:keydelete(MRef, 1, L),
121
acc(R, Ref, F, Fun, Acc)).
123
acc({Ref, RC}, Ref, F, Fun, Acc) ->
124
Fun(true, F, RC, Acc);
125
acc(Reason, _, F, Fun, Acc) ->
126
Fun(false, F, Reason, Acc).
129
receive {'DOWN', MRef, process, _, Reason} -> Reason end.
132
receive {'DOWN', MRef, process, _, Reason} -> {MRef, Reason} end.
134
%% ---------------------------------------------------------------------------
137
%% Parallel fold. Results are folded in order of the function list.
140
when is_function(Fun, 4) ->
142
recvl(run(Ref, L), Ref, Fun, Acc0).
144
recvl([], _, _, Acc) ->
146
recvl([{MRef, F} | L], Ref, Fun, Acc) ->
148
recvl(L, Ref, Fun, acc(R, Ref, F, Fun, Acc)).
150
%% ---------------------------------------------------------------------------
153
%% Sort a list into random order.
156
foldl(fun(true, _, S, false) -> S end,
167
{H, [T|Rest]} = lists:split(random:uniform(length(L)) - 1, L),
168
s([T|Acc], H ++ Rest).
170
%% ---------------------------------------------------------------------------
173
%% Evaluate a function in one of a number of forms.
183
when is_function(F) ->
191
when is_function(F,0) ->
194
%% ---------------------------------------------------------------------------
197
%% Write an arbitrary term to a named file.
199
write_priv(Config, Name, Term) ->
200
write(path(Config, Name), Term).
203
ok = file:write_file(Path, term_to_binary(Term)).
207
%% Read a term from a file.
209
read_priv(Config, Name) ->
210
read(path(Config, Name)).
213
{ok, Bin} = file:read_file(Path),
218
%% Modify a term in a file and return both old and new values.
220
map_priv(Config, Name, Fun1) ->
221
map(path(Config, Name), Fun1).
230
when is_atom(Name) ->
231
path(Config, ?L(Name));
232
path(Config, Name) ->
233
Dir = proplists:get_value(priv_dir, Config),
234
filename:join([Dir, Name]).
236
%% ---------------------------------------------------------------------------
239
%% Lookup the port number of a tcp/sctp listening transport.
244
lport(M, Ref, Tries) ->
245
lp(tmod(M), Ref, Tries).
248
L = [N || {listen, N, _} <- M:ports(Ref)],
249
if [] /= L orelse T =< 1 ->
252
receive after 50 -> ok end,
256
%% ---------------------------------------------------------------------------
259
%% Add a listening transport on the loopback address and a free port.
261
listen(SvcName, Prot) ->
262
listen(SvcName, Prot, []).
264
listen(SvcName, Prot, Opts) ->
265
add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}).
267
%% ---------------------------------------------------------------------------
270
%% Add a connecting transport on and connect to a listening transport
271
%% with the specified reference.
273
connect(Client, Prot, LRef) ->
274
connect(Client, Prot, LRef, []).
276
connect(Client, Prot, LRef, Opts) ->
277
[PortNr] = lport(Prot, LRef, 20),
278
Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}),
279
true = diameter:subscribe(Client),
281
{diameter_event, Client, {up, Ref, _, _, _}} -> ok
283
{Client, Prot, PortNr, process_info(self(), messages)}
287
%% ---------------------------------------------------------------------------
290
%% Remove the client transport and expect the server transport to go
293
disconnect(Client, Ref, Server, LRef) ->
294
true = diameter:subscribe(Server),
295
ok = diameter:remove_transport(Client, Ref),
297
{diameter_event, Server, {down, LRef, _, _}} -> ok
299
{Client, Ref, Server, LRef, process_info(self(), messages)}
302
%% ---------------------------------------------------------------------------
304
-define(ADDR, {127,0,0,1}).
306
add_transport(SvcName, T) ->
307
{ok, Ref} = diameter:add_transport(SvcName, T),
316
[{transport_module, tmod(Prot)},
317
{transport_config, [{ip, ?ADDR}, {port, 0} | opts(T)]}].
322
[{raddr, ?ADDR}, {rport, PortNr}].