%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2014-2015. 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%
%%
%%
%% Some gen_tcp-specific tests demonstrating problems that were
%% encountered during diameter development but have nothing
%% specifically to do with diameter. These can cause testcases in
%% other suites to fail.
%%
-module(diameter_gen_tcp_SUITE).
-export([suite/0,
all/0]).
%% testcases
-export([send_long/1,
connect/1]).
-define(LOOPBACK, {127,0,0,1}).
-define(GEN_OPTS, [binary, {active, true}, {ip, ?LOOPBACK}]).
%% ===========================================================================
suite() ->
[{timetrap, {seconds, 10}}].
all() ->
[connect, %% Appears to fail only when run first.
send_long].
%% ===========================================================================
%% send_long/1
%%
%% Test that a long message is received.
send_long(_) ->
{Sock, SendF} = connection(),
B = list_to_binary(lists:duplicate(1 bsl 20, $X)),
ok = SendF(B),
B = recv(Sock, size(B), []).
recv(_, 0, Acc) ->
list_to_binary(lists:reverse(Acc));
recv(Sock, N, Acc) ->
receive
{tcp, Sock, Bin} ->
recv(Sock, N - size(Bin), [Bin | Acc]);
T ->
{T, Acc}
end.
%% connection/0
connection() ->
{ok, LSock} = gen_tcp:listen(0, ?GEN_OPTS),
{ok, PortNr} = inet:port(LSock),
LPid = self(),
{Pid, MRef} = spawn_monitor(fun() -> connect(PortNr, LPid) end),
{ok, Sock} = gen_tcp:accept(LSock),
receive
{Pid, F} ->
{Sock, F};
{'DOWN', MRef, process, _, _} = T ->
T
end.
%% connect/2
connect(PortNr, LPid) ->
{ok, Sock} = gen_tcp:connect(?LOOPBACK, PortNr, ?GEN_OPTS),
LPid ! {self(), fun(B) -> send(Sock, B) end},
down(LPid).
%% send/2
%%
%% Send from a spawned process just to avoid sending from the
%% receiving process, in case it's significant.
send(Sock, Bin) ->
{_, MRef} = spawn_monitor(fun() -> exit(gen_tcp:send(Sock, Bin)) end),
down(MRef).
%% ===========================================================================
%% connect/1
%%
%% Test that simultaneous connections succeed. This fails sporadically
%% on OS X at the time of writing, when gen_tcp:connect/2 returns
%% {error, econnreset}.
connect(_) ->
{ok, LSock} = gen_tcp:listen(0, ?GEN_OPTS),
{ok, {_,PortNr}} = inet:sockname(LSock),
Count = lists:seq(1,8), %% 8 simultaneous connects
As = [gen_accept(LSock) || _ <- Count],
%% Wait for spawned processes to have called gen_tcp:accept/1
%% (presumably).
receive after 2000 -> ok end,
Cs = [gen_connect(PortNr) || _ <- Count],
[] = failures(Cs),
[] = failures(As).
failures(Monitors) ->
[RC || {_, MRef} <- Monitors, RC <- [down(MRef)], ok /= element(1, RC)].
gen_accept(LSock) ->
spawn_monitor(fun() ->
exit(gen_tcp:accept(LSock))
end).
gen_connect(PortNr) ->
spawn_monitor(fun() ->
exit(gen_tcp:connect(?LOOPBACK, PortNr, ?GEN_OPTS))
end).
%% ===========================================================================
%% down/1
down(Pid)
when is_pid(Pid) ->
down(monitor(process, Pid));
down(MRef) ->
receive {'DOWN', MRef, process, _, Reason} -> Reason end.