anchor.diff:           end_per_suite/1]).
   
   %% testcases
  --export([reopen/1, reopen/4]).
  -
  --export([start/3, %% diameter_transport callback
  -         id/1,    %% jitter callback
  -         run/1]).
  +-export([reopen/1, reopen/4, reopen/7]).
  +
  +-export([id/1,    %% jitter callback
  +         run1/1]).
  +
  +%% diameter_app callbacks
  +-export([peer_up/3,
  +         peer_down/3]).
  +
  +%% gen_tcp-ish interface
  +-export([listen/2,
  +         accept/1,
  +         connect/3,
  +         send/2,
  +         setopts/2]).
   
   -include("diameter.hrl").
   -include("diameter_ct.hrl").
  
positive.code: %%
  %% %CopyrightBegin%
  %%
  %% Copyright Ericsson AB 2010-2011. 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
  
negative.code: %% implement a transport process that plays the role of the peer
  %% Diameter node.
  
  %reopen(_) ->
  %    reopen(connect, ?WD(10000), 1, 'DWR');
  
  reopen(_) ->
      [] = run([[reopen, T, Wd, N, M]
                || Wd <- ?WD_TIMERS,       %% watchdog_timer value
                   T <- [listen, connect], %% watchdog to test
                   N <- [0,1,2],           %% DWR's to answer before ignoring
                   M <- ['DWR', 'DWA', 'RAA']]). %% how to induce failback
  
  reopen(Type, Wd, N, M) ->
      Server = start_service(),
      Client = start_service(),
  
      %% The peer to the transport whose watchdog is tested is given a
      %% long watchdog timeout so that it doesn't send DWR of its own.
      {Node, Peer} = {{[], Wd}, {[{module, ?MODULE}], ?WD(?PEER_WD)}},
  
      {{LH,LW},{CH,CW}} = case Type of
                              listen  -> {Node, Peer};
                              connect -> {Peer, Node}
                          end,
  
      LO = [{transport_module, diameter_tcp},
            {transport_config, LH ++ [{ip, ?ADDR}, {port, 0}]},
            {watchdog_timer, LW}],
  
      {ok, LRef} = diameter:add_transport(Server, {listen, LO}),
  
      [LP] = ?util:lport(tcp, LRef, 20),
  
      CO = [{transport_module, diameter_tcp},
            {transport_config, CH ++ [{ip, ?ADDR}, {port, 0},
                                      {raddr, ?ADDR}, {rport, LP}]},
            {watchdog_timer, CW}],
  
      %% Use a temporary process to ensure the connecting transport is
      %% added only once events from the listening transport are
      %% subscribed to.
      Pid = spawn(fun() -> receive _ -> ok end end),
  
      [] = run([[reopen, Type, T, LRef, Pid, Wd, N, M]
                || T <- [{listen, Server}, {connect, Client, CO}]]).
  
  %% start_service/1
  
  start_service() ->
      Name = hostname(),
      ok = diameter:start_service(Name, [{monitor, self()} | ?SERVICE(Name)]),
      Name.
  
  %% reopen/7
  
  reopen(Type, {listen = T, SvcName}, Ref, Pid, Wd, N, M) ->
      diameter:subscribe(SvcName),
      Pid ! ok,
      recv(Type, T, SvcName, Ref, Wd, N, M);
  
  reopen(Type, {connect = T, SvcName, Opts}, _, Pid, Wd, N, M) ->
      diameter:subscribe(SvcName),
      MRef = erlang:monitor(process, Pid),
      receive {'DOWN', MRef, process, _, _} -> ok end,
      {ok, Ref} = diameter:add_transport(SvcName, {T, Opts}),
      recv(Type, T, SvcName, Ref, Wd, N, M).
  
  %% recv/7
  
  %% The watchdog to be tested.
  recv(Type, Type, _SvcName, Ref, Wd, N, M) ->
      %% Connection should come up immediately as a consequence of
      %% starting the watchdog process. In the accepting case this
      %% results in a new watchdog on a transport waiting for a new
      %% connection.
  
      ?EVENT({watchdog, Ref, _, {initial, okay}, _}, 2000),
      ?EVENT({up, Ref, _, _, #diameter_packet{}}, 0),
  
      %% Low/high watchdog timeouts.
      WdL = jitter(Wd, -2000),
      WdH = jitter(Wd, 2000),
  
      %%   OKAY          Timer expires &      Failover()
      %%                 Pending              SetWatchdog()        SUSPECT
      %%
      %% The peer replies to N DWR's before becoming silent, we should
      %% go down after N+2 watchdog_timer expirations: that is, after
      %% the first unanswered DWR. Knowing the min/max watchdog timeout
      %% values gives the time interval in which the event is expected.
  
      ?EVENT({watchdog, Ref, _, {okay, suspect}, _}, N+2, WdL, WdH),
      ?EVENT({down, Ref, _, _}, 0),
  
      %%   SUSPECT       Receive DWA          Pending = FALSE
      %%                                      Failback()
  