summaryrefslogtreecommitdiff
path: root/lib/diameter/test/diameter_traffic_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter/test/diameter_traffic_SUITE.erl')
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl519
1 files changed, 165 insertions, 354 deletions
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index a12759bac2..95ea6be020 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2021. 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.
@@ -25,25 +25,17 @@
-module(diameter_traffic_SUITE).
+%% all tests, no common_test dependency
+-export([run/0,
+ run/1]).
+
+%% common_test wrapping
-export([suite/0,
all/0,
- groups/0,
- init_per_suite/0,
- init_per_suite/1,
- end_per_suite/1,
- init_per_group/1,
- init_per_group/2,
- end_per_group/2,
- init_per_testcase/2,
- end_per_testcase/2]).
+ parallel/1]).
%% testcases
--export([rfc4005/1,
- start/1,
- start_services/1,
- add_transports/1,
- result_codes/1,
- send_ok/1,
+-export([send_ok/1,
send_nok/1,
send_eval/1,
send_bad_answer/1,
@@ -97,11 +89,7 @@
send_multiple_filters_1/1,
send_multiple_filters_2/1,
send_multiple_filters_3/1,
- send_anything/1,
- remove_transports/1,
- empty/1,
- stop_services/1,
- stop/1]).
+ send_anything/1]).
%% diameter callbacks
-export([peer_up/4,
@@ -127,14 +115,6 @@
%% ===========================================================================
-%% Fraction of shuffle/parallel groups to randomly skip.
--define(SKIP, 0.90).
-
-%% Positive number of testcases from which to select (randomly) from
-%% tc(), the list of testcases to run, or [] to run all. The random
-%% selection is to limit the time it takes for the suite to run.
--define(LIMIT, #{tcp => 42, sctp => 5}).
-
-define(util, diameter_util).
-define(A, list_to_atom).
@@ -164,30 +144,19 @@
%% Which dictionary to use in the clients.
-define(RFCS, [rfc3588, rfc6733, rfc4005]).
-%% Whether to decode stringish Diameter types to strings, or leave
-%% them as binary.
--define(STRING_DECODES, [false, true]).
-
%% Which transport protocol to use.
--define(TRANSPORTS, [tcp, sctp]).
-
-%% Send from a dedicated process?
--define(SENDERS, [true, false]).
-
-%% Message callbacks from diameter_{tcp,sctp}?
--define(CALLBACKS, [true, false]).
-
--record(group,
- {transport,
- strings,
- encoding,
- client_service,
- client_dict,
- client_sender,
- server_service,
- server_decoding,
- server_sender,
- server_throttle}).
+-define(TRANSPORTS, [sctp || ?util:have_sctp()] ++ [tcp]).
+
+-record(group, {transport,
+ strings,
+ encoding,
+ client_service,
+ client_dict,
+ client_sender,
+ server_service,
+ server_decoding,
+ server_sender,
+ server_throttle}).
%% Not really what we should be setting unless the message is sent in
%% the common application but diameter doesn't care.
@@ -270,285 +239,156 @@
?'DIAMETER_BASE_TERMINATION-CAUSE_USER_MOVED').
%% ===========================================================================
+%% common_test wrapping
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 90}}].
all() ->
- [rfc4005, start, result_codes, {group, traffic}, empty, stop].
-
-%% Redefine this to run one or more groups for debugging purposes.
--define(GROUPS, []).
-%-define(GROUPS, [[sctp,rfc6733,record,map,false,false,true,false]]).
-
-%% Issues with gen_sctp sporadically cause huge numbers of failed
-%% testcases when running testcases in parallel.
-groups() ->
- Names = names([] == ?GROUPS orelse ?GROUPS),
- [{P, [P], Ts} || Ts <- [tc()], P <- [shuffle, parallel]]
- ++
- [{?util:name(N), [], [{group, if T == sctp; S -> shuffle;
- true -> parallel end}]}
- || [T,_,_,_,S|_] = N <- Names]
- ++
- [{T, [], [{group, ?util:name(N)} || N <- Names,
- T == hd(N)]}
- || T <- ?TRANSPORTS]
- ++
- [{traffic, [], [{group, T} || T <- ?TRANSPORTS]}].
-
-names() ->
- [[T,R,E,D,S,ST,SS,CS] || T <- ?TRANSPORTS,
- R <- ?RFCS,
- E <- ?ENCODINGS,
- D <- ?DECODINGS,
- S <- ?STRING_DECODES,
- ST <- ?CALLBACKS,
- SS <- ?SENDERS,
- CS <- ?SENDERS,
- ?SKIP =< rand:uniform()].
-
-names(true) ->
- names(names());
-
-names(Names) ->
- [N || N <- Names,
- [CS,SS|_] <- [lists:reverse(N)],
- SS orelse CS]. %% avoid deadlock
-
-%% --------------------
-
-init_per_suite() ->
- [{timetrap, {seconds, 60}}].
-
-init_per_suite(Config) ->
- [{rfc4005, compile_and_load()}, {sctp, ?util:have_sctp()} | Config].
-
-end_per_suite(_Config) ->
- code:delete(nas4005),
- code:purge(nas4005),
- ok.
+ [parallel].
-%% --------------------
+parallel(_Config) ->
+ run().
-init_per_group(_) ->
- [{timetrap, {seconds, 30}}].
+%% ===========================================================================
-init_per_group(Name, Config)
- when Name == shuffle;
- Name == parallel ->
- start_services(Config),
- add_transports(Config),
- replace({sleep, Name == parallel}, Config);
+%% run/0
+%%
+%% The test cases proper, do or (meaningfully) die.
+%%
+%% Randomly choose one of many configuration possibilities. Testing
+%% all of them takes too much time, and randomly skipping various
+%% cases (as used to be the case) just unnecessary complexity. There
+%% are run night after night in on various hosts, so just choosing a
+%% configuration results in sufficient coverage over time.
+
+run() ->
+ Svc = ?util:unique_string(),
+ run(#group{transport = ?util:choose(?TRANSPORTS),
+ strings = bool(),
+ encoding = ?util:choose(?ENCODINGS),
+ client_service = [$C | Svc],
+ client_dict = appdict(?util:choose(?RFCS)),
+ client_sender = bool(),
+ server_service = [$S | Svc],
+ server_decoding = ?util:choose(?DECODINGS),
+ server_sender = true, %% avoid deadlock
+ server_throttle = bool()}).
+
+%% run/1
+
+run(#group{} = Cfg) ->
+ _ = result_codes(Cfg),
+ io:format("config: ~p~n", [Cfg]),
+ try
+ ?util:run([{[fun traffic/1, Cfg], 60000}])
+ after
+ code:delete(nas4005),
+ code:purge(nas4005),
+ diameter:stop()
+ end.
-init_per_group(sctp = Name, Config) ->
- {_, Sctp} = lists:keyfind(Name, 1, Config),
- if Sctp ->
- Config;
- true ->
- {skip, Name}
- end;
+%% traffic/1
-init_per_group(Name, Config) ->
- Nas = proplists:get_value(rfc4005, Config, false),
- case ?util:name(Name) of
- [_,R,_,_,_,_,_,_] when R == rfc4005, true /= Nas ->
- {skip, rfc4005};
- [T,R,E,D,S,ST,SS,CS] ->
- G = #group{transport = T,
- strings = S,
- encoding = E,
- client_service = [$C|?util:unique_string()],
- client_dict = appdict(R),
- client_sender = CS,
- server_service = [$S|?util:unique_string()],
- server_decoding = D,
- server_sender = SS,
- server_throttle = ST},
- replace([{group, G}, {runlist, select(T)}], Config);
- _ ->
- Config
- end.
+traffic(#group{} = Cfg) ->
+ _ = compile_and_load(),
+ ok = diameter:start(),
+ LRef = server(Cfg),
+ ok = client(Cfg, LRef),
+ [] = send(Cfg),
+ ok = stop_services(Cfg),
+ [] = ets:tab2list(diameter_request).
-end_per_group(Name, Config)
- when Name == shuffle;
- Name == parallel ->
- remove_transports(Config),
- stop_services(Config);
+%% start_service/2
-end_per_group(_, _) ->
+start_service(Svc, Opts) ->
+ {ok, _} = {diameter:start_service(Svc, Opts), Opts},
ok.
-select(T) ->
- try maps:get(T, ?LIMIT) of
- N ->
- lists:sublist(?util:scramble(tc()), max(5, rand:uniform(N)))
- catch
- error:_ -> ?LIMIT
- end.
+%% send/1
-%% --------------------
-
-%% Work around common_test accumulating Config improperly, causing
-%% testcases to get Config from groups and suites they're not in.
-init_per_testcase(N, Config)
- when N == rfc4005;
- N == start;
- N == result_codes;
- N == empty;
- N == stop ->
- Config;
-
-%% Skip testcases that can reasonably fail under SCTP.
-init_per_testcase(Name, Config) ->
- TCs = proplists:get_value(runlist, Config, []),
- Run = [] == TCs orelse lists:member(Name, TCs),
- case [G || #group{transport = sctp} = G
- <- [proplists:get_value(group, Config)]]
- of
- [_] when Name == send_maxlen;
- Name == send_long ->
- {skip, sctp};
- _ when not Run ->
- {skip, random};
- _ ->
- proplists:get_value(sleep, Config, false)
- andalso timer:sleep(rand:uniform(200)),
- [{testcase, Name} | Config]
- end.
+send(Cfg) ->
+ Ref = make_ref(),
+ Refs = [R || {F,1} <- module_info(exports),
+ "send_" ++ _ <- [atom_to_list(F)],
+ I <- [fun() -> exit({Ref, send(F, Cfg)}) end],
+ {_,R} <- [spawn_monitor(I)]],
+ lists:filter(fun({R,_}) -> R /= Ref; (_) -> true end, wait(Refs)).
-end_per_testcase(_, _) ->
- ok.
+%% send/2
-%% replace/2
-%%
-%% Work around common_test running init functions inappropriately, and
-%% this accumulating more config than expected.
-
-replace(Pairs, Config)
- when is_list(Pairs) ->
- lists:foldl(fun replace/2, Config, Pairs);
-
-replace({Key, _} = T, Config) ->
- [T | lists:keydelete(Key, 1, Config)].
-
-%% --------------------
-
-%% Testcases to run when services are started and connections
-%% established.
-tc() ->
- [send_ok,
- send_nok,
- send_eval,
- send_bad_answer,
- send_protocol_error,
- send_experimental_result,
- send_arbitrary,
- send_proxy_info,
- send_unknown,
- send_unknown_short,
- send_unknown_mandatory,
- send_unknown_short_mandatory,
- send_noreply,
- send_grouped_error,
- send_unsupported,
- send_unsupported_app,
- send_error_bit,
- send_unsupported_version,
- send_long_avp_length,
- send_short_avp_length,
- send_zero_avp_length,
- send_invalid_avp_length,
- send_invalid_reject,
- send_unexpected_mandatory_decode,
- send_unexpected_mandatory,
- send_too_many,
- send_long,
- send_maxlen,
- send_nopeer,
- send_noapp,
- send_discard,
- send_any_1,
- send_any_2,
- send_all_1,
- send_all_2,
- send_timeout,
- send_error,
- send_detach,
- send_encode_error,
- send_destination_1,
- send_destination_2,
- send_destination_3,
- send_destination_4,
- send_destination_5,
- send_destination_6,
- send_bad_option_1,
- send_bad_option_2,
- send_bad_filter_1,
- send_bad_filter_2,
- send_bad_filter_3,
- send_bad_filter_4,
- send_multiple_filters_1,
- send_multiple_filters_2,
- send_multiple_filters_3,
- send_anything].
+send(F, #group{transport = sctp})
+ when F == send_long;
+ F == send_maxlen ->
+ ok;
-%% ===========================================================================
-%% start/stop testcases
+send(F, Cfg) ->
+ timer:sleep(rand:uniform(2000)),
+ apply(?MODULE, F, [[{testcase, F}, {group, Cfg}]]).
-start(_Config) ->
- ok = diameter:start().
+%% wait/1
-start_services(Config) ->
- #group{client_service = CN,
- server_service = SN,
- server_decoding = SD}
- = Grp
- = group(Config),
- ok = diameter:start_service(SN, [{traffic_counters, bool()},
- {decode_format, SD}
- | ?SERVICE(SN, Grp)]),
- ok = diameter:start_service(CN, [{traffic_counters, bool()},
- {sequence, ?CLIENT_MASK},
- {decode_format, map},
- {strict_arities, decode}
- | ?SERVICE(CN, Grp)]).
+wait(Refs)
+ when is_list(Refs) ->
+ lists:map(fun wait/1, Refs);
-bool() ->
- 0.5 =< rand:uniform().
+wait(MRef) ->
+ receive {'DOWN', MRef, process, _, T} -> T end.
+
+%% ===========================================================================
-add_transports(Config) ->
+%% server/1
+
+server(Config) ->
#group{transport = T,
- encoding = E,
- client_service = CN,
client_sender = CS,
server_service = SN,
+ server_decoding = SD,
server_sender = SS,
server_throttle = ST}
+ = Grp
= group(Config),
- LRef = ?util:listen(SN,
- [T,
- {sender, SS},
- {message_cb, ST andalso {?MODULE, message, [0]}}]
- ++ [{packet, hd(?util:scramble([false, raw]))}
- || T == sctp andalso CS]
- ++ [{unordered, unordered()} || T == sctp],
- [{capabilities_cb, fun capx/2},
- {pool_size, 8}
- | server_apps()]),
- Cs = [?util:connect(CN,
- [T, {sender, CS} | client_opts(T)],
- LRef,
- [{id, Id}
- | client_apps(R, [{'Origin-State-Id', origin(Id)}])])
- || D <- ?DECODINGS, %% for multiple candidate peers
- R <- ?RFCS,
- R /= rfc4005 orelse have_nas(),
- Id <- [{D,E}]],
- ?util:write_priv(Config, "transport", [LRef | Cs]).
+ ok = start_service(SN, [{traffic_counters, bool()},
+ {decode_format, SD}
+ | ?SERVICE(SN, Grp)]),
+ Cfg = [{sender, SS},
+ {message_cb, ST andalso {?MODULE, message, [0]}}]
+ ++ [{packet, ?util:choose([false, raw])} || T == sctp andalso CS]
+ ++ [{unordered, unordered()} || T == sctp],
+ Opts = [{capabilities_cb, fun capx/2},
+ {pool_size, 8}
+ | server_apps()],
+ _LRef = ?util:listen(SN, [T | Cfg], Opts).
+
+%% client/1
+
+client(Config, LRef) ->
+ #group{transport = T,
+ encoding = E,
+ client_service = CN,
+ client_sender = CS}
+ = Grp
+ = group(Config),
+ ok = start_service(CN, [{traffic_counters, bool()},
+ {sequence, ?CLIENT_MASK},
+ {decode_format, map},
+ {strict_arities, decode}
+ | ?SERVICE(CN, Grp)]),
+ _ = [?util:connect(CN, [T | C], LRef, O)
+ || C <- [[{sender, CS} | client_opts(T)]],
+ D <- ?DECODINGS, %% for multiple candidate peers
+ R <- ?RFCS,
+ R /= rfc4005 orelse have_nas(),
+ I <- [{D,E}],
+ O <- [[{id, I}
+ | client_apps(R, [{'Origin-State-Id', origin(I)}])]]],
+ ok.
+
+bool() ->
+ 0.5 =< rand:uniform().
unordered() ->
- element(rand:uniform(4), {true, false, 1, 2}).
+ ?util:choose([true, false, 1, 2]).
client_opts(tcp) ->
[];
@@ -567,32 +407,19 @@ server_apps() ->
{capabilities, [{'Auth-Application-Id', [0] ++ [1 || B]}, %% common, NAS
{'Acct-Application-Id', [3]}]}]. %% accounting
+client_apps(rfc4005, Caps) ->
+ [{applications, [nas4005]},
+ {capabilities, [{'Auth-Application-Id', [1]}, %% NAS
+ {'Acct-Application-Id', []}
+ | Caps]}];
client_apps(D, Caps) ->
- if D == rfc4005 ->
- [{applications, [nas4005]},
- {capabilities, [{'Auth-Application-Id', [1]}, %% NAS
- {'Acct-Application-Id', []}
- | Caps]}];
- true ->
- D0 = dict0(D),
- [{applications, [acct(D0), D0]},
- {capabilities, Caps}]
- end.
+ D0 = dict0(D),
+ [{applications, [acct(D0), D0]},
+ {capabilities, Caps}].
have_nas() ->
false /= code:is_loaded(nas4005).
-remove_transports(Config) ->
- #group{client_service = CN,
- server_service = SN}
- = group(Config),
- [LRef | Cs] = ?util:read_priv(Config, "transport"),
- try
- [] = [T || C <- Cs, T <- [?util:disconnect(CN, C, SN, LRef)], T /= ok]
- after
- ok = diameter:remove_transport(SN, LRef)
- end.
-
stop_services(Config) ->
#group{client_service = CN,
server_service = SN}
@@ -600,24 +427,12 @@ stop_services(Config) ->
ok = diameter:stop_service(CN),
ok = diameter:stop_service(SN).
-%% Ensure even transports have been removed from request table.
-empty(_Config) ->
- [] = ets:tab2list(diameter_request).
-
-stop(_Config) ->
- ok = diameter:stop().
-
capx(_, #diameter_caps{origin_host = {OH,DH}}) ->
io:format("connection: ~p -> ~p~n", [DH,OH]),
ok.
%% ===========================================================================
-%% Fail only this testcase if the RFC 4005 dictionary hasn't been
-%% successfully compiled and loaded.
-rfc4005(Config) ->
- true = proplists:get_value(rfc4005, Config).
-
%% Ensure that result codes have the expected values.
result_codes(_Config) ->
{2001,
@@ -1135,6 +950,8 @@ send_anything(Config) ->
%% ===========================================================================
+group(#group{} = Rec) ->
+ Rec;
group(Config) ->
#group{} = proplists:get_value(group, Config).
@@ -1903,25 +1720,19 @@ message(ack, _, N) ->
%% ------------------------------------------------------------------------
compile_and_load() ->
- try
- Path = hd([P || H <- [[here(), ".."], [code:lib_dir(diameter)]],
- P <- [filename:join(H ++ ["examples",
- "dict",
- "rfc4005_nas.dia"])],
- {ok, _} <- [file:read_file_info(P)]]),
- {ok, [Forms]}
- = diameter_make:codec(Path, [return,
- forms,
- {name, "nas4005"},
- {prefix, "nas"},
- {inherits, "common/diameter_gen_base_rfc3588"}]),
- {ok, nas4005, Bin, []} = compile:forms(Forms, [debug_info, return]),
- {module, nas4005} = code:load_binary(nas4005, "nas4005", Bin),
- true
- catch
- E:R:Stack ->
- {E, R, Stack}
- end.
+ Path = hd([P || H <- [[here(), ".."], [code:lib_dir(diameter)]],
+ P <- [filename:join(H ++ ["examples",
+ "dict",
+ "rfc4005_nas.dia"])],
+ {ok, _} <- [file:read_file_info(P)]]),
+ Opts = [return,
+ forms,
+ {name, "nas4005"},
+ {prefix, "nas"},
+ {inherits, "common/diameter_gen_base_rfc3588"}],
+ {ok, [Forms]} = diameter_make:codec(Path, Opts),
+ {ok, nas4005, Bin, []} = compile:forms(Forms, [debug_info, return]),
+ {module, nas4005} = code:load_binary(nas4005, "nas4005", Bin).
here() ->
filename:dirname(code:which(?MODULE)).