diff options
author | Anders Svensson <[email protected]> | 2012-11-18 19:38:51 +0100 |
---|---|---|
committer | Anders Svensson <[email protected]> | 2012-11-18 19:38:51 +0100 |
commit | 9c9c14f0be78a1a895e23681b4825d6de0b1da4f (patch) | |
tree | b5196c62730e8e9f5c1c1ec4aa4c24b8a9820ac2 /lib/diameter/test/diameter_dpr_SUITE.erl | |
parent | 87241d86716ae02507a5019c3a2f822ff6b334d4 (diff) | |
parent | d78891e4e1566923164185894beee5e25151399f (diff) | |
download | otp-9c9c14f0be78a1a895e23681b4825d6de0b1da4f.tar.gz otp-9c9c14f0be78a1a895e23681b4825d6de0b1da4f.tar.bz2 otp-9c9c14f0be78a1a895e23681b4825d6de0b1da4f.zip |
Merge branch 'anders/diameter/transport_shutdown/OTP-10493' into maint
* anders/diameter/transport_shutdown/OTP-10493:
Add simple DPR suite
Correct diameter:remove_transport/2 doc
Fix broken doc link
Ensure watchdog dies with transport if DPR was sent
Remove dead clause
Implement transport_opt() disconnect_cb
Document transport_opt() disconnect_cb
Diffstat (limited to 'lib/diameter/test/diameter_dpr_SUITE.erl')
-rw-r--r-- | lib/diameter/test/diameter_dpr_SUITE.erl | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/lib/diameter/test/diameter_dpr_SUITE.erl b/lib/diameter/test/diameter_dpr_SUITE.erl new file mode 100644 index 0000000000..9252650bf7 --- /dev/null +++ b/lib/diameter/test/diameter_dpr_SUITE.erl @@ -0,0 +1,196 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%% Tests of the disconnect_cb configuration. +%% + +-module(diameter_dpr_SUITE). + +-export([suite/0, + all/0, + groups/0, + init_per_group/2, + end_per_group/2]). + +%% testcases +-export([start/1, + connect/1, + remove_transport/1, + stop_service/1, + check/1, + stop/1]). + +%% disconnect_cb +-export([disconnect/5]). + +-include("diameter.hrl"). + +%% =========================================================================== + +-define(util, diameter_util). + +-define(ADDR, {127,0,0,1}). + +-define(CLIENT, "CLIENT"). +-define(SERVER, "SERVER"). + +-define(DICT_COMMON, ?DIAMETER_DICT_COMMON). +-define(APP_ID, ?DICT_COMMON:id()). + +%% Config for diameter:start_service/2. +-define(SERVICE(Host), + [{'Origin-Host', Host}, + {'Origin-Realm', "erlang.org"}, + {'Host-IP-Address', [?ADDR]}, + {'Vendor-Id', hd(Host)}, %% match this in disconnect/5 + {'Product-Name', "OTP/diameter"}, + {'Acct-Application-Id', [?APP_ID]}, + {restrict_connections, false}, + {application, [{dictionary, ?DICT_COMMON}, + {module, #diameter_callback{_ = false}}]}]). + +%% Disconnect reasons that diameter passes as the first argument of a +%% function configured as disconnect_cb. +-define(REASONS, [transport, service, application]). + +%% Valid values for Disconnect-Cause. +-define(CAUSES, [0, rebooting, 1, busy, 2, goaway]). + +%% Establish one client connection for element of this list, +%% configured with disconnect/5 as disconnect_cb and returning the +%% specified value. +-define(RETURNS, + [[close, {dpr, [{cause, invalid}]}], [ignore, close], []] + ++ [[{dpr, [{timeout, 5000}, {cause, T}]}] || T <- ?CAUSES]). + +%% =========================================================================== + +suite() -> + [{timetrap, {seconds, 60}}]. + +all() -> + [{group, R} || R <- ?REASONS]. + +%% The group determines how transports are terminated: by remove_transport, +%% stop_service or application stop. +groups() -> + Ts = tc(), + [{R, [], Ts} || R <- ?REASONS]. + +init_per_group(Name, Config) -> + [{group, Name} | Config]. + +end_per_group(_, _) -> + ok. + +tc() -> + [start, connect, remove_transport, stop_service, check, stop]. + +%% =========================================================================== +%% start/stop testcases + +start(_Config) -> + ok = diameter:start(), + ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER)), + ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT)). + +connect(Config) -> + Pid = spawn(fun init/0), %% process for disconnect_cb to bang + Grp = group(Config), + LRef = ?util:listen(?SERVER, tcp), + Refs = [?util:connect(?CLIENT, tcp, LRef, opts(RCs, {Grp, Pid})) + || RCs <- ?RETURNS], + ?util:write_priv(Config, config, [Pid | Refs]). + +%% Remove all the client transports only in the transport group. +remove_transport(Config) -> + transport == group(Config) + andalso (ok = diameter:remove_transport(?CLIENT, true)). + +%% Stop the service only in the service group. +stop_service(Config) -> + service == group(Config) + andalso (ok = diameter:stop_service(?CLIENT)). + +%% Check for callbacks and stop the service. (Not the other way around +%% for the timing reason explained below.) +check(Config) -> + Grp = group(Config), + [Pid | Refs] = ?util:read_priv(Config, config), + Pid ! self(), %% ask for dictionary + Dict = receive {Pid, D} -> D end, %% get it + check(Refs, ?RETURNS, Grp, Dict). %% check for callbacks + +stop(_Config) -> + ok = diameter:stop(). + +%% Whether or not there are callbacks after diameter:stop() depends on +%% timing as long as the server runs on the same node: a server +%% transport could close the connection before the client has chance +%% to apply its callback. Therefore, just check that there haven't +%% been any callbacks yet. +check(_, _, application, Dict) -> + [] = dict:to_list(Dict); + +check([], [], _, _) -> + ok; + +check([Ref | Refs], CBs, Grp, Dict) -> + check1(Ref, hd(CBs), Grp, Dict), + check(Refs, tl(CBs), Grp, Dict). + +check1(Ref, [ignore | RCs], Reason, Dict) -> + check1(Ref, RCs, Reason, Dict); + +check1(Ref, [_|_], Reason, Dict) -> + {ok, Reason} = dict:find(Ref, Dict); %% callback with expected reason + +check1(Ref, [], _, Dict) -> + error = dict:find(Ref, Dict). %% no callback + +%% ---------------------------------------- + +group(Config) -> + {group, Grp} = lists:keyfind(group, 1, Config), + Grp. + +%% Configure the callback with the group name (= disconnect reason) as +%% extra argument. +opts(RCs, T) -> + [{disconnect_cb, {?MODULE, disconnect, [T, RC]}} || RC <- RCs]. + +%% Match the group name with the disconnect reason to ensure the +%% callback is being called as expected. +disconnect(Reason, Ref, Peer, {Reason, Pid}, RC) -> + io:format("disconnect: ~p ~p~n", [Ref, Reason]), + {_, #diameter_caps{vendor_id = {$C,$S}}} = Peer, + Pid ! {Reason, Ref}, + RC. + +init() -> + exit(recv(dict:new())). + +recv(Dict) -> + receive + Pid when is_pid(Pid) -> + Pid ! {self(), Dict}; + {Reason, Ref} -> + recv(dict:store(Ref, Reason, Dict)) + end. |