diff options
Diffstat (limited to 'lib/diameter/test/diameter_util.erl')
-rw-r--r-- | lib/diameter/test/diameter_util.erl | 241 |
1 files changed, 135 insertions, 106 deletions
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl index d249b0e4fa..cdb6fa410f 100644 --- a/lib/diameter/test/diameter_util.erl +++ b/lib/diameter/test/diameter_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2017. All Rights Reserved. +%% Copyright Ericsson AB 2010-2022. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -31,6 +31,10 @@ fold/3, foldl/3, scramble/1, + choose/1, + tmpdir/0, + mktemp/1, + peer/1, unique_string/0, have_sctp/0, eprof/1]). @@ -42,11 +46,6 @@ disconnect/4, info/0]). -%% common_test-specific --export([write_priv/3, - read_priv/2, - map_priv/3]). - -define(L, atom_to_list). %% --------------------------------------------------------------------------- @@ -63,7 +62,7 @@ eprof(stop) -> %% --------------------------------------------------------------------------- %% name/2 %% -%% Contruct and deconstruct lists of atoms as atoms to work around +%% Construct and deconstruct lists of atoms as atoms to work around %% group names in common_test being restricted to atoms. name(Names) @@ -102,18 +101,37 @@ consult(Path) -> %% --------------------------------------------------------------------------- %% run/1 %% -%% Evaluate functions in parallel and return a list of those that -%% failed to return. The fun takes a boolean (did the function return -%% or not), the function that was evaluated, the return value or exit -%% reason and the prevailing accumulator. +%% Evaluate functions in parallel and raise an error exception if any +%% fail to return. run(L) -> - fold(fun cons/4, [], L). - -cons(true, _, _, Acc) -> - Acc; -cons(false, F, RC, Acc) -> - [{F, RC} | Acc]. + Ref = make_ref(), + AccF = fun(I, [F|T]) -> + Ref == (catch element(1, I)) + orelse error(#{failed => F, reason => I}), + T + end, + Pid = self(), + Funs = [fun() -> down(Pid, self()), {Ref, eval(F)} end || F <- L], + [] = fold(AccF, L, Funs). + +%% down/2 + +down(Parent, Worker) + when is_pid(Parent) -> + spawn(fun() -> + monitor(process, Worker), + down(monitor(process, Parent), Worker) + end); + +%% Die with the worker, kill the worker if the parent dies. +down(MRef, Pid) -> + receive + {'DOWN', MRef, process, _, _} -> + exit(Pid, kill); + {'DOWN', _, process, Pid, _} -> + ok + end. %% --------------------------------------------------------------------------- %% fold/3 @@ -121,61 +139,43 @@ cons(false, F, RC, Acc) -> %% Parallel fold. Results are folded in the order received. fold(Fun, Acc0, L) - when is_function(Fun, 4) -> - Ref = make_ref(), - %% Spawn a middleman to collect down messages from processes - %% spawned for each function so as not to assume that all DOWN - %% messages are ours. - MRef = run1([fun fold/4, Ref, Fun, Acc0, L], Ref), - {Ref, RC} = down(MRef), - RC. - -fold(Ref, Fun, Acc0, L) -> - recv(run(Ref, L), Ref, Fun, Acc0). - -run(Ref, L) -> - [{run1(F, Ref), F} || F <- L]. - -run1(F, Ref) -> - {_, MRef} = spawn_monitor(fun() -> exit({Ref, eval(F)}) end), - MRef. - -recv([], _, _, Acc) -> + when is_list(L) -> + fold(Fun, Acc0, lists:foldl(fun(F,A) -> + {P,M} = spawn_eval(F), + A#{M => P} + end, + #{}, + L)); + +fold(_, Acc, Map) + when 0 == map_size(Map) -> Acc; -recv(L, Ref, Fun, Acc) -> - {MRef, R} = down(), - {MRef, F} = lists:keyfind(MRef, 1, L), - recv(lists:keydelete(MRef, 1, L), - Ref, - Fun, - acc(R, Ref, F, Fun, Acc)). -acc({Ref, RC}, Ref, F, Fun, Acc) -> - Fun(true, F, RC, Acc); -acc(Reason, _, F, Fun, Acc) -> - Fun(false, F, Reason, Acc). +fold(Fun, Acc, #{} = Map) -> + receive + {'DOWN', MRef, process, _, Info} when is_map_key(MRef, Map) -> + fold(Fun, Fun(Info, Acc), maps:remove(MRef, Map)) + end. -down(MRef) -> - receive {'DOWN', MRef, process, _, Reason} -> Reason end. +%% spawn_eval/1 -down() -> - receive {'DOWN', MRef, process, _, Reason} -> {MRef, Reason} end. +spawn_eval(F) -> + spawn_monitor(fun() -> exit(eval(F)) end). %% --------------------------------------------------------------------------- %% foldl/3 %% %% Parallel fold. Results are folded in order of the function list. -foldl(Fun, Acc0, L) - when is_function(Fun, 4) -> - Ref = make_ref(), - recvl(run(Ref, L), Ref, Fun, Acc0). +foldl(Fun, Acc0, L) -> + lists:foldl(fun(R,A) -> acc(Fun, R, A) end, + Acc0, + [M || F <- L, {_,M} <- [spawn_eval(F)]]). -recvl([], _, _, Acc) -> - Acc; -recvl([{MRef, F} | L], Ref, Fun, Acc) -> - R = down(MRef), - recvl(L, Ref, Fun, acc(R, Ref, F, Fun, Acc)). +%% acc/3 + +acc(Fun, MRef, Acc) -> + receive {'DOWN', MRef, process, _, Info} -> Fun(Info, Acc) end. %% --------------------------------------------------------------------------- %% scramble/1 @@ -186,6 +186,71 @@ scramble(L) -> [X || {_,X} <- lists:sort([{rand:uniform(), T} || T <- L])]. %% --------------------------------------------------------------------------- +%% choose/1 +%% +%% Return a random element from a list. + +choose([_|_] = List) -> + hd(lists:nthtail(rand:uniform(length(List)) - 1, List)). + +%% --------------------------------------------------------------------------- +%% tmpdir/0 + +tmpdir() -> + case os:getenv("TMPDIR") of + false -> + "/tmp"; + Dir -> + Dir + end. + +%% mktemp/1 + +mktemp(Prefix) -> + Suf = integer_to_list(erlang:monotonic_time()), + Tmp = filename:join(tmpdir(), Prefix ++ "." ++ Suf), + ok = file:make_dir(Tmp), + Tmp. + +%% --------------------------------------------------------------------------- +%% peer/1 +%% +%% Start a peer that dies with the calling process, but start it from +%% a spawned process to make it insensitive to the exit reason of the +%% calling process. + +peer(#{name := _} = Opts) -> + Ref = make_ref(), + Pid = self(), + {_, MRef} = spawn_monitor(fun() -> peer(Opts, Pid, Ref) end), + receive + {Ref, T} -> + erlang:demonitor(MRef, [flush]), + T; + {'DOWN', MRef, process, _, T} -> + {error, T} + end; + +peer(#{} = Opts) -> + peer(Opts#{name => peer:random_name()}). + +%% peer/3 + +peer(Opts, Pid, Ref) -> + Cookie = atom_to_list(erlang:get_cookie()), + Args = [str(B) || B <- maps:get(args, Opts, [])], + Pid ! {Ref, peer:start_link(Opts#{args => ["-setcookie", Cookie | Args]})}, + MRef = monitor(process, Pid), + receive {'DOWN', MRef, process, _, _} -> ok end. + +%% str/1 +%% +%% Turn possible iodata() into string(). + +str(Bytes) -> + binary_to_list(iolist_to_binary(Bytes)). + +%% --------------------------------------------------------------------------- %% unique_string/0 unique_string() -> @@ -208,8 +273,7 @@ have_sctp(_) -> {ok, Sock} -> gen_sctp:close(Sock), true; - {error, E} when E == eprotonosupport; - E == esocktnosupport -> %% fail on any other reason + _ -> false end. @@ -218,6 +282,13 @@ have_sctp(_) -> %% %% Evaluate a function in one of a number of forms. +eval({F, infinity}) -> + eval(F); +eval({F, Tmo}) + when is_integer(Tmo) -> + {ok, _} = timer:exit_after(Tmo, timeout), + eval(F); + eval({M,[F|A]}) when is_atom(F) -> apply(M,F,A); @@ -231,55 +302,13 @@ eval([F|A]) eval(L) when is_list(L) -> - run(L); + [eval(F) || F <- L]; eval(F) when is_function(F,0) -> F(). %% --------------------------------------------------------------------------- -%% write_priv/3 -%% -%% Write an arbitrary term to a named file. - -write_priv(Config, Name, Term) -> - write(path(Config, Name), Term). - -write(Path, Term) -> - ok = file:write_file(Path, term_to_binary(Term)). - -%% read_priv/2 -%% -%% Read a term from a file. - -read_priv(Config, Name) -> - read(path(Config, Name)). - -read(Path) -> - {ok, Bin} = file:read_file(Path), - binary_to_term(Bin). - -%% map_priv/3 -%% -%% Modify a term in a file and return both old and new values. - -map_priv(Config, Name, Fun1) -> - map(path(Config, Name), Fun1). - -map(Path, Fun1) -> - T0 = read(Path), - T1 = Fun1(T0), - write(Path, T1), - {T0, T1}. - -path(Config, Name) - when is_atom(Name) -> - path(Config, ?L(Name)); -path(Config, Name) -> - Dir = proplists:get_value(priv_dir, Config), - filename:join([Dir, Name]). - -%% --------------------------------------------------------------------------- %% lport/2 %% %% Lookup the port number of a tcp/sctp listening transport. |