anchor.diff:      TPid.
   
   %% ===========================================================================
  +%% # suspect/1
  +%% ===========================================================================
  +
  +%% Configure transports to require a set number of watchdogs before
  +%% moving from OKAY to SUSPECT.
  +
  +suspect(_) ->
  +    [] = run([[abuse, [suspect, N]] || N <- [0,1,3]]).
  +
  +suspect(Type, Fake, Ref, N)
  +  when is_reference(Ref) ->
  +    {SvcName, TRef}
  +        = start(Type, Ref, {?WD(10000), [{suspect, N}], mod(Fake)}),
  +    {initial, okay} = ?WD_EVENT(TRef),
  +    suspect(TRef, Fake, SvcName, N);
  +
  +suspect(TRef, true, SvcName, _) ->
  +    reg(TRef, SvcName, 0),  %% disable outgoing DWA
  +    {okay, _} = ?WD_EVENT(TRef);
  +
  +suspect(TRef, false, SvcName, 0) ->  %% SUSPECT disabled
  +    %% Wait 2+ watchdogs and see that two unanswered watchdogs have
  +    %% been sent.
  +    [2,0,0,0] = receive
  +                    ?INFO({watchdog, TRef, _, _, _} = T) -> T
  +                after 28000 ->
  +                        wd_counts(SvcName)
  +                end;
  +
  +suspect(TRef, false, SvcName, N) ->
  +    {okay, suspect} = ?WD_EVENT(TRef),
  +    [N,0,0,0] = wd_counts(SvcName),
  +    {suspect, down} = ?WD_EVENT(TRef),
  +    [N,0,0,0] = wd_counts(SvcName).
  +
  +%% abuse/1
  +
  +abuse(F) ->
  +    [] = run([[abuse, F, T] || T <- [listen, connect]]).
  +
  +abuse(F, [_,_,_|_] = Args) ->
  +    ?LOG("~p", [Args]),
  +    apply(?MODULE, F, Args);
  +
  +abuse([F|A], Test) ->
  +    Ref = make_ref(),
  +    [] = run([[abuse, F, [T, T == Test, Ref] ++ A]
  +              || T <- [listen, connect]]);
  +
  +abuse(F, Test) ->
  +    abuse([F], Test).
  +
  +mod(true) ->
  +    [{module, ?MODULE}];
  +mod(false) ->
  +    [].
  +
  +%% ===========================================================================
  +%% # okay/1
  +%% ===========================================================================
  +
  +%% Configure the number of watchdog exchanges before moving from
  +%% REOPEN to OKAY.
  +
  +okay(_) ->
  +    [] = run([[abuse, [okay, N]] || N <- [0,2,3]]).
  +
  +okay(Type, Fake, Ref, N)
  +  when is_reference(Ref) ->
  +    {SvcName, TRef}
  +        = start(Type, Ref, {?WD(10000),
  +                            [{okay, choose(Fake, 0, N)}],
  +                            mod(Fake)}),
  +    {initial, okay} = ?WD_EVENT(TRef),
  +    okay(TRef,
  +         Fake,
  +         SvcName,
  +         choose(Type == listen, initial, down),
  +         N).
  +
  +okay(TRef, true, SvcName, Down, _) ->
  +    reg(TRef, SvcName, 0),  %% disable outgoing DWA
  +    {okay, down} = ?WD_EVENT(TRef),
  +    {Down, okay} = ?WD_EVENT(TRef),
  +    reg(TRef, SvcName, -1), %% enable outgoing DWA
  +    {okay, down} = ?WD_EVENT(TRef);
  +
  +okay(TRef, false, SvcName, Down, N) ->
  +    {okay, suspect} = ?WD_EVENT(TRef),
  +    [1,0,0,0] = wd_counts(SvcName),
  +    {suspect, down} = ?WD_EVENT(TRef),
  +    ok(TRef, SvcName, Down, N).
  +
  +ok(TRef, SvcName, Down, 0) ->
  +    %% Connection comes up without watchdog exchange.
  +    {Down, okay} = ?WD_EVENT(TRef),
  +    [1,0,0,0] = wd_counts(SvcName),
  +    %% Wait 2+ watchdog timeout to see that the connection stays up and
  +    %% two watchdogs are exchanged.
  +    ok = receive ?INFO({watchdog, TRef, _, _, _} = T) ->
  +                 T
  +         after 28000 ->
  +                 ok
  +         end,
  +    [3,0,0,2] = wd_counts(SvcName);
  +
  +ok(TRef, SvcName, Down, N) ->
  +    %% Connection required watchdog exchange before reaching OKAY.
  +    {Down, reopen} = ?WD_EVENT(TRef),
  +    {reopen, okay} = ?WD_EVENT(TRef),
  +    %% One DWR was sent in moving to expect, plus N more to reopen the
  +    %% connection.
  +    N1 = N+1,
  +    [N1,0,0,N] = wd_counts(SvcName).
  +
  +%% ===========================================================================
  +
  +%% wd_counts/1
  +
  +wd_counts(SvcName) ->
  +    [Info] = diameter:service_info(SvcName, transport),
  +    {_, Counters} = lists:keyfind(statistics, 1, Info),
  +    [proplists:get_value({{0,280,R}, D}, Counters, 0) || D <- [send,recv],
  +                                                         R <- [1,0]].
  +
  +%% start/3
  +
  +start(Type, Ref, T) ->
  +    Name = hostname(),
  +    true = diameter:subscribe(Name),
  +    ok = diameter:start_service(Name, [{monitor, self()} | ?SERVICE(Name)]),
  +    {ok, TRef} = diameter:add_transport(Name, {Type, opts(Type, Ref, T)}),
  +    true = diameter_reg:add_new({Type, Ref, Name}),
  +    {Name, TRef}.
  +
  +opts(Type, Ref, {Timer, Config, Mod}) ->
  +    [{transport_module, diameter_tcp},
  +     {transport_config, Mod ++ [{ip, ?ADDR}, {port, 0}] ++ cfg(Type, Ref)},
  +     {watchdog_timer, Timer},
  +     {watchdog_config, Config}].
  +
  +cfg(listen, _) ->
  +    [];
  +cfg(connect, Ref) ->
  +    [{{_, _, SvcName}, _Pid}] = diameter_reg:wait({listen, Ref, '_'}),
  +    [[{ref, LRef} | _]] = diameter:service_info(SvcName, transport),
  +    [LP] = ?util:lport(tcp, LRef, 20),
  +    [{raddr, ?ADDR}, {rport, LP}].
  +
  +%% ===========================================================================
   
   listen(PortNr, Opts) ->
       gen_tcp:listen(PortNr, Opts).
  
positive.code:          end_per_suite/1]).
  
  %% testcases
  -export([reopen/1, reopen/4, reopen/6]).
  
  -export([id/1,    %% jitter callback
           run1/1]).
  
  %% diameter_app callbacks
  -export([peer_up/3,
  
negative.code: %% Watchdog timers used by the testcases.
  -define(WD_TIMERS, [10000, ?WD(10000)]).
  
  %% Watchdog timer of the misbehaving node.
  -define(PEER_WD, 10000).
  
  %% A timeout that ensures one watchdog. To ensure only one watchdog
  %% requires (Wd + 2000) + 1000 < 2*(Wd - 2000) ==> 7000 < Wd for the
  %% case with random jitter.
  -define(ONE_WD(Wd), jitter(Wd,2000) + 1000).
  -define(INFO(T), #diameter_event{info = T}).
  
  %% Receive an event message from diameter.
  -define(EVENT(T),
  