aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/test/gen_tcp_misc_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/test/gen_tcp_misc_SUITE.erl')
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl1171
1 files changed, 733 insertions, 438 deletions
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 81c6dcd0fd..04c0c48e3a 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,15 +19,15 @@
%%
-module(gen_tcp_misc_SUITE).
--include_lib("test_server/include/test_server.hrl").
-
-%-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
controlling_process/1, controlling_process_self/1,
no_accept/1, close_with_pending_output/1, active_n/1,
- data_before_close/1, iter_max_socks/1, get_status/1,
+ data_before_close/1,
+ iter_max_socks/0, iter_max_socks/1,
+ get_status/1,
passive_sockets/1, accept_closed_by_other_process/1,
init_per_testcase/2, end_per_testcase/2,
otp_3924/1, otp_3924_sender/4, closed_socket/1,
@@ -41,7 +41,8 @@
busy_send/1, busy_disconnect_passive/1, busy_disconnect_active/1,
fill_sendq/1, partial_recv_and_close/1,
partial_recv_and_close_2/1,partial_recv_and_close_3/1,so_priority/1,
- % Accept tests
+ recvtos/1, recvttl/1, recvtosttl/1, recvtclass/1,
+ %% Accept tests
primitive_accept/1,multi_accept_close_listen/1,accept_timeout/1,
accept_timeouts_in_order/1,accept_timeouts_in_order2/1,
accept_timeouts_in_order3/1,accept_timeouts_in_order4/1,
@@ -50,49 +51,24 @@
killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1,
several_accepts_in_one_go/1, accept_system_limit/1,
active_once_closed/1, send_timeout/1, send_timeout_active/1,
- otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, wrapping_oct/1,
- otp_9389/1]).
+ otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1,
+ wrapping_oct/0, wrapping_oct/1, otp_9389/1, otp_13939/1,
+ otp_12242/1]).
%% Internal exports.
-export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1,
oct_acceptor/1,
otp_7731_server/1, zombie_server/2, do_iter_max_socks/2]).
-init_per_testcase(wrapping_oct, Config) when is_list(Config) ->
- Dog = case os:type() of
- {ose,_} ->
- test_server:timetrap(test_server:minutes(20));
- _Else ->
- test_server:timetrap(test_server:seconds(600))
- end,
- [{watchdog, Dog}|Config];
-init_per_testcase(iter_max_socks, Config) when is_list(Config) ->
- Dog = case os:type() of
- {win32,_} ->
- test_server:timetrap(test_server:minutes(30));
- _Else ->
- test_server:timetrap(test_server:seconds(240))
- end,
- [{watchdog, Dog}|Config];
-init_per_testcase(accept_system_limit, Config) when is_list(Config) ->
- case os:type() of
- {ose,_} ->
- {skip,"Skip in OSE"};
- _ ->
- Dog = test_server:timetrap(test_server:seconds(240)),
- [{watchdog,Dog}|Config]
- end;
-init_per_testcase(wrapping_oct, Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(600)),
- [{watchdog, Dog}|Config];
-init_per_testcase(_Func, Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(240)),
- [{watchdog, Dog}|Config].
-end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+init_per_testcase(_Func, Config) ->
+ Config.
+
+end_per_testcase(_Func, _Config) ->
+ ok.
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,4}}].
all() ->
[controlling_process, controlling_process_self, no_accept,
@@ -109,7 +85,8 @@ all() ->
busy_disconnect_passive, busy_disconnect_active,
fill_sendq, partial_recv_and_close,
partial_recv_and_close_2, partial_recv_and_close_3,
- so_priority, primitive_accept,
+ so_priority, recvtos, recvttl, recvtosttl,
+ recvtclass, primitive_accept,
multi_accept_close_listen, accept_timeout,
accept_timeouts_in_order, accept_timeouts_in_order2,
accept_timeouts_in_order3, accept_timeouts_in_order4,
@@ -119,7 +96,8 @@ all() ->
killing_multi_acceptors2, several_accepts_in_one_go, accept_system_limit,
active_once_closed, send_timeout, send_timeout_active, otp_7731,
wrapping_oct,
- zombie_sockets, otp_7816, otp_8102, otp_9389].
+ zombie_sockets, otp_7816, otp_8102, otp_9389,
+ otp_12242].
groups() ->
[].
@@ -136,11 +114,13 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-default_options(doc) ->
- ["Tests kernel application variables inet_default_listen_options and "
- "inet_default_connect_options"];
-default_options(suite) ->
- [];
+-define(UNIQ_NODE_NAME,
+ list_to_atom(?MODULE_STRING ++ "__" ++
+ atom_to_list(?FUNCTION_NAME) ++ "_" ++
+ integer_to_list(erlang:unique_integer([positive])))).
+
+%% Tests kernel application variables inet_default_listen_options and
+%% inet_default_connect_options.
default_options(Config) when is_list(Config) ->
%% First check the delay_send option
{true,true,true}=do_delay_send_1(),
@@ -215,7 +195,7 @@ default_options(Config) when is_list(Config) ->
do_delay_on_other_node(XArgs, Function) ->
Dir = filename:dirname(code:which(?MODULE)),
- {ok,Node} = test_server:start_node(test_default_options_slave,slave,
+ {ok,Node} = test_server:start_node(?UNIQ_NODE_NAME, slave,
[{args,"-pa " ++ Dir ++ " " ++ XArgs}]),
Res = rpc:call(Node,erlang,apply,[Function,[]]),
test_server:stop_node(Node),
@@ -312,11 +292,9 @@ do_delay_send_7() ->
gen_tcp:close(LS),
{B1,B2,B3}.
-controlling_process(doc) ->
- ["Open a listen port and change controlling_process for it",
- "The result should be ok of done by the owner process,"
- "Otherwise is should return {error,not_owner} or similar"];
-controlling_process(suite) -> [];
+%% Open a listen port and change controlling_process for it
+%% The result should be ok of done by the owner process,
+%% Otherwise is should return {error,not_owner} or similar.
controlling_process(Config) when is_list(Config) ->
{ok,S} = gen_tcp:listen(0,[]),
Pid2 = spawn(?MODULE,not_owner,[S]),
@@ -345,9 +323,8 @@ not_owner(S) ->
ok
end.
-controlling_process_self(doc) ->
- ["Open a listen port and assign the controlling process to "
- "it self, then exit and make sure the port is closed properly."];
+%% Open a listen port and assign the controlling process to
+%% it self, then exit and make sure the port is closed properly.
controlling_process_self(Config) when is_list(Config) ->
S = self(),
process_flag(trap_exit,true),
@@ -371,11 +348,9 @@ controlling_process_self(Config) when is_list(Config) ->
end.
-no_accept(doc) ->
- ["Open a listen port and connect to it, then close the listen port ",
- "without doing any accept. The connected socket should receive ",
- "a tcp_closed message."];
-no_accept(suite) -> [];
+%% Open a listen port and connect to it, then close the listen port
+%% without doing any accept. The connected socket should receive
+%% a tcp_closed message.
no_accept(Config) when is_list(Config) ->
{ok, L} = gen_tcp:listen(0, []),
{ok, {_, Port}} = inet:sockname(L),
@@ -385,14 +360,12 @@ no_accept(Config) when is_list(Config) ->
{tcp_closed, Client} ->
ok
after 5000 ->
- test_server:fail(never_closed)
+ ct:fail(never_closed)
end.
-close_with_pending_output(doc) ->
- ["Send several packets to a socket and close it. All packets should arrive ",
- "to the other end."];
-close_with_pending_output(suite) -> [];
+%% Send several packets to a socket and close it. All packets should
+%% arrive to the other end.
close_with_pending_output(Config) when is_list(Config) ->
{ok, L} = gen_tcp:listen(0, [binary, {active, false}]),
{ok, {_, Port}} = inet:sockname(L),
@@ -408,16 +381,16 @@ close_with_pending_output(Config) when is_list(Config) ->
gen_tcp:close(A),
gen_tcp:close(L);
{ok, Bin} ->
- test_server:fail({small_packet,
+ ct:fail({small_packet,
byte_size(Bin)});
Error ->
- test_server:fail({unexpected, Error})
+ ct:fail({unexpected, Error})
end,
ok;
{error, no_remote_hosts} ->
{skipped,"No remote hosts"};
{error, Other} ->
- ?t:fail({failed_to_start_slave_node, Other})
+ ct:fail({failed_to_start_slave_node, Other})
end.
sender(Port, Packets, Host) ->
@@ -435,9 +408,7 @@ send_loop(Sock, Data, Left) ->
send_loop(Sock, Data, Left-1).
%% Test {active,N} option
-active_n(doc) ->
- ["Verify operation of the {active,N} option."];
-active_n(suite) -> [];
+%% Verify operation of the {active,N} option.
active_n(Config) when is_list(Config) ->
N = 3,
LS = ok(gen_tcp:listen(0, [{active,N}])),
@@ -547,9 +518,7 @@ active_n(Config) when is_list(Config) ->
%% I expect propagation of a close to be quite fast
%% so 100 ms seems reasonable.
-otp_3924(doc) ->
- ["Tests that a socket can be closed fast enough."];
-otp_3924(suite) -> [];
+%% Tests that a socket can be closed fast enough.
otp_3924(Config) when is_list(Config) ->
MaxDelay = (case has_superfluous_schedulers() of
true -> 4;
@@ -567,8 +536,8 @@ otp_3924_1(MaxDelay) ->
{ok, Node} = start_node(otp_3924),
DataLen = 100*1024,
Data = otp_3924_data(DataLen),
- % Repeat the test a couple of times to prevent the test from passing
- % by chance.
+ %% Repeat the test a couple of times to prevent the test from passing
+ %% by chance.
repeat(10, fun(N) ->
ok = otp_3924(MaxDelay, Node, Data, DataLen, N)
end),
@@ -607,17 +576,17 @@ otp_3924_receive_data(LSock, Sender, MaxDelay, Len, N) ->
process_flag(priority, OP),
receive
{'EXIT', _, TimeoutRef} ->
- test_server:fail({close_not_fast_enough,MaxDelay,N});
+ ct:fail({close_not_fast_enough,MaxDelay,N});
{'EXIT', Sender, Reason} ->
- test_server:fail({sender_exited, Reason});
+ ct:fail({sender_exited, Reason});
{'EXIT', _Other, Reason} ->
- test_server:fail({linked_process_exited, Reason})
+ ct:fail({linked_process_exited, Reason})
after 0 ->
case Data of
{'EXIT', {A,B}} ->
- test_server:fail({A,B,N});
+ ct:fail({A,B,N});
{'EXIT', Failure} ->
- test_server:fail(Failure);
+ ct:fail(Failure);
_ ->
Data
end
@@ -676,8 +645,7 @@ otp_3924_sender(Receiver, Host, Port, Data) ->
end.
-data_before_close(doc) ->
- ["Tests that a huge amount of data can be received before a close."];
+%% Tests that a huge amount of data can be received before a close.
data_before_close(Config) when is_list(Config) ->
{ok, L} = gen_tcp:listen(0, [binary]),
{ok, {_, TcpPort}} = inet:sockname(L),
@@ -689,7 +657,7 @@ data_before_close(Config) when is_list(Config) ->
io:format("Result: ~p", [Result]);
{Wrong, Result} ->
io:format("Result: ~p", [Result]),
- test_server:fail({wrong_count, Wrong})
+ ct:fail({wrong_count, Wrong})
end,
ok.
@@ -714,11 +682,9 @@ make_zero_packet(N) when N rem 2 == 0 ->
make_zero_packet(N) ->
P = make_zero_packet(N div 2),
[0, P|P].
-get_status(doc) ->
- ["OTP-2924",
- "test that the socket process does not crash when sys:get_status(Pid)",
- "is called."];
-get_status(suite) -> [];
+
+%% OTP-2924. Test that the socket process does not crash when
+%% sys:get_status(Pid) is called.
get_status(Config) when is_list(Config) ->
{ok,{socket,Pid,_,_}} = gen_tcp:listen(5678,[]),
{status,Pid,_,_} = sys:get_status(Pid).
@@ -726,9 +692,11 @@ get_status(Config) when is_list(Config) ->
-define(RECOVER_SLEEP, 60000).
-define(RETRY_SLEEP, 15000).
-iter_max_socks(doc) ->
- ["Open as many sockets as possible. Do this several times and check ",
- "that we get the same number of sockets every time."];
+iter_max_socks() ->
+ [{timetrap,{minutes,30}}].
+
+%% Open as many sockets as possible. Do this several times and check
+%% that we get the same number of sockets every time.
iter_max_socks(Config) when is_list(Config) ->
N = case os:type() of {win32,_} -> 10; _ -> 20 end,
%% Run on a different node in order to limit the effect if this test fails.
@@ -757,7 +725,7 @@ do_iter_max_socks(N, First) when is_integer(First) ->
true ->
io:format("Sleeping for ~p seconds...~n",
[?RETRY_SLEEP/1000]),
- ?t:sleep(?RETRY_SLEEP),
+ ct:sleep(?RETRY_SLEEP),
io:format("Trying again...~n", []),
RetryMS = max_socks(),
if RetryMS == First ->
@@ -775,10 +743,10 @@ all_equal([Rule | T]) ->
all_equal(Rule, [Rule | T]) ->
all_equal(Rule, T);
all_equal(_, [_ | _]) ->
- ?t:sleep(?RECOVER_SLEEP), % Wait a while and *hope* that we'll
- % recover so other tests won't be
- % affected.
- ?t:fail(max_socket_mismatch);
+ ct:sleep(?RECOVER_SLEEP), % Wait a while and *hope* that we'll
+ %% recover so other tests won't be
+ %% affected.
+ ct:fail(max_socket_mismatch);
all_equal(_Rule, []) ->
ok.
@@ -820,21 +788,20 @@ start_remote(Name) ->
Pa = filename:dirname(code:which(?MODULE)),
test_server:start_node(Name, slave, [{remote, true}, {args, "-pa " ++ Pa}]).
-passive_sockets(doc) ->
- ["Tests that when 'the other side' on a passive socket closes, the connecting",
- "side still can read until the end of data."];
+%% Tests that when 'the other side' on a passive socket closes, the
+%% connecting, side still can read until the end of data.
passive_sockets(Config) when is_list(Config) ->
spawn_link(?MODULE, passive_sockets_server,
[[{active,false}],self()]),
receive
{socket,Port} -> ok
end,
- ?t:sleep(500),
+ ct:sleep(500),
case gen_tcp:connect("localhost", Port, [{active, false}]) of
{ok, Sock} ->
passive_sockets_read(Sock);
Error ->
- ?t:fail({"Could not connect to server", Error})
+ ct:fail({"Could not connect to server", Error})
end.
%%
@@ -850,7 +817,7 @@ passive_sockets_read(Sock) ->
gen_tcp:close(Sock);
Error ->
gen_tcp:close(Sock),
- ?t:fail({"Did not get {error, closed} before other error", Error})
+ ct:fail({"Did not get {error, closed} before other error", Error})
end.
passive_sockets_server(Opts, Parent) ->
@@ -860,17 +827,17 @@ passive_sockets_server(Opts, Parent) ->
Parent ! {socket,Port},
passive_sockets_server_accept(LSock);
Error ->
- ?t:fail({"Could not create listen socket", Error})
+ ct:fail({"Could not create listen socket", Error})
end.
passive_sockets_server_accept(Sock) ->
case gen_tcp:accept(Sock) of
{ok, Socket} ->
- ?t:sleep(500), % Simulate latency
+ timer:sleep(500), % Simulate latency
passive_sockets_server_send(Socket, 5),
passive_sockets_server_accept(Sock);
Error ->
- ?t:fail({"Could not accept connection", Error})
+ ct:fail({"Could not accept connection", Error})
end.
passive_sockets_server_send(Socket, 0) ->
@@ -880,16 +847,15 @@ passive_sockets_server_send(Socket, X) ->
Data = lists:duplicate(1024*X, $a),
case gen_tcp:send(Socket, Data) of
ok ->
- ?t:sleep(50), % Simulate some processing.
+ ct:sleep(50), % Simulate some processing.
passive_sockets_server_send(Socket, X-1);
{error, _Reason} ->
- ?t:fail("Failed to send data")
+ ct:fail("Failed to send data")
end.
-accept_closed_by_other_process(doc) ->
- ["Tests the return value from gen_tcp:accept when ",
- "the socket is closed from another process. (OTP-3817)"];
+%% Tests the return value from gen_tcp:accept when
+%% the socket is closed from another process. (OTP-3817)
accept_closed_by_other_process(Config) when is_list(Config) ->
Parent = self(),
{ok, ListenSocket} = gen_tcp:listen(0, []),
@@ -904,7 +870,7 @@ accept_closed_by_other_process(Config) when is_list(Config) ->
{Child, {error, closed}} ->
ok;
{Child, Other} ->
- ?t:fail({"Wrong result of gen_tcp:accept", Other})
+ ct:fail({"Wrong result of gen_tcp:accept", Other})
end.
repeat(N, Fun) ->
@@ -917,10 +883,7 @@ repeat(_, _, _) ->
ok.
-closed_socket(suite) ->
- [];
-closed_socket(doc) ->
- ["Tests the response when using a closed socket as argument"];
+%% Tests the response when using a closed socket as argument.
closed_socket(Config) when is_list(Config) ->
{ok, LS1} = gen_tcp:listen(0, []),
erlang:yield(),
@@ -932,7 +895,7 @@ closed_socket(Config) when is_list(Config) ->
%% in inet_db processes the 'EXIT' message from the port,
%% the socket is unregistered.
%%
- %% test_server:sleep(test_server:seconds(2)),
+ %% ct:sleep({seconds,2})
%%
{error, R_send} = gen_tcp:send(LS1, "data"),
{error, R_recv} = gen_tcp:recv(LS1, 17),
@@ -974,7 +937,7 @@ shutdown_common(Active) ->
do_sort(P, []),
receive
Any ->
- ?t:fail({unexpected_message,Any})
+ ct:fail({unexpected_message,Any})
after 0 -> ok
end.
@@ -1055,7 +1018,7 @@ shutdown_pending(Config) when is_list(Config) ->
io:format("~p\n", [Msg]),
N = list_to_integer(Msg) - 5;
Other ->
- ?t:fail({unexpected,Other})
+ ct:fail({unexpected,Other})
end,
ok.
@@ -1107,9 +1070,9 @@ show_econnreset_active(Config) when is_list(Config) ->
{tcp_closed, S} ->
ok;
Other ->
- ?t:fail({unexpected1, Other})
+ ct:fail({unexpected1, Other})
after 1000 ->
- ?t:fail({timeout, {server, no_tcp_closed}})
+ ct:fail({timeout, {server, no_tcp_closed}})
end,
%% Now test with option switched on.
@@ -1128,14 +1091,14 @@ show_econnreset_active(Config) when is_list(Config) ->
{tcp_closed, S1} ->
ok;
Other1 ->
- ?t:fail({unexpected2, Other1})
+ ct:fail({unexpected2, Other1})
after 1 ->
- ?t:fail({timeout, {server, no_tcp_closed}})
+ ct:fail({timeout, {server, no_tcp_closed}})
end;
Other2 ->
- ?t:fail({unexpected3, Other2})
+ ct:fail({unexpected3, Other2})
after 1000 ->
- ?t:fail({timeout, {server, no_tcp_error}})
+ ct:fail({timeout, {server, no_tcp_error}})
end.
show_econnreset_active_once(Config) when is_list(Config) ->
@@ -1149,7 +1112,7 @@ show_econnreset_active_once(Config) when is_list(Config) ->
ok = gen_tcp:close(L),
ok = inet:setopts(Client, [{linger, {true, 0}}]),
ok = gen_tcp:close(Client),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
ok = receive Msg -> {unexpected_msg, Msg} after 0 -> ok end,
ok = inet:setopts(S, [{active, once}]),
receive
@@ -1158,14 +1121,14 @@ show_econnreset_active_once(Config) when is_list(Config) ->
{tcp_closed, S} ->
ok;
Other1 ->
- ?t:fail({unexpected1, Other1})
+ ct:fail({unexpected1, Other1})
after 1 ->
- ?t:fail({timeout, {server, no_tcp_closed}})
+ ct:fail({timeout, {server, no_tcp_closed}})
end;
Other2 ->
- ?t:fail({unexpected2, Other2})
+ ct:fail({unexpected2, Other2})
after 1000 ->
- ?t:fail({timeout, {server, no_tcp_error}})
+ ct:fail({timeout, {server, no_tcp_error}})
end.
show_econnreset_passive(Config) when is_list(Config) ->
@@ -1177,7 +1140,7 @@ show_econnreset_passive(Config) when is_list(Config) ->
ok = gen_tcp:close(L),
ok = inet:setopts(S, [{linger, {true, 0}}]),
ok = gen_tcp:close(S),
- ok = ?t:sleep(1),
+ ok = ct:sleep(1),
{error, closed} = gen_tcp:recv(Client, 0),
%% Now test with option switched on.
@@ -1190,7 +1153,7 @@ show_econnreset_passive(Config) when is_list(Config) ->
ok = gen_tcp:close(L1),
ok = inet:setopts(S1, [{linger, {true, 0}}]),
ok = gen_tcp:close(S1),
- ok = ?t:sleep(1),
+ ok = ct:sleep(1),
{error, econnreset} = gen_tcp:recv(Client1, 0).
econnreset_after_sync_send(Config) when is_list(Config) ->
@@ -1202,7 +1165,7 @@ econnreset_after_sync_send(Config) when is_list(Config) ->
ok = gen_tcp:close(L),
ok = inet:setopts(S, [{linger, {true, 0}}]),
ok = gen_tcp:close(S),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
{error, closed} = gen_tcp:send(Client, "Whatever"),
%% Now test with option switched on.
@@ -1215,7 +1178,7 @@ econnreset_after_sync_send(Config) when is_list(Config) ->
ok = gen_tcp:close(L1),
ok = inet:setopts(S1, [{linger, {true, 0}}]),
ok = gen_tcp:close(S1),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
{error, econnreset} = gen_tcp:send(Client1, "Whatever").
econnreset_after_async_send_active(Config) when is_list(Config) ->
@@ -1232,23 +1195,23 @@ econnreset_after_async_send_active(Config) when is_list(Config) ->
case erlang:port_info(Client, queue_size) of
{queue_size, N} when N > 0 -> ok;
{queue_size, 0} when OS =:= win32 -> ok;
- {queue_size, 0} = T -> ?t:fail(T)
+ {queue_size, 0} = T -> ct:fail(T)
end,
ok = gen_tcp:send(S, "Whatever"),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
ok = inet:setopts(S, [{linger, {true, 0}}]),
ok = gen_tcp:close(S),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
receive
{tcp, Client, "Whatever"} ->
receive
{tcp_closed, Client} ->
ok;
Other1 ->
- ?t:fail({unexpected1, Other1})
+ ct:fail({unexpected1, Other1})
end;
Other2 ->
- ?t:fail({unexpected2, Other2})
+ ct:fail({unexpected2, Other2})
end,
%% Now test with option switched on.
@@ -1263,13 +1226,13 @@ econnreset_after_async_send_active(Config) when is_list(Config) ->
case erlang:port_info(Client1, queue_size) of
{queue_size, N1} when N1 > 0 -> ok;
{queue_size, 0} when OS =:= win32 -> ok;
- {queue_size, 0} = T1 -> ?t:fail(T1)
+ {queue_size, 0} = T1 -> ct:fail(T1)
end,
ok = gen_tcp:send(S1, "Whatever"),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
ok = inet:setopts(S1, [{linger, {true, 0}}]),
ok = gen_tcp:close(S1),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
receive
{tcp, Client1, "Whatever"} ->
receive
@@ -1278,13 +1241,13 @@ econnreset_after_async_send_active(Config) when is_list(Config) ->
{tcp_closed, Client1} ->
ok;
Other3 ->
- ?t:fail({unexpected3, Other3})
+ ct:fail({unexpected3, Other3})
end;
Other4 ->
- ?t:fail({unexpected4, Other4})
+ ct:fail({unexpected4, Other4})
end;
Other5 ->
- ?t:fail({unexpected5, Other5})
+ ct:fail({unexpected5, Other5})
end.
econnreset_after_async_send_active_once(Config) when is_list(Config) ->
@@ -1302,13 +1265,13 @@ econnreset_after_async_send_active_once(Config) when is_list(Config) ->
case erlang:port_info(Client, queue_size) of
{queue_size, N} when N > 0 -> ok;
{queue_size, 0} when OS =:= win32 -> ok;
- {queue_size, 0} = T -> ?t:fail(T)
+ {queue_size, 0} = T -> ct:fail(T)
end,
ok = gen_tcp:send(S, "Whatever"),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
ok = inet:setopts(S, [{linger, {true, 0}}]),
ok = gen_tcp:close(S),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
ok = receive Msg -> {unexpected_msg, Msg} after 0 -> ok end,
ok = inet:setopts(Client, [{active, once}]),
receive
@@ -1317,10 +1280,10 @@ econnreset_after_async_send_active_once(Config) when is_list(Config) ->
{tcp_closed, Client} ->
ok;
Other ->
- ?t:fail({unexpected1, Other})
+ ct:fail({unexpected1, Other})
end;
Other ->
- ?t:fail({unexpected2, Other})
+ ct:fail({unexpected2, Other})
end.
econnreset_after_async_send_passive(Config) when is_list(Config) ->
@@ -1341,10 +1304,10 @@ econnreset_after_async_send_passive(Config) when is_list(Config) ->
case erlang:port_info(Client, queue_size) of
{queue_size, N} when N > 0 -> ok;
{queue_size, 0} when OS =:= win32 -> ok;
- {queue_size, 0} = T -> ?t:fail(T)
+ {queue_size, 0} = T -> ct:fail(T)
end,
ok = gen_tcp:close(S),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
{error, closed} = gen_tcp:recv(Client, 0),
%% Now test with option switched on.
@@ -1360,7 +1323,7 @@ econnreset_after_async_send_passive(Config) when is_list(Config) ->
ok = gen_tcp:send(S1, "Whatever"),
ok = gen_tcp:send(Client1, Payload),
ok = gen_tcp:close(S1),
- ok = ?t:sleep(20),
+ ok = ct:sleep(20),
{error, econnreset} = gen_tcp:recv(Client1, 0).
%%
@@ -1387,11 +1350,11 @@ linger_zero(Config) when is_list(Config) ->
case erlang:port_info(Client, queue_size) of
{queue_size, N} when N > 0 -> ok;
{queue_size, 0} when OS =:= win32 -> ok;
- {queue_size, 0} = T -> ?t:fail(T)
+ {queue_size, 0} = T -> ct:fail(T)
end,
ok = inet:setopts(Client, [{linger, {true, 0}}]),
ok = gen_tcp:close(Client),
- ok = ?t:sleep(1),
+ ok = ct:sleep(1),
undefined = erlang:port_info(Client, connected),
{error, econnreset} = gen_tcp:recv(S, PayloadSize).
@@ -1466,7 +1429,7 @@ busy_send_loop(Server, Client, N) ->
busy_send_2(Server, Client, N+1)
after 10000 ->
%% If this happens, see busy_send_srv
- ?t:fail({timeout,{server,not_send,flush([])}})
+ ct:fail({timeout,{server,not_send,flush([])}})
end
end.
@@ -1477,7 +1440,7 @@ busy_send_2(Server, Client, _N) ->
{Server,[closed]} ->
receive {Client,[0,{error,closed}]} -> ok end
after 10000 ->
- ?t:fail({timeout,{server,not_closed,flush([])}})
+ ct:fail({timeout,{server,not_closed,flush([])}})
end.
busy_send_srv(L, Master, Msg) ->
@@ -1567,7 +1530,7 @@ busy_disconnect_active_send(S, Data) ->
{error,closed} ->
receive
{tcp_closed,S} -> ok;
- _Other -> ?t:fail()
+ _Other -> ct:fail(failed)
end
end.
@@ -1613,52 +1576,56 @@ fill_sendq(Config) when is_list(Config) ->
Master = self(),
Server =
spawn_link(fun () ->
- {ok,L} = gen_tcp:listen
- (0, [{active,false},binary,
- {reuseaddr,true},{packet,0}]),
+ {ok,L} = gen_tcp:listen(0, [{active,false},binary,
+ {reuseaddr,true},{packet,0}]),
{ok,Port} = inet:port(L),
Master ! {self(),client,
fill_sendq_client(Port, Master)},
fill_sendq_srv(L, Master)
end),
io:format("~p Server~n", [Server]),
- receive {Server,client,Client} ->
- io:format("~p Client~n", [Client]),
- receive {Server,reader,Reader} ->
- io:format("~p Reader~n", [Reader]),
- fill_sendq_loop(Server, Client, Reader)
+ receive
+ {Server,client,Client} ->
+ io:format("~p Client~n", [Client]),
+ receive
+ {Server,reader,Reader} ->
+ io:format("~p Reader~n", [Reader]),
+ fill_sendq_loop(Server, Client, Reader)
end
end.
fill_sendq_loop(Server, Client, Reader) ->
%% Master
%%
- receive {Server,send} ->
+ receive
+ {Server,send} ->
fill_sendq_loop(Server, Client, Reader)
after 2000 ->
%% Send queue full, sender blocked -> close client.
io:format("Send timeout, closing Client...~n", []),
Client ! {self(),close},
- receive {Server,[{error,closed}]} ->
- io:format("Got server closed.~n"),
- receive {Reader,[{error,closed}]} ->
- io:format
- ("Got reader closed.~n"),
- ok
- after 3000 ->
- ?t:fail({timeout,{closed,reader}})
- end;
- {Reader,[{error,closed}]} ->
- io:format("Got reader closed.~n"),
- receive {Server,[{error,closed}]} ->
- io:format("Got server closed~n"),
- ok
- after 3000 ->
- ?t:fail({timeout,{closed,server}})
- end
- after 3000 ->
- ?t:fail({timeout,{closed,[server,reader]}})
- end
+ receive
+ {Server,[{error,closed}]} ->
+ io:format("Got server closed.~n"),
+ receive
+ {Reader,[{error,closed}]} ->
+ io:format("Got reader closed.~n"),
+ ok
+ after 3000 ->
+ ct:fail({timeout,{closed,reader}})
+ end;
+ {Reader,[{error,closed}]} ->
+ io:format("Got reader closed.~n"),
+ receive
+ {Server,[{error,closed}]} ->
+ io:format("Got server closed~n"),
+ ok
+ after 3000 ->
+ ct:fail({timeout,{closed,server}})
+ end
+ after 3000 ->
+ ct:fail({timeout,{closed,[server,reader]}})
+ end
end.
fill_sendq_srv(L, Master) ->
@@ -1865,7 +1832,7 @@ test_prio_accept_async() ->
spawn(?MODULE,priority_server,[{self(),Ref}]),
Port = receive
{Ref,P} -> P
- after 5000 -> ?t:fail({error,"helper process timeout"})
+ after 5000 -> ct:fail({error,"helper process timeout"})
end,
receive
after 3000 -> ok
@@ -1879,15 +1846,15 @@ test_prio_accept_async() ->
{Ref,{ok,[{priority,4},{tos,Tos1}]}} ->
ok;
{Ref,Error} ->
- ?t:fail({missmatch,Error})
- after 5000 -> ?t:fail({error,"helper process timeout"})
+ ct:fail({missmatch,Error})
+ after 5000 -> ct:fail({error,"helper process timeout"})
end,
receive
{Ref,{ok,[{priority,4},{tos,Tos1}]}} ->
ok;
{Ref,Error2} ->
- ?t:fail({missmatch,Error2})
- after 5000 -> ?t:fail({error,"helper process timeout"})
+ ct:fail({missmatch,Error2})
+ after 5000 -> ct:fail({error,"helper process timeout"})
end,
{ok,[{priority,4},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]),
@@ -1909,10 +1876,6 @@ priority_server({Parent,Ref}) ->
test_prio_fail() ->
{ok,L} = gen_tcp:listen(0, [{active,false}]),
{error,_} = inet:setopts(L,[{priority,1000}]),
-% This error could only happen in linux kernels earlier than 2.6.24.4
-% Privilege check is now disabled and IP_TOS can never fail (only silently
-% be masked).
-% {error,_} = inet:setopts(L,[{tos,6 bsl 5}]),
gen_tcp:close(L),
ok.
@@ -1924,10 +1887,7 @@ test_prio_udp() ->
gen_udp:close(S),
ok.
-so_priority(doc) ->
- ["Tests the so_priority and ip_tos options on sockets when applicable."];
-so_priority(suite) ->
- [];
+%% Tests the so_priority and ip_tos options on sockets when applicable.
so_priority(Config) when is_list(Config) ->
{ok,L} = gen_tcp:listen(0, [{active,false}]),
ok = inet:setopts(L,[{priority,1}]),
@@ -1947,7 +1907,7 @@ so_priority(Config) when is_list(Config) ->
{unix,linux} ->
case os:version() of
{X,Y,_} when (X > 2) or ((X =:= 2) and (Y >= 4)) ->
- ?t:fail({error,
+ ct:fail({error,
"so_priority should work on this "
"OS, but does not"});
_ ->
@@ -1958,10 +1918,236 @@ so_priority(Config) when is_list(Config) ->
end
end.
+
+
+%% IP_RECVTOS and IP_RECVTCLASS for IP_PKTOPTIONS
+%% does not seem to be implemented in Linux until kernel 3.1
+%%
+%% It seems pktoptions does not return valid values
+%% for IPv4 connect sockets. On the accept socket
+%% we get valid values, but on the connect socket we get
+%% the default values for TOS and TTL.
+%%
+%% Therefore the argument CheckConnect that enables
+%% checking the returned values for the connect socket.
+%% It is only used for recvtclass that is an IPv6 option
+%% and there we get valid values from both socket ends.
+
+recvtos(_Config) ->
+ test_pktoptions(
+ inet, [{recvtos,tos,96}],
+ fun recvtos_ok/2,
+ false).
+
+recvtosttl(_Config) ->
+ test_pktoptions(
+ inet, [{recvtos,tos,96},{recvttl,ttl,33}],
+ fun (OSType, OSVer) ->
+ recvtos_ok(OSType, OSVer) andalso recvttl_ok(OSType, OSVer)
+ end,
+ false).
+
+recvttl(_Config) ->
+ test_pktoptions(
+ inet, [{recvttl,ttl,33}],
+ fun recvttl_ok/2,
+ false).
+
+recvtclass(_Config) ->
+ {ok,IFs} = inet:getifaddrs(),
+ case
+ [Name ||
+ {Name,Opts} <- IFs,
+ lists:member({addr,{0,0,0,0,0,0,0,1}}, Opts)]
+ of
+ [_] ->
+ test_pktoptions(
+ inet6, [{recvtclass,tclass,224}],
+ fun recvtclass_ok/2,
+ true);
+ [] ->
+ {skip,{ipv6_not_supported,IFs}}
+ end.
+
+%% These version numbers are the highest noted in daily tests
+%% where the test fails for a plausible reason, so
+%% skip on that platform.
+%%
+%% On newer versions it might be fixed, but we'll see about that
+%% when machines with newer versions gets installed...
+%% If the test still fails for a plausible reason these
+%% version numbers simply should be increased.
+%% Or maybe we should change to only test on known good
+%% platforms - change {unix,_} to false?
+
+%% pktoptions is not supported for IPv4
+recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0});
+recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0});
+%% Using the option returns einval, so it is not implemented.
+recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0});
+recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0});
+%% Does not return any value - not implemented for pktoptions
+recvtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0});
+%%
+recvtos_ok({unix,_}, _) -> true;
+recvtos_ok(_, _) -> false.
+
+%% pktoptions is not supported for IPv4
+recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0});
+recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0});
+%% Using the option returns einval, so it is not implemented.
+recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0});
+recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0});
+%%
+recvttl_ok({unix,linux}, _) -> true;
+recvttl_ok({unix,_}, _) -> true;
+recvttl_ok(_, _) -> false.
+
+%% pktoptions is not supported for IPv6
+recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0});
+recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0});
+recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0});
+%% Using the option returns einval, so it is not implemented.
+recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0});
+%% Does not return any value - not implemented for pktoptions
+recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0});
+%%
+recvtclass_ok({unix,_}, _) -> true;
+recvtclass_ok(_, _) -> false.
+
+semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) ->
+ if
+ X1 > X2 -> false;
+ X1 < X2 -> true;
+ Y1 > Y2 -> false;
+ Y1 < Y2 -> true;
+ Z1 > Z2 -> false;
+ Z1 < Z2 -> true;
+ true -> false
+ end;
+semver_lt(_, {_,_,_}) -> false.
+
+test_pktoptions(Family, Spec, OSFilter, CheckConnect) ->
+ OSType = os:type(),
+ OSVer = os:version(),
+ case OSFilter(OSType, OSVer) of
+ true ->
+ io:format("Os: ~p, ~p~n", [OSType,OSVer]),
+ test_pktoptions(Family, Spec, CheckConnect, OSType, OSVer);
+ false ->
+ {skip,{not_supported_for_os_version,{OSType,OSVer}}}
+ end.
+%%
+test_pktoptions(Family, Spec, CheckConnect, OSType, OSVer) ->
+ Timeout = 5000,
+ RecvOpts = [RecvOpt || {RecvOpt,_,_} <- Spec],
+ TrueRecvOpts = [{RecvOpt,true} || {RecvOpt,_,_} <- Spec],
+ FalseRecvOpts = [{RecvOpt,false} || {RecvOpt,_,_} <- Spec],
+ Opts = [Opt || {_,Opt,_} <- Spec],
+ OptsVals = [{Opt,Val} || {_,Opt,Val} <- Spec],
+ Address =
+ case Family of
+ inet ->
+ {127,0,0,1};
+ inet6 ->
+ {0,0,0,0,0,0,0,1}
+ end,
+ %%
+ %% Set RecvOpts on listen socket
+ {ok,L} =
+ gen_tcp:listen(
+ 0,
+ [Family,binary,{active,false},{send_timeout,Timeout}
+ |TrueRecvOpts]),
+ {ok,P} = inet:port(L),
+ {ok,TrueRecvOpts} = inet:getopts(L, RecvOpts),
+ {ok,OptsValsDefault} = inet:getopts(L, Opts),
+ %%
+ %% Set RecvOpts and Option values on connect socket
+ {ok,S2} =
+ gen_tcp:connect(
+ Address, P,
+ [Family,binary,{active,false},{send_timeout,Timeout}
+ |TrueRecvOpts ++ OptsVals],
+ Timeout),
+ {ok,TrueRecvOpts} = inet:getopts(S2, RecvOpts),
+ {ok,OptsVals} = inet:getopts(S2, Opts),
+ %%
+ %% Accept socket inherits the options from listen socket
+ {ok,S1} = gen_tcp:accept(L, Timeout),
+ {ok,TrueRecvOpts} = inet:getopts(S1, RecvOpts),
+ {ok,OptsValsDefault} = inet:getopts(S1, Opts),
+%%% %%
+%%% %% Handshake
+%%% ok = gen_tcp:send(S1, <<"hello">>),
+%%% {ok,<<"hello">>} = gen_tcp:recv(S2, 5, Timeout),
+%%% ok = gen_tcp:send(S2, <<"hi">>),
+%%% {ok,<<"hi">>} = gen_tcp:recv(S1, 2, Timeout),
+ %%
+ %% Verify returned remote options
+ {ok,[{pktoptions,OptsVals1}]} = inet:getopts(S1, [pktoptions]),
+ {ok,[{pktoptions,OptsVals2}]} = inet:getopts(S2, [pktoptions]),
+ (Result1 = sets_eq(OptsVals1, OptsVals))
+ orelse io:format(
+ "Accept differs: ~p neq ~p~n", [OptsVals1,OptsVals]),
+ (Result2 = sets_eq(OptsVals2, OptsValsDefault))
+ orelse io:format(
+ "Connect differs: ~p neq ~p~n",
+ [OptsVals2,OptsValsDefault]),
+ %%
+ ok = gen_tcp:close(S2),
+ ok = gen_tcp:close(S1),
+ %%
+ %%
+ %% Clear RecvOpts on listen socket and set Option values
+ ok = inet:setopts(L, FalseRecvOpts ++ OptsVals),
+ {ok,FalseRecvOpts} = inet:getopts(L, RecvOpts),
+ {ok,OptsVals} = inet:getopts(L, Opts),
+ %%
+ %% Set RecvOpts on connecting socket
+ %%
+ {ok,S4} =
+ gen_tcp:connect(
+ Address, P,
+ [Family,binary,{active,false},{send_timeout,Timeout}
+ |TrueRecvOpts],
+ Timeout),
+ {ok,TrueRecvOpts} = inet:getopts(S4, RecvOpts),
+ {ok,OptsValsDefault} = inet:getopts(S4, Opts),
+ %%
+ %% Accept socket inherits the options from listen socket
+ {ok,S3} = gen_tcp:accept(L, Timeout),
+ {ok,FalseRecvOpts} = inet:getopts(S3, RecvOpts),
+ {ok,OptsVals} = inet:getopts(S3, Opts),
+ %%
+ %% Verify returned remote options
+ {ok,[{pktoptions,[]}]} = inet:getopts(S3, [pktoptions]),
+ {ok,[{pktoptions,OptsVals4}]} = inet:getopts(S4, [pktoptions]),
+ (Result3 = sets_eq(OptsVals4, OptsVals))
+ orelse io:format(
+ "Accept2 differs: ~p neq ~p~n", [OptsVals4,OptsVals]),
+ %%
+ ok = gen_tcp:close(S4),
+ ok = gen_tcp:close(S3),
+ ok = gen_tcp:close(L),
+ (Result1 and ((not CheckConnect) or (Result2 and Result3)))
+ orelse
+ exit({failed,
+ [{OptsVals1,OptsVals4,OptsVals},
+ {OptsVals2,OptsValsDefault}],
+ {OSType,OSVer}}),
+%% exit({{OSType,OSVer},success}), % In search for the truth
+ ok.
+
+sets_eq(L1, L2) ->
+ lists:sort(L1) == lists:sort(L2).
+
+
+
%% Accept test utilities (suites are below)
millis() ->
- erlang:monotonic_time(milli_seconds).
+ erlang:monotonic_time(millisecond).
collect_accepts(0,_) -> [];
collect_accepts(N,Tmo) ->
@@ -2006,10 +2192,7 @@ mktmofun(Tmo,Parent,LS) ->
fun() -> Parent ! {accepted,self(), catch gen_tcp:accept(LS,Tmo)} end.
%% Accept tests
-primitive_accept(suite) ->
- [];
-primitive_accept(doc) ->
- ["Test singular accept"];
+%% Test singular accept.
primitive_accept(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
{ok,PortNo}=inet:port(LS),
@@ -2027,10 +2210,7 @@ primitive_accept(Config) when is_list(Config) ->
end.
-multi_accept_close_listen(suite) ->
- [];
-multi_accept_close_listen(doc) ->
- ["Closing listen socket when multi-accepting"];
+%% Closing listen socket when multi-accepting.
multi_accept_close_listen(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2043,10 +2223,7 @@ multi_accept_close_listen(Config) when is_list(Config) ->
ok = ?EXPECT_ACCEPTS([{_,{error,closed}},{_,{error,closed}},
{_,{error,closed}},{_,{error,closed}}],4,500).
-accept_timeout(suite) ->
- [];
-accept_timeout(doc) ->
- ["Single accept with timeout"];
+%% Single accept with timeout.
accept_timeout(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2054,10 +2231,7 @@ accept_timeout(Config) when is_list(Config) ->
P = spawn(F),
ok = ?EXPECT_ACCEPTS([{P,{error,timeout}}],1,2000).
-accept_timeouts_in_order(suite) ->
- [];
-accept_timeouts_in_order(doc) ->
- ["Check that multi-accept timeouts happen in the correct order"];
+%% Check that multi-accept timeouts happen in the correct order.
accept_timeouts_in_order(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2068,10 +2242,7 @@ accept_timeouts_in_order(Config) when is_list(Config) ->
ok = ?EXPECT_ACCEPTS([{P1,{error,timeout}},{P2,{error,timeout}},
{P3,{error,timeout}},{P4,{error,timeout}}],infinity,2000).
-accept_timeouts_in_order2(suite) ->
- [];
-accept_timeouts_in_order2(doc) ->
- ["Check that multi-accept timeouts happen in the correct order (more)"];
+%% Check that multi-accept timeouts happen in the correct order (more).
accept_timeouts_in_order2(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2082,10 +2253,7 @@ accept_timeouts_in_order2(Config) when is_list(Config) ->
ok = ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P3,{error,timeout}},
{P2,{error,timeout}},{P1,{error,timeout}}],infinity,2000).
-accept_timeouts_in_order3(suite) ->
- [];
-accept_timeouts_in_order3(doc) ->
- ["Check that multi-accept timeouts happen in the correct order (even more)"];
+%% Check that multi-accept timeouts happen in the correct order (even more).
accept_timeouts_in_order3(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2096,11 +2264,8 @@ accept_timeouts_in_order3(Config) when is_list(Config) ->
ok = ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P1,{error,timeout}},
{P3,{error,timeout}},{P2,{error,timeout}}],infinity,2000).
-accept_timeouts_in_order4(suite) ->
- [];
-accept_timeouts_in_order4(doc) ->
- ["Check that multi-accept timeouts happen in the correct order after "
- "mixing millsec and sec timeouts"];
+%% Check that multi-accept timeouts happen in the correct order after
+%% mixing millsec and sec timeouts.
accept_timeouts_in_order4(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2111,11 +2276,8 @@ accept_timeouts_in_order4(Config) when is_list(Config) ->
ok = ?EXPECT_ACCEPTS([{P1,{error,timeout}},{P2,{error,timeout}},
{P4,{error,timeout}},{P3,{error,timeout}}],infinity,2000).
-accept_timeouts_in_order5(suite) ->
- [];
-accept_timeouts_in_order5(doc) ->
- ["Check that multi-accept timeouts happen in the correct order after "
- "mixing millsec and sec timeouts (more)"];
+%% Check that multi-accept timeouts happen in the correct order after
+%% mixing millsec and sec timeouts (more).
accept_timeouts_in_order5(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2126,11 +2288,8 @@ accept_timeouts_in_order5(Config) when is_list(Config) ->
ok = ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P1,{error,timeout}},
{P3,{error,timeout}},{P2,{error,timeout}}],infinity,2000).
-accept_timeouts_in_order6(suite) ->
- [];
-accept_timeouts_in_order6(doc) ->
- ["Check that multi-accept timeouts happen in the correct order after "
- "mixing millsec and sec timeouts (even more)"];
+%% Check that multi-accept timeouts happen in the correct order after
+%% mixing millsec and sec timeouts (even more).
accept_timeouts_in_order6(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2141,11 +2300,8 @@ accept_timeouts_in_order6(Config) when is_list(Config) ->
ok = ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P2,{error,timeout}},
{P3,{error,timeout}},{P1,{error,timeout}}],infinity,2000).
-accept_timeouts_in_order7(suite) ->
- [];
-accept_timeouts_in_order7(doc) ->
- ["Check that multi-accept timeouts happen in the correct order after "
- "mixing millsec and sec timeouts (even more++)"];
+%% Check that multi-accept timeouts happen in the correct order after
+%% mixing millsec and sec timeouts (even more++).
accept_timeouts_in_order7(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2162,10 +2318,7 @@ accept_timeouts_in_order7(Config) when is_list(Config) ->
{P1,{error,timeout}},{P3,{error,timeout}},
{P8,{error,timeout}},{P7,{error,timeout}}],infinity,2000).
-accept_timeouts_mixed(suite) ->
- [];
-accept_timeouts_mixed(doc) ->
- ["Check that multi-accept timeouts behave correctly when mixed with successful timeouts"];
+%% Check that multi-accept timeouts behave correctly when mixed with successful timeouts.
accept_timeouts_mixed(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2185,10 +2338,7 @@ accept_timeouts_mixed(Config) when is_list(Config) ->
gen_tcp:connect("localhost",PortNo,[]),
ok = ?EXPECT_ACCEPTS([{P4,{ok,Port1}}] when is_port(Port1),infinity,100).
-killing_acceptor(suite) ->
- [];
-killing_acceptor(doc) ->
- ["Check that single acceptor behaves as expected when killed"];
+%% Check that single acceptor behaves as expected when killed.
killing_acceptor(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Pid = spawn(fun() -> erlang:display({accepted,self(),gen_tcp:accept(LS)}) end),
@@ -2203,10 +2353,7 @@ killing_acceptor(Config) when is_list(Config) ->
false = lists:member(accepting, L2),
ok.
-killing_multi_acceptors(suite) ->
- [];
-killing_multi_acceptors(doc) ->
- ["Check that multi acceptors behaves as expected when killed"];
+%% Check that multi acceptors behaves as expected when killed.
killing_multi_acceptors(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2228,10 +2375,7 @@ killing_multi_acceptors(Config) when is_list(Config) ->
false = lists:member(accepting, L3),
ok.
-killing_multi_acceptors2(suite) ->
- [];
-killing_multi_acceptors2(doc) ->
- ["Check that multi acceptors behaves as expected when killed (more)"];
+%% Check that multi acceptors behaves as expected when killed (more).
killing_multi_acceptors2(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2265,11 +2409,8 @@ killing_multi_acceptors2(Config) when is_list(Config) ->
false = lists:member(accepting, L5),
ok.
-several_accepts_in_one_go(suite) ->
- [];
-several_accepts_in_one_go(doc) ->
- ["checks that multi-accept works when more than one accept can be "
- "done at once (wb test of inet_driver)"];
+%% Checks that multi-accept works when more than one accept can be
+%% done at once (wb test of inet_driver).
several_accepts_in_one_go(Config) when is_list(Config) ->
{ok,LS}=gen_tcp:listen(0,[]),
Parent = self(),
@@ -2294,7 +2435,7 @@ wait_until_accepting(Proc,0) ->
exit({timeout_waiting_for_accepting,Proc});
wait_until_accepting(Proc,N) ->
case process_info(Proc,current_function) of
- {current_function,{prim_inet,accept0,2}} ->
+ {current_function,{prim_inet,accept0,3}} ->
case process_info(Proc,status) of
{status,waiting} ->
ok;
@@ -2312,11 +2453,8 @@ wait_until_accepting(Proc,N) ->
end.
-accept_system_limit(suite) ->
- [];
-accept_system_limit(doc) ->
- ["Check that accept returns {error, system_limit} "
- "(and not {error, enfile}) when running out of ports"];
+%% Check that accept returns {error, system_limit}
+%% (and not {error, enfile}) when running out of ports.
accept_system_limit(Config) when is_list(Config) ->
{ok, LS} = gen_tcp:listen(0, []),
{ok, TcpPort} = inet:port(LS),
@@ -2365,10 +2503,7 @@ open_ports(L) ->
end.
-active_once_closed(suite) ->
- [];
-active_once_closed(doc) ->
- ["Check that active once and tcp_close messages behave as expected"];
+%% Check that active once and tcp_close messages behave as expected.
active_once_closed(Config) when is_list(Config) ->
(fun() ->
{Loop,A} = setup_closed_ao(),
@@ -2416,75 +2551,84 @@ active_once_closed(Config) when is_list(Config) ->
ok = receive {tcp_closed, A} -> ok after 1000 -> error end
end)().
-send_timeout(suite) ->
- [];
-send_timeout(doc) ->
- ["Test the send_timeout socket option"];
+%% Test the send_timeout socket option.
send_timeout(Config) when is_list(Config) ->
+ Dir = filename:dirname(code:which(?MODULE)),
+ {ok,RNode} = test_server:start_node(?UNIQ_NODE_NAME, slave,
+ [{args,"-pa " ++ Dir}]),
+
%% Basic
- BasicFun =
- fun(AutoClose) ->
- {Loop,A,RNode} = setup_timeout_sink(1000, AutoClose),
- {error,timeout} =
- Loop(fun() ->
- Res = gen_tcp:send(A,<<1:10000>>),
- %%erlang:display(Res),
- Res
- end),
- %% Check that the socket is not busy/closed...
- Error = after_send_timeout(AutoClose),
- {error,Error} = gen_tcp:send(A,<<"Hej">>),
- test_server:stop_node(RNode)
- end,
- BasicFun(false),
- BasicFun(true),
- %% Check timeout length
+ send_timeout_basic(false, RNode),
+ send_timeout_basic(true, RNode),
+
+ BinData = <<1:10000>>,
+
+ %% Check timeout length.
Self = self(),
Pid = spawn(fun() ->
- {Loop,A,RNode} = setup_timeout_sink(1000, true),
- {error,timeout} = Loop(fun() ->
- Res = gen_tcp:send(A,<<1:10000>>),
- %%erlang:display(Res),
- Self ! Res,
- Res
- end),
- test_server:stop_node(RNode)
+ A = setup_timeout_sink(RNode, 1000, true),
+ Send = fun() ->
+ Res = gen_tcp:send(A, BinData),
+ Self ! Res,
+ Res
+ end,
+ {error,timeout} = timeout_sink_loop(Send)
end),
Diff = get_max_diff(),
io:format("Max time for send: ~p~n",[Diff]),
true = (Diff > 500) and (Diff < 1500),
- %% Let test_server slave die...
+
+ %% Wait for the process to die.
Mon = erlang:monitor(process, Pid),
receive {'DOWN',Mon,process,Pid,_} -> ok end,
+
%% Check that parallell writers do not hang forever
- ParaFun =
- fun(AutoClose) ->
- {Loop,A,RNode} = setup_timeout_sink(1000, AutoClose),
- SenderFun = fun() ->
- {error,Error} =
- Loop(fun() ->
- gen_tcp:send(A, <<1:10000>>)
- end),
- Self ! {error,Error}
- end,
- spawn_link(SenderFun),
- spawn_link(SenderFun),
- receive
- {error,timeout} -> ok
- after 10000 ->
- exit(timeout)
- end,
- NextErr = after_send_timeout(AutoClose),
- receive
- {error,NextErr} -> ok
- after 10000 ->
- exit(timeout)
- end,
- {error,NextErr} = gen_tcp:send(A,<<"Hej">>),
- test_server:stop_node(RNode)
- end,
- ParaFun(false),
- ParaFun(true),
+ send_timeout_para(false, RNode),
+ send_timeout_para(true, RNode),
+
+ test_server:stop_node(RNode),
+
+ ok.
+
+send_timeout_basic(AutoClose, RNode) ->
+ BinData = <<1:10000>>,
+
+ A = setup_timeout_sink(RNode, 1000, AutoClose),
+ Send = fun() -> gen_tcp:send(A, BinData) end,
+ {error,timeout} = timeout_sink_loop(Send),
+
+ %% Check that the socket is not busy/closed...
+ Error = after_send_timeout(AutoClose),
+ {error,Error} = gen_tcp:send(A, <<"Hej">>),
+ ok.
+
+send_timeout_para(AutoClose, RNode) ->
+ BinData = <<1:10000>>,
+
+ A = setup_timeout_sink(RNode, 1000, AutoClose),
+ Self = self(),
+ SenderFun = fun() ->
+ Send = fun() -> gen_tcp:send(A, BinData) end,
+ {error,Error} = timeout_sink_loop(Send),
+ Self ! {error,Error}
+ end,
+ spawn_link(SenderFun),
+ spawn_link(SenderFun),
+
+ receive
+ {error,timeout} -> ok
+ after 10000 ->
+ exit(timeout)
+ end,
+
+ NextErr = after_send_timeout(AutoClose),
+ receive
+ {error,NextErr} -> ok
+ after 10000 ->
+ exit(timeout)
+ end,
+
+ {error,NextErr} = gen_tcp:send(A, <<"Hej">>),
ok.
mad_sender(S) ->
@@ -2500,46 +2644,41 @@ mad_sender(S) ->
flush() ->
receive
_X ->
- %erlang:display(_X),
flush()
after 0 ->
ok
end.
-send_timeout_active(suite) ->
- [];
-send_timeout_active(doc) ->
- ["Test the send_timeout socket option for active sockets"];
+%% Test the send_timeout socket option for active sockets.
send_timeout_active(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(20)),
- %% Basic
- BasicFun =
- fun(AutoClose) ->
- {Loop,A,RNode,C} = setup_active_timeout_sink(1, AutoClose),
- inet:setopts(A, [{active, once}]),
- Mad = spawn_link(RNode,fun() -> mad_sender(C) end),
- {error,timeout} =
- Loop(fun() ->
- receive
- {tcp, _Sock, _Data} ->
- inet:setopts(A, [{active, once}]),
- Res = gen_tcp:send(A,lists:duplicate(1000, $a)),
- %erlang:display(Res),
- Res;
- Err ->
- io:format("sock closed: ~p~n", [Err]),
- Err
- end
- end),
- unlink(Mad),
- exit(Mad,kill),
- test_server:stop_node(RNode)
+ Dir = filename:dirname(code:which(?MODULE)),
+ {ok,RNode} = test_server:start_node(?UNIQ_NODE_NAME, slave,
+ [{args,"-pa " ++ Dir}]),
+ do_send_timeout_active(false, RNode),
+ do_send_timeout_active(true, RNode),
+ test_server:stop_node(RNode),
+ ok.
+
+do_send_timeout_active(AutoClose, RNode) ->
+ {A,C} = setup_active_timeout_sink(RNode, 1, AutoClose),
+ inet:setopts(A, [{active, once}]),
+ Mad = spawn_link(RNode, fun() -> mad_sender(C) end),
+ ListData = lists:duplicate(1000, $a),
+ F = fun() ->
+ receive
+ {tcp, _Sock, _Data} ->
+ inet:setopts(A, [{active, once}]),
+ Res = gen_tcp:send(A, ListData),
+ Res;
+ Err ->
+ io:format("sock closed: ~p~n", [Err]),
+ Err
+ end
end,
- BasicFun(false),
- flush(),
- BasicFun(true),
+ {error,timeout} = timeout_sink_loop(F),
+ unlink(Mad),
+ exit(Mad, kill),
flush(),
- test_server:timetrap_cancel(Dog),
ok.
after_send_timeout(AutoClose) ->
@@ -2581,9 +2720,9 @@ get_max_diff(Max) ->
setup_closed_ao() ->
Dir = filename:dirname(code:which(?MODULE)),
- {ok,R} = test_server:start_node(test_default_options_slave,slave,
+ {ok,R} = test_server:start_node(?UNIQ_NODE_NAME, slave,
[{args,"-pa " ++ Dir}]),
- Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
+ Host = get_hostname(node()),
{ok, L} = gen_tcp:listen(0, [{active,false},{packet,2}]),
Fun = fun(F) ->
receive
@@ -2622,11 +2761,8 @@ setup_closed_ao() ->
test_server:stop_node(R),
{Loop,A}.
-setup_timeout_sink(Timeout, AutoClose) ->
- Dir = filename:dirname(code:which(?MODULE)),
- {ok,R} = test_server:start_node(test_default_options_slave,slave,
- [{args,"-pa " ++ Dir}]),
- Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
+setup_timeout_sink(RNode, Timeout, AutoClose) ->
+ Host = get_hostname(node()),
{ok, L} = gen_tcp:listen(0, [{active,false},{packet,2},
{send_timeout,Timeout},
{send_timeout_close,AutoClose}]),
@@ -2637,7 +2773,7 @@ setup_timeout_sink(Timeout, AutoClose) ->
die -> ok
end
end,
- Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
+ Pid = rpc:call(RNode, erlang, spawn, [fun() -> Fun(Fun) end]),
{ok, Port} = inet:port(L),
Remote = fun(Fu) ->
Pid ! {self(), Fu},
@@ -2651,36 +2787,23 @@ setup_timeout_sink(Timeout, AutoClose) ->
{ok,A} = gen_tcp:accept(L),
gen_tcp:send(A,"Hello"),
{ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end),
- Loop2 = fun(_,_,0) ->
- {failure, timeout};
- (L2,F2,N) ->
- Ret = F2(),
- io:format("~p~n",[Ret]),
- case Ret of
- ok -> receive after 1 -> ok end,
- L2(L2,F2,N-1);
- Other -> Other
- end
- end,
- Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
- {Loop,A,R}.
-
-setup_active_timeout_sink(Timeout, AutoClose) ->
- Dir = filename:dirname(code:which(?MODULE)),
- {ok,R} = test_server:start_node(test_default_options_slave,slave,
- [{args,"-pa " ++ Dir}]),
- Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
- {ok, L} = gen_tcp:listen(0, [binary,{active,false},{packet,0},{nodelay, true},{keepalive, true},
- {send_timeout,Timeout},
- {send_timeout_close,AutoClose}]),
+ A.
+
+setup_active_timeout_sink(RNode, Timeout, AutoClose) ->
+ Host = get_hostname(node()),
+ ListenOpts = [binary,{active,false},{packet,0},
+ {nodelay,true},{keepalive,true},
+ {send_timeout,Timeout},{send_timeout_close,AutoClose}],
+ {ok, L} = gen_tcp:listen(0, ListenOpts),
Fun = fun(F) ->
receive
{From,X} when is_function(X) ->
- From ! {self(),X()}, F(F);
+ From ! {self(),X()},
+ F(F);
die -> ok
end
end,
- Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
+ Pid = rpc:call(RNode, erlang, spawn, [fun() -> Fun(Fun) end]),
{ok, Port} = inet:port(L),
Remote = fun(Fu) ->
Pid ! {self(), Fu},
@@ -2688,26 +2811,22 @@ setup_active_timeout_sink(Timeout, AutoClose) ->
end
end,
{ok, C} = Remote(fun() ->
- gen_tcp:connect(Host,Port,
- [{active,false}])
+ gen_tcp:connect(Host, Port, [{active,false}])
end),
{ok,A} = gen_tcp:accept(L),
- gen_tcp:send(A,"Hello"),
- {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C,0) end),
- Loop2 = fun(_,_,0) ->
- {failure, timeout};
- (L2,F2,N) ->
- Ret = F2(),
- io:format("~p~n",[Ret]),
- case Ret of
- ok -> receive after 1 -> ok end,
- L2(L2,F2,N-1);
- Other -> Other
- end
- end,
- Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
- {Loop,A,R,C}.
+ gen_tcp:send(A, "Hello"),
+ {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C, 0) end),
+ {A,C}.
+timeout_sink_loop(Action) ->
+ Ret = Action(),
+ case Ret of
+ ok ->
+ receive after 1 -> ok end,
+ timeout_sink_loop(Action);
+ Other ->
+ Other
+ end.
has_superfluous_schedulers() ->
case {erlang:system_info(schedulers),
@@ -2718,10 +2837,8 @@ has_superfluous_schedulers() ->
end.
-otp_7731(suite) -> [];
-otp_7731(doc) ->
- "Leaking message from inet_drv {inet_reply,P,ok} "
- "when a socket sending resumes working after a send_timeout";
+%% Leaking message from inet_drv {inet_reply,P,ok}
+%% when a socket sending resumes working after a send_timeout.
otp_7731(Config) when is_list(Config) ->
ServerPid = spawn_link(?MODULE, otp_7731_server, [self()]),
receive {ServerPid, ready, PortNum} -> ok end,
@@ -2738,7 +2855,7 @@ otp_7731(Config) when is_list(Config) ->
%% Now make sure inet_drv does not leak any internal messages.
receive Msg ->
- test_server:fail({unexpected, Msg})
+ ct:fail({unexpected, Msg})
after 1000 ->
ok
end,
@@ -2791,8 +2908,7 @@ otp_7731_recv(Socket) ->
%% OTP-7615: TCP-ports hanging in CLOSING state when sending large
%% buffer followed by a recv() that returns error due to closed
%% connection.
-zombie_sockets(suite) -> [];
-zombie_sockets(doc) -> ["OTP-7615 Leaking closed ports."];
+%% OTP-7615 Leaking closed ports.
zombie_sockets(Config) when is_list(Config) ->
register(zombie_collector,self()),
Calls = 10,
@@ -2872,9 +2988,7 @@ zombie_serve_client(Socket, Bin) ->
gen_tcp:close(Socket),
zombie_collector ! {closed, Socket}.
-otp_7816(suite) -> [];
-otp_7816(doc) ->
- "Hanging send on windows when sending iolist with more than 16 binaries.";
+%% Hanging send on windows when sending iolist with more than 16 binaries.
otp_7816(Config) when is_list(Config) ->
Client = self(),
Server = spawn_link(fun()-> otp_7816_server(Client) end),
@@ -2963,8 +3077,7 @@ otp_7816_recv(CSocket, BytesLeft) ->
error
end.
-otp_8102(doc) -> ["Receive a packet with a faulty packet header"];
-otp_8102(suite) -> [];
+%% Receive a packet with a faulty packet header.
otp_8102(Config) when is_list(Config) ->
{ok, LSocket} = gen_tcp:listen(0, []),
{ok, {_, PortNum}} = inet:sockname(LSocket),
@@ -3004,8 +3117,7 @@ otp_8102_do(LSocket, PortNum, {Bin,PType}) ->
gen_tcp:close(SSocket),
gen_tcp:close(RSocket).
-otp_9389(doc) -> ["Verify packet_size handles long HTTP header lines"];
-otp_9389(suite) -> [];
+%% Verify packet_size handles long HTTP header lines.
otp_9389(Config) when is_list(Config) ->
{ok, LS} = gen_tcp:listen(0, [{active,false}]),
{ok, {_, PortNum}} = inet:sockname(LS),
@@ -3064,10 +3176,10 @@ otp_9389_loop(S, OrigLinkHdr, State) ->
error({timeout,header})
end.
-wrapping_oct(doc) ->
- "Check that 64bit octet counters work.";
-wrapping_oct(suite) ->
- [];
+wrapping_oct() ->
+ [{timetrap,{minutes,10}}].
+
+%% Check that 64bit octet counters work.
wrapping_oct(Config) when is_list(Config) ->
{ok,Sock} = gen_tcp:listen(0,[{active,false},{mode,binary}]),
{ok,Port} = inet:port(Sock),
@@ -3131,3 +3243,186 @@ oct_aloop(S,X,Times) ->
end.
ok({ok,V}) -> V.
+
+get_hostname(Name) ->
+ "@"++Host = lists:dropwhile(fun(C) -> C =/= $@ end, atom_to_list(Name)),
+ Host.
+
+otp_13939(doc) ->
+ ["Check that writing to a remotely closed socket doesn't block forever "
+ "when exit_on_close is false."];
+otp_13939(suite) ->
+ [];
+otp_13939(Config) when is_list(Config) ->
+ {Pid, Ref} = spawn_opt(
+ fun() ->
+ {ok, Listener} = gen_tcp:listen(0, [{exit_on_close, false}]),
+ {ok, Port} = inet:port(Listener),
+
+ spawn_link(
+ fun() ->
+ {ok, Client} = gen_tcp:connect("localhost", Port,
+ [{active, false}]),
+ ok = gen_tcp:close(Client)
+ end),
+
+ {ok, Accepted} = gen_tcp:accept(Listener),
+
+ ok = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>),
+
+ %% The bug surfaces when there's a delay between the send
+ %% operations; inet:getstat is a red herring.
+ timer:sleep(100),
+
+ {error, Code} = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>),
+ ct:pal("gen_tcp:send returned ~p~n", [Code])
+ end, [link, monitor]),
+
+ receive
+ {'DOWN', Ref, process, Pid, normal} ->
+ ok
+ after 1000 ->
+ demonitor(Ref, [flush]),
+ exit(Pid, normal),
+ ct:fail("Server process blocked on send.")
+ end.
+
+otp_12242(Config) when is_list(Config) ->
+ case os:type() of
+ {win32,_} ->
+ %% Even if we set sndbuf and recbuf to small sizes
+ %% Windows either happily accepts to send GBytes of data
+ %% in no time, so the second send below that is supposed
+ %% to time out just succedes, or the first send that
+ %% is supposed to fill the inet_drv I/O queue and
+ %% start waiting for when more data can be sent
+ %% instead sends all data but suffers a send
+ %% failure that closes the socket
+ {skipped,backpressure_broken_on_win32};
+ _ ->
+ %% Find the IPv4 address of an up and running interface
+ %% that is not loopback nor pointtopoint
+ {ok,IFList} = inet:getifaddrs(),
+ ct:pal("IFList ~p~n", [IFList]),
+ case
+ lists:flatten(
+ [lists:filtermap(
+ fun ({addr,Addr}) when tuple_size(Addr) =:= 4 ->
+ {true,Addr};
+ (_) ->
+ false
+ end, Opts)
+ || {_,Opts} <- IFList,
+ case lists:keyfind(flags, 1, Opts) of
+ {_,Flags} ->
+ lists:member(up, Flags)
+ andalso
+ lists:member(running, Flags)
+ andalso
+ not lists:member(loopback, Flags)
+ andalso
+ not lists:member(pointtopoint, Flags);
+ false ->
+ false
+ end])
+ of
+ [Addr|_] ->
+ otp_12242(Addr);
+ Other ->
+ {skipped,{no_external_address,Other}}
+ end
+ end;
+%%
+otp_12242(Addr) when tuple_size(Addr) =:= 4 ->
+ ct:timetrap(30000),
+ ct:pal("Using address ~p~n", [Addr]),
+ Bufsize = 16 * 1024,
+ Datasize = 128 * 1024 * 1024, % At least 1 s on GBit interface
+ Blob = binary:copy(<<$x>>, Datasize),
+ LOpts =
+ [{backlog,4},{reuseaddr,true},{ip,Addr},
+ binary,{active,false},
+ {recbuf,Bufsize},{sndbuf,Bufsize},{buffer,Bufsize}],
+ COpts =
+ [binary,{active,false},{ip,Addr},
+ {linger,{true,1}}, % 1 s
+ {send_timeout,500},
+ {recbuf,Bufsize},{sndbuf,Bufsize},{buffer,Bufsize}],
+ Dir = filename:dirname(code:which(?MODULE)),
+ {ok,ListenerNode} =
+ test_server:start_node(
+ ?UNIQ_NODE_NAME, slave, [{args,"-pa " ++ Dir}]),
+ Tester = self(),
+ Listener =
+ spawn(
+ ListenerNode,
+ fun () ->
+ {ok,L} = gen_tcp:listen(0, LOpts),
+ {ok,LPort} = inet:port(L),
+ Tester ! {self(),port,LPort},
+ {ok,A} = gen_tcp:accept(L),
+ ok = gen_tcp:close(L),
+ receive
+ {Tester,stop} ->
+ ok = gen_tcp:close(A)
+ end
+ end),
+ ListenerMref = monitor(process, Listener),
+ LPort = receive {Listener,port,P} -> P end,
+ {ok,C} = gen_tcp:connect(Addr, LPort, COpts, infinity),
+ {ok,ReadCOpts} = inet:getopts(C, [recbuf,sndbuf,buffer]),
+ ct:pal("ReadCOpts ~p~n", [ReadCOpts]),
+ %%
+ %% Fill the buffers
+ ct:pal("Sending ~p bytes~n", [Datasize]),
+ ok = gen_tcp:send(C, Blob),
+ ct:pal("Sent ~p bytes~n", [Datasize]),
+ %% Spawn the Closer,
+ %% try to ensure that the close call is in progress
+ %% before the owner proceeds with sending
+ Owner = self(),
+ {_Closer,CloserMref} =
+ spawn_opt(
+ fun () ->
+ Owner ! {tref, erlang:start_timer(50, Owner, closing)},
+ ct:pal("Calling gen_tcp:close(C)~n"),
+ try gen_tcp:close(C) of
+ Result ->
+ ct:pal("gen_tcp:close(C) -> ~p~n", [Result]),
+ ok = Result
+ catch
+ Class:Reason:Stacktrace ->
+ ct:pal(
+ "gen_tcp:close(C) >< ~p:~p~n ~p~n",
+ [Class,Reason,Stacktrace]),
+ erlang:raise(Class, Reason, Stacktrace)
+ end
+ end, [link,monitor]),
+ receive
+ {tref,Tref} ->
+ receive {timeout,Tref,_} -> ok end,
+ ct:pal("Sending ~p bytes again~n", [Datasize]),
+ %% Now should the close be in progress...
+ %% All buffers are full, remote end is not reading,
+ %% and the send timeout is 1 s so this will timeout:
+ {error,timeout} = gen_tcp:send(C, Blob),
+ ct:pal("Sending ~p bytes again timed out~n", [Datasize]),
+ ok = inet:setopts(C, [{send_timeout,10000}]),
+ %% There is a hidden timeout here. Port close is sampled
+ %% every 5 s by prim_inet:send_recv_reply.
+ %% Linger is 3 s so the Closer will finish this send:
+ ct:pal("Sending ~p bytes with 10 s timeout~n", [Datasize]),
+ {error,closed} = gen_tcp:send(C, Blob),
+ ct:pal("Sending ~p bytes with 10 s timeout was closed~n",
+ [Datasize]),
+ normal = wait(CloserMref),
+ ct:pal("The Closer has exited~n"),
+ Listener ! {Tester,stop},
+ receive {'DOWN',ListenerMref,_,_,_} -> ok end,
+ ct:pal("The Listener has exited~n"),
+ test_server:stop_node(ListenerNode),
+ ok
+ end.
+
+wait(Mref) ->
+ receive {'DOWN',Mref,_,_,Reason} -> Reason end.