From e54fc6d2a4749f637f29e726de55102d37a9dbb1 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 22 Oct 2018 14:20:46 +0200 Subject: [socket-nif|test] Add test cases for UDP recvfrom and recvmsg Add test cases for udp local socket close for UDP recvfrom and recvmsg. OTP-14831 --- erts/emulator/test/socket_SUITE.erl | 583 +++++++++++++++++++++++++++++++++++- 1 file changed, 575 insertions(+), 8 deletions(-) diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl index 0edcc3eb04..85b8fcce77 100644 --- a/erts/emulator/test/socket_SUITE.erl +++ b/erts/emulator/test/socket_SUITE.erl @@ -73,8 +73,12 @@ sc_cpe_socket_cleanup_udp6/1, sc_lc_recv_response_tcp4/1, sc_lc_recv_response_tcp6/1, + sc_lc_recvfrom_response_udp4/1, + sc_lc_recvfrom_response_udp6/1, sc_lc_recvmsg_response_tcp4/1, sc_lc_recvmsg_response_tcp6/1, + sc_lc_recvmsg_response_udp4/1, + sc_lc_recvmsg_response_udp6/1, sc_lc_acceptor_response_tcp4/1, sc_lc_acceptor_response_tcp6/1, sc_rc_recv_response_tcp4/1, @@ -217,8 +221,13 @@ sc_lc_cases() -> sc_lc_recv_response_tcp4, sc_lc_recv_response_tcp6, + sc_lc_recvfrom_response_udp4, + sc_lc_recvfrom_response_udp6, + sc_lc_recvmsg_response_tcp4, sc_lc_recvmsg_response_tcp6, + sc_lc_recvmsg_response_udp4, + sc_lc_recvmsg_response_udp6, sc_lc_acceptor_response_tcp4, sc_lc_acceptor_response_tcp6 @@ -1238,24 +1247,22 @@ api_opt_simple_otp_controlling_process() -> i("start tcp (stream) socket"), ClientInitState1 = #{}, - #ev{pid = Pid1} = Client1 = evaluator_start("tcp-client", - ClientSeq, ClientInitState1), + Client1 = evaluator_start("tcp-client", ClientSeq, ClientInitState1), TesterInitState1 = #{domain => inet, type => stream, protocol => tcp, - client => Pid1}, + client => Client1#ev.pid}, Tester1 = evaluator_start("tcp-tester", TesterSeq, TesterInitState1), i("await tcp evaluator"), ok = await_evaluator_finish([Tester1, Client1]), i("start udp (dgram) socket"), ClientInitState2 = #{}, - #ev{pid = Pid2} = Client2 = evaluator_start("udp-client", - ClientSeq, ClientInitState2), + Client2 = evaluator_start("udp-client", ClientSeq, ClientInitState2), TesterInitState2 = #{domain => inet, type => dgram, protocol => udp, - client => Pid2}, + client => Client2#ev.pid}, Tester2 = evaluator_start("udp-tester", TesterSeq, TesterInitState2), i("await udp evaluator"), ok = await_evaluator_finish([Tester2, Client2]). @@ -3605,7 +3612,7 @@ sc_lc_receive_response_tcp(InitState) -> {'DOWN', _, process, Pid, Reason} -> ee("Unexpected DOWN regarding cient ~p: " "~n ~p", [Pid, Reason]), - {error, {unexpected_exit, acceptor}}; + {error, {unexpected_exit, client}}; {ready, Pid} -> ok end @@ -3642,7 +3649,7 @@ sc_lc_receive_response_tcp(InitState) -> {'DOWN', _, process, Pid, Reason} -> ee("Unexpected DOWN regarding client ~p: " "~n ~p", [Pid, Reason]), - {error, {unexpected_exit, acceptor}}; + {error, {unexpected_exit, client}}; {ready, Pid} -> ok end @@ -3827,6 +3834,525 @@ sc_lc_receive_response_tcp(InitState) -> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while a process is calling the recvfrom function. +%% Socket is IPv4. +%% + +sc_lc_recvfrom_response_udp4(suite) -> + []; +sc_lc_recvfrom_response_udp4(doc) -> + []; +sc_lc_recvfrom_response_udp4(_Config) when is_list(_Config) -> + tc_try(sc_lc_recvfrom_response_udp4, + fun() -> + ?TT(?SECS(30)), + Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, + InitState = #{domain => inet, + type => dgram, + protocol => udp, + recv => Recv}, + ok = sc_lc_receive_response_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recv function. +%% Socket is IPv6. + +sc_lc_recvfrom_response_udp6(suite) -> + []; +sc_lc_recvfrom_response_udp6(doc) -> + []; +sc_lc_recvfrom_response_udp6(_Config) when is_list(_Config) -> + tc_try(sc_lc_recvfrom_response_udp6, + fun() -> + not_yet_implemented(), + ?TT(?SECS(30)), + Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, + InitState = #{domain => inet6, + recv => Recv}, + ok = sc_lc_receive_response_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +sc_lc_receive_response_udp(InitState) -> + PrimServerSeq = + [ + %% *** Init part *** + #{desc => "await start", + cmd => fun(State) -> + receive + {start, Tester} -> + {ok, State#{tester => Tester}} + end + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + #{desc => "local address", + cmd => fun(#{domain := Domain} = State) -> + LAddr = which_local_addr(Domain), + LSA = #{family => Domain, addr => LAddr}, + {ok, State#{lsa => LSA}} + end}, + #{desc => "open socket", + cmd => fun(#{domain := Domain} = State) -> + Sock = sock_open(Domain, dgram, udp), + SA = sock_sockname(Sock), + {ok, State#{sock => Sock, sa => SA}} + end}, + #{desc => "bind socket", + cmd => fun(#{sock := Sock, lsa := LSA}) -> + sock_bind(Sock, LSA), + ok + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, sock := Sock}) -> + Tester ! {ready, self(), Sock}, + ok + end}, + + %% The actual test + #{desc => "await continue (receive)", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {continue, Tester, Timeout} -> + {ok, State#{timeout => Timeout}} + end + end}, + #{desc => "receive with timeout", + cmd => fun(#{sock := Sock, recv := Recv, timeout := Timeout}) -> + case Recv(Sock, Timeout) of + {error, timeout} -> + ok; + {ok, _} -> + {error, unexpected_success}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (timeout)", + cmd => fun(#{tester := Tester}) -> + Tester ! {ready, self()}, + ok + end}, + #{desc => "await continue (close)", + cmd => fun(#{tester := Tester} = _State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {continue, Tester} -> + ok + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + case socket:close(Sock) of + ok -> + {ok, maps:remove(sock, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (closed)", + cmd => fun(#{tester := Tester}) -> + Tester ! {ready, self()}, + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {terminate, Tester} -> + {ok, maps:remove(tester, State)} + end + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + SecServerSeq = + [ + %% *** Init part *** + #{desc => "await start", + cmd => fun(State) -> + receive + {start, Tester, Sock} -> + {ok, State#{tester => Tester, sock => Sock}} + end + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + Tester ! {ready, self()}, + ok + end}, + + %% The actual test + #{desc => "await continue (receive)", + cmd => fun(#{tester := Tester} = _State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {continue, Tester} -> + ok + end + end}, + #{desc => "receive", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + %% ok = socket:setopt(Sock, otp, debug, true), + case Recv(Sock, infinity) of + {error, closed} -> + {ok, maps:remove(sock, State)}; + {ok, _} -> + {error, unexpected_success}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (closed)", + cmd => fun(#{tester := Tester}) -> + Tester ! {ready, self()}, + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + receive + {'DOWN', _, process, Tester, Reason} -> + ee("Unexpected DOWN regarding tester ~p: " + "~n ~p", [Reason]), + {error, {unexpected_exit, tester}}; + {terminate, Tester} -> + {ok, maps:remove(tester, State)} + end + end}, + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor primary server", + cmd => fun(#{prim_server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor secondary server 1", + cmd => fun(#{sec_server1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor secondary server 2", + cmd => fun(#{sec_server2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor secondary server 3", + cmd => fun(#{sec_server3 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the primary server + #{desc => "order 'primary server' start", + cmd => fun(#{prim_server := Pid} = _State) -> + Pid ! {start, self()}, + ok + end}, + #{desc => "await 'primary server' ready (init)", + cmd => fun(#{prim_server := Pid} = State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding prim-server ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, prim_server}}; + {ready, Pid, Sock} -> + {ok, State#{sock => Sock}} + end + end}, + + %% Start the secondary server 1 + #{desc => "order 'secondary server 1' start", + cmd => fun(#{sec_server1 := Pid, sock := Sock} = _State) -> + Pid ! {start, self(), Sock}, + ok + end}, + #{desc => "await 'secondary server 1' ready (init)", + cmd => fun(#{sec_server1 := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding sec-server ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, sec_server1}}; + {ready, Pid} -> + ok + end + end}, + + %% Start the secondary server 2 + #{desc => "order 'secondary server 2' start", + cmd => fun(#{sec_server2 := Pid, sock := Sock} = _State) -> + Pid ! {start, self(), Sock}, + ok + end}, + #{desc => "await 'secondary server 2' ready (init)", + cmd => fun(#{sec_server2 := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding sec-server ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, sec_server1}}; + {ready, Pid} -> + ok + end + end}, + + %% Start the secondary server 3 + #{desc => "order 'secondary server 3' start", + cmd => fun(#{sec_server3 := Pid, sock := Sock} = _State) -> + Pid ! {start, self(), Sock}, + ok + end}, + #{desc => "await 'secondary server 3' ready (init)", + cmd => fun(#{sec_server3 := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding sec-server ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, sec_server1}}; + {ready, Pid} -> + ok + end + end}, + + + %% The actual test + %% Make all the seondary servers continue, with an infinit recvfrom + %% and then the prim-server with a timed recvfrom. + %% After the prim server notifies us (about the timeout) we order it + %% to close the socket, which should cause the all the secondary + %% server to return with error-closed. + + #{desc => "order 'secondary server 1' to continue", + cmd => fun(#{sec_server1 := Pid} = _State) -> + Pid ! {continue, self()}, + ok + end}, + #{desc => "sleep", + cmd => fun(_) -> + ?SLEEP(?SECS(1)), + ok + end}, + #{desc => "order 'secondary server 2' to continue", + cmd => fun(#{sec_server2 := Pid} = _State) -> + Pid ! {continue, self()}, + ok + end}, + #{desc => "sleep", + cmd => fun(_) -> + ?SLEEP(?SECS(1)), + ok + end}, + #{desc => "order 'secondary server 3' to continue", + cmd => fun(#{sec_server3 := Pid} = _State) -> + Pid ! {continue, self()}, + ok + end}, + #{desc => "sleep", + cmd => fun(_) -> + ?SLEEP(?SECS(1)), + ok + end}, + #{desc => "order 'primary server' to continue (recvfrom)", + cmd => fun(#{prim_server := Pid} = _State) -> + Pid ! {continue, self(), ?SECS(5)}, + ok + end}, + #{desc => "await 'primary server' ready (timeout)", + cmd => fun(#{prim_server := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding prim-server ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, prim_server}}; + {ready, Pid} -> + ok + end + end}, + #{desc => "order 'primary server' to continue (close)", + cmd => fun(#{prim_server := Pid} = _State) -> + Pid ! {continue, self()}, + ok + end}, + #{desc => "await 'primary server' ready (closed)", + cmd => fun(#{prim_server := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding prim-server ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, prim_server}}; + {ready, Pid} -> + ok + end + end}, + #{desc => "await 'secondary server 1' ready (closed)", + cmd => fun(#{sec_server1 := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding sec-server-1 ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, sec_server1}}; + {ready, Pid} -> + ok + end + end}, + #{desc => "await 'secondary server 2' ready (closed)", + cmd => fun(#{sec_server2 := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding sec-server-2 ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, sec_server2}}; + {ready, Pid} -> + ok + end + end}, + #{desc => "await 'secondary server 3' ready (closed)", + cmd => fun(#{sec_server3 := Pid} = _State) -> + receive + {'DOWN', _, process, Pid, Reason} -> + ee("Unexpected DOWN regarding sec-server-3 ~p: " + "~n ~p", [Pid, Reason]), + {error, {unexpected_exit, sec_server3}}; + {ready, Pid} -> + ok + end + end}, + + + %% Terminations + #{desc => "order 'secondary server 3' to terminate", + cmd => fun(#{sec_server3 := Pid} = _State) -> + Pid ! {terminate, self()}, + ok + end}, + #{desc => "await 'secondary server 3' termination", + cmd => fun(#{sec_server3 := Pid} = State) -> + receive + {'DOWN', _, process, Pid, _} -> + {ok, maps:remove(sec_server3, State)} + end + end}, + #{desc => "order 'secondary server 2' to terminate", + cmd => fun(#{sec_server2 := Pid} = _State) -> + Pid ! {terminate, self()}, + ok + end}, + #{desc => "await 'secondary server 2' termination", + cmd => fun(#{sec_server2 := Pid} = State) -> + receive + {'DOWN', _, process, Pid, _} -> + {ok, maps:remove(sec_server2, State)} + end + end}, + #{desc => "order 'secondary server 1' to terminate", + cmd => fun(#{sec_server1 := Pid} = _State) -> + Pid ! {terminate, self()}, + ok + end}, + #{desc => "await 'secondary server 1' termination", + cmd => fun(#{sec_server1 := Pid} = State) -> + receive + {'DOWN', _, process, Pid, _} -> + {ok, maps:remove(sec_server1, State)} + end + end}, + #{desc => "order 'primary server' to terminate", + cmd => fun(#{prim_server := Pid} = _State) -> + Pid ! {terminate, self()}, + ok + end}, + #{desc => "await 'primary server' termination", + cmd => fun(#{prim_server := Pid} = State) -> + receive + {'DOWN', _, process, Pid, _} -> + {ok, maps:remove(prim_server, State)} + end + end}, + + + %% *** We are done *** + #{desc => "finish", + cmd => fun(_) -> + {ok, normal} + end} + ], + + + i("start 'primary server' evaluator"), + PrimSrvInitState = InitState, + PrimServer = evaluator_start("prim-server", PrimServerSeq, PrimSrvInitState), + + i("start 'secondary server 1' evaluator"), + SecSrvInitState = #{recv => maps:get(recv, InitState)}, + SecServer1 = evaluator_start("sec-server-1", SecServerSeq, SecSrvInitState), + + i("start 'secondary server 2' evaluator"), + SecServer2 = evaluator_start("sec-server-2", SecServerSeq, SecSrvInitState), + + i("start 'secondary server 3' evaluator"), + SecServer3 = evaluator_start("sec-server-3", SecServerSeq, SecSrvInitState), + + i("start 'tester' evaluator"), + TesterInitState = #{prim_server => PrimServer#ev.pid, + sec_server1 => SecServer1#ev.pid, + sec_server2 => SecServer2#ev.pid, + sec_server3 => SecServer3#ev.pid}, + Tester = evaluator_start("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = await_evaluator_finish([PrimServer, + SecServer1, SecServer2, SecServer3, + Tester]). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test what happens when a socket is %% remotely closed while the process is calling the recv function. @@ -3925,6 +4451,47 @@ sc_lc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> end). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recvmsg function. +%% Socket is IPv4. + +sc_lc_recvmsg_response_udp4(suite) -> + []; +sc_lc_recvmsg_response_udp4(doc) -> + []; +sc_lc_recvmsg_response_udp4(_Config) when is_list(_Config) -> + tc_try(sc_lc_recvmsg_response_udp4, + fun() -> + ?TT(?SECS(10)), + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => inet, + recv => Recv}, + ok = sc_lc_receive_response_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recvmsg function. +%% Socket is IPv6. + +sc_lc_recvmsg_response_udp6(suite) -> + []; +sc_lc_recvmsg_response_udp6(doc) -> + []; +sc_lc_recvmsg_response_udp6(_Config) when is_list(_Config) -> + tc_try(sc_recvmsg_response_udp6, + fun() -> + not_yet_implemented(), + ?TT(?SECS(10)), + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => inet6, + recv => Recv}, + ok = sc_lc_receive_response_udp(InitState) + end). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test what happens when a socket is %% remotely closed while the process is calling the recvmsg function. -- cgit v1.2.3