1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2014-2017. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%% Some gen_tcp-specific tests demonstrating problems that were
23%% encountered during diameter development but have nothing
24%% specifically to do with diameter. These can cause testcases in
25%% other suites to fail.
26%%
27
28-module(diameter_gen_tcp_SUITE).
29
30-export([suite/0,
31         all/0]).
32
33%% testcases
34-export([send_long/1,
35         connect/1]).
36
37-define(LOOPBACK, {127,0,0,1}).
38-define(GEN_OPTS, [binary, {active, true}, {ip, ?LOOPBACK}]).
39
40%% ===========================================================================
41
42suite() ->
43    [{timetrap, {seconds, 10}}].
44
45all() ->
46    [connect,     %% Appears to fail only when run first.
47     send_long].
48
49%% ===========================================================================
50
51%% send_long/1
52%%
53%% Test that a long message is received.
54
55send_long(_) ->
56    {Sock, SendF} = connection(),
57    B = binary:copy(<<$X>>, 1 bsl 20),
58    ok = SendF(B),
59    B = recv(Sock, size(B), []).
60
61recv(_, 0, Acc) ->
62    list_to_binary(lists:reverse(Acc));
63recv(Sock, N, Acc) ->
64    receive
65        {tcp, Sock, Bin} ->
66            recv(Sock, N - size(Bin), [Bin | Acc]);
67        T ->
68            {T, Acc}
69    end.
70
71%% connection/0
72
73connection() ->
74    {ok, LSock} = gen_tcp:listen(0, ?GEN_OPTS),
75    {ok, PortNr} = inet:port(LSock),
76    LPid = self(),
77    {Pid, MRef} = spawn_monitor(fun() -> connect(PortNr, LPid) end),
78    {ok, Sock} = gen_tcp:accept(LSock),
79    receive
80        {Pid, F} ->
81            {Sock, F};
82        {'DOWN', MRef, process, _, _} = T ->
83            T
84    end.
85
86%% connect/2
87
88connect(PortNr, LPid) ->
89    {ok, Sock} = gen_tcp:connect(?LOOPBACK, PortNr, ?GEN_OPTS),
90    LPid ! {self(), fun(B) -> send(Sock, B) end},
91    down(LPid).
92
93%% send/2
94%%
95%% Send from a spawned process just to avoid sending from the
96%% receiving process, in case it's significant.
97
98send(Sock, Bin) ->
99    {_, MRef} = spawn_monitor(fun() -> exit(gen_tcp:send(Sock, Bin)) end),
100    down(MRef).
101
102%% ===========================================================================
103
104%% connect/1
105%%
106%% Test that simultaneous connections succeed. This fails sporadically
107%% on OS X at the time of writing, when gen_tcp:connect/2 returns
108%% {error, econnreset}.
109
110connect(_) ->
111    {ok, LSock} = gen_tcp:listen(0, ?GEN_OPTS),
112    {ok, {_,PortNr}} = inet:sockname(LSock),
113    Count = lists:seq(1,8),  %% 8 simultaneous connects
114    As = [gen_accept(LSock) || _ <- Count],
115    %% Wait for spawned processes to have called gen_tcp:accept/1
116    %% (presumably).
117    receive after 2000 -> ok end,
118    Cs = [gen_connect(PortNr) || _ <- Count],
119    [] = failures(Cs),
120    [] = failures(As).
121
122failures(Monitors) ->
123    [RC || {_, MRef} <- Monitors, RC <- [down(MRef)], ok /= element(1, RC)].
124
125gen_accept(LSock) ->
126    spawn_monitor(fun() ->
127                          exit(gen_tcp:accept(LSock))
128                  end).
129
130gen_connect(PortNr) ->
131    spawn_monitor(fun() ->
132                          exit(gen_tcp:connect(?LOOPBACK, PortNr, ?GEN_OPTS))
133                  end).
134
135%% ===========================================================================
136
137%% down/1
138
139down(Pid)
140  when is_pid(Pid) ->
141    down(monitor(process, Pid));
142
143down(MRef) ->
144    receive {'DOWN', MRef, process, _, Reason} -> Reason end.
145