diff options
Diffstat (limited to 'erts/emulator/test')
25 files changed, 12945 insertions, 992 deletions
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 43975d1800..c5abd04e07 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -612,6 +612,16 @@ binary_to_existing_atom(Config) when is_list(Config) -> UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1), UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1), + + %% ERL-944; a binary that was too large would overflow the latin1-to-utf8 + %% conversion buffer. + OverflowAtom = <<0:511/unit:8, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133>>, + {'EXIT', _} = (catch binary_to_existing_atom(OverflowAtom, latin1)), + ok. diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 4fb339926e..fbd1325c3a 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1438,7 +1438,8 @@ sleeper() -> gc_test(Config) when is_list(Config) -> %% Note: This test is only relevant for REFC binaries. %% Therefore, we take care that all binaries are REFC binaries. - B = list_to_binary(lists:seq(0, ?heap_binary_size)), + true = 192 > ?heap_binary_size, + B = list_to_binary(lists:seq(1, 192)), Self = self(), F1 = fun() -> gc(), @@ -1447,22 +1448,22 @@ gc_test(Config) when is_list(Config) -> end, F = fun() -> receive go -> ok end, - {binary,[{_,65,1}]} = process_info(self(), binary), + {binary,[{_,192,1}]} = process_info(self(), binary), gc(), - {B1,B2} = my_split_binary(B, 4), + {B1,B2} = my_split_binary(B, 68), gc(), gc(), {binary,L1} = process_info(self(), binary), [Binfo1,Binfo2,Binfo3] = L1, - {_,65,3} = Binfo1 = Binfo2 = Binfo3, - 65 = size(B), - 4 = size(B1), - 61 = size(B2), + {_,192,3} = Binfo1 = Binfo2 = Binfo3, + 192 = size(B), + 68 = size(B1), + 124 = size(B2), F1() end, gc(), gc(), - 65 = size(B), + 192 = size(B), gc_test1(spawn_opt(erlang, apply, [F,[]], [link,{fullsweep_after,0}])). gc_test1(Pid) -> diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 7885d35d9d..9dcdd60060 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -1400,6 +1400,10 @@ get_conflicting_unicode_atoms(CIX, N) -> %% The message_latency_large tests that small distribution messages are %% not blocked by other large distribution messages. Basically it tests %% that fragmentation of distribution messages works. +%% +%% Because of large problems to get reliable values from these testcases +%% they no longer fail when the latency is incorrect. However, they are +%% kept as they continue to find bugs in the distribution implementation. message_latency_large_message(Config) when is_list(Config) -> measure_latency_large_message(?FUNCTION_NAME, fun(Dropper, Payload) -> Dropper ! Payload end). @@ -1484,7 +1488,11 @@ measure_latency_large_message(Nodename, DataFun) -> case {lists:max(Times), lists:min(Times)} of {Max, Min} when Max * 0.25 > Min, BuildType =:= opt -> - ct:fail({incorrect_latency, IndexTimes}); + %% We only issue a comment for this failure as the + %% testcases proved very difficult to run successfully + %% on many platforms. + ct:comment({incorrect_latency, IndexTimes}), + ok; _ -> ok end. @@ -1503,10 +1511,7 @@ measure_latency(DataFun, Dropper, Echo, Payload) -> end end) || _ <- lists:seq(1,2)], - [receive - {monitor, _Sender, busy_dist_port, _Info} -> - ok - end || _ <- lists:seq(1,10)], + wait_for_busy_dist(2 * 60 * 1000, 10), {TS, Times} = timer:tc(fun() -> @@ -1530,6 +1535,18 @@ measure_latency(DataFun, Dropper, Echo, Payload) -> end || {Sender, Ref} <- Senders], TS. +wait_for_busy_dist(_Tmo, 0) -> + ok; +wait_for_busy_dist(Tmo, N) -> + T0 = erlang:monotonic_time(millisecond), + receive + {monitor, _Sender, busy_dist_port, _Info} -> + wait_for_busy_dist(Tmo - (erlang:monotonic_time(millisecond) - T0), N - 1) + after Tmo -> + ct:log("Timed out waiting for busy_dist, ~p left",[N]), + timeout + end. + flush() -> receive _ -> @@ -2600,7 +2617,7 @@ verify_nc(Node) -> demonitor(MonRef,[flush]), ok; {Ref, Error} -> - ct:log("~p",[Error]), + ct:log("~s",[Error]), ct:fail(failed_nc_refc_check); {'DOWN', MonRef, _, _, _} = Down -> ct:log("~p",[Down]), diff --git a/erts/emulator/test/dump_SUITE.erl b/erts/emulator/test/dump_SUITE.erl index 9f8ac42fa9..6133b82756 100644 --- a/erts/emulator/test/dump_SUITE.erl +++ b/erts/emulator/test/dump_SUITE.erl @@ -68,12 +68,14 @@ signal_abort(Config) -> {ok, Node} = start_node(Config), - _P1 = spawn(Node, ?MODULE, load, []), - _P2 = spawn(Node, ?MODULE, load, []), - _P3 = spawn(Node, ?MODULE, load, []), - _P4 = spawn(Node, ?MODULE, load, []), - _P5 = spawn(Node, ?MODULE, load, []), - _P6 = spawn(Node, ?MODULE, load, []), + SO = rpc:call(Node, erlang, system_info, [schedulers_online]), + + _P1 = spawn_opt(Node, ?MODULE, load, [], [{scheduler, (0 rem SO) + 1}]), + _P2 = spawn_opt(Node, ?MODULE, load, [], [{scheduler, (1 rem SO) + 1}]), + _P3 = spawn_opt(Node, ?MODULE, load, [], [{scheduler, (2 rem SO) + 1}]), + _P4 = spawn_opt(Node, ?MODULE, load, [], [{scheduler, (3 rem SO) + 1}]), + _P5 = spawn_opt(Node, ?MODULE, load, [], [{scheduler, (4 rem SO) + 1}]), + _P6 = spawn_opt(Node, ?MODULE, load, [], [{scheduler, (5 rem SO) + 1}]), timer:sleep(500), @@ -140,13 +142,13 @@ free_dump(Config) when is_list(Config) -> {ok, NodeA} = start_node(Config), {ok, NodeB} = start_node(Config), - Self = self(), PidA = spawn_link( NodeA, fun() -> Self ! ready, + Reason = lists:duplicate(1000000,100), receive ok -> spawn(fun() -> @@ -154,24 +156,29 @@ free_dump(Config) when is_list(Config) -> timer:sleep(5), receive M -> - io:format("~p",[M]), - erlang:halt("dump") - end + io:format("~p",[M]) +%% We may want to add this timeout here in-case no busy condition is triggered +%% after 60 * 1000 -> +%% io:format("Timeout") + end, + erlang:halt("dump") end), - exit(lists:duplicate(1000000,100)) + exit(Reason) end end), - spawn_link(NodeB, - fun() -> - [erlang:monitor(process, PidA) || _ <- lists:seq(1,10000)], - Self ! done, - receive _ -> ok end - end), + PidB = spawn_link(NodeB, + fun() -> + [erlang:monitor(process, PidA) || _ <- lists:seq(1,10000)], + Self ! done, + receive _ -> ok end + end), receive done -> ok end, true = rpc:call(NodeA, os, putenv, ["ERL_CRASH_DUMP",Dump]), - ct:pal("~p",[rpc:call(NodeA, distribution_SUITE, make_busy, [NodeB, 1000])]), + %% Make the node busy towards NodeB for 10 seconds. + BusyPid = rpc:call(NodeA, distribution_SUITE, make_busy, [NodeB,10000]), + ct:pal("~p",[BusyPid]), receive ready -> unlink(PidA), PidA ! ok end, @@ -185,6 +192,10 @@ free_dump(Config) when is_list(Config) -> file:delete(Dump), + unlink(PidB), + + rpc:call(NodeB, erlang, halt, [0]), + ok. diff --git a/erts/emulator/test/esock_ttest/esock-ttest b/erts/emulator/test/esock_ttest/esock-ttest index f0d363ab30..2ded557484 100755 --- a/erts/emulator/test/esock_ttest/esock-ttest +++ b/erts/emulator/test/esock_ttest/esock-ttest @@ -50,43 +50,50 @@ usage() -> "~n units (server or client)." "~n" "~n options: " - "~n --help Display this info and exit. " - "~n --server [server-options] Start a server. " - "~n There are no mandatory server options." - "~n --client client-options Start a client" - "~n Some client options are mandatory and" - "~n others optional." - "~n --active <active> boolean() | once." - "~n Valid for both client and server." - "~n Defaults to: false" - "~n --transport <transport> Which transport to use: gen|sock[:plain|msg]" - "~n gen: gen_tcp" - "~n sock: socket" - "~n plain: recv/send (default)" - "~n msg: recvmsg/sendmsg" - "~n Defaults to: sock:plain" - "~n --scon <addr>:<port> Address and port of the server." - "~n The address part is in the standard form:" - "~n \"a.b.c.d\"." - "~n Only valid for client." - "~n Mandatory." - "~n --msg-id <1|2|3> Choose which message to use during the test." - "~n Basically: " - "~n 1: small" - "~n 2: medium" - "~n 3: large" - "~n Defaults to: 1" - "~n --max-outstanding <Num> How many messages to send before waiting for" - "~n a reply." - "~n Valid only for client." - "~n Defaults to: " - "~n MsgID 1: 100" - "~n MsgID 2: 10" - "~n MsgID 3: 1" - "~n --runtime <Time> Time of the test in seconds." - "~n Only valid for client." - "~n Mandatory." - "~n Defaults to: 60 (seconds)" + "~n --help Display this info and exit. " + "~n --server [server-options] Start a server. " + "~n There are no mandatory server options." + "~n --client client-options Start a client" + "~n Some client options are mandatory and" + "~n others optional." + "~n --domain <domain> local | inet | inet6" + "~n Which domain to use." + "~n Only valid for server." + "~n Defaults to: inet" + "~n --async Asynchronous mode (Timeout = nowait)" + "~n This option is only valid for transport = sock." + "~n Also, its only used when active =/= false." + "~n --active <active> boolean() | once." + "~n Valid for both client and server." + "~n Defaults to: false" + "~n --transport <transport> Which transport to use: gen|sock[:plain|msg]" + "~n gen: gen_tcp" + "~n sock: socket" + "~n plain: recv/send (default)" + "~n msg: recvmsg/sendmsg" + "~n Defaults to: sock:plain" + "~n --scon <addr>:<port>|<path> Server info." + "~n The address part is in the standard form:" + "~n \"a.b.c.d\"." + "~n <path> is used for Unix Domain sockets (local)." + "~n Only valid, and mandatory, for client." + "~n --msg-id <1|2|3> Choose which message to use during the test." + "~n Basically: " + "~n 1: small" + "~n 2: medium" + "~n 3: large" + "~n Defaults to: 1" + "~n --max-outstanding <Num> How many messages to send before waiting for" + "~n a reply." + "~n Valid only for client." + "~n Defaults to: " + "~n MsgID 1: 100" + "~n MsgID 2: 10" + "~n MsgID 3: 1" + "~n --runtime <Time> Time of the test in seconds." + "~n Only valid for client." + "~n Mandatory." + "~n Defaults to: 60 (seconds)" "~n" "~n" "~n", @@ -106,6 +113,8 @@ process_args(Args) -> process_server_args(Args) -> Defaults = #{role => server, + domain => inet, + async => false, active => false, transport => {sock, plain}}, process_server_args(Args, Defaults). @@ -113,6 +122,15 @@ process_server_args(Args) -> process_server_args([], State) -> State; +process_server_args(["--domain", Domain|Args], State) + when ((Domain =:= "local") orelse + (Domain =:= "inet") orelse + (Domain =:= "inet6")) -> + process_server_args(Args, State#{domain => list_to_atom(Domain)}); + +process_server_args(["--async"|Args], State) -> + process_server_args(Args, State#{async => true}); + process_server_args(["--active", Active|Args], State) when ((Active =:= "false") orelse (Active =:= "once") orelse @@ -134,10 +152,11 @@ process_server_args([Arg|_], _State) -> process_client_args(Args) -> Defaults = #{role => client, + async => false, active => false, transport => {sock, plain}, %% Will cause error if not provided - %% Should be "addr:port" + %% Should be "addr:port or string() server => undefined, msg_id => 1, %% Will be filled in based on msg_id if not provided @@ -148,10 +167,13 @@ process_client_args(Args) -> process_client_args([], State) -> process_client_args_ensure_max_outstanding(State); +process_client_args(["--async"|Args], State) -> + process_client_args(Args, State#{async => true}); + process_client_args(["--active", Active|Args], State) - when ((Active =:= "false") orelse - (Active =:= "once") orelse - (Active =:= "true")) -> + when (Active =:= "false") orelse + (Active =:= "once") orelse + (Active =:= "true") -> process_client_args(Args, State#{active => list_to_atom(Active)}); process_client_args(["--transport", "gen" | Args], State) -> @@ -181,7 +203,7 @@ process_client_args(["--max-outstanding", Max|Args], State) -> end; process_client_args(["--scon", Server|Args], State) -> - case string:tokens(Server, [$:]) of + case string:split(Server, ":", trailing) of [AddrStr,PortStr] -> Addr = case inet:parse_address(AddrStr) of {ok, A} -> @@ -199,6 +221,8 @@ process_client_args(["--scon", Server|Args], State) -> usage(f("Invalid Server Port: ~s", [PortStr])) end, process_client_args(Args, State#{server => {Addr, Port}}); + [Path] -> + process_client_args(Args, State#{server => Path}); _ -> usage(f("Invalid Server: ~s", [Server])) end; @@ -249,9 +273,11 @@ process_client_args_ensure_max_outstanding( %% ========================================================================== exec(#{role := server, + domain := Domain, active := Active, - transport := gen}) -> - case socket_test_ttest_tcp_server_gen:start(Active) of + transport := gen}) + when (Domain =:= inet) orelse (Domain =:= inet6) -> + case socket_test_ttest_tcp_server_gen:start(Domain, Active) of {ok, {Pid, _}} -> MRef = erlang:monitor(process, Pid), receive @@ -264,9 +290,11 @@ exec(#{role := server, error end; exec(#{role := server, + domain := Domain, + async := Async, active := Active, transport := {sock, Method}}) -> - case socket_test_ttest_tcp_server_socket:start(Method, Active) of + case socket_test_ttest_tcp_server_socket:start(Method, Domain, Async, Active) of {ok, {Pid, _}} -> MRef = erlang:monitor(process, Pid), receive @@ -283,15 +311,15 @@ exec(#{role := client, server := undefined}) -> usage("Mandatory option 'server' not provided"); exec(#{role := client, - server := {Addr, Port}, + server := {_Addr, _Port} = ServerInfo, active := Active, transport := gen, msg_id := MsgID, max_outstanding := MaxOutstanding, runtime := RunTime}) -> case socket_test_ttest_tcp_client_gen:start(true, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime) of {ok, Pid} -> @@ -306,16 +334,18 @@ exec(#{role := client, error end; exec(#{role := client, - server := {Addr, Port}, + server := ServerInfo, + async := Async, active := Active, transport := {sock, Method}, msg_id := MsgID, max_outstanding := MaxOutstanding, runtime := RunTime}) -> case socket_test_ttest_tcp_client_socket:start(true, + Async, + Active, Method, - Active, - Addr, Port, + ServerInfo, MsgID, MaxOutstanding, RunTime) of {ok, Pid} -> diff --git a/erts/emulator/test/esock_ttest/esock-ttest-client b/erts/emulator/test/esock_ttest/esock-ttest-client index 1ab56f2d44..5ae05d03b8 100755 --- a/erts/emulator/test/esock_ttest/esock-ttest-client +++ b/erts/emulator/test/esock_ttest/esock-ttest-client @@ -20,51 +20,71 @@ # %CopyrightEnd% # +# +# This is just a simple convenience wrapper to the esock-ttest. +# That means that there are some options not available here. +# + EMU=$ERL_TOP/erts/emulator EMU_TEST=$EMU/test ESOCK_TTEST=$EMU_TEST/esock_ttest RUNTIME=30 +# RUNTIME=60 +# RUNTIME=600 + +if [ $# = 3 ]; then + MSGID=$1 + SERVER_INFO=$2:$3 + + ITERATIONS="\ + gen false $MSGID + gen true $MSGID + gen once $MSGID + sock false $MSGID --async + sock true $MSGID --async + sock once $MSGID --async" + +else + if [ $# = 2 ]; then + MSGID=$1 + SERVER_INFO=$2 + + ITERATIONS="\ + sock false $MSGID --async + sock true $MSGID --async + sock once $MSGID --async" + + else + echo "Invalid number of args" + exit 1; + fi +fi -MSGID=$1 -SERVER_ADDR=$2 -SERVER_PORT=$3 # --------------------------------------------------------------------------- -ITERATIONS="\ - gen false $MSGID - gen true $MSGID - gen once $MSGID - sock false $MSGID - sock true $MSGID - sock once $MSGID" - -# gen false 2 -# gen true 2 -# gen once 2 -# sock false 2 -# sock true 2 -# sock once 2 -# gen false 3 -# gen true 3 -# gen once 3 -# sock false 3 -# sock true 3 -# sock once 3 -# +# For when we have figured out how to configure local for gen_tcp... + +#ITERATIONS="\ +# gen false $MSGID +# gen true $MSGID +# gen once $MSGID +# sock false $MSGID +# sock true $MSGID +# sock once $MSGID" # --------------------------------------------------------------------------- echo "$ITERATIONS" | - while read TRANSPORT ACTIVE MSG_ID; do + while read TRANSPORT ACTIVE MSG_ID ASYNC; do echo "" echo "=========== transport = $TRANSPORT, active = $ACTIVE, msg-id = $MSG_ID ===========" # The /dev/null at the end is necessary because erlang "does things" with stdin # and this case would cause the 'while read' to "fail" so that we only would # loop one time - $ESOCK_TTEST/esock-ttest --client --transport $TRANSPORT --active $ACTIVE --msg-id $MSG_ID --scon $SERVER_ADDR:$SERVER_PORT --runtime $RUNTIME </dev/null + $ESOCK_TTEST/esock-ttest --client --transport $TRANSPORT $ASYNC --active $ACTIVE --msg-id $MSG_ID --scon $SERVER_INFO --runtime $RUNTIME </dev/null echo "" done diff --git a/erts/emulator/test/esock_ttest/esock-ttest-server-sock b/erts/emulator/test/esock_ttest/esock-ttest-server-sock index 4ec0d335d9..c443d42e64 100755 --- a/erts/emulator/test/esock_ttest/esock-ttest-server-sock +++ b/erts/emulator/test/esock_ttest/esock-ttest-server-sock @@ -24,9 +24,33 @@ EMU=$ERL_TOP/erts/emulator EMU_TEST=$EMU/test ESOCK_TTEST=$EMU_TEST/esock_ttest -if [ $# = 1 ]; then - ACTIVE="--active $1" +# $1 - async - boolean() +# $2 - active - once | boolean() +# [$3 - domain - inet (default) | inet6 | local] +if [ $# -ge 2 ]; then + + async=$1 + active=$2 + + if [ $async = true ]; then + ASYNC="--async" + else + ASYNC= + fi + + ACTIVE="--active $active" + + if [ $# = 3 ]; then + DOMAIN="--domain $3" + fi + + +else + echo "<ERROR> Missing args: async and active" + echo "" + exit 1 fi -$ESOCK_TTEST/esock-ttest --server --transport sock $ACTIVE + +$ESOCK_TTEST/esock-ttest --server $DOMAIN $ASYNC --transport sock $ACTIVE diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index c4d9ea515a..154bce3c35 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -23,6 +23,7 @@ -export([all/0, suite/0, badmatch/1, pending_errors/1, nil_arith/1, top_of_stacktrace/1, stacktrace/1, nested_stacktrace/1, raise/1, gunilla/1, per/1, + change_exception_class/1, exception_with_heap_frag/1, backtrace_depth/1, line_numbers/1]). @@ -48,6 +49,7 @@ suite() -> all() -> [badmatch, pending_errors, nil_arith, top_of_stacktrace, stacktrace, nested_stacktrace, raise, gunilla, per, + change_exception_class, exception_with_heap_frag, backtrace_depth, line_numbers]. -define(try_match(E), @@ -512,6 +514,38 @@ t1(_,X,_) -> t2(_,X,_) -> (X bsl 1) + 1. +change_exception_class(_Config) -> + try + change_exception_class_1(fun() -> throw(arne) end) + catch + error:arne -> + ok; + Class:arne -> + ct:fail({wrong_exception_class,Class}) + end. + +change_exception_class_1(F) -> + try + change_exception_class_2(F) + after + %% The exception would be caught and rethrown using + %% an i_raise instruction. Before the correction + %% of the raw_raise instruction, the change of class + %% would not stick. + io:put_chars("Exception automatically rethrown here\n") + end. + +change_exception_class_2(F) -> + try + F() + catch + throw:Reason:Stack -> + %% Translated to a raw_raise instruction. + %% The change of exception class would not stick + %% if the i_raise instruction was later executed. + erlang:raise(error, Reason, Stack) + end. + %% %% Make sure that even if a BIF builds an heap fragment, then causes an exception, %% the stacktrace term will still be OK (specifically, that it does not contain diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 2cbde621ce..ad8ef0feff 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -27,7 +27,7 @@ fun_to_port/1,t_phash/1,t_phash2/1,md5/1, refc/1,refc_ets/1,refc_dist/1, const_propagation/1,t_arity/1,t_is_function2/1, - t_fun_info/1,t_fun_info_mfa/1]). + t_fun_info/1,t_fun_info_mfa/1,t_fun_to_list/1]). -export([nothing/0]). @@ -44,7 +44,7 @@ all() -> equality, ordering, fun_to_port, t_phash, t_phash2, md5, refc, refc_ets, refc_dist, const_propagation, t_arity, t_is_function2, t_fun_info, - t_fun_info_mfa]. + t_fun_info_mfa,t_fun_to_list]. %% Test that the correct EXIT code is returned for all types of bad funs. bad_apply(Config) when is_list(Config) -> @@ -802,6 +802,12 @@ t_fun_info_mfa(Config) when is_list(Config) -> {'EXIT',_} = (catch erlang:fun_info_mfa(id(d))), ok. +t_fun_to_list(Config) when is_list(Config) -> + "fun a:b/1" = erlang:fun_to_list(fun a:b/1), + "fun 'a-esc':'b-esc'/1" = erlang:fun_to_list(fun 'a-esc':'b-esc'/1), + "fun 'a-esc':b/1" = erlang:fun_to_list(fun 'a-esc':b/1), + "fun a:'b-esc'/1" = erlang:fun_to_list(fun a:'b-esc'/1), + ok. bad_info(Term) -> try erlang:fun_info(Term, module) of diff --git a/erts/emulator/test/multi_load_SUITE.erl b/erts/emulator/test/multi_load_SUITE.erl index edf3205812..c79e2b6dcd 100644 --- a/erts/emulator/test/multi_load_SUITE.erl +++ b/erts/emulator/test/multi_load_SUITE.erl @@ -30,7 +30,15 @@ all() -> [many,on_load,errors]. many(_Config) -> - Ms = make_modules(100, fun many_module/1), + + N = case erlang:system_info(build_type) of + valgrind -> + 10; + _ -> + 100 + end, + + Ms = make_modules(N, fun many_module/1), io:put_chars("Light load\n" "=========="), diff --git a/erts/emulator/test/net_SUITE.erl b/erts/emulator/test/net_SUITE.erl index 6111fc76a5..c6e77a5373 100644 --- a/erts/emulator/test/net_SUITE.erl +++ b/erts/emulator/test/net_SUITE.erl @@ -20,6 +20,8 @@ %% %% This test suite is basically a "placeholder" for a proper test suite... +%% Also we should really call prim_net directly, and not net (since that does +%% not even reside here). %% %% Run the entire test suite: @@ -127,6 +129,7 @@ api_basic_cases() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_per_suite(Config) -> + %% We test on the socket module for simplicity case lists:member(socket, erlang:loaded()) of true -> case os:type() of diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index ef4635a6f5..c44693f8d9 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -51,7 +51,8 @@ unique_pid/1, iter_max_procs/1, magic_ref/1, - dist_entry_gc/1]). + dist_entry_gc/1, + persistent_term/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -63,7 +64,8 @@ all() -> node_table_gc, dist_link_refc, dist_monitor_refc, node_controller_refc, ets_refc, match_spec_refc, timer_refc, pid_wrap, port_wrap, bad_nc, - unique_pid, iter_max_procs, magic_ref]. + unique_pid, iter_max_procs, + magic_ref, persistent_term]. init_per_suite(Config) -> Config. @@ -570,7 +572,17 @@ node_controller_refc(Config) when is_list(Config) -> wait_until(fun () -> not is_process_alive(P) end), lists:foreach(fun (Proc) -> garbage_collect(Proc) end, processes()), false = get_node_references({Node,Creation}), - false = get_dist_references(Node), + wait_until(fun () -> + case get_dist_references(Node) of + false -> + true; + [{{system,thread_progress_delete_timer}, + [{system,1}]}] -> + false; + Other -> + ct:fail(Other) + end + end), false = lists:member(Node, nodes(known)), nc_refc_check(node()), erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value @@ -871,7 +883,22 @@ magic_ref(Config) when is_list(Config) -> {'DOWN', Mon, process, Pid, _} -> ok end, - {Addr0, 2, true} = erts_debug:get_internal_state({magic_ref,MRef0}), + MaxTime = erlang:monotonic_time(millisecond) + 1000, + %% The DOWN signal is sent before heap is cleaned up, + %% so we might need to wait some time after the DOWN + %% signal has been received before the heap actually + %% has been cleaned up... + wait_until(fun () -> + case erts_debug:get_internal_state({magic_ref,MRef0}) of + {Addr0, 2, true} -> + true; + {Addr0, 3, true} -> + true = MaxTime >= erlang:monotonic_time(millisecond), + false; + Error -> + ct:fail(Error) + end + end), id(MRef0), id(MRef1), MRefExt = term_to_binary(erts_debug:set_internal_state(make, magic_ref)), @@ -881,6 +908,44 @@ magic_ref(Config) when is_list(Config) -> true = erts_debug:get_internal_state({magic_ref,MRef2}), ok. +persistent_term(Config) when is_list(Config) -> + {ok, Node} = start_node(get_nodefirstname()), + Self = self(), + NcData = make_ref(), + RPid = spawn_link(Node, + fun () -> + Self ! {NcData, self(), hd(erlang:ports()), erlang:make_ref()} + end), + Data = receive + {NcData, RPid, RPort, RRef} -> + {RPid, RPort, RRef} + end, + unlink(RPid), + stop_node(Node), + Stuff = lists:foldl(fun (N, Acc) -> + persistent_term:put({?MODULE, N}, Data), + persistent_term:erase({?MODULE, N-1}), + node_container_refc_check(node()), + Data = persistent_term:get({?MODULE, N}), + try + persistent_term:get({?MODULE, N-1}) + catch + error:badarg -> + ok + end, + case N rem 4 of + 0 -> [persistent_term:get({?MODULE, N})|Acc]; + _ -> Acc + end + end, + [], + lists:seq(1, 100)), + persistent_term:erase({?MODULE, 100}), + receive after 2000 -> ok end, %% give literal gc some time to run... + node_container_refc_check(node()), + id(Stuff), + ok. + lost_pending_connection(Node) -> _ = (catch erts_internal:new_connection(Node)), diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index b530ced566..13dde12e69 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -1098,42 +1098,86 @@ process_info_status_handled_signal(Config) when is_list(Config) -> %% OTP-15709 %% Provoke a bug where process_info(reductions) returned wrong result %% because REDS_IN (def_arg_reg[5]) is read when the process in not running. +%% +%% And a bug where process_info(reductions) on a process which was releasing its +%% main lock during execution could result in negative reduction diffs. process_info_reductions(Config) when is_list(Config) -> - pi_reductions_tester(spawn_link(fun() -> pi_reductions_spinnloop() end)), - pi_reductions_tester(spawn_link(fun() -> pi_reductions_recvloop() end)), + {S1, S2} = case erlang:system_info(schedulers) of + 1 -> {1,1}; + _ -> {1,2} + end, + io:format("Run on schedulers ~p and ~p\n", [S1,S2]), + Boss = self(), + Doer = spawn_opt(fun () -> + pi_reductions_tester(true, 10, fun pi_reductions_spinnloop/0, S2), + pi_reductions_tester(true, 10, fun pi_reductions_recvloop/0, S2), + pi_reductions_tester(false, 100, fun pi_reductions_main_unlocker/0, S2), + Boss ! {self(), done} + end, + [link, {scheduler, S1}]), + + {Doer, done} = receive M -> M end, ok. -pi_reductions_tester(Pid) -> - {_, DiffList} = - lists:foldl(fun(_, {Prev, Acc}) -> - %% Add another item that force sending the request - %% as a signal, like 'current_function'. - PI = process_info(Pid, [reductions, current_function]), - [{reductions,Reds}, {current_function,_}] = PI, - Diff = Reds - Prev, - {Diff, true} = {Diff, (Diff >= 0)}, - {Diff, true} = {Diff, (Diff =< 1000*1000)}, - {Reds, [Diff | Acc]} - end, - {0, []}, - lists:seq(1,10)), +pi_reductions_tester(ForceSignal, MaxCalls, Fun, S2) -> + Pid = spawn_opt(Fun, [link, {scheduler,S2}]), + Extra = case ForceSignal of + true -> + %% Add another item that force sending the request + %% as a signal, like 'current_function'. + [current_function]; + false -> + [] + end, + LoopFun = fun Me(Calls, Prev, Acc0) -> + PI = process_info(Pid, [reductions | Extra]), + [{reductions,Reds} | _] = PI, + Diff = Reds - Prev, + %% Verify we get sane non-negative reduction diffs + {Diff, true} = {Diff, (Diff >= 0)}, + {Diff, true} = {Diff, (Diff =< 1000*1000)}, + Acc1 = [Diff | Acc0], + case Calls >= MaxCalls of + true -> Acc1; + false -> Me(Calls+1, Reds, Acc1) + end + end, + DiffList = LoopFun(0, 0, []), unlink(Pid), exit(Pid,kill), - io:format("Reduction diffs: ~p\n", [DiffList]), + io:format("Reduction diffs: ~p\n", [lists:reverse(DiffList)]), ok. pi_reductions_spinnloop() -> %% 6 args to make use of def_arg_reg[5] which is also used as REDS_IN - pi_reductions_spinnloop(1, atom, "hej", self(), make_ref(), 3.14). + pi_reductions_spinnloop(999*1000, atom, "hej", self(), make_ref(), 3.14). -pi_reductions_spinnloop(A,B,C,D,E,F) -> - pi_reductions_spinnloop(B,C,D,E,F,A). +pi_reductions_spinnloop(N,A,B,C,D,E) when N > 0 -> + pi_reductions_spinnloop(N-1,B,C,D,E,A); +pi_reductions_spinnloop(0,_,_,_,_,_) -> + %% Stop to limit max number of reductions consumed + pi_reductions_recvloop(). pi_reductions_recvloop() -> receive "a free lunch" -> false end. +pi_reductions_main_unlocker() -> + Other = spawn_link(fun() -> receive die -> ok end end), + pi_reductions_main_unlocker_loop(Other). + +pi_reductions_main_unlocker_loop(Other) -> + %% Assumption: register(OtherPid, Name) will unlock main lock of calling + %% process during execution. + register(pi_reductions_main_unlocker, Other), + unregister(pi_reductions_main_unlocker), + + %% Yield in order to increase probability of process_info sometimes probing + %% this process when it's not RUNNING. + erlang:yield(), + pi_reductions_main_unlocker_loop(Other). + %% Tests erlang:bump_reductions/1. bump_reductions(Config) when is_list(Config) -> @@ -2556,14 +2600,20 @@ garb_other_running(Config) when is_list(Config) -> no_priority_inversion(Config) when is_list(Config) -> Prio = process_flag(priority, max), - HTLs = lists:map(fun (_) -> + Master = self(), + Executing = make_ref(), + HTLs = lists:map(fun (Sched) -> spawn_opt(fun () -> + Master ! {self(), Executing}, tok_loop() end, - [{priority, high}, monitor, link]) + [{priority, high}, + {scheduler, Sched}, + monitor, + link]) end, - lists:seq(1, 2*erlang:system_info(schedulers))), - receive after 500 -> ok end, + lists:seq(1, erlang:system_info(schedulers_online))), + lists:foreach(fun ({P, _}) -> receive {P,Executing} -> ok end end, HTLs), LTL = spawn_opt(fun () -> tok_loop() end, @@ -2585,14 +2635,19 @@ no_priority_inversion(Config) when is_list(Config) -> no_priority_inversion2(Config) when is_list(Config) -> Prio = process_flag(priority, max), - MTLs = lists:map(fun (_) -> + Master = self(), + Executing = make_ref(), + MTLs = lists:map(fun (Sched) -> spawn_opt(fun () -> + Master ! {self(), Executing}, tok_loop() end, - [{priority, max}, monitor, link]) + [{priority, max}, + {scheduler, Sched}, + monitor, link]) end, - lists:seq(1, 2*erlang:system_info(schedulers))), - receive after 2000 -> ok end, + lists:seq(1, erlang:system_info(schedulers_online))), + lists:foreach(fun ({P, _}) -> receive {P,Executing} -> ok end end, MTLs), {PL, ML} = spawn_opt(fun () -> tok_loop() end, diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl index e3545ccbf9..4980ea2a82 100644 --- a/erts/emulator/test/socket_SUITE.erl +++ b/erts/emulator/test/socket_SUITE.erl @@ -58,6 +58,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct_event.hrl"). +-include("socket_test_evaluator.hrl"). %% Suite exports -export([suite/0, all/0, groups/0]). @@ -67,18 +68,72 @@ %% Test cases -export([ + %% *** API Misc *** + api_m_debug/1, + %% *** API Basic *** api_b_open_and_close_udp4/1, api_b_open_and_close_tcp4/1, + api_b_open_and_close_udpL/1, + api_b_open_and_close_tcpL/1, api_b_sendto_and_recvfrom_udp4/1, + api_b_sendto_and_recvfrom_udpL/1, api_b_sendmsg_and_recvmsg_udp4/1, + api_b_sendmsg_and_recvmsg_udpL/1, api_b_send_and_recv_tcp4/1, + api_b_send_and_recv_tcpL/1, api_b_sendmsg_and_recvmsg_tcp4/1, + api_b_sendmsg_and_recvmsg_tcpL/1, + + %% *** API async *** + api_a_connect_tcp4/1, + api_a_connect_tcp6/1, + api_a_sendto_and_recvfrom_udp4/1, + api_a_sendto_and_recvfrom_udp6/1, + api_a_sendmsg_and_recvmsg_udp4/1, + api_a_sendmsg_and_recvmsg_udp6/1, + api_a_send_and_recv_tcp4/1, + api_a_send_and_recv_tcp6/1, + api_a_sendmsg_and_recvmsg_tcp4/1, + api_a_sendmsg_and_recvmsg_tcp6/1, + api_a_recvfrom_cancel_udp4/1, + api_a_recvfrom_cancel_udp6/1, + api_a_recvmsg_cancel_udp4/1, + api_a_recvmsg_cancel_udp6/1, + api_a_accept_cancel_tcp4/1, + api_a_accept_cancel_tcp6/1, + api_a_recv_cancel_tcp4/1, + api_a_recv_cancel_tcp6/1, + api_a_recvmsg_cancel_tcp4/1, + api_a_recvmsg_cancel_tcp6/1, + api_a_mrecvfrom_cancel_udp4/1, + api_a_mrecvfrom_cancel_udp6/1, + api_a_mrecvmsg_cancel_udp4/1, + api_a_mrecvmsg_cancel_udp6/1, + api_a_maccept_cancel_tcp4/1, + api_a_maccept_cancel_tcp6/1, + api_a_mrecv_cancel_tcp4/1, + api_a_mrecv_cancel_tcp6/1, + api_a_mrecvmsg_cancel_tcp4/1, + api_a_mrecvmsg_cancel_tcp6/1, + %% *** API Options *** api_opt_simple_otp_options/1, api_opt_simple_otp_rcvbuf_option/1, api_opt_simple_otp_controlling_process/1, + api_opt_sock_acceptconn_udp/1, + api_opt_sock_acceptconn_tcp/1, + api_opt_sock_acceptfilter/1, + api_opt_sock_bindtodevice/1, + api_opt_sock_broadcast/1, + api_opt_sock_debug/1, + api_opt_sock_domain/1, + api_opt_sock_dontroute/1, + api_opt_sock_error/1, + api_opt_sock_keepalive/1, + api_opt_sock_linger/1, + api_opt_ip_add_drop_membership/1, %% *** API Operation Timeout *** api_to_connect_tcp4/1, @@ -107,57 +162,92 @@ %% *** Socket Closure *** sc_cpe_socket_cleanup_tcp4/1, sc_cpe_socket_cleanup_tcp6/1, + sc_cpe_socket_cleanup_tcpL/1, sc_cpe_socket_cleanup_udp4/1, sc_cpe_socket_cleanup_udp6/1, + sc_cpe_socket_cleanup_udpL/1, sc_lc_recv_response_tcp4/1, sc_lc_recv_response_tcp6/1, + sc_lc_recv_response_tcpL/1, sc_lc_recvfrom_response_udp4/1, sc_lc_recvfrom_response_udp6/1, + sc_lc_recvfrom_response_udpL/1, sc_lc_recvmsg_response_tcp4/1, sc_lc_recvmsg_response_tcp6/1, + sc_lc_recvmsg_response_tcpL/1, sc_lc_recvmsg_response_udp4/1, sc_lc_recvmsg_response_udp6/1, + sc_lc_recvmsg_response_udpL/1, sc_lc_acceptor_response_tcp4/1, sc_lc_acceptor_response_tcp6/1, + sc_lc_acceptor_response_tcpL/1, sc_rc_recv_response_tcp4/1, sc_rc_recv_response_tcp6/1, + sc_rc_recv_response_tcpL/1, sc_rc_recvmsg_response_tcp4/1, sc_rc_recvmsg_response_tcp6/1, + sc_rc_recvmsg_response_tcpL/1, sc_rs_recv_send_shutdown_receive_tcp4/1, sc_rs_recv_send_shutdown_receive_tcp6/1, + sc_rs_recv_send_shutdown_receive_tcpL/1, sc_rs_recvmsg_send_shutdown_receive_tcp4/1, sc_rs_recvmsg_send_shutdown_receive_tcp6/1, + sc_rs_recvmsg_send_shutdown_receive_tcpL/1, %% *** Traffic *** + traffic_send_and_recv_counters_tcp4/1, + traffic_send_and_recv_counters_tcp6/1, + traffic_send_and_recv_counters_tcpL/1, + traffic_sendmsg_and_recvmsg_counters_tcp4/1, + traffic_sendmsg_and_recvmsg_counters_tcp6/1, + traffic_sendmsg_and_recvmsg_counters_tcpL/1, + traffic_sendto_and_recvfrom_counters_udp4/1, + traffic_sendto_and_recvfrom_counters_udp6/1, + traffic_sendto_and_recvfrom_counters_udpL/1, + traffic_sendmsg_and_recvmsg_counters_udp4/1, + traffic_sendmsg_and_recvmsg_counters_udp6/1, + traffic_sendmsg_and_recvmsg_counters_udpL/1, + traffic_send_and_recv_chunks_tcp4/1, traffic_send_and_recv_chunks_tcp6/1, + traffic_send_and_recv_chunks_tcpL/1, traffic_ping_pong_small_send_and_recv_tcp4/1, traffic_ping_pong_small_send_and_recv_tcp6/1, + traffic_ping_pong_small_send_and_recv_tcpL/1, traffic_ping_pong_medium_send_and_recv_tcp4/1, traffic_ping_pong_medium_send_and_recv_tcp6/1, + traffic_ping_pong_medium_send_and_recv_tcpL/1, traffic_ping_pong_large_send_and_recv_tcp4/1, traffic_ping_pong_large_send_and_recv_tcp6/1, + traffic_ping_pong_large_send_and_recv_tcpL/1, traffic_ping_pong_small_sendto_and_recvfrom_udp4/1, traffic_ping_pong_small_sendto_and_recvfrom_udp6/1, + traffic_ping_pong_small_sendto_and_recvfrom_udpL/1, traffic_ping_pong_medium_sendto_and_recvfrom_udp4/1, traffic_ping_pong_medium_sendto_and_recvfrom_udp6/1, + traffic_ping_pong_medium_sendto_and_recvfrom_udpL/1, traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4/1, traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6/1, + traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6/1, + traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL/1, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4/1, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6/1, + traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL/1, traffic_ping_pong_small_sendmsg_and_recvmsg_udp4/1, traffic_ping_pong_small_sendmsg_and_recvmsg_udp6/1, + traffic_ping_pong_small_sendmsg_and_recvmsg_udpL/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6/1, + traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL/1, %% *** Time Test *** %% Server: transport = gen_tcp, active = false @@ -325,24 +415,33 @@ %% Client: transport = socket(tcp) ttest_ssockf_csockf_small_tcp4/1, ttest_ssockf_csockf_small_tcp6/1, + ttest_ssockf_csockf_small_tcpL/1, ttest_ssockf_csockf_medium_tcp4/1, ttest_ssockf_csockf_medium_tcp6/1, + ttest_ssockf_csockf_medium_tcpL/1, ttest_ssockf_csockf_large_tcp4/1, ttest_ssockf_csockf_large_tcp6/1, + ttest_ssockf_csockf_large_tcpL/1, ttest_ssockf_csocko_small_tcp4/1, ttest_ssockf_csocko_small_tcp6/1, + ttest_ssockf_csocko_small_tcpL/1, ttest_ssockf_csocko_medium_tcp4/1, ttest_ssockf_csocko_medium_tcp6/1, + ttest_ssockf_csocko_medium_tcpL/1, ttest_ssockf_csocko_large_tcp4/1, ttest_ssockf_csocko_large_tcp6/1, + ttest_ssockf_csocko_large_tcpL/1, ttest_ssockf_csockt_small_tcp4/1, ttest_ssockf_csockt_small_tcp6/1, + ttest_ssockf_csockt_small_tcpL/1, ttest_ssockf_csockt_medium_tcp4/1, ttest_ssockf_csockt_medium_tcp6/1, + ttest_ssockf_csockt_medium_tcpL/1, ttest_ssockf_csockt_large_tcp4/1, ttest_ssockf_csockt_large_tcp6/1, + ttest_ssockf_csockt_large_tcpL/1, %% Server: transport = socket(tcp), active = once %% Client: transport = gen_tcp @@ -371,24 +470,33 @@ %% Client: transport = socket(tcp) ttest_ssocko_csockf_small_tcp4/1, ttest_ssocko_csockf_small_tcp6/1, + ttest_ssocko_csockf_small_tcpL/1, ttest_ssocko_csockf_medium_tcp4/1, + ttest_ssocko_csockf_medium_tcpL/1, ttest_ssocko_csockf_medium_tcp6/1, ttest_ssocko_csockf_large_tcp4/1, ttest_ssocko_csockf_large_tcp6/1, + ttest_ssocko_csockf_large_tcpL/1, ttest_ssocko_csocko_small_tcp4/1, ttest_ssocko_csocko_small_tcp6/1, + ttest_ssocko_csocko_small_tcpL/1, ttest_ssocko_csocko_medium_tcp4/1, ttest_ssocko_csocko_medium_tcp6/1, + ttest_ssocko_csocko_medium_tcpL/1, ttest_ssocko_csocko_large_tcp4/1, ttest_ssocko_csocko_large_tcp6/1, + ttest_ssocko_csocko_large_tcpL/1, ttest_ssocko_csockt_small_tcp4/1, ttest_ssocko_csockt_small_tcp6/1, + ttest_ssocko_csockt_small_tcpL/1, ttest_ssocko_csockt_medium_tcp4/1, ttest_ssocko_csockt_medium_tcp6/1, + ttest_ssocko_csockt_medium_tcpL/1, ttest_ssocko_csockt_large_tcp4/1, ttest_ssocko_csockt_large_tcp6/1, + ttest_ssocko_csockt_large_tcpL/1, %% Server: transport = socket(tcp), active = true %% Client: transport = gen_tcp @@ -417,53 +525,63 @@ %% Client: transport = socket(tcp) ttest_ssockt_csockf_small_tcp4/1, ttest_ssockt_csockf_small_tcp6/1, + ttest_ssockt_csockf_small_tcpL/1, ttest_ssockt_csockf_medium_tcp4/1, ttest_ssockt_csockf_medium_tcp6/1, + ttest_ssockt_csockf_medium_tcpL/1, ttest_ssockt_csockf_large_tcp4/1, ttest_ssockt_csockf_large_tcp6/1, + ttest_ssockt_csockf_large_tcpL/1, ttest_ssockt_csocko_small_tcp4/1, ttest_ssockt_csocko_small_tcp6/1, + ttest_ssockt_csocko_small_tcpL/1, ttest_ssockt_csocko_medium_tcp4/1, ttest_ssockt_csocko_medium_tcp6/1, + ttest_ssockt_csocko_medium_tcpL/1, ttest_ssockt_csocko_large_tcp4/1, ttest_ssockt_csocko_large_tcp6/1, + ttest_ssockt_csocko_large_tcpL/1, ttest_ssockt_csockt_small_tcp4/1, ttest_ssockt_csockt_small_tcp6/1, + ttest_ssockt_csockt_small_tcpL/1, ttest_ssockt_csockt_medium_tcp4/1, ttest_ssockt_csockt_medium_tcp6/1, + ttest_ssockt_csockt_medium_tcpL/1, ttest_ssockt_csockt_large_tcp4/1, - ttest_ssockt_csockt_large_tcp6/1 + ttest_ssockt_csockt_large_tcp6/1, + ttest_ssockt_csockt_large_tcpL/1 %% Tickets ]). --include("socket_test_evaluator.hrl"). - %% Internal exports %% -export([]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --define(BASIC_REQ, <<"hejsan">>). --define(BASIC_REP, <<"hoppsan">>). +-define(LIB, socket_test_lib). +-define(TTEST_LIB, socket_test_ttest_lib). +-define(LOGGER, socket_test_logger). + +-define(BASIC_REQ, <<"hejsan">>). +-define(BASIC_REP, <<"hoppsan">>). --define(DATA, <<"HOPPSAN">>). % Temporary --define(FAIL(R), exit(R)). +-define(DATA, <<"HOPPSAN">>). % Temporary +-define(FAIL(R), exit(R)). --define(SLEEP(T), receive after T -> ok end). +-define(SLEEP(T), receive after T -> ok end). --define(MINS(M), timer:minutes(M)). --define(SECS(S), timer:seconds(S)). +-define(MINS(M), timer:minutes(M)). +-define(SECS(S), timer:seconds(S)). --define(TT(T), ct:timetrap(T)). +-define(TT(T), ct:timetrap(T)). + +-define(F(F, A), ?LIB:f(F, A)). --define(LIB, socket_test_lib). --define(TTEST_LIB, socket_test_ttest_lib). --define(LOGGER, socket_test_logger). -define(TPP_SMALL, lists:seq(1, 8)). -define(TPP_MEDIUM, lists:flatten(lists:duplicate(1024, ?TPP_SMALL))). @@ -483,10 +601,10 @@ suite() -> {timetrap,{minutes,1}}]. all() -> - Groups = [{api, "ESOCK_TEST_API", include}, - {socket_closure, "ESOCK_TEST_SOCK_CLOSE", include}, - {traffic, "ESOCK_TEST_TRAFFIC", include}, - {ttest, "ESOCK_TEST_TTEST", exclude}], + Groups = [{api, "ESOCK_TEST_API", include}, + {socket_close, "ESOCK_TEST_SOCK_CLOSE", include}, + {traffic, "ESOCK_TEST_TRAFFIC", include}, + {ttest, "ESOCK_TEST_TTEST", exclude}], [use_group(Group, Env, Default) || {Group, Env, Default} <- Groups]. use_group(Group, Env, Default) -> @@ -508,99 +626,201 @@ use_group(Group, Env, Default) -> groups() -> - [{api, [], api_cases()}, - {api_basic, [], api_basic_cases()}, - {api_options, [], api_options_cases()}, - {api_op_with_timeout, [], api_op_with_timeout_cases()}, - {socket_closure, [], socket_closure_cases()}, - {sc_ctrl_proc_exit, [], sc_cp_exit_cases()}, - {sc_local_close, [], sc_lc_cases()}, - {sc_remote_close, [], sc_rc_cases()}, - {sc_remote_shutdown, [], sc_rs_cases()}, - {traffic, [], traffic_cases()}, - {ttest, [], ttest_cases()}, - {ttest_sgenf, [], ttest_sgenf_cases()}, - {ttest_sgenf_cgen, [], ttest_sgenf_cgen_cases()}, - {ttest_sgenf_cgenf, [], ttest_sgenf_cgenf_cases()}, - {ttest_sgenf_cgeno, [], ttest_sgenf_cgeno_cases()}, - {ttest_sgenf_cgent, [], ttest_sgenf_cgent_cases()}, - {ttest_sgenf_csock, [], ttest_sgenf_csock_cases()}, - {ttest_sgenf_csockf, [], ttest_sgenf_csockf_cases()}, - {ttest_sgenf_csocko, [], ttest_sgenf_csocko_cases()}, - {ttest_sgenf_csockt, [], ttest_sgenf_csockt_cases()}, - {ttest_sgeno, [], ttest_sgeno_cases()}, - {ttest_sgeno_cgen, [], ttest_sgeno_cgen_cases()}, - {ttest_sgeno_cgenf, [], ttest_sgeno_cgenf_cases()}, - {ttest_sgeno_cgeno, [], ttest_sgeno_cgeno_cases()}, - {ttest_sgeno_cgent, [], ttest_sgeno_cgent_cases()}, - {ttest_sgeno_csock, [], ttest_sgeno_csock_cases()}, - {ttest_sgeno_csockf, [], ttest_sgeno_csockf_cases()}, - {ttest_sgeno_csocko, [], ttest_sgeno_csocko_cases()}, - {ttest_sgeno_csockt, [], ttest_sgeno_csockt_cases()}, - {ttest_sgent, [], ttest_sgent_cases()}, - {ttest_sgent_cgen, [], ttest_sgent_cgen_cases()}, - {ttest_sgent_cgenf, [], ttest_sgent_cgenf_cases()}, - {ttest_sgent_cgeno, [], ttest_sgent_cgeno_cases()}, - {ttest_sgent_cgent, [], ttest_sgent_cgent_cases()}, - {ttest_sgent_csock, [], ttest_sgent_csock_cases()}, - {ttest_sgent_csockf, [], ttest_sgent_csockf_cases()}, - {ttest_sgent_csocko, [], ttest_sgent_csocko_cases()}, - {ttest_sgent_csockt, [], ttest_sgent_csockt_cases()}, - {ttest_ssockf, [], ttest_ssockf_cases()}, - {ttest_ssockf_cgen, [], ttest_ssockf_cgen_cases()}, - {ttest_ssockf_cgenf, [], ttest_ssockf_cgenf_cases()}, - {ttest_ssockf_cgeno, [], ttest_ssockf_cgeno_cases()}, - {ttest_ssockf_cgent, [], ttest_ssockf_cgent_cases()}, - {ttest_ssockf_csock, [], ttest_ssockf_csock_cases()}, - {ttest_ssockf_csockf, [], ttest_ssockf_csockf_cases()}, - {ttest_ssockf_csocko, [], ttest_ssockf_csocko_cases()}, - {ttest_ssockf_csockt, [], ttest_ssockf_csockt_cases()}, - {ttest_ssocko, [], ttest_ssocko_cases()}, - {ttest_ssocko_cgen, [], ttest_ssocko_cgen_cases()}, - {ttest_ssocko_cgenf, [], ttest_ssocko_cgenf_cases()}, - {ttest_ssocko_cgeno, [], ttest_ssocko_cgeno_cases()}, - {ttest_ssocko_cgent, [], ttest_ssocko_cgent_cases()}, - {ttest_ssocko_csock, [], ttest_ssocko_csock_cases()}, - {ttest_ssocko_csockf, [], ttest_ssocko_csockf_cases()}, - {ttest_ssocko_csocko, [], ttest_ssocko_csocko_cases()}, - {ttest_ssocko_csockt, [], ttest_ssocko_csockt_cases()}, - {ttest_ssockt, [], ttest_ssockt_cases()}, - {ttest_ssockt_cgen, [], ttest_ssockt_cgen_cases()}, - {ttest_ssockt_cgenf, [], ttest_ssockt_cgenf_cases()}, - {ttest_ssockt_cgeno, [], ttest_ssockt_cgeno_cases()}, - {ttest_ssockt_cgent, [], ttest_ssockt_cgent_cases()}, - {ttest_ssockt_csock, [], ttest_ssockt_csock_cases()}, - {ttest_ssockt_csockf, [], ttest_ssockt_csockf_cases()}, - {ttest_ssockt_csocko, [], ttest_ssockt_csocko_cases()}, - {ttest_ssockt_csockt, [], ttest_ssockt_csockt_cases()} + [{api, [], api_cases()}, + {api_misc, [], api_misc_cases()}, + {api_basic, [], api_basic_cases()}, + {api_async, [], api_async_cases()}, + {api_options, [], api_options_cases()}, + {api_options_otp, [], api_options_otp_cases()}, + {api_options_socket, [], api_options_socket_cases()}, + {api_option_sock_acceptconn, [], api_option_sock_acceptconn_cases()}, + {api_options_ip, [], api_options_ip_cases()}, + %% {api_options_ipv6, [], api_options_ipv6_cases()}, + %% {api_options_tcp, [], api_options_tcp_cases()}, + %% {api_options_udp, [], api_options_udp_cases()}, + %% {api_options_sctp, [], api_options_sctp_cases()}, + {api_op_with_timeout, [], api_op_with_timeout_cases()}, + {socket_close, [], socket_close_cases()}, + {sc_ctrl_proc_exit, [], sc_cp_exit_cases()}, + {sc_local_close, [], sc_lc_cases()}, + {sc_remote_close, [], sc_rc_cases()}, + {sc_remote_shutdown, [], sc_rs_cases()}, + {traffic, [], traffic_cases()}, + {traffic_counters, [], traffic_counters_cases()}, + {traffic_chunks, [], traffic_chunks_cases()}, + {traffic_pp_send_recv, [], traffic_pp_send_recv_cases()}, + {traffic_pp_sendto_recvfrom, [], traffic_pp_sendto_recvfrom_cases()}, + {traffic_pp_sendmsg_recvmsg, [], traffic_pp_sendmsg_recvmsg_cases()}, + {ttest, [], ttest_cases()}, + {ttest_sgenf, [], ttest_sgenf_cases()}, + {ttest_sgenf_cgen, [], ttest_sgenf_cgen_cases()}, + {ttest_sgenf_cgenf, [], ttest_sgenf_cgenf_cases()}, + {ttest_sgenf_cgeno, [], ttest_sgenf_cgeno_cases()}, + {ttest_sgenf_cgent, [], ttest_sgenf_cgent_cases()}, + {ttest_sgenf_csock, [], ttest_sgenf_csock_cases()}, + {ttest_sgenf_csockf, [], ttest_sgenf_csockf_cases()}, + {ttest_sgenf_csocko, [], ttest_sgenf_csocko_cases()}, + {ttest_sgenf_csockt, [], ttest_sgenf_csockt_cases()}, + {ttest_sgeno, [], ttest_sgeno_cases()}, + {ttest_sgeno_cgen, [], ttest_sgeno_cgen_cases()}, + {ttest_sgeno_cgenf, [], ttest_sgeno_cgenf_cases()}, + {ttest_sgeno_cgeno, [], ttest_sgeno_cgeno_cases()}, + {ttest_sgeno_cgent, [], ttest_sgeno_cgent_cases()}, + {ttest_sgeno_csock, [], ttest_sgeno_csock_cases()}, + {ttest_sgeno_csockf, [], ttest_sgeno_csockf_cases()}, + {ttest_sgeno_csocko, [], ttest_sgeno_csocko_cases()}, + {ttest_sgeno_csockt, [], ttest_sgeno_csockt_cases()}, + {ttest_sgent, [], ttest_sgent_cases()}, + {ttest_sgent_cgen, [], ttest_sgent_cgen_cases()}, + {ttest_sgent_cgenf, [], ttest_sgent_cgenf_cases()}, + {ttest_sgent_cgeno, [], ttest_sgent_cgeno_cases()}, + {ttest_sgent_cgent, [], ttest_sgent_cgent_cases()}, + {ttest_sgent_csock, [], ttest_sgent_csock_cases()}, + {ttest_sgent_csockf, [], ttest_sgent_csockf_cases()}, + {ttest_sgent_csocko, [], ttest_sgent_csocko_cases()}, + {ttest_sgent_csockt, [], ttest_sgent_csockt_cases()}, + {ttest_ssockf, [], ttest_ssockf_cases()}, + {ttest_ssockf_cgen, [], ttest_ssockf_cgen_cases()}, + {ttest_ssockf_cgenf, [], ttest_ssockf_cgenf_cases()}, + {ttest_ssockf_cgeno, [], ttest_ssockf_cgeno_cases()}, + {ttest_ssockf_cgent, [], ttest_ssockf_cgent_cases()}, + {ttest_ssockf_csock, [], ttest_ssockf_csock_cases()}, + {ttest_ssockf_csockf, [], ttest_ssockf_csockf_cases()}, + {ttest_ssockf_csocko, [], ttest_ssockf_csocko_cases()}, + {ttest_ssockf_csockt, [], ttest_ssockf_csockt_cases()}, + {ttest_ssocko, [], ttest_ssocko_cases()}, + {ttest_ssocko_cgen, [], ttest_ssocko_cgen_cases()}, + {ttest_ssocko_cgenf, [], ttest_ssocko_cgenf_cases()}, + {ttest_ssocko_cgeno, [], ttest_ssocko_cgeno_cases()}, + {ttest_ssocko_cgent, [], ttest_ssocko_cgent_cases()}, + {ttest_ssocko_csock, [], ttest_ssocko_csock_cases()}, + {ttest_ssocko_csockf, [], ttest_ssocko_csockf_cases()}, + {ttest_ssocko_csocko, [], ttest_ssocko_csocko_cases()}, + {ttest_ssocko_csockt, [], ttest_ssocko_csockt_cases()}, + {ttest_ssockt, [], ttest_ssockt_cases()}, + {ttest_ssockt_cgen, [], ttest_ssockt_cgen_cases()}, + {ttest_ssockt_cgenf, [], ttest_ssockt_cgenf_cases()}, + {ttest_ssockt_cgeno, [], ttest_ssockt_cgeno_cases()}, + {ttest_ssockt_cgent, [], ttest_ssockt_cgent_cases()}, + {ttest_ssockt_csock, [], ttest_ssockt_csock_cases()}, + {ttest_ssockt_csockf, [], ttest_ssockt_csockf_cases()}, + {ttest_ssockt_csocko, [], ttest_ssockt_csocko_cases()}, + {ttest_ssockt_csockt, [], ttest_ssockt_csockt_cases()} %% {tickets, [], ticket_cases()} ]. api_cases() -> [ + {group, api_misc}, {group, api_basic}, + {group, api_async}, {group, api_options}, {group, api_op_with_timeout} ]. +api_misc_cases() -> + [ + api_m_debug + ]. + api_basic_cases() -> [ api_b_open_and_close_udp4, api_b_open_and_close_tcp4, + api_b_open_and_close_udpL, + api_b_open_and_close_tcpL, api_b_sendto_and_recvfrom_udp4, + api_b_sendto_and_recvfrom_udpL, api_b_sendmsg_and_recvmsg_udp4, + api_b_sendmsg_and_recvmsg_udpL, api_b_send_and_recv_tcp4, - api_b_sendmsg_and_recvmsg_tcp4 + api_b_send_and_recv_tcpL, + api_b_sendmsg_and_recvmsg_tcp4, + api_b_sendmsg_and_recvmsg_tcpL + ]. + +api_async_cases() -> + [ + api_a_connect_tcp4, + api_a_connect_tcp6, + api_a_sendto_and_recvfrom_udp4, + api_a_sendto_and_recvfrom_udp6, + api_a_sendmsg_and_recvmsg_udp4, + api_a_sendmsg_and_recvmsg_udp6, + api_a_send_and_recv_tcp4, + api_a_send_and_recv_tcp6, + api_a_sendmsg_and_recvmsg_tcp4, + api_a_sendmsg_and_recvmsg_tcp6, + api_a_recvfrom_cancel_udp4, + api_a_recvfrom_cancel_udp6, + api_a_recvmsg_cancel_udp4, + api_a_recvmsg_cancel_udp6, + api_a_accept_cancel_tcp4, + api_a_accept_cancel_tcp6, + api_a_recv_cancel_tcp4, + api_a_recv_cancel_tcp6, + api_a_recvmsg_cancel_tcp4, + api_a_recvmsg_cancel_tcp6, + api_a_mrecvfrom_cancel_udp4, + api_a_mrecvfrom_cancel_udp6, + api_a_mrecvmsg_cancel_udp4, + api_a_mrecvmsg_cancel_udp6, + api_a_maccept_cancel_tcp4, + api_a_maccept_cancel_tcp6, + api_a_mrecv_cancel_tcp4, + api_a_mrecv_cancel_tcp6, + api_a_mrecvmsg_cancel_tcp4, + api_a_mrecvmsg_cancel_tcp6 ]. api_options_cases() -> [ + {group, api_options_otp}, + {group, api_options_socket}, + {group, api_options_ip}% , + %% {group, api_options_ipv6}, + %% {group, api_options_tcp}, + %% {group, api_options_udp}, + %% {group, api_options_sctp} + ]. + +api_options_otp_cases() -> + [ api_opt_simple_otp_options, api_opt_simple_otp_rcvbuf_option, api_opt_simple_otp_controlling_process ]. +api_options_socket_cases() -> + [ + {group, api_option_sock_acceptconn}, + api_opt_sock_acceptfilter, + api_opt_sock_bindtodevice, + api_opt_sock_broadcast, + api_opt_sock_debug, + api_opt_sock_domain, + api_opt_sock_dontroute, + api_opt_sock_error, + api_opt_sock_keepalive, + api_opt_sock_linger + ]. + +api_option_sock_acceptconn_cases() -> + [ + api_opt_sock_acceptconn_udp, + api_opt_sock_acceptconn_tcp + ]. + +api_options_ip_cases() -> + [ + api_opt_ip_add_drop_membership + ]. + +%% api_options_ipv6_cases() -> +%% [ +%% ]. + api_op_with_timeout_cases() -> [ api_to_connect_tcp4, @@ -629,7 +849,7 @@ api_op_with_timeout_cases() -> %% These cases tests what happens when the socket is closed/shutdown, %% locally or remotely. -socket_closure_cases() -> +socket_close_cases() -> [ {group, sc_ctrl_proc_exit}, {group, sc_local_close}, @@ -638,13 +858,15 @@ socket_closure_cases() -> ]. %% These cases are all about socket cleanup after the controlling process -%% exits *without* calling socket:close/1. +%% exits *without* explicitly calling socket:close/1. sc_cp_exit_cases() -> [ sc_cpe_socket_cleanup_tcp4, sc_cpe_socket_cleanup_tcp6, + sc_cpe_socket_cleanup_tcpL, sc_cpe_socket_cleanup_udp4, - sc_cpe_socket_cleanup_udp6 + sc_cpe_socket_cleanup_udp6, + sc_cpe_socket_cleanup_udpL ]. %% These cases tests what happens when the socket is closed locally. @@ -652,17 +874,22 @@ sc_lc_cases() -> [ sc_lc_recv_response_tcp4, sc_lc_recv_response_tcp6, + sc_lc_recv_response_tcpL, sc_lc_recvfrom_response_udp4, sc_lc_recvfrom_response_udp6, + sc_lc_recvfrom_response_udpL, sc_lc_recvmsg_response_tcp4, sc_lc_recvmsg_response_tcp6, + sc_lc_recvmsg_response_tcpL, sc_lc_recvmsg_response_udp4, sc_lc_recvmsg_response_udp6, + sc_lc_recvmsg_response_udpL, sc_lc_acceptor_response_tcp4, - sc_lc_acceptor_response_tcp6 + sc_lc_acceptor_response_tcp6, + sc_lc_acceptor_response_tcpL ]. %% These cases tests what happens when the socket is closed remotely. @@ -670,9 +897,11 @@ sc_rc_cases() -> [ sc_rc_recv_response_tcp4, sc_rc_recv_response_tcp6, + sc_rc_recv_response_tcpL, sc_rc_recvmsg_response_tcp4, - sc_rc_recvmsg_response_tcp6 + sc_rc_recvmsg_response_tcp6, + sc_rc_recvmsg_response_tcpL ]. %% These cases tests what happens when the socket is shutdown/closed remotely @@ -681,43 +910,89 @@ sc_rs_cases() -> [ sc_rs_recv_send_shutdown_receive_tcp4, sc_rs_recv_send_shutdown_receive_tcp6, + sc_rs_recv_send_shutdown_receive_tcpL, sc_rs_recvmsg_send_shutdown_receive_tcp4, - sc_rs_recvmsg_send_shutdown_receive_tcp6 + sc_rs_recvmsg_send_shutdown_receive_tcp6, + sc_rs_recvmsg_send_shutdown_receive_tcpL ]. traffic_cases() -> [ + {group, traffic_counters}, + {group, traffic_chunks}, + {group, traffic_pp_send_recv}, + {group, traffic_pp_sendto_recvfrom}, + {group, traffic_pp_sendmsg_recvmsg} + ]. + +traffic_counters_cases() -> + [ + traffic_send_and_recv_counters_tcp4, + traffic_send_and_recv_counters_tcp6, + traffic_send_and_recv_counters_tcpL, + traffic_sendmsg_and_recvmsg_counters_tcp4, + traffic_sendmsg_and_recvmsg_counters_tcp6, + traffic_sendmsg_and_recvmsg_counters_tcpL, + traffic_sendto_and_recvfrom_counters_udp4, + traffic_sendto_and_recvfrom_counters_udp6, + traffic_sendto_and_recvfrom_counters_udpL, + traffic_sendmsg_and_recvmsg_counters_udp4, + traffic_sendmsg_and_recvmsg_counters_udp6, + traffic_sendmsg_and_recvmsg_counters_udpL + ]. + +traffic_chunks_cases() -> + [ traffic_send_and_recv_chunks_tcp4, traffic_send_and_recv_chunks_tcp6, + traffic_send_and_recv_chunks_tcpL + ]. +traffic_pp_send_recv_cases() -> + [ traffic_ping_pong_small_send_and_recv_tcp4, traffic_ping_pong_small_send_and_recv_tcp6, + traffic_ping_pong_small_send_and_recv_tcpL, traffic_ping_pong_medium_send_and_recv_tcp4, traffic_ping_pong_medium_send_and_recv_tcp6, + traffic_ping_pong_medium_send_and_recv_tcpL, traffic_ping_pong_large_send_and_recv_tcp4, traffic_ping_pong_large_send_and_recv_tcp6, + traffic_ping_pong_large_send_and_recv_tcpL + ]. +traffic_pp_sendto_recvfrom_cases() -> + [ traffic_ping_pong_small_sendto_and_recvfrom_udp4, traffic_ping_pong_small_sendto_and_recvfrom_udp6, + traffic_ping_pong_small_sendto_and_recvfrom_udpL, traffic_ping_pong_medium_sendto_and_recvfrom_udp4, traffic_ping_pong_medium_sendto_and_recvfrom_udp6, + traffic_ping_pong_medium_sendto_and_recvfrom_udpL + ]. +traffic_pp_sendmsg_recvmsg_cases() -> + [ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4, traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6, + traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6, + traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6, + traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL, traffic_ping_pong_small_sendmsg_and_recvmsg_udp4, traffic_ping_pong_small_sendmsg_and_recvmsg_udp6, + traffic_ping_pong_small_sendmsg_and_recvmsg_udpL, traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4, - traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6 + traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6, + traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL ]. - - + ttest_cases() -> [ %% Server: transport = gen_tcp, active = false @@ -1123,12 +1398,15 @@ ttest_ssockf_csockf_cases() -> [ ttest_ssockf_csockf_small_tcp4, ttest_ssockf_csockf_small_tcp6, + ttest_ssockf_csockf_small_tcpL, ttest_ssockf_csockf_medium_tcp4, ttest_ssockf_csockf_medium_tcp6, + ttest_ssockf_csockf_medium_tcpL, ttest_ssockf_csockf_large_tcp4, - ttest_ssockf_csockf_large_tcp6 + ttest_ssockf_csockf_large_tcp6, + ttest_ssockf_csockf_large_tcpL ]. %% Server: transport = socket(tcp), active = false @@ -1137,12 +1415,15 @@ ttest_ssockf_csocko_cases() -> [ ttest_ssockf_csocko_small_tcp4, ttest_ssockf_csocko_small_tcp6, + ttest_ssockf_csocko_small_tcpL, ttest_ssockf_csocko_medium_tcp4, ttest_ssockf_csocko_medium_tcp6, + ttest_ssockf_csocko_medium_tcpL, ttest_ssockf_csocko_large_tcp4, - ttest_ssockf_csocko_large_tcp6 + ttest_ssockf_csocko_large_tcp6, + ttest_ssockf_csocko_large_tcpL ]. %% Server: transport = socket(tcp), active = false @@ -1151,12 +1432,15 @@ ttest_ssockf_csockt_cases() -> [ ttest_ssockf_csockt_small_tcp4, ttest_ssockf_csockt_small_tcp6, + ttest_ssockf_csockt_small_tcpL, ttest_ssockf_csockt_medium_tcp4, ttest_ssockf_csockt_medium_tcp6, + ttest_ssockf_csockt_medium_tcpL, ttest_ssockf_csockt_large_tcp4, - ttest_ssockf_csockt_large_tcp6 + ttest_ssockf_csockt_large_tcp6, + ttest_ssockf_csockt_large_tcpL ]. %% Server: transport = socket(tcp), active = once @@ -1232,12 +1516,15 @@ ttest_ssocko_csockf_cases() -> [ ttest_ssocko_csockf_small_tcp4, ttest_ssocko_csockf_small_tcp6, + ttest_ssocko_csockf_small_tcpL, ttest_ssocko_csockf_medium_tcp4, ttest_ssocko_csockf_medium_tcp6, + ttest_ssocko_csockf_medium_tcpL, ttest_ssocko_csockf_large_tcp4, - ttest_ssocko_csockf_large_tcp6 + ttest_ssocko_csockf_large_tcp6, + ttest_ssocko_csockf_large_tcpL ]. %% Server: transport = socket(tcp), active = once @@ -1246,12 +1533,15 @@ ttest_ssocko_csocko_cases() -> [ ttest_ssocko_csocko_small_tcp4, ttest_ssocko_csocko_small_tcp6, + ttest_ssocko_csocko_small_tcpL, ttest_ssocko_csocko_medium_tcp4, ttest_ssocko_csocko_medium_tcp6, + ttest_ssocko_csocko_medium_tcpL, ttest_ssocko_csocko_large_tcp4, - ttest_ssocko_csocko_large_tcp6 + ttest_ssocko_csocko_large_tcp6, + ttest_ssocko_csocko_large_tcpL ]. %% Server: transport = socket(tcp), active = once @@ -1260,12 +1550,15 @@ ttest_ssocko_csockt_cases() -> [ ttest_ssocko_csockt_small_tcp4, ttest_ssocko_csockt_small_tcp6, + ttest_ssocko_csockt_small_tcpL, ttest_ssocko_csockt_medium_tcp4, ttest_ssocko_csockt_medium_tcp6, + ttest_ssocko_csockt_medium_tcpL, ttest_ssocko_csockt_large_tcp4, - ttest_ssocko_csockt_large_tcp6 + ttest_ssocko_csockt_large_tcp6, + ttest_ssocko_csockt_large_tcpL ]. %% Server: transport = socket(tcp), active = true @@ -1341,12 +1634,15 @@ ttest_ssockt_csockf_cases() -> [ ttest_ssockt_csockf_small_tcp4, ttest_ssockt_csockf_small_tcp6, + ttest_ssockt_csockf_small_tcpL, ttest_ssockt_csockf_medium_tcp4, ttest_ssockt_csockf_medium_tcp6, + ttest_ssockt_csockf_medium_tcpL, ttest_ssockt_csockf_large_tcp4, - ttest_ssockt_csockf_large_tcp6 + ttest_ssockt_csockf_large_tcp6, + ttest_ssockt_csockf_large_tcpL ]. %% Server: transport = socket(tcp), active = true @@ -1355,12 +1651,15 @@ ttest_ssockt_csocko_cases() -> [ ttest_ssockt_csocko_small_tcp4, ttest_ssockt_csocko_small_tcp6, + ttest_ssockt_csocko_small_tcpL, ttest_ssockt_csocko_medium_tcp4, ttest_ssockt_csocko_medium_tcp6, + ttest_ssockt_csocko_medium_tcpL, ttest_ssockt_csocko_large_tcp4, - ttest_ssockt_csocko_large_tcp6 + ttest_ssockt_csocko_large_tcp6, + ttest_ssockt_csocko_large_tcpL ]. %% Server: transport = socket(tcp), active = true @@ -1369,12 +1668,15 @@ ttest_ssockt_csockt_cases() -> [ ttest_ssockt_csockt_small_tcp4, ttest_ssockt_csockt_small_tcp6, + ttest_ssockt_csockt_small_tcpL, ttest_ssockt_csockt_medium_tcp4, ttest_ssockt_csockt_medium_tcp6, + ttest_ssockt_csockt_medium_tcpL, ttest_ssockt_csockt_large_tcp4, - ttest_ssockt_csockt_large_tcp6 + ttest_ssockt_csockt_large_tcp6, + ttest_ssockt_csockt_large_tcpL ]. %% ticket_cases() -> @@ -1413,11 +1715,12 @@ init_per_group(ttest = _GroupName, Config) -> io:format("init_per_group(~w) -> entry with" "~n Config: ~p" "~n", [_GroupName, Config]), + ttest_manager_start(), case lists:keysearch(esock_test_ttest_runtime, 1, Config) of {value, _} -> Config; false -> - [{esock_test_ttest_runtime, which_ttest_runtime_env()}|Config] + [{esock_test_ttest_runtime, which_ttest_runtime_env()} | Config] end; init_per_group(_GroupName, Config) -> Config. @@ -1426,6 +1729,7 @@ end_per_group(ttest = _GroupName, Config) -> io:format("init_per_group(~w) -> entry with" "~n Config: ~p" "~n", [_GroupName, Config]), + ttest_manager_stop(), lists:keydelete(esock_test_ttest_runtime, 1, Config); end_per_group(_GroupName, Config) -> Config. @@ -1435,16 +1739,16 @@ init_per_testcase(_TC, Config) -> io:format("init_per_testcase(~w) -> entry with" "~n Config: ~p" "~n", [_TC, Config]), - case quiet_mode(Config) of - default -> - ?LOGGER:start(); - Quiet -> - ?LOGGER:start(Quiet) - end, + %% case quiet_mode(Config) of + %% default -> + %% ?LOGGER:start(); + %% Quiet -> + %% ?LOGGER:start(Quiet) + %% end, Config. end_per_testcase(_TC, Config) -> - ?LOGGER:stop(), + %% ?LOGGER:stop(), Config. @@ -1465,6 +1769,80 @@ quiet_mode(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% +%% API MISC %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% A simple test case that tests that the global debug can be channged. +%% At the same time, it will test the info function (since it uses it +%% for verification). + +api_m_debug(suite) -> + []; +api_m_debug(doc) -> + []; +api_m_debug(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_m_debug, + fun() -> has_bugfree_gcc() end, + fun() -> + ok = api_m_debug() + end). + +%% For some reason this test case triggers a gcc bug, which causes +%% a segfault, on an ancient Fedora 16 VM. So, check the version of gcc... +%% Not pretty, but the simplest way to skip (without actually testing for the host). +has_bugfree_gcc() -> + has_bugfree_gcc(os:type()). + +%% Make sure we are on linux +has_bugfree_gcc({unix, linux}) -> + has_bugfree_gcc2(string:trim(os:cmd("cat /etc/issue"))); +has_bugfree_gcc(_) -> + ok. + +%% Make sure we are on Fedora 16 +has_bugfree_gcc2("Fedora release 16 " ++ _) -> + has_bugfree_gcc3(os:cmd("gcc --version")); +has_bugfree_gcc2("Welcome to SUSE Linux " ++ _) -> + has_bugfree_gcc4(os:cmd("gcc --version")); +has_bugfree_gcc2(_) -> + ok. + +has_bugfree_gcc3("gcc (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2" ++ _) -> + skip("Buggy GCC"); +has_bugfree_gcc3(_) -> + ok. + +has_bugfree_gcc4("gcc (SUSE Linux) 4.3.2" ++ _) -> + skip("Buggy GCC"); +has_bugfree_gcc4(_) -> + ok. + +api_m_debug() -> + i("get initial info"), + #{debug := D0} = socket:info(), + D1 = not D0, + i("set new debug (~w => ~w)", [D0, D1]), + ok = socket:debug(D1), + i("get updated info (~w)", [D1]), + #{debug := D1} = socket:info(), + D2 = not D1, + i("set new debug (~w => ~w)", [D1, D2]), + ok = socket:debug(D2), + i("get updated info (~w)", [D2]), + #{debug := D2} = socket:info(), + i("ok"), + ok. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% %% API BASIC %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1510,6 +1888,46 @@ api_b_open_and_close_tcp4(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Basically open (create) and close an Unix Domain dgram (UDP) socket. +%% With some extra checks... +api_b_open_and_close_udpL(suite) -> + []; +api_b_open_and_close_udpL(doc) -> + []; +api_b_open_and_close_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_open_and_close_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => dgram, + protocol => default}, + ok = api_b_open_and_close(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically open (create) and close an Unix Domain stream (TCP) socket. +%% With some extra checks... +api_b_open_and_close_tcpL(suite) -> + []; +api_b_open_and_close_tcpL(doc) -> + []; +api_b_open_and_close_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_open_and_close_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => stream, + protocol => default}, + ok = api_b_open_and_close(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + api_b_open_and_close(InitState) -> Seq = [ @@ -1633,6 +2051,37 @@ api_b_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) -> socket:recvfrom(Sock) end, InitState = #{domain => inet, + proto => udp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv4 UDP (dgram) socket using +%% sendto and recvfrom. +api_b_sendto_and_recvfrom_udpL(suite) -> + []; +api_b_sendto_and_recvfrom_udpL(doc) -> + []; +api_b_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_sendto_and_recvfrom_udpL, + fun() -> + has_support_unix_domain_socket(), + unix_domain_socket_host_cond() + end, + fun() -> + Send = fun(Sock, Data, Dest) -> + socket:sendto(Sock, Data, Dest) + end, + Recv = fun(Sock) -> + socket:recvfrom(Sock) + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_udp(InitState) @@ -1665,17 +2114,66 @@ api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> end, Recv = fun(Sock) -> %% We have some issues on old darwing... - socket:setopt(Sock, otp, debug, true), + %% socket:setopt(Sock, otp, debug, true), case socket:recvmsg(Sock) of {ok, #{addr := Source, iov := [Data]}} -> - socket:setopt(Sock, otp, debug, false), + %% socket:setopt(Sock, otp, debug, false), {ok, {Source, Data}}; {error, _} = ERROR -> ERROR end end, InitState = #{domain => inet, + proto => udp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv4 UDP (dgram) socket +%% using sendmsg and recvmsg. +api_b_sendmsg_and_recvmsg_udpL(suite) -> + []; +api_b_sendmsg_and_recvmsg_udpL(doc) -> + []; +api_b_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_sendmsg_and_recvmsg_udpL, + fun() -> + has_support_unix_domain_socket(), + unix_domain_socket_host_cond() + end, + fun() -> + Send = fun(Sock, Data, Dest) -> + %% We need tests for this, + %% but this is not the place it. + %% CMsgHdr = #{level => ip, + %% type => tos, + %% data => reliability}, + %% CMsgHdrs = [CMsgHdr], + MsgHdr = #{addr => Dest, + %% ctrl => CMsgHdrs, + iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + %% We have some issues on old darwing... + %% socket:setopt(Sock, otp, debug, true), + case socket:recvmsg(Sock) of + {ok, #{addr := Source, + iov := [Data]}} -> + %% socket:setopt(Sock, otp, debug, false), + {ok, {Source, Data}}; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_udp(InitState) @@ -1688,42 +2186,64 @@ api_b_send_and_recv_udp(InitState) -> Seq = [ #{desc => "local address", - cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, - {ok, State#{lsa => LSA}} + cmd => fun(#{domain := local = Domain} = State) -> + LSASrc = which_local_socket_addr(Domain), + LSADst = which_local_socket_addr(Domain), + {ok, State#{lsa_src => LSASrc, + lsa_dst => LSADst}}; + (#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa_src => LSA, + lsa_dst => LSA}} end}, + #{desc => "open src socket", - cmd => fun(#{domain := Domain} = State) -> - Sock = sock_open(Domain, dgram, udp), - SASrc = sock_sockname(Sock), - {ok, State#{sock_src => Sock, sa_src => SASrc}} + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + Sock = sock_open(Domain, dgram, Proto), + {ok, State#{sock_src => Sock}} end}, #{desc => "bind src", - cmd => fun(#{sock_src := Sock, lsa := LSA}) -> - sock_bind(Sock, LSA), - ok + cmd => fun(#{sock_src := Sock, lsa_src := LSA}) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("src bound"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("src bind failed: ~p", [Reason]), + ERROR + end end}, #{desc => "sockname src socket", cmd => fun(#{sock_src := Sock} = State) -> SASrc = sock_sockname(Sock), - %% ei("src sockaddr: ~p", [SASrc]), + ?SEV_IPRINT("src sockaddr: " + "~n ~p", [SASrc]), {ok, State#{sa_src => SASrc}} end}, + #{desc => "open dst socket", - cmd => fun(#{domain := Domain} = State) -> - Sock = sock_open(Domain, dgram, udp), + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + Sock = sock_open(Domain, dgram, Proto), {ok, State#{sock_dst => Sock}} end}, #{desc => "bind dst", - cmd => fun(#{sock_dst := Sock, lsa := LSA}) -> - sock_bind(Sock, LSA), - ok + cmd => fun(#{sock_dst := Sock, lsa_dst := LSA}) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("src bound"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("src bind failed: ~p", [Reason]), + ERROR + end end}, #{desc => "sockname dst socket", cmd => fun(#{sock_dst := Sock} = State) -> SADst = sock_sockname(Sock), - %% ei("dst sockaddr: ~p", [SADst]), + ?SEV_IPRINT("dst sockaddr: " + "~n ~p", [SADst]), {ok, State#{sa_dst => SADst}} end}, #{desc => "send req (to dst)", @@ -1761,12 +2281,32 @@ api_b_send_and_recv_udp(InitState) -> end end}, #{desc => "close src socket", - cmd => fun(#{sock_src := Sock}) -> - ok = socket:close(Sock) + cmd => fun(#{domain := local, + sock_src := Sock, + lsa_src := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> maps:remove(lsa_src, State) end, + fun() -> State end), + {ok, maps:remove(sock_src, State1)}; + (#{sock_src := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock_src, State)} end}, #{desc => "close dst socket", - cmd => fun(#{sock_dst := Sock}) -> - ok = socket:close(Sock) + cmd => fun(#{domain := local, + sock_dst := Sock, + lsa_dst := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> maps:remove(lsa_dst, State) end, + fun() -> State end), + {ok, maps:remove(sock_dst, State1)}; + (#{sock_dst := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock_dst, State)} end}, %% *** We are done *** @@ -1796,6 +2336,34 @@ api_b_send_and_recv_tcp4(_Config) when is_list(_Config) -> socket:recv(Sock) end, InitState = #{domain => inet, + proto => tcp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the "common" functions (send and recv) +%% on an Unix Domain (stream) socket (TCP). +api_b_send_and_recv_tcpL(suite) -> + []; +api_b_send_and_recv_tcpL(doc) -> + []; +api_b_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_b_send_and_recv_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + Recv = fun(Sock) -> + socket:recv(Sock) + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_tcp(InitState) @@ -1828,6 +2396,56 @@ api_b_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) -> end end, InitState = #{domain => inet, + proto => tcp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the msg functions (sendmsg and recvmsg) +%% on an Unix Domain (stream) socket (TCP). +api_b_sendmsg_and_recvmsg_tcpL(suite) -> + []; +api_b_sendmsg_and_recvmsg_tcpL(doc) -> + []; +api_b_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_b_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Send = fun(Sock, Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock) of + %% On some platforms, the address + %% is *not* provided (e.g. FreeBSD) + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + %% On some platforms, the address + %% *is* provided (e.g. linux) + {ok, #{addr := #{family := local}, + iov := [Data]}} -> + socket:setopt(Sock, + otp, + debug, + false), + {ok, Data}; + {error, _} = ERROR -> + socket:setopt(Sock, + otp, + debug, + false), + ERROR + end + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_tcp(InitState) @@ -1855,13 +2473,13 @@ api_b_send_and_recv_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -1869,9 +2487,19 @@ api_b_send_and_recv_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, lsa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -1882,7 +2510,12 @@ api_b_send_and_recv_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, lsa := #{path := Path}}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, lport := Port}) -> + %% This is actually not used for unix domain socket ?SEV_ANNOUNCE_READY(Tester, init, Port), ok end}, @@ -1946,12 +2579,29 @@ api_b_send_and_recv_tcp(InitState) -> end end}, #{desc => "close connection socket", - cmd => fun(#{csock := Sock}) -> - socket:close(Sock) + cmd => fun(#{csock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(csock, State)} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := Sock}) -> - socket:close(Sock) + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := LSock} = State) -> + case socket:close(LSock) of + ok -> + {ok, maps:remove(lsock, State)}; + {error, _} = ERROR -> + ERROR + end end}, %% *** We are done *** @@ -1962,7 +2612,10 @@ api_b_send_and_recv_tcp(InitState) -> [ %% *** Wait for start order *** #{desc => "await start (from tester)", - cmd => fun(State) -> + cmd => fun(#{domain := local} = State) -> + {Tester, Path} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_path => Path}}; + (State) -> {Tester, Port} = ?SEV_AWAIT_START(), {ok, State#{tester => Tester, server_port => Port}} end}, @@ -1974,16 +2627,20 @@ api_b_send_and_recv_tcp(InitState) -> %% *** The init part *** #{desc => "which server (local) address", - cmd => fun(#{domain := Domain, server_port := Port} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, - SSA = LSA#{port => Port}, + cmd => fun(#{domain := local = Domain, + server_path := Path} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = #{family => Domain, path => Path}, + {ok, State#{local_sa => LSA, server_sa => SSA}}; + (#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, {ok, State#{local_sa => LSA, server_sa => SSA}} end}, #{desc => "create socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -2054,8 +2711,20 @@ api_b_send_and_recv_tcp(InitState) -> end end}, #{desc => "close socket", - cmd => fun(#{sock := Sock}) -> - socket:close(Sock) + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} end}, %% *** We are done *** @@ -2105,11 +2774,7 @@ api_b_send_and_recv_tcp(InitState) -> ?SEV_ANNOUNCE_CONTINUE(Server, accept), ok end}, - #{desc => "sleep", - cmd => fun(_) -> - ?SLEEP(?SECS(1)), - ok - end}, + ?SEV_SLEEP(?SECS(1)), #{desc => "order client to continue (with connect)", cmd => fun(#{client := Client} = _State) -> ?SEV_ANNOUNCE_CONTINUE(Client, connect), @@ -2196,6 +2861,4313 @@ api_b_send_and_recv_tcp(InitState) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically establish a TCP connection via an async connect. IPv4. + +api_a_connect_tcp4(suite) -> + []; +api_a_connect_tcp4(doc) -> + []; +api_a_connect_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_connect_tcp4, + fun() -> + ok = api_a_connect_tcpD(inet) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically establish a TCP connection via an async connect. IPv6. + +api_a_connect_tcp6(suite) -> + []; +api_a_connect_tcp6(doc) -> + []; +api_a_connect_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_connect_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + ok = api_a_connect_tcpD(inet6) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_connect_tcpD(Domain) -> + Connect = fun(Sock, SockAddr) -> + socket:connect(Sock, SockAddr, nowait) + end, + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + Recv = fun(Sock) -> + socket:recv(Sock) + end, + InitState = #{domain => Domain, + connect => Connect, + send => Send, + recv => Recv}, + api_a_connect_tcp(InitState). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_connect_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock) of + {ok, Sock} -> + ?SEV_IPRINT("accepted: ~n ~p", [Sock]), + {ok, State#{csock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept), + ok + end}, + + #{desc => "await continue (recv_req)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_req) + end}, + #{desc => "recv req", + cmd => fun(#{csock := Sock, recv := Recv}) -> + case Recv(Sock) of + {ok, ?BASIC_REQ} -> + ok; + {ok, UnexpData} -> + {error, {unexpected_data, UnexpData}}; + {error, _} = ERROR -> + %% At the moment there is no way to get + %% status or state for the socket... + ERROR + end + end}, + #{desc => "announce ready (recv_req)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_req), + ok + end}, + #{desc => "await continue (send_rep)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_rep) + end}, + #{desc => "send rep", + cmd => fun(#{csock := Sock, send := Send}) -> + Send(Sock, ?BASIC_REP) + end}, + #{desc => "announce ready (send_rep)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_rep), + ok + end}, + + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close connection socket", + cmd => fun(#{csock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(csock, State)} + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(lsock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + ClientSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + {Tester, Port} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_port => Port}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** The init part *** + #{desc => "which server (local) address", + cmd => fun(#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, + {ok, State#{local_sa => LSA, server_sa => SSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% *** The actual test *** + #{desc => "await continue (async connect)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, async_connect) + end}, + #{desc => "connect (async) to server", + cmd => fun(#{sock := Sock, + server_sa := SSA, + connect := Connect} = State) -> + case Connect(Sock, SSA) of + ok -> + ?SEV_IPRINT("ok -> " + "unexpected success => SKIP", + []), + {skip, unexpected_success}; + {select, {select_info, ST, SR}} -> + ?SEV_IPRINT("select ->" + "~n tag: ~p" + "~n ref: ~p", [ST, SR]), + {ok, State#{connect_stag => ST, + connect_sref => SR}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (connect select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{sock := Sock, connect_sref := Ref}) -> + receive + {'$socket', Sock, select, Ref} -> + ?SEV_IPRINT("select message ->" + "~n ref: ~p", [Ref]), + ok + after 5000 -> + ?SEV_EPRINT("timeout: " + "~n message queue: ~p", + [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "connect (async) to server", + cmd => fun(#{sock := Sock, server_sa := SSA, connect := Connect}) -> + case Connect(Sock, SSA) of + ok -> + ok; + {select, SelectInfo} -> + {error, {unexpected_select, SelectInfo}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (connect)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect), + ok + end}, + #{desc => "get peername", + cmd => fun(#{sock := Sock} = _State) -> + case socket:peername(Sock) of + {ok, SockAddr} -> + ?SEV_IPRINT("Peer Name: ~p", [SockAddr]), + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "await continue (send_req)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_req) + end}, + #{desc => "send req", + cmd => fun(#{sock := Sock, send := Send}) -> + Send(Sock, ?BASIC_REQ) + end}, + #{desc => "announce ready (send_req)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_req), + ok + end}, + #{desc => "await continue (recv_rep)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_rep) + end}, + #{desc => "recv rep", + cmd => fun(#{sock := Sock, recv := Recv}) -> + case Recv(Sock) of + {ok, ?BASIC_REP} -> + ok; + {ok, UnexpData} -> + {error, {unexpected_data, UnexpData}}; + {error, _} = ERROR -> + %% At the moment there is no way to get + %% status or state for the socket... + ERROR + end + end}, + #{desc => "announce ready (recv_rep)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_rep), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + State2 = maps:remove(sock, State), + State3 = maps:remove(connect_stag, State2), + State4 = maps:remove(connect_sref, State3), + {ok, State4} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_port => Port}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid, server_port := Port} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Port), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + + %% *** The actual test *** + #{desc => "order client to continue (async connect)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, async_connect), + ok + end}, + #{desc => "await client ready (connect select)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, connect_select) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + #{desc => "await client ready (select)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, select) + end}, + #{desc => "await client ready (connect)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, connect) + end}, + #{desc => "await server ready (accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, accept) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order server to recv test req (recv req)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, recv_req), + ok + end}, + #{desc => "order client to send test req (send req)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, send_req), + ok + end}, + #{desc => "await client ready (send_req)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, send_req) + end}, + #{desc => "await server ready (recv_req)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, recv_req) + end}, + #{desc => "order client to recv test rep (send rep)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, recv_rep), + ok + end}, + #{desc => "order server to send test rep (send rep)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, send_rep), + ok + end}, + #{desc => "await server ready (send_rep)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, send_rep) + end}, + #{desc => "await client ready (recv_rep)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, recv_rep) + end}, + + + %% *** Termination *** + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Client), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + ?SEV_AWAIT_TERMINATION(Client), + State1 = maps:remove(client, State), + {ok, State1} + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start client evaluator"), + Client = ?SEV_START("client", ClientSeq, InitState), + i("await evaluator(s)"), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv4 UDP (dgram) socket using +%% sendto and recvfrom. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recvfrom, +%% since its much more difficult to "arrange" for sendto. +%% +api_a_sendto_and_recvfrom_udp4(suite) -> + []; +api_a_sendto_and_recvfrom_udp4(doc) -> + []; +api_a_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_a_sendto_and_recvfrom_udp4, + fun() -> + Send = fun(Sock, Data, Dest) -> + socket:sendto(Sock, Data, Dest) + end, + Recv = fun(Sock) -> + socket:recvfrom(Sock, 0, nowait) + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv6 UDP (dgram) socket using +%% sendto and recvfrom. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recvfrom, +%% since its much more difficult to "arrange" for sendto. +%% +api_a_sendto_and_recvfrom_udp6(suite) -> + []; +api_a_sendto_and_recvfrom_udp6(doc) -> + []; +api_a_sendto_and_recvfrom_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_a_sendto_and_recvfrom_udp6, + fun() -> has_support_ipv6() end, + fun() -> + Send = fun(Sock, Data, Dest) -> + socket:sendto(Sock, Data, Dest) + end, + Recv = fun(Sock) -> + socket:recvfrom(Sock, 0, nowait) + end, + InitState = #{domain => inet6, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv4 UDP (dgram) socket using +%% sendto and recvfrom. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recvmsg, +%% since its much more difficult to "arrange" for sendmsg. +%% +api_a_sendmsg_and_recvmsg_udp4(suite) -> + []; +api_a_sendmsg_and_recvmsg_udp4(doc) -> + []; +api_a_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_a_sendmsg_and_recvmsg_udp4, + fun() -> + Send = fun(Sock, Data, Dest) -> + MsgHdr = #{addr => Dest, + %% ctrl => CMsgHdrs, + iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, #{addr := Source, + iov := [Data]}} -> + {ok, {Source, Data}}; + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv6 UDP (dgram) socket using +%% sendto and recvfrom. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recvmsg, +%% since its much more difficult to "arrange" for sendmsg. +%% +api_a_sendmsg_and_recvmsg_udp6(suite) -> + []; +api_a_sendmsg_and_recvmsg_udp6(doc) -> + []; +api_a_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_a_sendmsg_and_recvmsg_udp6, + fun() -> has_support_ipv6() end, + fun() -> + Send = fun(Sock, Data, Dest) -> + MsgHdr = #{addr => Dest, + %% ctrl => CMsgHdrs, + iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, #{addr := Source, + iov := [Data]}} -> + {ok, {Source, Data}}; + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet6, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_send_and_recv_udp(InitState) -> + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind socket (to local address)", + cmd => fun(#{sock := Sock, local_sa := LSA} = State) -> + case socket:bind(Sock, LSA) of + {ok, Port} -> + {ok, State#{port => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, local_sa := LSA, port := Port}) -> + ServerSA = LSA#{port => Port}, + ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), + ok + end}, + + %% The actual test + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, Tag, RecvRef}} -> + ?SEV_IPRINT("expected select: " + "~n Tag: ~p" + "~n Ref: ~p", [Tag, RecvRef]), + {ok, State#{recv_stag => Tag, + recv_sref => RecvRef}}; + {ok, X} -> + {error, {unexpected_succes, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{sock := Sock, recv_sref := RecvRef}) -> + receive + {'$socket', Sock, select, RecvRef} -> + ok + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "now read the data (request)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {ok, {Src, ?BASIC_REQ}} -> + {ok, State#{req_src => Src}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv request)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_req), + ok + end}, + + #{desc => "await continue (send reply)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply) + end}, + #{desc => "send reply", + cmd => fun(#{sock := Sock, req_src := Src, send := Send}) -> + Send(Sock, ?BASIC_REP, Src) + end}, + #{desc => "announce ready (send)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + State2 = maps:remove(tester, State), + State3 = maps:remove(recv_stag, State2), + State4 = maps:remove(recv_sref, State3), + State5 = maps:remove(req_src, State4), + {ok, State5}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + ClientSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + {Tester, ServerSA} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, + server_sa => ServerSA}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {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 (to local address)", + cmd => fun(#{sock := Sock, lsa := LSA}) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (send request)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_req) + end}, + #{desc => "send request", + cmd => fun(#{sock := Sock, server_sa := Server, send := Send}) -> + Send(Sock, ?BASIC_REQ, Server) + end}, + #{desc => "announce ready (send request)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_req), + ok + end}, + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv reply (with nowait)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, Tag, RecvRef}} -> + {ok, State#{recv_stag => Tag, + recv_sref => RecvRef}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{sock := Sock, recv_sref := RecvRef}) -> + receive + {'$socket', Sock, select, RecvRef} -> + ok + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "now read the data (reply)", + cmd => fun(#{sock := Sock, recv := Recv}) -> + case Recv(Sock) of + {ok, {_Src, ?BASIC_REP}} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv reply)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_rep), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + State2 = maps:remove(tester, State), + State3 = maps:remove(recv_stag, State2), + State4 = maps:remove(recv_sref, State3), + {ok, State4}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, ServerSA} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_sa => ServerSA}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid, + server_sa := ServerSA} = _State) -> + ?SEV_ANNOUNCE_START(Pid, ServerSA), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + %% The actual test + #{desc => "order server continue (recv)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await server ready (recv_select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + + #{desc => "order client continue (send request)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, send_req), + ok + end}, + #{desc => "await client ready (send request)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, send_req) + end}, + #{desc => "await server ready (select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, select) + end}, + #{desc => "await server ready (recv request)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, recv_req) + end}, + + #{desc => "order client continue (recv)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await client ready (recv_select)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, recv_select) + end}, + #{desc => "order server continue (send reply)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, send_reply), + ok + end}, + #{desc => "await server ready (send)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, send) + end}, + #{desc => "await client ready (select)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, select) + end}, + #{desc => "await client ready (recv reply)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, recv_rep) + end}, + + %% Terminations + #{desc => "order client to terminate", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(client, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(server, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + ServerInitState = InitState, + Server = ?SEV_START("server", ServerSeq, ServerInitState), + + i("start client evaluator(s)"), + ClientInitState = InitState, + Client = ?SEV_START("client", ClientSeq, ClientInitState), + + i("start 'tester' evaluator"), + TesterInitState = #{server => Server#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the "common" functions (send and recv) +%% on an IPv4 TCP (stream) socket. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recv, +%% since its much more difficult to "arrange" for send. +%% We *also* test async for accept. +api_a_send_and_recv_tcp4(suite) -> + []; +api_a_send_and_recv_tcp4(doc) -> + []; +api_a_send_and_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_send_and_recv_tcp4, + fun() -> + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + Recv = fun(Sock) -> + socket:recv(Sock, 0, nowait) + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the "common" functions (send and recv) +%% on an IPv6 TCP (stream) socket. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recv, +%% since its much more difficult to "arrange" for send. +%% We *also* test async for accept. +api_a_send_and_recv_tcp6(suite) -> + []; +api_a_send_and_recv_tcp6(doc) -> + []; +api_a_send_and_recv_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_send_and_recv_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + Recv = fun(Sock) -> + socket:recv(Sock, 0, nowait) + end, + InitState = #{domain => inet6, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the msg functions (sendmsg and recvmsg) +%% on an IPv4 TCP (stream) socket. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recvmsg, +%% since its much more difficult to "arrange" for sendmsg. +%% We *also* test async for accept. +api_a_sendmsg_and_recvmsg_tcp4(suite) -> + []; +api_a_sendmsg_and_recvmsg_tcp4(doc) -> + []; +api_a_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_sendmsg_and_recvmsg_tcp4, + fun() -> + Send = fun(Sock, Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the msg functions (sendmsg and recvmsg) +%% on an IPv6 TCP (stream) socket. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recvmsg, +%% since its much more difficult to "arrange" for sendmsg. +%% We *also* test async for accept. +api_a_sendmsg_and_recvmsg_tcp6(suite) -> + []; +api_a_sendmsg_and_recvmsg_tcp6(doc) -> + []; +api_a_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_sendmsg_and_recvmsg_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + Send = fun(Sock, Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet6, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_send_and_recv_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection (nowait)", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock, nowait) of + {select, {select_info, Tag, Ref}} -> + ?SEV_IPRINT("accept select: " + "~n Tag: ~p" + "~n Ref: ~p", [Tag, Ref]), + {ok, State#{accept_stag => Tag, + accept_sref => Ref}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{lsock := Sock, accept_sref := Ref}) -> + receive + {'$socket', Sock, select, Ref} -> + ok + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "await connection (again)", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock, nowait) of + {ok, Sock} -> + ?SEV_IPRINT("accepted: " + "~n Sock: ~p", [Sock]), + {ok, State#{csock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept), + ok + end}, + + #{desc => "await continue (recv request)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_req) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{csock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, Tag, Ref}} -> + ?SEV_IPRINT("recv select: " + "~n Tag: ~p" + "~n Ref: ~p", [Tag, Ref]), + {ok, State#{recv_stag => Tag, + recv_sref => Ref}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{csock := Sock, recv_sref := RecvRef}) -> + receive + {'$socket', Sock, select, RecvRef} -> + ok + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "now read the data (request)", + cmd => fun(#{csock := Sock, recv := Recv} = _State) -> + case Recv(Sock) of + {ok, ?BASIC_REQ} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv request)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_req), + ok + end}, + + #{desc => "await continue (send reply)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_rep) + end}, + #{desc => "send reply", + cmd => fun(#{csock := Sock, send := Send}) -> + Send(Sock, ?BASIC_REP) + end}, + #{desc => "announce ready (send reply)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_rep), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close connection socket", + cmd => fun(#{csock := Sock}) -> + socket:close(Sock) + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + ClientSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + {Tester, Port} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_port => Port}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** The init part *** + #{desc => "which server (local) address", + cmd => fun(#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, + {ok, State#{local_sa => LSA, server_sa => SSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% *** The actual test *** + #{desc => "await continue (connect)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, connect) + end}, + #{desc => "connect to server", + cmd => fun(#{sock := Sock, server_sa := SSA}) -> + socket:connect(Sock, SSA) + end}, + #{desc => "announce ready (connect)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect), + ok + end}, + + #{desc => "await continue (send request)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_req) + end}, + #{desc => "send request (to server)", + cmd => fun(#{sock := Sock, send := Send}) -> + ok = Send(Sock, ?BASIC_REQ) + end}, + #{desc => "announce ready (send request)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_req), + ok + end}, + + #{desc => "try recv reply (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, Tag, Ref}} -> + ?SEV_IPRINT("recv select: " + "~n Tag: ~p" + "~n Ref: ~p", [Tag, Ref]), + {ok, State#{recv_stag => Tag, + recv_sref => Ref}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{sock := Sock, recv_sref := RecvRef}) -> + receive + {'$socket', Sock, select, RecvRef} -> + ok + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "now read the data (reply)", + cmd => fun(#{sock := Sock, recv := Recv}) -> + {ok, ?BASIC_REP} = Recv(Sock), + ok + end}, + #{desc => "announce ready (recv reply)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_rep), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_port => Port}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid, server_port := Port} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Port), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + %% *** The actual test *** + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + #{desc => "await server ready (accept select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, accept_select) + end}, + #{desc => "order client to continue (with connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, connect), + ok + end}, + #{desc => "await server ready (select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, select) + end}, + #{desc => "await server ready (accept)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, accept) + end}, + #{desc => "await client ready (connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, client, connect) + end}, + + #{desc => "order server to continue (recv request)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv_req), + ok + end}, + #{desc => "await server ready (recv select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + #{desc => "order client to continue (send request)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, send_req), + ok + end}, + #{desc => "await client ready (send request)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, send_req) + end}, + #{desc => "await server ready (select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, select) + end}, + #{desc => "await server ready (recv request)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, recv_req) + end}, + + #{desc => "order client to continue (recv reply)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv_rep), + ok + end}, + #{desc => "await client ready (recv select)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, client, recv_select) + end}, + #{desc => "order server to continue (send reply)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, send_rep), + ok + end}, + #{desc => "await server ready (send reply)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, send_rep) + end}, + #{desc => "await client ready (select)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, client, select) + end}, + #{desc => "await client ready (reply recv)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, recv_rep) + end}, + + + %% *** Termination *** + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Client), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + ?SEV_AWAIT_TERMINATION(Client), + State1 = maps:remove(client, State), + {ok, State1} + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start client evaluator"), + Client = ?SEV_START("client", ClientSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recvfrom, +%% wait some time and then cancel. IPv4 +%% +api_a_recvfrom_cancel_udp4(suite) -> + []; +api_a_recvfrom_cancel_udp4(doc) -> + []; +api_a_recvfrom_cancel_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recvfrom_cancel_udp4, + fun() -> + Recv = fun(Sock) -> + case socket:recvfrom(Sock, 0, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_recv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recvfrom, +%% wait some time and then cancel. IPv6 +%% +api_a_recvfrom_cancel_udp6(suite) -> + []; +api_a_recvfrom_cancel_udp6(doc) -> + []; +api_a_recvfrom_cancel_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recvfrom_cancel_udp6, + fun() -> has_support_ipv6() end, + fun() -> + Recv = fun(Sock) -> + case socket:recvfrom(Sock, 0, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet6, + recv => Recv}, + ok = api_a_recv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recvmsg, +%% wait some time and then cancel. IPv4 +%% +api_a_recvmsg_cancel_udp4(suite) -> + []; +api_a_recvmsg_cancel_udp4(doc) -> + []; +api_a_recvmsg_cancel_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recvmsg_cancel_udp4, + fun() -> + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_recv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recvmsg, +%% wait some time and then cancel. IPv6 +%% +api_a_recvmsg_cancel_udp6(suite) -> + []; +api_a_recvmsg_cancel_udp6(doc) -> + []; +api_a_recvmsg_cancel_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recvmsg_cancel_udp6, + fun() -> has_support_ipv6() end, + fun() -> + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet6, + recv => Recv}, + ok = api_a_recv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_recv_cancel_udp(InitState) -> + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind socket (to local address)", + cmd => fun(#{sock := Sock, local_sa := LSA} = State) -> + case socket:bind(Sock, LSA) of + {ok, Port} -> + {ok, State#{port => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, local_sa := LSA, port := Port}) -> + ServerSA = LSA#{port => Port}, + ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), + ok + end}, + + %% The actual test + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, SelectInfo} -> + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message (without success)", + cmd => fun(#{sock := Sock}) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}} + after 5000 -> + ok + end + end}, + #{desc => "announce ready (no select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, no_select), + ok + end}, + #{desc => "await continue (cancel)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, cancel) + end}, + #{desc => "cancel", + cmd => fun(#{sock := Sock, recv_select_info := SelectInfo}) -> + ok = socket:cancel(Sock, SelectInfo) + end}, + #{desc => "announce ready (cancel)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, cancel), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + State2 = maps:remove(tester, State), + State3 = maps:remove(recv_stag, State2), + State4 = maps:remove(recv_sref, State3), + State5 = maps:remove(req_src, State4), + {ok, State5}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, ServerSA} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_sa => ServerSA}} + end}, + + %% The actual test + #{desc => "order server continue (recv)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await server ready (recv select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + #{desc => "await server ready (no select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, no_select) + end}, + #{desc => "order server continue (cancel)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, cancel), + ok + end}, + #{desc => "await server ready (cancel)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, cancel) + end}, + + %% Terminations + #{desc => "order server to terminate", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(server, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + ServerInitState = InitState, + Server = ?SEV_START("server", ServerSeq, ServerInitState), + + i("start 'tester' evaluator"), + TesterInitState = #{server => Server#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Server, Tester]). + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to accept, +%% wait some time and then cancel. IPv4 +%% +api_a_accept_cancel_tcp4(suite) -> + []; +api_a_accept_cancel_tcp4(doc) -> + []; +api_a_accept_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_accept_cancel_tcp4, + fun() -> + Accept = fun(Sock) -> + case socket:accept(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + accept => Accept}, + ok = api_a_accept_cancel_tcp(InitState) + end). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to accept, +%% wait some time and then cancel. IPv6 +%% +api_a_accept_cancel_tcp6(suite) -> + []; +api_a_accept_cancel_tcp6(doc) -> + []; +api_a_accept_cancel_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_accept_cancel_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + Accept = fun(Sock) -> + case socket:accept(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet6, + accept => Accept}, + ok = api_a_accept_cancel_tcp(InitState) + end). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_accept_cancel_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection (nowait)", + cmd => fun(#{lsock := LSock, accept := Accept} = State) -> + case Accept(LSock) of + {select, {select_info, T, R} = SelectInfo} -> + ?SEV_IPRINT("accept select: " + "~n T: ~p" + "~n R: ~p", [T, R]), + {ok, State#{accept_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept_select), + ok + end}, + #{desc => "await select message (without success)", + cmd => fun(#{lsock := Sock}) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}} + after 5000 -> + ok + end + end}, + #{desc => "announce ready (no select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, no_select), + ok + end}, + #{desc => "await continue (cancel)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, cancel) + end}, + #{desc => "cancel", + cmd => fun(#{lsock := Sock, accept_select_info := SelectInfo}) -> + ok = socket:cancel(Sock, SelectInfo) + end}, + #{desc => "announce ready (cancel)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, cancel), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_port => Port}} + end}, + + %% *** The actual test *** + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + #{desc => "await server ready (accept select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, accept_select) + end}, + #{desc => "await server ready (no select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, no_select) + end}, + #{desc => "order server to continue (cancel)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, cancel), + ok + end}, + #{desc => "await server ready (cancel)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, cancel) + end}, + + %% *** Termination *** + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recv, +%% wait some time and then cancel. IPv4 +%% +api_a_recv_cancel_tcp4(suite) -> + []; +api_a_recv_cancel_tcp4(doc) -> + []; +api_a_recv_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recv_cancel_tcp4, + fun() -> + Recv = fun(Sock) -> + socket:recv(Sock, 0, nowait) + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_recv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recv, +%% wait some time and then cancel. IPv6 +%% +api_a_recv_cancel_tcp6(suite) -> + []; +api_a_recv_cancel_tcp6(doc) -> + []; +api_a_recv_cancel_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recv_cancel_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + Recv = fun(Sock) -> + socket:recv(Sock, 0, nowait) + end, + InitState = #{domain => inet6, + recv => Recv}, + ok = api_a_recv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recvmsg, +%% wait some time and then cancel. IPv4 +%% +api_a_recvmsg_cancel_tcp4(suite) -> + []; +api_a_recvmsg_cancel_tcp4(doc) -> + []; +api_a_recvmsg_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recvmsg_cancel_tcp4, + fun() -> + Recv = fun(Sock) -> + socket:recvmsg(Sock, nowait) + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_recv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recvmsg, +%% wait some time and then cancel. IPv6 +%% +api_a_recvmsg_cancel_tcp6(suite) -> + []; +api_a_recvmsg_cancel_tcp6(doc) -> + []; +api_a_recvmsg_cancel_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recvmsg_cancel_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + Recv = fun(Sock) -> + socket:recvmsg(Sock, nowait) + end, + InitState = #{domain => inet6, + recv => Recv}, + ok = api_a_recv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_recv_cancel_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection (nowait)", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock) of + {ok, CSock} -> + {ok, State#{csock => CSock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept), + ok + end}, + + #{desc => "await continue (nowait recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{csock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, T, R} = SelectInfo} -> + ?SEV_IPRINT("recv select: " + "~n Tag: ~p" + "~n Ref: ~p", [T, R]), + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{csock := Sock}) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}} + after 5000 -> + ok + end + end}, + #{desc => "announce ready (no select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, no_select), + ok + end}, + #{desc => "await continue (cancel)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, cancel) + end}, + #{desc => "cancel", + cmd => fun(#{csock := Sock, recv_select_info := SelectInfo}) -> + ok = socket:cancel(Sock, SelectInfo) + end}, + #{desc => "announce ready (cancel)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, cancel), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close connection socket", + cmd => fun(#{csock := Sock}) -> + socket:close(Sock) + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + ClientSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + {Tester, Port} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_port => Port}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** The init part *** + #{desc => "which server (local) address", + cmd => fun(#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, + {ok, State#{local_sa => LSA, server_sa => SSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% *** The actual test *** + #{desc => "await continue (connect)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, connect) + end}, + #{desc => "connect to server", + cmd => fun(#{sock := Sock, server_sa := SSA}) -> + socket:connect(Sock, SSA) + end}, + #{desc => "announce ready (connect)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_port => Port}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid, server_port := Port} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Port), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + %% *** The actual test *** + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + #{desc => "order client to continue (connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, connect), + ok + end}, + #{desc => "await client ready (connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, client, connect) + end}, + #{desc => "await server ready (accept)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, accept) + end}, + + #{desc => "order server to continue (recv)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await server ready (recv select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + #{desc => "await server ready (no select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, no_select) + end}, + #{desc => "order server to continue (send request)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, cancel), + ok + end}, + #{desc => "await server ready (cancel)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, cancel) + end}, + + %% *** Termination *** + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Client), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + ?SEV_AWAIT_TERMINATION(Client), + State1 = maps:remove(client, State), + {ok, State1} + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start client evaluator"), + Client = ?SEV_START("client", ClientSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recvfrom +%% (from *several* processes), wait some time and then cancel. +%% This should result in abort messages to the 'other' processes. IPv4 +%% +api_a_mrecvfrom_cancel_udp4(suite) -> + []; +api_a_mrecvfrom_cancel_udp4(doc) -> + []; +api_a_mrecvfrom_cancel_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecvfrom_cancel_udp4, + fun() -> + Recv = fun(Sock) -> + case socket:recvfrom(Sock, 0, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_mrecv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recvfrom +%% (from *several* processes), wait some time and then cancel. +%% This should result in abort messages to the 'other' processes. IPv6 +%% +api_a_mrecvfrom_cancel_udp6(suite) -> + []; +api_a_mrecvfrom_cancel_udp6(doc) -> + []; +api_a_mrecvfrom_cancel_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecvfrom_cancel_udp6, + fun() -> has_support_ipv6() end, + fun() -> + Recv = fun(Sock) -> + case socket:recvfrom(Sock, 0, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet6, + recv => Recv}, + ok = api_a_mrecv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg +%% (from *several* processes), wait some time and then cancel. +%% This should result in abort messages to the 'other' processes. IPv4 +%% +api_a_mrecvmsg_cancel_udp4(suite) -> + []; +api_a_mrecvmsg_cancel_udp4(doc) -> + []; +api_a_mrecvmsg_cancel_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecvmsg_cancel_udp4, + fun() -> + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_mrecv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg +%% (from *several* processes), wait some time and then cancel. +%% This should result in abort messages to the 'other' processes. IPv6 +%% +api_a_mrecvmsg_cancel_udp6(suite) -> + []; +api_a_mrecvmsg_cancel_udp6(doc) -> + []; +api_a_mrecvmsg_cancel_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecvmsg_cancel_udp6, + fun() -> has_support_ipv6() end, + fun() -> + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet6, + recv => Recv}, + ok = api_a_mrecv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_mrecv_cancel_udp(InitState) -> + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind socket (to local address)", + cmd => fun(#{sock := Sock, local_sa := LSA} = State) -> + case socket:bind(Sock, LSA) of + {ok, Port} -> + {ok, State#{port => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, sock := Sock}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Sock), + ok + end}, + + %% The actual test + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, SelectInfo} -> + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await abort message", + cmd => fun(#{sock := Sock, + recv_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(sock, State)} + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + State2 = maps:remove(tester, State), + State3 = maps:remove(recv_stag, State2), + State4 = maps:remove(recv_sref, State3), + State5 = maps:remove(req_src, State4), + {ok, State5}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + AltServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + {Tester, Sock} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, sock => Sock}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, SelectInfo} -> + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await abort message", + cmd => fun(#{sock := Sock, + recv_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(sock, State)} + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + ?SEV_IPRINT("terminating"), + State1 = maps:remove(recv_select_info, State), + State2 = maps:remove(tester, State1), + State3 = maps:remove(sock, State2), + {ok, State3}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 1", + cmd => fun(#{alt_server1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 2", + cmd => fun(#{alt_server2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{sock => Sock}} + end}, + + %% Start the alt-server 1 + #{desc => "order alt-server 1 start", + cmd => fun(#{alt_server1 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 1 ready (init)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, alt_server1, init) + end}, + + %% Start the alt-server 2 + #{desc => "order alt-server 2 start", + cmd => fun(#{alt_server2 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 2 ready (init)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, alt_server2, init) + end}, + + + %% The actual test + #{desc => "order server continue (recv)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await server ready (recv select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + + #{desc => "order alt-server 1 continue (recv)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await alt-server 1 ready (recv select)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, recv_select) + end}, + + #{desc => "order alt-server 2 continue (recv)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await alt-server 2 ready (recv select)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, recv_select) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "close the socket", + cmd => fun(#{sock := Sock} = _State) -> + socket:close(Sock) + end}, + + #{desc => "await server ready (abort)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, abort) + end}, + #{desc => "await alt-server 1 ready (abort)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort) + end}, + #{desc => "await alt-server 2 ready (abort)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort) + end}, + + %% Terminations + #{desc => "order alt-server 2 to terminate", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 2 termination", + cmd => fun(#{alt_server2 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server2, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order alt-server 1 to terminate", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 1 termination", + cmd => fun(#{alt_server1 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server1, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order server to terminate", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(server, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start alt-server 1 evaluator"), + AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState), + + i("start alt-server 2 evaluator"), + AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState), + + i("start 'tester' evaluator"), + TesterInitState = #{server => Server#ev.pid, + alt_server1 => AltServer1#ev.pid, + alt_server2 => AltServer2#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Tester]). + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to accept +%% (from *several* processes), wait some time and then cancel, +%% This should result in abort messages to the 'other' processes. IPv4 +%% +api_a_maccept_cancel_tcp4(suite) -> + []; +api_a_maccept_cancel_tcp4(doc) -> + []; +api_a_maccept_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_maccept_cancel_tcp4, + fun() -> + Accept = fun(Sock) -> + case socket:accept(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + accept => Accept}, + ok = api_a_maccept_cancel_tcp(InitState) + end). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to accept +%% (from *several* processes), wait some time and then cancel, +%% This should result in abort messages to the 'other' processes. IPv6 +%% +api_a_maccept_cancel_tcp6(suite) -> + []; +api_a_maccept_cancel_tcp6(doc) -> + []; +api_a_maccept_cancel_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_maccept_cancel_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + Accept = fun(Sock) -> + case socket:accept(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet6, + accept => Accept}, + ok = api_a_maccept_cancel_tcp(InitState) + end). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_maccept_cancel_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lsock := Sock}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Sock), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection (nowait)", + cmd => fun(#{lsock := LSock, accept := Accept} = State) -> + case Accept(LSock) of + {select, {select_info, T, R} = SelectInfo} -> + ?SEV_IPRINT("accept select: " + "~n T: ~p" + "~n R: ~p", [T, R]), + {ok, State#{accept_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept_select), + ok + end}, + #{desc => "await select message (without success)", + cmd => fun(#{lsock := Sock, + accept_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(lsock, State)} + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + AltServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + {Tester, Sock} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, lsock => Sock}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "try accept request (with nowait, expect select)", + cmd => fun(#{lsock := Sock, accept := Accept} = State) -> + case Accept(Sock) of + {select, SelectInfo} -> + {ok, State#{accept_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept_select), + ok + end}, + #{desc => "await abort message", + cmd => fun(#{lsock := Sock, + accept_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(sock, State)} + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + ?SEV_IPRINT("terminating"), + State1 = maps:remove(tester, State), + State2 = maps:remove(accept_select_info, State1), + State3 = maps:remove(lsock, State2), + {ok, State3}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 1", + cmd => fun(#{alt_server1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 2", + cmd => fun(#{alt_server2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{sock => Sock}} + end}, + + %% Start the alt-server 1 + #{desc => "order alt-server 1 start", + cmd => fun(#{alt_server1 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 1 ready (init)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, alt_server1, init) + end}, + + %% Start the alt-server 2 + #{desc => "order alt-server 2 start", + cmd => fun(#{alt_server2 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 2 ready (init)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, alt_server2, init) + end}, + + + %% *** The actual test *** + #{desc => "order server continue (accept)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, accept), + ok + end}, + #{desc => "await server ready (accept select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, accept_select) + end}, + + #{desc => "order alt-server 1 continue (accept)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, accept), + ok + end}, + #{desc => "await alt-server 1 ready (accept select)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, accept_select) + end}, + + #{desc => "order alt-server 2 continue (accept)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, accept), + ok + end}, + #{desc => "await alt-server 2 ready (accept select)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, accept_select) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "close the socket", + cmd => fun(#{sock := Sock} = _State) -> + socket:close(Sock) + end}, + + #{desc => "await server ready (abort)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, abort) + end}, + #{desc => "await alt-server 1 ready (abort)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort) + end}, + #{desc => "await alt-server 2 ready (abort)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort) + end}, + + + %% *** Termination *** + #{desc => "order alt-server 2 to terminate", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 2 termination", + cmd => fun(#{alt_server2 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server2, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order alt-server 1 to terminate", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 1 termination", + cmd => fun(#{alt_server1 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server1, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start alt-server 1 evaluator"), + AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState), + + i("start alt-server 2 evaluator"), + AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid, + alt_server1 => AltServer1#ev.pid, + alt_server2 => AltServer2#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recv +%% (from *several* processes), wait some time and then cancel, +%% This should result in abort messages to the 'other' processes. IPv4 +%% +api_a_mrecv_cancel_tcp4(suite) -> + []; +api_a_mrecv_cancel_tcp4(doc) -> + []; +api_a_mrecv_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecv_cancel_tcp4, + fun() -> + Recv = fun(Sock) -> + socket:recv(Sock, 0, nowait) + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_mrecv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recv +%% (from *several* processes), wait some time and then cancel, +%% This should result in abort messages to the 'other' processes. IPv6 +%% +api_a_mrecv_cancel_tcp6(suite) -> + []; +api_a_mrecv_cancel_tcp6(doc) -> + []; +api_a_mrecv_cancel_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecv_cancel_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + Recv = fun(Sock) -> + socket:recv(Sock, 0, nowait) + end, + InitState = #{domain => inet6, + recv => Recv}, + ok = api_a_mrecv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg +%% (from *several* processes), wait some time and then cancel, +%% This should result in abort messages to the 'other' processes. IPv4 +%% +api_a_mrecvmsg_cancel_tcp4(suite) -> + []; +api_a_mrecvmsg_cancel_tcp4(doc) -> + []; +api_a_mrecvmsg_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecvmsg_cancel_tcp4, + fun() -> + Recv = fun(Sock) -> + socket:recvmsg(Sock, nowait) + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_mrecv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg +%% (from *several* processes), wait some time and then cancel, +%% This should result in abort messages to the 'other' processes. IPv6 +%% +api_a_mrecvmsg_cancel_tcp6(suite) -> + []; +api_a_mrecvmsg_cancel_tcp6(doc) -> + []; +api_a_mrecvmsg_cancel_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecvmsg_cancel_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + Recv = fun(Sock) -> + socket:recvmsg(Sock, nowait) + end, + InitState = #{domain => inet6, + recv => Recv}, + ok = api_a_mrecv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_mrecv_cancel_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection (nowait)", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock) of + {ok, CSock} -> + {ok, State#{csock => CSock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept)", + cmd => fun(#{tester := Tester, csock := Sock}) -> + ?SEV_ANNOUNCE_READY(Tester, accept, Sock), + ok + end}, + + #{desc => "await continue (nowait recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{csock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, T, R} = SelectInfo} -> + ?SEV_IPRINT("recv select: " + "~n Tag: ~p" + "~n Ref: ~p", [T, R]), + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{csock := Sock, + recv_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(sock, State)} + after 5000 -> + ok + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + AltServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + {Tester, Sock} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, sock => Sock}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, SelectInfo} -> + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await abort message", + cmd => fun(#{sock := Sock, + recv_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(sock, State)} + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + ?SEV_IPRINT("terminating"), + State1 = maps:remove(recv_select_info, State), + State2 = maps:remove(tester, State1), + State3 = maps:remove(sock, State2), + {ok, State3}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + ClientSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + {Tester, Port} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_port => Port}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** The init part *** + #{desc => "which server (local) address", + cmd => fun(#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, + {ok, State#{local_sa => LSA, server_sa => SSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% *** The actual test *** + #{desc => "await continue (connect)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, connect) + end}, + #{desc => "connect to server", + cmd => fun(#{sock := Sock, server_sa := SSA}) -> + socket:connect(Sock, SSA) + end}, + #{desc => "announce ready (connect)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 1", + cmd => fun(#{alt_server1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 2", + cmd => fun(#{alt_server2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_port => Port}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid, server_port := Port} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Port), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + #{desc => "order client to continue (connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, connect), + ok + end}, + #{desc => "await client ready (connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, client, connect) + end}, + #{desc => "await server ready (accept)", + cmd => fun(#{server := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, accept), + {ok, State#{sock => Sock}} + end}, + + %% Start the alt server 1 + #{desc => "order alt-server 1 start", + cmd => fun(#{alt_server1 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 1 ready (init)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, init) + end}, + + %% Start the alt server 2 + #{desc => "order alt-server 2 start", + cmd => fun(#{alt_server2 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 2 ready (init)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, init) + end}, + + + %% *** The actual test *** + #{desc => "order server continue (recv)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await server ready (recv select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + + #{desc => "order alt-server 1 continue (recv)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await alt-server 1 ready (recv select)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, recv_select) + end}, + + #{desc => "order alt-server 2 continue (recv)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await alt-server 2 ready (recv select)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, recv_select) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "close the socket", + cmd => fun(#{sock := Sock} = _State) -> + socket:close(Sock) + end}, + + #{desc => "await server ready (abort)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, abort) + end}, + #{desc => "await alt-server 1 ready (abort)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort) + end}, + #{desc => "await alt-server 2 ready (abort)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort) + end}, + + %% Terminations + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Client), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + ?SEV_AWAIT_TERMINATION(Client), + State1 = maps:remove(client, State), + {ok, State1} + end}, + + #{desc => "order alt-server 2 to terminate", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 2 termination", + cmd => fun(#{alt_server2 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server2, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order alt-server 1 to terminate", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 1 termination", + cmd => fun(#{alt_server1 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server1, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order server to terminate", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(server, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start alt-server 1 evaluator"), + AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState), + + i("start alt-server 2 evaluator"), + AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState), + + i("start client evaluator"), + Client = ?SEV_START("client", ClientSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid, + alt_server1 => AltServer1#ev.pid, + alt_server2 => AltServer2#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Client, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% API OPTIONS %% @@ -2530,8 +7502,7 @@ api_opt_simple_otp_rcvbuf_option() -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", @@ -2776,8 +7747,7 @@ api_opt_simple_otp_rcvbuf_option() -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create socket", @@ -3173,7 +8143,7 @@ api_opt_simple_otp_controlling_process(suite) -> api_opt_simple_otp_controlling_process(doc) -> []; api_opt_simple_otp_controlling_process(_Config) when is_list(_Config) -> - ?TT(?SECS(5)), + ?TT(?SECS(30)), tc_try(api_opt_simple_otp_controlling_process, fun() -> api_opt_simple_otp_controlling_process() end). @@ -3418,6 +8388,2027 @@ api_opt_simple_otp_controlling_process() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option acceptconn for UDP. +%% This should be possible to get but not set. + +api_opt_sock_acceptconn_udp(suite) -> + []; +api_opt_sock_acceptconn_udp(doc) -> + []; +api_opt_sock_acceptconn_udp(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(api_opt_sock_acceptconn_udp, + fun() -> + has_support_sock_acceptconn() + end, + fun() -> api_opt_sock_acceptconn_udp() end). + + + +api_opt_sock_acceptconn_udp() -> + Opt = acceptconn, + Set = fun(S, Val) -> + socket:setopt(S, socket, Opt, Val) + end, + Get = fun(S) -> + socket:getopt(S, socket, Opt) + end, + + TesterSeq = + [ + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "[get] verify socket (before bind)", + cmd => fun(#{sock := Sock} = _State) -> + case Get(Sock) of + {ok, false} -> + ?SEV_IPRINT("Expected Success: " + "Not accepting connections"), + ok; + {ok, true} -> + ?SEV_EPRINT("Unexpected Success: " + "Accepting connections"), + {error, {unexpected_success, {Opt, true}}}; + {error, enoprotoopt = Reason} -> + %% On some platforms this is not accepted + %% for UDP, so skip this part (UDP). + ?SEV_EPRINT("Expected Failure: " + "~p => SKIP", [Reason]), + (catch socket:close(Sock)), + {skip, Reason}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[set] verify socket (before bind)", + cmd => fun(#{sock := Sock} = _State) -> + case Set(Sock, true) of + {error, Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", + [Reason]), + ok; + ok -> + ?SEV_EPRINT("Unexpected Success: " + "Set acceptconn (=true)"), + {error, unexpected_success} + end + end}, + + #{desc => "bind socket to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "[get] verify socket (after bind)", + cmd => fun(#{sock := Sock} = _State) -> + case Get(Sock) of + {ok, false} -> + ?SEV_IPRINT("Expected Success: " + "Not accepting connections"), + ok; + {ok, true} -> + ?SEV_EPRINT("Unexpected Success: " + "Accepting connections"), + {error, {unexpected_success, {Opt, true}}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[set] verify socket (after bind)", + cmd => fun(#{sock := Sock} = _State) -> + case Set(Sock, true) of + {error, Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", + [Reason]), + ok; + ok -> + ?SEV_EPRINT("Unexpected Success: " + "Set acceptconn (=true)"), + {error, unexpected_success} + end + end}, + + %% *** Termination *** + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + Domain = inet, + + i("start tester evaluator"), + InitState = #{domain => Domain}, + Tester = ?SEV_START("tester", TesterSeq, InitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option acceptconn for TCP. +%% This should be possible to get but not set. + +api_opt_sock_acceptconn_tcp(suite) -> + []; +api_opt_sock_acceptconn_tcp(doc) -> + []; +api_opt_sock_acceptconn_tcp(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(api_opt_sock_acceptconn_tcp, + fun() -> + has_support_sock_acceptconn() + end, + fun() -> api_opt_sock_acceptconn_tcp() end). + + + +api_opt_sock_acceptconn_tcp() -> + Opt = acceptconn, + Set = fun(S, Val) -> + socket:setopt(S, socket, Opt, Val) + end, + Get = fun(S) -> + socket:getopt(S, socket, Opt) + end, + + TesterSeq = + [ + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "[get] verify listen socket (before bind)", + cmd => fun(#{lsock := Sock} = _State) -> + case Get(Sock) of + {ok, false} -> + ?SEV_IPRINT("Expected Success: " + "Not accepting connections"), + ok; + {ok, true} -> + ?SEV_EPRINT("Unexpected Success: " + "Accepting connections"), + {error, {unexpected_success, {Opt, true}}}; + {error, enoprotoopt = Reason} -> + ?SEV_EPRINT("Expected Failure: " + "~p => SKIP", [Reason]), + (catch socket:close(Sock)), + {skip, Reason}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[set] verify listen socket (before bind)", + cmd => fun(#{lsock := Sock} = _State) -> + case Set(Sock, true) of + {error, Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", [Reason]), + ok; + ok -> + ?SEV_EPRINT("Unexpected Success: " + "Set acceptconn (=true)"), + {error, unexpected_success} + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "bind listen socket to local address", + cmd => fun(#{lsock := Sock, local_sa := LSA} = State) -> + case socket:bind(Sock, LSA) of + {ok, Port} -> + {ok, State#{server_sa => LSA#{port => Port}}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "[get] verify listen socket (after bind)", + cmd => fun(#{lsock := Sock} = _State) -> + case Get(Sock) of + {ok, false} -> + ?SEV_IPRINT("Expected Success: " + "Not accepting connections"), + ok; + {ok, true} -> + ?SEV_EPRINT("Unexpected Success: " + "Accepting connections"), + {error, {unexpected_success, {Opt, true}}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[set] verify listen socket (after bind)", + cmd => fun(#{lsock := Sock} = _State) -> + case Set(Sock, true) of + {error, Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", [Reason]), + ok; + ok -> + ?SEV_EPRINT("Unexpected Success: " + "Set acceptconn (=true)"), + {error, unexpected_success} + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "make listen socket accept connections", + cmd => fun(#{lsock := Sock} = _State) -> + case socket:listen(Sock) of + ok -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "[get] verify listen socket (after listen)", + cmd => fun(#{lsock := Sock} = _State) -> + case Get(Sock) of + {ok, true} -> + ?SEV_IPRINT("Expected Success: " + "Accepting connections"), + ok; + {ok, false} -> + ?SEV_EPRINT("Unexpected Success: " + "Not accepting connections"), + {error, {unexpected_success, {Opt, false}}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[set] verify listen socket (after listen)", + cmd => fun(#{lsock := Sock} = _State) -> + case Set(Sock, false) of + {error, Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", [Reason]), + ok; + ok -> + ?SEV_EPRINT("Unexpected Success: " + "Set acceptconn (=false)"), + {error, unexpected_success} + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "create (connecting) socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{csockc => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "bind connecting socket to local address", + cmd => fun(#{csockc := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "[get] verify connecting socket (before connect)", + cmd => fun(#{csockc := Sock} = _State) -> + case Get(Sock) of + {ok, false} -> + ?SEV_IPRINT("Expected Success: " + "Not accepting connections"), + ok; + {ok, true} -> + ?SEV_EPRINT("Unexpected Success: " + "Accepting connections"), + {error, {unexpected_success, {Opt, true}}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[set] verify connecting socket (before connect)", + cmd => fun(#{csockc := Sock} = _State) -> + case Set(Sock, true) of + {error, Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", [Reason]), + ok; + ok -> + ?SEV_EPRINT("Unexpected Success: " + "Set acceptconn (=true)"), + {error, unexpected_success} + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "connect to server", + cmd => fun(#{csockc := Sock, server_sa := SSA} = _State) -> + case socket:connect(Sock, SSA) of + ok -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "accept connection", + cmd => fun(#{lsock := Sock} = State) -> + case socket:accept(Sock) of + {ok, CSock} -> + {ok, State#{csocks => CSock}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "[get] verify connecting socket (after connect)", + cmd => fun(#{csockc := Sock} = _State) -> + case Get(Sock) of + {ok, false} -> + ?SEV_IPRINT("Expected Success: " + "Not accepting connections"), + ok; + {ok, true} -> + ?SEV_EPRINT("Unexpected Success: " + "Accepting connections"), + {error, {unexpected_success, {Opt, true}}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[set] verify connecting socket (after connect)", + cmd => fun(#{csockc := Sock} = _State) -> + case Set(Sock, true) of + {error, Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", [Reason]), + ok; + ok -> + ?SEV_EPRINT("Unexpected Success: " + "Set acceptconn (=true)"), + {error, unexpected_success} + end + end}, + + #{desc => "[get] verify connected socket", + cmd => fun(#{csocks := Sock} = _State) -> + case Get(Sock) of + {ok, false} -> + ?SEV_IPRINT("Expected Success: " + "Not accepting connections"), + ok; + {ok, true} -> + ?SEV_EPRINT("Unexpected Success: " + "Accepting connections"), + {error, {unexpected_success, {Opt, true}}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[set] verify connected socket", + cmd => fun(#{csocks := Sock} = _State) -> + case Set(Sock, true) of + {error, Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", [Reason]), + ok; + ok -> + ?SEV_EPRINT("Unexpected Success: " + "Set acceptconn (=true)"), + {error, unexpected_success} + end + end}, + + #{desc => "[get] verify listen socket (after connect)", + cmd => fun(#{lsock := Sock} = _State) -> + case Get(Sock) of + {ok, true} -> + ?SEV_IPRINT("Expected Success: " + "Accepting connections"), + ok; + {ok, false} -> + ?SEV_EPRINT("Unexpected Success: " + "Not accepting connections"), + {error, {unexpected_success, {Opt, false}}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[set] verify listen socket (after connect)", + cmd => fun(#{lsock := Sock} = _State) -> + case Set(Sock, false) of + {error, Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", [Reason]), + ok; + ok -> + ?SEV_EPRINT("Unexpected Success: " + "Set acceptconn (=false)"), + {error, unexpected_success} + end + end}, + + %% *** Termination *** + #{desc => "close connecting socket(s)", + cmd => fun(#{csockc := Sock} = State0) -> + socket:close(Sock), + State1 = maps:remove(csockc, State0), + State2 = maps:remove(csocks, State1), %% Auto-close + {ok, maps:remove(csockc, State2)} + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(lsock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + Domain = inet, + + i("start tester evaluator"), + InitState = #{domain => Domain}, + Tester = ?SEV_START("tester", TesterSeq, InitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option acceptfilter. PLACEHOLDER! + +api_opt_sock_acceptfilter(suite) -> + []; +api_opt_sock_acceptfilter(doc) -> + []; +api_opt_sock_acceptfilter(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(api_opt_sock_acceptfilter, + fun() -> not_yet_implemented() end, + fun() -> ok end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option bindtodevice. +%% It has not always been possible to 'get' this option +%% (atleast on linux). + +api_opt_sock_bindtodevice(suite) -> + []; +api_opt_sock_bindtodevice(doc) -> + []; +api_opt_sock_bindtodevice(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(api_opt_sock_bindtodevice, + fun() -> has_support_sock_bindtodevice() end, + fun() -> api_opt_sock_bindtodevice() end). + + +api_opt_sock_bindtodevice() -> + Opt = bindtodevice, + Set = fun(S, Val) -> + socket:setopt(S, socket, Opt, Val) + end, + Get = fun(S) -> + socket:getopt(S, socket, Opt) + end, + + TesterSeq = + [ + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + case ?LIB:which_local_host_info(Domain) of + {ok, #{name := Name, addr := Addr}} -> + ?SEV_IPRINT("local host info (~p): " + "~n Name: ~p" + "~n Addr: ~p", + [Domain, Name, Addr]), + LSA = #{family => Domain, + addr => Addr}, + {ok, State#{dev => Name, + local_sa => LSA}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create UDP socket 1", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{usock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create UDP socket 2", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{usock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create TCP socket 1", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{tsock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create TCP socket 2", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{tsock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "[get] verify UDP socket 1 (before bindtodevice)", + cmd => fun(#{usock1 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, enoprotoopt = Reason} -> + ?SEV_EPRINT("Unexpected Failure: ~p => SKIP", + [Reason]), + {skip, Reason}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[get] verify UDP socket 2 (before bind)", + cmd => fun(#{usock2 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify TCP socket 1 (before bindtodevice)", + cmd => fun(#{tsock1 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify TCP socket 2 (before bind)", + cmd => fun(#{tsock2 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "Bind UDP socket 1 to device", + cmd => fun(#{usock1 := Sock, dev := Dev} = State) -> + case Set(Sock, Dev) of + ok -> + ?SEV_IPRINT("Expected Success"), + ok; + {error, eperm = Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", [Reason]), + (catch socket:close(Sock)), + {ok, State#{usock1 => skip}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "Bind UDP socket 2 to local address", + cmd => fun(#{usock2 := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("Expected Success"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "Bind TCP socket 1 to device", + cmd => fun(#{usock1 := USock1, + tsock1 := Sock, dev := Dev} = State) -> + case Set(Sock, Dev) of + ok -> + ?SEV_IPRINT("Expected Success"), + ok; + {error, eperm = Reason} when (USock1 =:= skip) -> + ?SEV_IPRINT("Expected Failure: ~p", [Reason]), + {skip, Reason}; + {error, eperm = Reason} -> + ?SEV_IPRINT("Expected Failure: ~p", [Reason]), + (catch socket:close(Sock)), + {ok, State#{tsock1 => skip}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "Bind TCP socket 2 to local address", + cmd => fun(#{tsock2 := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("Expected Success"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "[get] verify UDP socket 1 (after bindtodevice)", + cmd => fun(#{usock1 := skip} = _State) -> + ?SEV_IPRINT("SKIP'ed (previous eperm)"), + ok; + (#{usock1 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify UDP socket 2 (after bind)", + cmd => fun(#{usock2 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify TCP socket 1 (after bindtodevice)", + cmd => fun(#{tsock1 := skip} = _State) -> + ?SEV_IPRINT("SKIP'ed (previous eperm)"), + ok; + (#{tsock1 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify TCP socket 2 (after bind)", + cmd => fun(#{tsock2 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + %% *** Termination *** + #{desc => "close UDP socket 1", + cmd => fun(#{usock1 := skip} = State) -> + ?SEV_IPRINT("SKIP'ed (already closed)"), + {ok, maps:remove(usock1, State)}; + (#{usock1 := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(usock1, State)} + end}, + #{desc => "close UDP socket 2", + cmd => fun(#{usock2 := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(usock2, State)} + end}, + #{desc => "close TCP socket 1", + cmd => fun(#{tsock1 := skip} = State) -> + ?SEV_IPRINT("SKIP'ed (already closed)"), + {ok, maps:remove(tsock1, State)}; + (#{tsock1 := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(tsock1, State)} + end}, + #{desc => "close TCP socket 2", + cmd => fun(#{tsock2 := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(tsock2, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + Domain = inet, + + i("start tester evaluator"), + InitState = #{domain => Domain}, + Tester = ?SEV_START("tester", TesterSeq, InitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option broadcast. +%% Make it possible for datagram sockets to send packets to a broadcast +%% address (IPv4 only). + +api_opt_sock_broadcast(suite) -> + []; +api_opt_sock_broadcast(doc) -> + []; +api_opt_sock_broadcast(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(api_opt_sock_broadcast, + fun() -> has_support_sock_broadcast() end, + fun() -> api_opt_sock_broadcast() end). + + +api_opt_sock_broadcast() -> + Opt = broadcast, + Set = fun(S, Val) when is_boolean(Val) -> + socket:setopt(S, socket, Opt, Val) + end, + Get = fun(S) -> + socket:getopt(S, socket, Opt) + end, + + TesterSeq = + [ + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + case ?LIB:which_local_host_info(Domain) of + {ok, #{name := Name, + addr := Addr, + broadaddr := BAddr}} -> + ?SEV_IPRINT("local host info: " + "~n Name: ~p" + "~n Addr: ~p" + "~n Broadcast Addr: ~p", + [Name, Addr, BAddr]), + LSA = #{family => Domain, + addr => Addr}, + BSA = #{family => Domain, + addr => BAddr}, + {ok, State#{lsa => LSA, + bsa => BSA}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "[socket 1] create UDP socket (listening 1)", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "[socket 1] Bind UDP socket (to limited broadcast address)", + cmd => fun(#{sock1 := Sock} = State) -> + BSA = #{family => inet, + addr => broadcast}, + ?SEV_IPRINT("Try bind (socket 1) to: " + "~n ~p", [BSA]), + case socket:bind(Sock, BSA) of + {ok, Port} -> + ?SEV_IPRINT("Expected Success (bound): ~p", + [Port]), + {ok, State#{sa1 => BSA#{port => Port}}}; + {error, eaddrnotavail = Reason} -> + ?SEV_IPRINT("~p => " + "SKIP limited broadcast test", + [Reason]), + {ok, State#{sa1 => skip}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[socket 1] UDP socket sockname", + cmd => fun(#{sa1 := skip} = _State) -> + ?SEV_IPRINT("SKIP limited broadcast test"), + ok; + (#{sock1 := Sock} = _State) -> + case socket:sockname(Sock) of + {ok, SA} -> + ?SEV_IPRINT("SA: ~p", [SA]), + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "[socket 2] create UDP socket (listening 2)", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "[socket 2] Bind UDP socket (to subnet-directed broadcast address)", + cmd => fun(#{sock2 := Sock, + bsa := BSA} = State) -> + ?SEV_IPRINT("Try bind (socket 1) to: " + "~n ~p", [BSA]), + case socket:bind(Sock, BSA) of + {ok, Port} -> + ?SEV_IPRINT("Expected Success (bound): ~p", + [Port]), + {ok, State#{sa2 => BSA#{port => Port}}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[socket 2] UDP socket sockname", + cmd => fun(#{sock2 := Sock} = _State) -> + case socket:sockname(Sock) of + {ok, SA} -> + ?SEV_IPRINT("SA: ~p", [SA]), + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "[socket 3] create UDP socket (sender)", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock3 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "[socket 3][get] verify UDP socket (before bind and set)", + cmd => fun(#{sock3 := Sock} = _State) -> + case Get(Sock) of + {ok, false} -> + ?SEV_IPRINT("Expected Success: " + "broadcast not allowed"), + ok; + {ok, true} -> + ?SEV_IPRINT("Unexpected Success result: " + "broadcast already allowed"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[socket 3] Try make broadcast allowed", + cmd => fun(#{sock3 := Sock} = _State) -> + case Set(Sock, true) of + ok -> + ?SEV_IPRINT("Expected Success: " + "broadcast now allowed"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[socket 3] verify UDP socket broadcast allowed", + cmd => fun(#{sock3 := Sock} = _State) -> + case Get(Sock) of + {ok, true} -> + ?SEV_IPRINT("Expected Success: " + "broadcast allowed"), + ok; + {ok, false} -> + ?SEV_IPRINT("Unexpected Success result: " + "broadcast *not* allowed"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[socket 3] Bind UDP socket (to local address)", + cmd => fun(#{sock3 := Sock, lsa := LSA} = State) -> + ?SEV_IPRINT("Try bind (socket 2) to: " + "~n ~p", [LSA]), + case socket:bind(Sock, LSA) of + {ok, Port} -> + ?SEV_IPRINT("Expected Success (bound): ~p", + [Port]), + {ok, State#{sa3 => LSA#{port => Port}}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[socket 3] verify UDP socket (after set)", + cmd => fun(#{sock3 := Sock} = _State) -> + case Get(Sock) of + {ok, true} -> + ?SEV_IPRINT("Expected Success: " + "broadcast allowed"), + ok; + {ok, false} -> + ?SEV_IPRINT("Unexpected Success result: " + "broadcast not allowed"), + {error, not_allowed}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "[socket 3] try send to limited broadcast address", + cmd => fun(#{sa1 := skip} = _State) -> + ?SEV_IPRINT("SKIP limited broadcast test"), + ok; + (#{sock3 := Sock, + sa1 := Dest} = _State) -> + Data = list_to_binary("hejsan"), + ?SEV_IPRINT("try send to bradcast address: " + "~n ~p", [Dest]), + case socket:sendto(Sock, Data, Dest) of + ok -> + ?SEV_IPRINT("Expected Success: " + "broadcast message sent"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[socket 1] try recv", + cmd => fun(#{sa1 := skip} = _State) -> + ?SEV_IPRINT("SKIP limited broadcast test"), + ok; + (#{sock1 := Sock} = State) -> + case socket:recvfrom(Sock, 0, 5000) of + {ok, _} -> + ?SEV_IPRINT("Expected Success: " + "received message"), + ok; + {error, timeout = Reason} -> + %% Some platforms seem to balk at this. + %% It spossible to bind to this, and + %% send to it, but no data is received. + %% At some point we should investigate... + %% For now, we just skip this part of + %% the test... + ?SEV_IPRINT("Unexpected Failure: ~p", + [Reason]), + {ok, State#{sa1 => skip}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "[socket 3] try send to subnet-directed broadcast address", + cmd => fun(#{sock3 := Sock, + sa2 := Dest} = _State) -> + Data = list_to_binary("hejsan"), + ?SEV_IPRINT("try send to bradcast address: " + "~n ~p", [Dest]), + case socket:sendto(Sock, Data, Dest) of + ok -> + ?SEV_IPRINT("Expected Success: " + "broadcast message sent"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "[socket 2] try recv", + cmd => fun(#{sock2 := Sock, sa1 := SA1} = _State) -> + case socket:recvfrom(Sock, 0, 5000) of + {ok, _} -> + ?SEV_IPRINT("Expected Success: " + "received message"), + ok; + {error, timeout = Reason} when (SA1 =:= skip) -> + ?SEV_IPRINT("Unexpected Failure: ~p", + [Reason]), + {skip, "receive timeout"}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + + %% *** Termination *** + #{desc => "[socket 3] close UDP socket (sender)", + cmd => fun(#{sock3 := Sock} = State0) -> + socket:close(Sock), + State1 = maps:remove(sock3, State0), + State2 = maps:remove(sa3, State1), + {ok, State2} + end}, + #{desc => "[socket 2] close UDP socket (listener 2)", + cmd => fun(#{sock2 := Sock} = State0) -> + socket:close(Sock), + State1 = maps:remove(sock2, State0), + State2 = maps:remove(sa2, State1), + {ok, State2} + end}, + #{desc => "[socket 1] close UDP socket (listener 1)", + cmd => fun(#{sock1 := Sock} = State0) -> + socket:close(Sock), + State1 = maps:remove(sock1, State0), + State2 = maps:remove(sa1, State1), + {ok, State2} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + Domain = inet, + + i("start tester evaluator"), + InitState = #{domain => Domain}, + Tester = ?SEV_START("tester", TesterSeq, InitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option debug. +%% On linux, this test requires that the user running the test to have +%% CAP_NET_ADMIN capabilities or be root (effective user ID of 0), +%% therefor we explicitly test for the result eacces when attempting to +%% set, and skip if we get it. + +api_opt_sock_debug(suite) -> + []; +api_opt_sock_debug(doc) -> + []; +api_opt_sock_debug(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_opt_sock_debug, + fun() -> has_support_sock_debug() end, + fun() -> api_opt_sock_debug() end). + + +api_opt_sock_debug() -> + Opt = debug, + Set = fun(S, Val) when is_integer(Val) -> + socket:setopt(S, socket, Opt, Val) + end, + Get = fun(S) -> + socket:getopt(S, socket, Opt) + end, + + TesterSeq = + [ + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + case ?LIB:which_local_host_info(Domain) of + {ok, #{name := Name, + addr := Addr, + broadaddr := BAddr}} -> + ?SEV_IPRINT("local host info: " + "~n Name: ~p" + "~n Addr: ~p" + "~n Broadcast Addr: ~p", + [Name, Addr, BAddr]), + LSA = #{family => Domain, + addr => Addr}, + BSA = #{family => Domain, + addr => BAddr}, + {ok, State#{lsa => LSA, + bsa => BSA}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "create UDP socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "Get current debug value", + cmd => fun(#{sock := Sock} = State) -> + case Get(Sock) of + {ok, Debug} when is_integer(Debug) -> + ?SEV_IPRINT("Success: ~p", [Debug]), + {ok, State#{debug => Debug}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "Try enable socket debug", + cmd => fun(#{sock := Sock, debug := Debug} = State) -> + NewDebug = Debug + 1, + case Set(Sock, NewDebug) of + ok -> + ?SEV_IPRINT("Expected Success"), + {ok, State#{debug => NewDebug}}; + {error, eacces = Reason} -> + ?SEV_EPRINT("NO ACCESS => SKIP"), + {skip, Reason}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "Get current (new) debug value", + cmd => fun(#{sock := Sock, debug := Debug} = _State) -> + case Get(Sock) of + {ok, Debug} when is_integer(Debug) -> + ?SEV_IPRINT("Success: ~p", [Debug]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected failure: ~p", + [Reason]), + ERROR + end + end}, + + %% *** Termination *** + #{desc => "close UDP socket", + cmd => fun(#{sock := Sock} = State0) -> + socket:close(Sock), + State1 = maps:remove(sock, State0), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + Domain = inet, + + i("start tester evaluator"), + InitState = #{domain => Domain}, + Tester = ?SEV_START("tester", TesterSeq, InitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option domain. +%% This is a read only option. Also not available on all platforms. + +api_opt_sock_domain(suite) -> + []; +api_opt_sock_domain(doc) -> + []; +api_opt_sock_domain(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_opt_sock_domain, + fun() -> has_support_sock_domain() end, + fun() -> api_opt_sock_domain() end). + + +api_opt_sock_domain() -> + Opt = domain, + Get = fun(S) -> + socket:getopt(S, socket, Opt) + end, + + TesterSeq = + [ + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + case ?LIB:which_local_host_info(Domain) of + {ok, #{name := Name, + addr := Addr, + broadaddr := BAddr}} -> + ?SEV_IPRINT("local host info: " + "~n Name: ~p" + "~n Addr: ~p" + "~n Broadcast Addr: ~p", + [Name, Addr, BAddr]), + LSA = #{family => Domain, + addr => Addr}, + BSA = #{family => Domain, + addr => BAddr}, + {ok, State#{lsa => LSA, + bsa => BSA}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "create IPv4 UDP socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{usock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "Get domain for the UDP socket", + cmd => fun(#{domain := Domain, usock := Sock} = _State) -> + case Get(Sock) of + {ok, Domain} -> + ?SEV_IPRINT("Success: ~p", [Domain]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "create TCP socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{tsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "Get domain for the TCP socket", + cmd => fun(#{domain := Domain, tsock := Sock} = _State) -> + case Get(Sock) of + {ok, Domain} -> + ?SEV_IPRINT("Success: ~p", [Domain]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected failure: ~p", + [Reason]), + ERROR + end + end}, + + %% *** Termination *** + #{desc => "close UDP socket", + cmd => fun(#{usock := Sock} = State0) -> + socket:close(Sock), + State1 = maps:remove(usock, State0), + {ok, State1} + end}, + #{desc => "close TCP socket", + cmd => fun(#{tsock := Sock} = State0) -> + socket:close(Sock), + State1 = maps:remove(tsock, State0), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + Domain = inet, + + i("start tester evaluator"), + InitState = #{domain => Domain}, + Tester = ?SEV_START("tester", TesterSeq, InitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option dontroute. +%% The man page has the following to say: +%% "Don't send via a gateway, send only to directly connected hosts. +%% The same effect can be achieved by setting the MSG_DONTROUTE +%% flag on a socket send(2) operation." +%% Since its "kind of" difficult to check if it actually takes an +%% effect (you would need a gateway for that and a machine "on the +%% other side"), we only test if we can set and get the value. +%% Better then nothing. + +api_opt_sock_dontroute(suite) -> + []; +api_opt_sock_dontroute(doc) -> + []; +api_opt_sock_dontroute(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_opt_sock_dontroute, + fun() -> has_support_sock_dontroute() end, + fun() -> api_opt_sock_dontroute() end). + + +api_opt_sock_dontroute() -> + Opt = dontroute, + Set = fun(S, Val) when is_boolean(Val) -> + socket:setopt(S, socket, Opt, Val) + end, + Get = fun(S) -> + socket:getopt(S, socket, Opt) + end, + + TesterSeq = + [ + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + case ?LIB:which_local_host_info(Domain) of + {ok, #{name := Name, + addr := Addr, + broadaddr := BAddr}} -> + ?SEV_IPRINT("local host info: " + "~n Name: ~p" + "~n Addr: ~p" + "~n Broadcast Addr: ~p", + [Name, Addr, BAddr]), + LSA = #{family => Domain, + addr => Addr}, + BSA = #{family => Domain, + addr => BAddr}, + {ok, State#{lsa => LSA, + bsa => BSA}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "create UDP socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "Get current value", + cmd => fun(#{sock := Sock} = State) -> + case Get(Sock) of + {ok, Val} when is_boolean(Val) -> + ?SEV_IPRINT("Success: ~p", [Val]), + {ok, State#{dontroute => Val}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "Try change value", + cmd => fun(#{sock := Sock, dontroute := Current} = State) -> + New = not Current, + ?SEV_IPRINT("Change from ~p to ~p", [Current, New]), + case Set(Sock, New) of + ok -> + ?SEV_IPRINT("Expected Success"), + {ok, State#{dontroute => New}}; + {error, eopnotsupp = Reason} -> + ?SEV_EPRINT("Expected Failure: ~p", + [Reason]), + {skip, Reason}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "Verify changed value", + cmd => fun(#{sock := Sock, dontroute := Val} = _State) -> + case Get(Sock) of + {ok, Val} -> + ?SEV_IPRINT("Expected Success"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected failure: ~p", + [Reason]), + ERROR + end + end}, + + %% *** Termination *** + #{desc => "close UDP socket", + cmd => fun(#{sock := Sock} = State0) -> + socket:close(Sock), + State1 = maps:remove(sock, State0), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + Domain = inet, + + i("start tester evaluator"), + InitState = #{domain => Domain}, + Tester = ?SEV_START("tester", TesterSeq, InitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option error. PLACEHOLDER! + +api_opt_sock_error(suite) -> + []; +api_opt_sock_error(doc) -> + []; +api_opt_sock_error(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_opt_sock_error, + fun() -> not_yet_implemented() end, + fun() -> ok end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option keepalive. +%% This is bit tricky to test, partly because we have no control over +%% the underlying TCP timeouts. So, for now, we just test that we can +%% change the value. + +api_opt_sock_keepalive(suite) -> + []; +api_opt_sock_keepalive(doc) -> + []; +api_opt_sock_keepalive(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_opt_sock_keepalive, + fun() -> has_support_sock_keepalive() end, + fun() -> api_opt_sock_keepalive() end). + + +api_opt_sock_keepalive() -> + Opt = keepalive, + Set = fun(S, Val) when is_boolean(Val) -> + socket:setopt(S, socket, Opt, Val) + end, + Get = fun(S) -> + socket:getopt(S, socket, Opt) + end, + + TesterSeq = + [ + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + case ?LIB:which_local_host_info(Domain) of + {ok, #{name := Name, + addr := Addr, + broadaddr := BAddr}} -> + ?SEV_IPRINT("local host info: " + "~n Name: ~p" + "~n Addr: ~p" + "~n Broadcast Addr: ~p", + [Name, Addr, BAddr]), + LSA = #{family => Domain, + addr => Addr}, + BSA = #{family => Domain, + addr => BAddr}, + {ok, State#{lsa => LSA, + bsa => BSA}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "create TCP socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "Get current value", + cmd => fun(#{sock := Sock} = State) -> + case Get(Sock) of + {ok, Val} when is_boolean(Val) -> + ?SEV_IPRINT("Success: ~p", [Val]), + {ok, State#{keepalive => Val}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "Try change the value", + cmd => fun(#{sock := Sock, keepalive := Current} = State) -> + New = not Current, + ?SEV_IPRINT("Try change value from ~p to ~p", + [Current, New]), + case Set(Sock, New) of + ok -> + ?SEV_IPRINT("Expected Success"), + {ok, State#{keepalive => New}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "Verify (new) current value", + cmd => fun(#{sock := Sock, keepalive := Val} = _State) -> + case Get(Sock) of + {ok, Val} -> + ?SEV_IPRINT("Expected Success (~p)", [Val]), + ok; + {ok, OtherVal} -> + ?SEV_IPRINT("Unexpected Success: ~p", + [OtherVal]), + {error, {unexpected_success_value, + Val, OtherVal}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected failure: ~p", + [Reason]), + ERROR + end + end}, + + %% *** Termination *** + #{desc => "close UDP socket", + cmd => fun(#{sock := Sock} = State0) -> + socket:close(Sock), + State1 = maps:remove(sock, State0), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + Domain = inet, + + i("start tester evaluator"), + InitState = #{domain => Domain}, + Tester = ?SEV_START("tester", TesterSeq, InitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests the socket option linger. PLACEHOLDER! + +api_opt_sock_linger(suite) -> + []; +api_opt_sock_linger(doc) -> + []; +api_opt_sock_linger(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_opt_sock_linger, + fun() -> not_yet_implemented() end, + fun() -> ok end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests that the add_mambership and drop_membership ip options work. +%% We create one server and two clients. The server only send messages, +%% the clients only receives messages. +%% An UDP datagram is forbidden (RFC 1122) from having a source address +%% that is a multicast address (or a broadcast address). +%% So, the server create a socket "for sending" and the clients sockets +%% "for receiving". +%% Sending socket: Bound to the local address (and any port). +%% When sending, the dest will be the multicast address +%% and port of the receiving socket. +%% Receiving socket: Bound to the multicast address and port. +api_opt_ip_add_drop_membership(suite) -> + []; +api_opt_ip_add_drop_membership(doc) -> + ["OTP-15908 (ERL-980)"]; +api_opt_ip_add_drop_membership(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(api_opt_ip_add_drop_membership, + fun() -> + has_support_ip_add_membership(), + has_support_ip_drop_membership(), + has_support_ip_multicast() + end, + fun() -> api_opt_ip_add_drop_membership() end). + + +api_opt_ip_add_drop_membership() -> + Set = fun(S, Key, Val) -> + socket:setopt(S, ip, Key, Val) + end, + AddMembership = fun(S, Val) -> Set(S, add_membership, Val) end, + DropMembership = fun(S, Val) -> Set(S, drop_membership, Val) end, + + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + {Tester, MSA} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, msa => MSA}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make recv socket reuse addr", + cmd => fun(#{sock := Sock} = _State) -> + case socket:setopt(Sock, socket, reuseaddr, true) of + ok -> + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Failed set reuseaddr: " + "~n ~p", [Reason]), + ERROR + end + end}, + #{desc => "bind recv socket to multicast address", + cmd => fun(#{sock := Sock, msa := MSA} = State) -> + case socket:bind(Sock, MSA) of + {ok, Port} -> + ?SEV_IPRINT("bound to:" + "~n ~p", [Port]), + {ok, State#{msa => MSA#{port => Port}}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (add_membership)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, add_membership) + end}, + #{desc => "add membership", + cmd => fun(#{sock := Sock, + msa := #{addr := MAddr}, + local_sa := #{addr := Addr}} = State) -> + MReq = #{multiaddr => MAddr, + interface => Addr}, + ?SEV_IPRINT("try add membership to:" + "~n ~p", [MReq]), + case AddMembership(Sock, MReq) of + ok -> + ?SEV_IPRINT("membership added"), + {ok, State#{mreq => MReq}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Failed adding membership to: " + "~n ~p" + "~n Reason: ~p", + [MReq, Reason]), + ERROR + end + end}, + #{desc => "announce ready (add-membership)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, add_membership), + ok + end}, + + #{desc => "await continue (drop_membership)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, drop_membership) + end}, + #{desc => "drop membership", + cmd => fun(#{sock := Sock, + mreq := MReq} = State) -> + ?SEV_IPRINT("try drop membership from:" + "~n ~p", [MReq]), + case DropMembership(Sock, MReq) of + ok -> + ?SEV_IPRINT("membership dropped"), + {ok, maps:remove(mreq, State)}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Failed drop membership from: " + "~n ~p" + "~n Reason: ~p", + [MReq, Reason]), + ERROR + end + end}, + #{desc => "announce ready (drop-membership)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, drop_membership), + ok + end}, + + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Server} = _State) -> + _MRef = erlang:monitor(process, Server), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid, msa := MSA} = _State) -> + ?SEV_ANNOUNCE_START(Pid, MSA), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = _State) -> + case ?SEV_AWAIT_READY(Pid, server, init) of + ok -> + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Start of server failed: " + "~n ~p", [Reason]), + ERROR + end + end}, + + + %% *** The actual test *** + #{desc => "order server to continue (add-membership)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, add_membership), + ok + end}, + #{desc => "await server ready (add-membership)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, add_membership) + end}, + + #{desc => "order server to continue (drop-membership)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, drop_membership), + ok + end}, + #{desc => "await server ready (drop-membership)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, drop_membership) + end}, + + ?SEV_SLEEP(?SECS(1)), + + %% *** Termination *** + #{desc => "order server terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + {ok, maps:remove(server, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + Domain = inet, + i("get multicast address"), + MAddr = which_ip_multicast_address(), + MSA = #{family => Domain, addr => MAddr}, + + i("start server evaluator"), + ServerInitState = #{domain => Domain}, + Server = ?SEV_START("server", ServerSeq, ServerInitState), + + i("start tester evaluator"), + TesterInitState = #{domain => Domain, + msa => MSA, + server => Server#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester, Server]). + + + +which_ip_multicast_address() -> + which_multicast_address(inet). + +which_multicast_address(Domain) -> + case os:type() of + {unix, linux} -> + WhichMAddr = fun([_, _, MAddr]) -> MAddr end, + which_multicast_address2(Domain, WhichMAddr); + + {unix, sunos} -> + WhichMAddr = fun([_, MAddr, _]) -> MAddr end, + which_multicast_address2(Domain, WhichMAddr); + + Type -> + %% Actually, what is "not supported". is netstat! + not_supported({multicast, Type}) + end. + +%% Note that the 'netstat -g' table looks different on linux and SunOS +%% Linux: IfName - RefCnt - Group +%% SunOS: IfName - Group - RefCnt + +which_multicast_address2(Domain, WhichMAddr) -> + IfName = which_local_host_ifname(Domain), + %% On some platforms the netstat barfs out some crap on stderr + %% before the actual info... + case os:cmd("netstat -g 2>/dev/null | grep " ++ IfName) of + [] -> + %% Can't figure out if we support multicast or not... + not_supported(no_netstat); + NetstatGroupsStr -> + try + begin + NetstatGroups0 = string:tokens(NetstatGroupsStr, [$\n]), + NetstatGroups = [string:tokens(G, [$ ]) || + G <- NetstatGroups0], + MAddrs = [WhichMAddr(NetstatGroup) || + NetstatGroup <- NetstatGroups], + which_multicast_address3(Domain, MAddrs) + end + catch + throw:E:_ -> + throw(E); + C:E:S -> + not_supported({multicast, {C,E,S}}) + end + end. + +which_multicast_address3(_Domain, []) -> + not_supported({multicast, no_valid_addrs}); +which_multicast_address3(Domain, [MAddrStr|MAddrs]) -> + %% Even on linux some of these are not actually addresses, but + %% "host names", such as all-systems.mcast.net. But both + %% address strings, such as "224.0.0.251" and host name strings + %% gets translated into an address by the inet:inet:getaddr/2. + case inet:getaddr(MAddrStr, Domain) of + {ok, MAddr} -> + MAddr; + {error, _} -> + which_multicast_address3(Domain, MAddrs) + end. + +which_local_host_ifname(Domain) -> + case ?LIB:which_local_host_info(Domain) of + {ok, #{name := Name}} -> + Name; + {error, Reason} -> + not_supported({multicast, Reason}) + end. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% API OPERATIONS WITH TIMEOUT %% @@ -3434,11 +10425,11 @@ api_to_connect_tcp4(suite) -> api_to_connect_tcp4(doc) -> []; api_to_connect_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), Cond = fun() -> api_to_connect_cond() end, tc_try(api_to_connect_tcp4, Cond, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet, backlog => 1, timeout => 5000, @@ -3454,7 +10445,9 @@ api_to_connect_cond() -> %% So, just to simplify, we require atleast 4.15 api_to_connect_cond({unix, linux}, {Maj, Min, _Rev}) -> if - ((Maj >= 4) andalso (Min >= 15)) -> + (Maj > 4) -> + ok; + ((Maj =:= 4) andalso (Min >= 15)) -> ok; true -> skip("TC does not work") @@ -3491,10 +10484,10 @@ api_to_connect_tcp6(suite) -> api_to_connect_tcp6(doc) -> []; api_to_connect_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_connect_tcp6, fun() -> has_support_ipv6(), api_to_connect_cond() end, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet6, backlog => 1, timeout => 5000, @@ -3531,8 +10524,7 @@ api_to_connect_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", @@ -3603,8 +10595,7 @@ api_to_connect_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create node", @@ -3754,8 +10745,7 @@ api_to_connect_tcp(InitState) -> end}, #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "order server start", @@ -3906,9 +10896,7 @@ api_toc_tcp_client_await_terminate(Parent) -> end. api_to_connect_tcp_await_timeout(To, ServerSA, Domain, ConLimit) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), NewSock = fun() -> S = case socket:open(Domain, stream, tcp) of {ok, Sock} -> @@ -3986,9 +10974,9 @@ api_to_accept_tcp4(suite) -> api_to_accept_tcp4(doc) -> []; api_to_accept_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_accept_tcp4, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet, timeout => 5000}, ok = api_to_accept_tcp(InitState) end). @@ -4003,10 +10991,10 @@ api_to_accept_tcp6(suite) -> api_to_accept_tcp6(doc) -> []; api_to_accept_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_accept_tcp4, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet6, timeout => 5000}, ok = api_to_accept_tcp(InitState) end). @@ -4020,8 +11008,7 @@ api_to_accept_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", @@ -4145,8 +11132,7 @@ api_to_maccept_tcp(InitState) -> end}, #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", @@ -4489,6 +11475,7 @@ api_to_send_tcp6(doc) -> []; api_to_send_tcp6(_Config) when is_list(_Config) -> tc_try(api_to_send_tcp6, + fun() -> has_support_ipv6() end, fun() -> not_yet_implemented()%% , %% ok = api_to_send_tcp(inet6) @@ -4521,6 +11508,7 @@ api_to_sendto_udp6(doc) -> []; api_to_sendto_udp6(_Config) when is_list(_Config) -> tc_try(api_to_sendto_udp6, + fun() -> has_support_ipv6() end, fun() -> not_yet_implemented()%% , %% ok = api_to_sendto_to_udp(inet6) @@ -4553,6 +11541,7 @@ api_to_sendmsg_tcp6(doc) -> []; api_to_sendmsg_tcp6(_Config) when is_list(_Config) -> tc_try(api_to_sendmsg_tcp6, + fun() -> has_support_ipv6() end, fun() -> not_yet_implemented()%% , %% ok = api_to_sendmsg_tcp(inet6) @@ -4587,6 +11576,7 @@ api_to_recv_udp6(doc) -> []; api_to_recv_udp6(_Config) when is_list(_Config) -> tc_try(api_to_recv_udp6, + fun() -> has_support_ipv6() end, fun() -> not_yet_implemented()%% , %% ok = api_to_recv_udp(inet6) @@ -4602,9 +11592,9 @@ api_to_recv_tcp4(suite) -> api_to_recv_tcp4(doc) -> []; api_to_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recv_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recv(Sock, 0, To) end, InitState = #{domain => inet, recv => Recv, @@ -4622,12 +11612,12 @@ api_to_recv_tcp6(suite) -> api_to_recv_tcp6(doc) -> []; api_to_recv_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recv_tcp6, fun() -> has_support_ipv6() end, fun() -> case socket:supports(ipv6) of true -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recv(Sock, 0, To) end, @@ -4663,8 +11653,7 @@ api_to_receive_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", @@ -4782,10 +11771,8 @@ api_to_receive_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain, server_port := Port} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, - SSA = LSA#{port => Port}, + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, {ok, State#{local_sa => LSA, server_sa => SSA}} end}, #{desc => "create socket", @@ -4961,9 +11948,9 @@ api_to_recvfrom_udp4(suite) -> api_to_recvfrom_udp4(doc) -> []; api_to_recvfrom_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvfrom_udp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, 0, To) end, InitState = #{domain => inet, recv => Recv, @@ -4981,10 +11968,10 @@ api_to_recvfrom_udp6(suite) -> api_to_recvfrom_udp6(doc) -> []; api_to_recvfrom_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvfrom_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, 0, To) end, InitState = #{domain => inet6, recv => Recv, @@ -5001,8 +11988,7 @@ api_to_receive_udp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create socket", @@ -5078,9 +12064,9 @@ api_to_recvmsg_udp4(suite) -> api_to_recvmsg_udp4(doc) -> []; api_to_recvmsg_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_udp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet, recv => Recv, @@ -5098,10 +12084,10 @@ api_to_recvmsg_udp6(suite) -> api_to_recvmsg_udp6(doc) -> []; api_to_recvmsg_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet6, recv => Recv, @@ -5119,9 +12105,9 @@ api_to_recvmsg_tcp4(suite) -> api_to_recvmsg_tcp4(doc) -> []; api_to_recvmsg_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet, recv => Recv, @@ -5139,10 +12125,10 @@ api_to_recvmsg_tcp6(suite) -> api_to_recvmsg_tcp6(doc) -> []; api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet6, recv => Recv, @@ -5170,9 +12156,9 @@ sc_cpe_socket_cleanup_tcp4(suite) -> sc_cpe_socket_cleanup_tcp4(doc) -> []; sc_cpe_socket_cleanup_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_cpe_socket_cleanup_tcp4, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet, type => stream, protocol => tcp}, @@ -5190,10 +12176,10 @@ sc_cpe_socket_cleanup_tcp6(suite) -> sc_cpe_socket_cleanup_tcp6(doc) -> []; sc_cpe_socket_cleanup_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_cpe_socket_cleanup_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet6, type => stream, protocol => tcp}, @@ -5204,6 +12190,27 @@ sc_cpe_socket_cleanup_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the sockets are cleaned up %% ("removed") when the controlling process terminates (without explicitly +%% calling the close function). For a Unix Domain (stream) socket (TCP). + +sc_cpe_socket_cleanup_tcpL(suite) -> + []; +sc_cpe_socket_cleanup_tcpL(doc) -> + []; +sc_cpe_socket_cleanup_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_cpe_socket_cleanup_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => stream, + protocol => default}, + ok = sc_cpe_socket_cleanup(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sockets are cleaned up +%% ("removed") when the controlling process terminates (without explicitly %% calling the close function). For a IPv4 UDP (dgram) socket. sc_cpe_socket_cleanup_udp4(suite) -> @@ -5211,9 +12218,9 @@ sc_cpe_socket_cleanup_udp4(suite) -> sc_cpe_socket_cleanup_udp4(doc) -> []; sc_cpe_socket_cleanup_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_cpe_socket_cleanup_udp4, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet, type => dgram, protocol => udp}, @@ -5232,10 +12239,10 @@ sc_cpe_socket_cleanup_udp6(suite) -> sc_cpe_socket_cleanup_udp6(doc) -> []; sc_cpe_socket_cleanup_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_cpe_socket_cleanup_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet6, type => dgram, protocol => udp}, @@ -5244,6 +12251,28 @@ sc_cpe_socket_cleanup_udp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sockets are cleaned up +%% ("removed") when the controlling process terminates (without explicitly +%% calling the close function). For a Unix Domain (dgram) socket (UDP). + +sc_cpe_socket_cleanup_udpL(suite) -> + []; +sc_cpe_socket_cleanup_udpL(doc) -> + []; +sc_cpe_socket_cleanup_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_cpe_socket_cleanup_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => dgram, + protocol => default}, + ok = sc_cpe_socket_cleanup(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_cpe_socket_cleanup(InitState) -> OwnerSeq = @@ -5337,8 +12366,15 @@ sc_cpe_socket_cleanup(InitState) -> ERROR end end}, + + ?SEV_SLEEP(?SECS(5)), + %% The reason we get closed, is that as long as there is a ref to %% the resource (socket), then it will not be garbage collected. + %% Note that its still a race that the nif has processed that the + %% "controlling process" has terminated. There really is no + %% proper timeout for this, but the 5 seconds "should" be enough... + %% We should really have some way to subscribe to socket events... #{desc => "verify no socket (closed)", cmd => fun(#{owner := Pid, sock := Sock} = _State) -> case socket:getopt(Sock, otp, controlling_process) of @@ -5386,12 +12422,11 @@ sc_lc_recv_response_tcp4(suite) -> sc_lc_recv_response_tcp4(doc) -> []; sc_lc_recv_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_recv_response_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -5408,13 +12443,12 @@ sc_lc_recv_response_tcp6(suite) -> sc_lc_recv_response_tcp6(doc) -> []; sc_lc_recv_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_recv_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -5422,6 +12456,28 @@ sc_lc_recv_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% 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 Unix Domain (stream) socket. + +sc_lc_recv_response_tcpL(suite) -> + []; +sc_lc_recv_response_tcpL(doc) -> + []; +sc_lc_recv_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_lc_recv_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_lc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_lc_receive_response_tcp(InitState) -> %% This (acceptor) is the server that accepts connections. @@ -5444,15 +12500,13 @@ sc_lc_receive_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, - {ok, State#{local_sa => LSA}} + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", cmd => fun(#{domain := Domain, - type := Type, protocol := Proto} = State) -> - case socket:open(Domain, Type, Proto) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -5460,9 +12514,22 @@ sc_lc_receive_response_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + ?SEV_IPRINT("bind to LSA: " + "~n ~p", [LSA]), + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, + lsa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -5473,7 +12540,12 @@ sc_lc_receive_response_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, + lsa := #{path := Path}}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, lport := Port}) -> ?SEV_ANNOUNCE_READY(Tester, init, Port), ok end}, @@ -5494,7 +12566,8 @@ sc_lc_receive_response_tcp(InitState) -> cmd => fun(#{lsock := LSock} = State) -> case socket:accept(LSock) of {ok, Sock} -> - ?SEV_IPRINT("connection accepted"), + ?SEV_IPRINT("connection accepted: " + "~n ~p", [socket:sockname(Sock)]), {ok, State#{csock => Sock}}; {error, _} = ERROR -> ERROR @@ -5525,9 +12598,8 @@ sc_lc_receive_response_tcp(InitState) -> ?SEV_AWAIT_CONTINUE(Tester, tester, close), ok end}, - #{desc => "close the connection socket", + #{desc => "close connection socket", cmd => fun(#{csock := Sock} = State) -> - %% ok = socket:setopt(Sock, otp, debug, true), case socket:close(Sock) of ok -> {ok, maps:remove(csock, State)}; @@ -5551,8 +12623,19 @@ sc_lc_receive_response_tcp(InitState) -> ERROR end end}, - #{desc => "close socket", - cmd => fun(#{lsock := Sock} = State) -> + #{desc => "close listen socket", + cmd => fun(#{domain := local, + lsock := Sock, + lsa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() ->maps:remove(lsa, State) end, + fun() -> State end), + State2 = maps:remove(lsock, State1), + State3 = maps:remove(lport, State2), + {ok, State3}; + (#{lsock := Sock} = State) -> case socket:close(Sock) of ok -> State1 = maps:remove(lsock, State), @@ -5667,15 +12750,13 @@ sc_lc_receive_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create socket", cmd => fun(#{domain := Domain, - type := Type, protocol := Proto} = State) -> - case socket:open(Domain, Type, Proto) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -5684,6 +12765,8 @@ sc_lc_receive_response_tcp(InitState) -> end}, #{desc => "bind socket to local address", cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + ?SEV_IPRINT("bind to LSA: " + "~n ~p", [LSA]), case socket:bind(Sock, LSA) of {ok, _} -> ok; @@ -5699,7 +12782,19 @@ sc_lc_receive_response_tcp(InitState) -> %% The actual test #{desc => "await continue (connect)", - cmd => fun(#{tester := Tester, local_sa := LSA} = State) -> + cmd => fun(#{domain := local = Domain, + tester := Tester} = State) -> + case ?SEV_AWAIT_CONTINUE(Tester, tester, connect) of + {ok, ServerPath} -> + ?SEV_IPRINT("Server Path: " + "~n ~s", [ServerPath]), + ServerSA = #{family => Domain, + path => ServerPath}, + {ok, State#{server_sa => ServerSA}}; + {error, _} = ERROR -> + ERROR + end; + (#{tester := Tester, local_sa := LSA} = State) -> case ?SEV_AWAIT_CONTINUE(Tester, tester, connect) of {ok, Port} -> ServerSA = LSA#{port => Port}, @@ -5729,7 +12824,18 @@ sc_lc_receive_response_tcp(InitState) -> end end}, #{desc => "close socket", - cmd => fun(#{sock := Sock} = State) -> + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + sock_close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> sock_close(Sock), {ok, maps:remove(sock, State)} end}, @@ -5776,8 +12882,8 @@ sc_lc_receive_response_tcp(InitState) -> #{desc => "await acceptor ready (init)", cmd => fun(#{acceptor := Pid} = State) -> case ?SEV_AWAIT_READY(Pid, acceptor, init) of - {ok, Port} -> - {ok, State#{lport => Port}}; + {ok, PortOrPath} -> + {ok, State#{server_info => PortOrPath}}; {error, _} = ERROR -> ERROR end @@ -5834,8 +12940,8 @@ sc_lc_receive_response_tcp(InitState) -> end}, ?SEV_SLEEP(?SECS(1)), #{desc => "order client to continue (connect)", - cmd => fun(#{client := Pid, lport := Port} = _State) -> - ?SEV_ANNOUNCE_CONTINUE(Pid, connect, Port), + cmd => fun(#{client := Pid, server_info := Info} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, connect, Info), ok end}, #{desc => "await acceptor ready (accept)", @@ -6002,12 +13108,11 @@ sc_lc_recvfrom_response_udp4(suite) -> sc_lc_recvfrom_response_udp4(doc) -> []; sc_lc_recvfrom_response_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), 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) @@ -6024,13 +13129,36 @@ sc_lc_recvfrom_response_udp6(suite) -> sc_lc_recvfrom_response_udp6(doc) -> []; sc_lc_recvfrom_response_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_lc_recvfrom_response_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, - InitState = #{domain => inet6, - recv => Recv}, + InitState = #{domain => inet6, + 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 Unix Domainm (dgram) socket. + +sc_lc_recvfrom_response_udpL(suite) -> + []; +sc_lc_recvfrom_response_udpL(doc) -> + []; +sc_lc_recvfrom_response_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_lc_recvfrom_response_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, ok = sc_lc_receive_response_udp(InitState) end). @@ -6055,20 +13183,25 @@ sc_lc_receive_response_udp(InitState) -> %% *** Init part *** #{desc => "local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "open socket", - cmd => fun(#{domain := Domain} = State) -> - Sock = sock_open(Domain, dgram, udp), + cmd => fun(#{domain := Domain, protocol := Proto} = State) -> + Sock = sock_open(Domain, dgram, Proto), SA = sock_sockname(Sock), {ok, State#{sock => Sock, sa => SA}} end}, #{desc => "bind socket", cmd => fun(#{sock := Sock, local_sa := LSA}) -> - sock_bind(Sock, LSA), - ok + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("src bound"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("src bind failed: ~p", [Reason]), + ERROR + end end}, #{desc => "announce ready (init)", cmd => fun(#{tester := Tester, sock := Sock}) -> @@ -6107,7 +13240,18 @@ sc_lc_receive_response_udp(InitState) -> ok = ?SEV_AWAIT_CONTINUE(Tester, tester, close) end}, #{desc => "close socket", - cmd => fun(#{sock := Sock} = State) -> + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> case socket:close(Sock) of ok -> {ok, maps:remove(sock, State)}; @@ -6400,9 +13544,9 @@ sc_lc_receive_response_udp(InitState) -> 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}, + sec_server1 => SecServer1#ev.pid, + sec_server2 => SecServer2#ev.pid, + sec_server3 => SecServer3#ev.pid}, Tester = ?SEV_START("tester", TesterSeq, TesterInitState), i("await evaluator"), @@ -6422,12 +13566,11 @@ sc_lc_recvmsg_response_tcp4(suite) -> sc_lc_recvmsg_response_tcp4(doc) -> []; sc_lc_recvmsg_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_recvmsg_response_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -6444,13 +13587,12 @@ sc_lc_recvmsg_response_tcp6(suite) -> sc_lc_recvmsg_response_tcp6(doc) -> []; sc_lc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_recvmsg_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -6460,6 +13602,28 @@ sc_lc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 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 Unix Domain (stream) socket. + +sc_lc_recvmsg_response_tcpL(suite) -> + []; +sc_lc_recvmsg_response_tcpL(doc) -> + []; +sc_lc_recvmsg_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_recvmsg_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_lc_receive_response_tcp(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 IPv4. sc_lc_recvmsg_response_udp4(suite) -> @@ -6471,8 +13635,9 @@ sc_lc_recvmsg_response_udp4(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, - InitState = #{domain => inet, - recv => Recv}, + InitState = #{domain => inet, + protocol => udp, + recv => Recv}, ok = sc_lc_receive_response_udp(InitState) end). @@ -6492,8 +13657,32 @@ sc_lc_recvmsg_response_udp6(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, - InitState = #{domain => inet6, - recv => Recv}, + InitState = #{domain => inet6, + 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 recvmsg function. +%% Socket is Unix Domain (dgram) socket. + +sc_lc_recvmsg_response_udpL(suite) -> + []; +sc_lc_recvmsg_response_udpL(doc) -> + []; +sc_lc_recvmsg_response_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_recvmsg_response_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, ok = sc_lc_receive_response_udp(InitState) end). @@ -6511,11 +13700,10 @@ sc_lc_acceptor_response_tcp4(suite) -> sc_lc_acceptor_response_tcp4(doc) -> []; sc_lc_acceptor_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_acceptor_response_tcp4, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet, - type => stream, protocol => tcp}, ok = sc_lc_acceptor_response_tcp(InitState) end). @@ -6533,18 +13721,39 @@ sc_lc_acceptor_response_tcp6(suite) -> sc_lc_acceptor_response_tcp6(doc) -> []; sc_lc_acceptor_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_acceptor_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), - InitState = #{domain => inet, - type => stream, + InitState = #{domain => inet6, protocol => tcp}, ok = sc_lc_acceptor_response_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the accept function. +%% We test what happens with a non-controlling_process also, since we +%% git the setup anyway. +%% Socket is Unix Domain (stream) socket. + +sc_lc_acceptor_response_tcpL(suite) -> + []; +sc_lc_acceptor_response_tcpL(doc) -> + []; +sc_lc_acceptor_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_lc_acceptor_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + protocol => default}, + ok = sc_lc_acceptor_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_lc_acceptor_response_tcp(InitState) -> PrimAcceptorSeq = @@ -6564,15 +13773,13 @@ sc_lc_acceptor_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", cmd => fun(#{domain := Domain, - type := Type, protocol := Proto} = State) -> - case socket:open(Domain, Type, Proto) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -6609,8 +13816,8 @@ sc_lc_acceptor_response_tcp(InitState) -> end end}, #{desc => "await connection", - cmd => fun(#{sock := Sock, timeout := Timeout} = _State) -> - case socket:accept(Sock, Timeout) of + cmd => fun(#{sock := LSock, timeout := Timeout} = _State) -> + case socket:accept(LSock, Timeout) of {error, timeout} -> ok; {ok, Sock} -> @@ -6631,7 +13838,25 @@ sc_lc_acceptor_response_tcp(InitState) -> ok = ?SEV_AWAIT_CONTINUE(Tester, tester, close) end}, #{desc => "close socket", - cmd => fun(#{sock := Sock} = State) -> + cmd => fun(#{domain := local, + sock := Sock, + lsa := #{path := Path}} = State) -> + case socket:close(Sock) of + ok -> + State1 = + unlink_path(Path, + fun() -> + maps:remove(lsa, State) + end, + fun() -> + State + end), + {ok, maps:remove(sock, State1)}; + {error, _} = ERROR -> + unlink_path(Path), + ERROR + end; + (#{sock := Sock} = State) -> case socket:close(Sock) of ok -> {ok, maps:remove(sock, State)}; @@ -6946,12 +14171,11 @@ sc_rc_recv_response_tcp4(suite) -> sc_rc_recv_response_tcp4(doc) -> []; sc_rc_recv_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recv_response_tcp4, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -6968,13 +14192,12 @@ sc_rc_recv_response_tcp6(suite) -> sc_rc_recv_response_tcp6(doc) -> []; sc_rc_recv_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recv_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -6982,6 +14205,28 @@ sc_rc_recv_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recv function. +%% Socket is Unix Domain (stream) socket. + +sc_rc_recv_response_tcpL(suite) -> + []; +sc_rc_recv_response_tcpL(doc) -> + []; +sc_rc_recv_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_rc_recv_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_rc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_rc_receive_response_tcp(InitState) -> %% Each connection are handled by handler processes. @@ -7004,13 +14249,12 @@ sc_rc_receive_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, protocol := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -7018,7 +14262,17 @@ sc_rc_receive_response_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, + local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> {ok, State#{lport => Port}}; @@ -7031,7 +14285,15 @@ sc_rc_receive_response_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, + local_sa := LSA}) -> + %% Actually we only need to send the path, + %% but to keep it simple, we send the "same" + %% as for non-local. + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -7213,7 +14475,25 @@ sc_rc_receive_response_tcp(InitState) -> {ok, State2} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := LSock} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := #{path := Path}} = State) -> + case socket:close(LSock) of + ok -> + State1 = + unlink_path(Path, + fun() -> + maps:remove(lsa, State) + end, + fun() -> + State + end), + {ok, maps:remove(lsock, State1)}; + {error, _} = ERROR -> + unlink_path(Path), + ERROR + end; + (#{lsock := LSock} = State) -> case socket:close(LSock) of ok -> {ok, maps:remove(lsock, State)}; @@ -7270,8 +14550,10 @@ sc_rc_receive_response_tcp(InitState) -> ok end}, #{desc => "order remote client to start", - cmd => fun(#{rclient := Client, server_sa := ServerSA}) -> - ?SEV_ANNOUNCE_START(Client, ServerSA), + cmd => fun(#{rclient := Client, + server_sa := ServerSA, + protocol := Proto}) -> + ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}), ok end}, #{desc => "await remote client ready", @@ -7672,16 +14954,16 @@ sc_rc_tcp_client_start(Node) -> sc_rc_tcp_client(Parent) -> sc_rc_tcp_client_init(Parent), - ServerSA = sc_rc_tcp_client_await_start(Parent), + {ServerSA, Proto} = sc_rc_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = sc_rc_tcp_client_create(Domain), - sc_rc_tcp_client_bind(Sock, Domain), + Sock = sc_rc_tcp_client_create(Domain, Proto), + Path = sc_rc_tcp_client_bind(Sock, Domain), sc_rc_tcp_client_announce_ready(Parent, init), sc_rc_tcp_client_await_continue(Parent, connect), sc_rc_tcp_client_connect(Sock, ServerSA), sc_rc_tcp_client_announce_ready(Parent, connect), sc_rc_tcp_client_await_continue(Parent, close), - sc_rc_tcp_client_close(Sock), + sc_rc_tcp_client_close(Sock, Path), sc_rc_tcp_client_announce_ready(Parent, close), Reason = sc_rc_tcp_client_await_terminate(Parent), ?SEV_IPRINT("terminate"), @@ -7697,9 +14979,9 @@ sc_rc_tcp_client_await_start(Parent) -> i("sc_rc_tcp_client_await_start -> entry"), ?SEV_AWAIT_START(Parent). -sc_rc_tcp_client_create(Domain) -> +sc_rc_tcp_client_create(Domain, Proto) -> i("sc_rc_tcp_client_create -> entry"), - case socket:open(Domain, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> case socket:getopt(Sock, otp, fd) of {ok, FD} -> @@ -7714,12 +14996,17 @@ sc_rc_tcp_client_create(Domain) -> sc_rc_tcp_client_bind(Sock, Domain) -> i("sc_rc_tcp_client_bind -> entry"), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; {error, Reason} -> exit({bind, Reason}) end. @@ -7741,13 +15028,17 @@ sc_rc_tcp_client_connect(Sock, ServerSA) -> exit({connect, Reason}) end. -sc_rc_tcp_client_close(Sock) -> +sc_rc_tcp_client_close(Sock, Path) -> i("sc_rc_tcp_client_close -> entry"), case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. sc_rc_tcp_client_await_terminate(Parent) -> @@ -7827,12 +15118,11 @@ sc_rc_recvmsg_response_tcp4(suite) -> sc_rc_recvmsg_response_tcp4(doc) -> []; sc_rc_recvmsg_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recvmsg_response_tcp4, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -7849,13 +15139,12 @@ sc_rc_recvmsg_response_tcp6(suite) -> sc_rc_recvmsg_response_tcp6(doc) -> []; sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recvmsg_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -7863,6 +15152,28 @@ sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recvmsg function. +%% Socket is Unix Domain (stream) socket. + +sc_rc_recvmsg_response_tcpL(suite) -> + []; +sc_rc_recvmsg_response_tcpL(doc) -> + []; +sc_rc_recvmsg_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_rc_recvmsg_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_rc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test what happens when a socket is %% remotely closed while the process is calling the recv function. %% The remote client sends data, then shutdown(write) and then the @@ -7872,6 +15183,7 @@ sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> %% To minimize the chance of "weirdness", we should really have test cases %% where the two sides of the connection is on different machines. But for %% now, we will make do with different VMs on the same host. +%% This would of course not work for Unix Domain sockets. %% sc_rs_recv_send_shutdown_receive_tcp4(suite) -> @@ -7879,9 +15191,9 @@ sc_rs_recv_send_shutdown_receive_tcp4(suite) -> sc_rs_recv_send_shutdown_receive_tcp4(doc) -> []; sc_rs_recv_send_shutdown_receive_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rs_recv_send_shutdown_receive_tcp4, fun() -> - ?TT(?SECS(30)), MsgData = ?DATA, Recv = fun(Sock) -> socket:recv(Sock) @@ -7890,6 +15202,7 @@ sc_rs_recv_send_shutdown_receive_tcp4(_Config) when is_list(_Config) -> socket:send(Sock, Data) end, InitState = #{domain => inet, + proto => tcp, recv => Recv, send => Send, data => MsgData}, @@ -7921,6 +15234,39 @@ sc_rs_recv_send_shutdown_receive_tcp6(_Config) when is_list(_Config) -> socket:send(Sock, Data) end, InitState = #{domain => inet6, + proto => tcp, + recv => Recv, + send => Send, + data => MsgData}, + ok = sc_rs_send_shutdown_receive_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recv function. +%% The remote client sends data, then shutdown(write) and then the +%% reader attempts a recv. +%% Socket is Unix Domain (stream) socket. + +sc_rs_recv_send_shutdown_receive_tcpL(suite) -> + []; +sc_rs_recv_send_shutdown_receive_tcpL(doc) -> + []; +sc_rs_recv_send_shutdown_receive_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_rs_recv_send_shutdown_receive_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + MsgData = ?DATA, + Recv = fun(Sock) -> + socket:recv(Sock) + end, + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + InitState = #{domain => local, + proto => default, recv => Recv, send => Send, data => MsgData}, @@ -7952,13 +15298,12 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> i("get local address for ~p", [Domain]), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -7966,9 +15311,19 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + local_sa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -7979,7 +15334,11 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -8090,7 +15449,18 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> {ok, State2} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := LSock} = State) -> + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := LSock} = State) -> case socket:close(LSock) of ok -> {ok, maps:remove(lsock, State)}; @@ -8148,8 +15518,10 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> ok end}, #{desc => "order remote client to start", - cmd => fun(#{rclient := Client, server_sa := ServerSA}) -> - ?SEV_ANNOUNCE_START(Client, ServerSA), + cmd => fun(#{rclient := Client, + proto := Proto, + server_sa := ServerSA}) -> + ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}), ok end}, #{desc => "await remote client ready", @@ -8479,12 +15851,14 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> i("start server evaluator"), ServerInitState = #{domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), recv => maps:get(recv, InitState)}, Server = ?SEV_START("server", ServerSeq, ServerInitState), i("start client evaluator"), ClientInitState = #{host => local_host(), domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), send => maps:get(send, InitState)}, Client = ?SEV_START("client", ClientSeq, ClientInitState), @@ -8506,10 +15880,10 @@ sc_rs_tcp_client_start(Node, Send) -> sc_rs_tcp_client(Parent, Send) -> sc_rs_tcp_client_init(Parent), - ServerSA = sc_rs_tcp_client_await_start(Parent), + {ServerSA, Proto} = sc_rs_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = sc_rs_tcp_client_create(Domain), - sc_rs_tcp_client_bind(Sock, Domain), + Sock = sc_rs_tcp_client_create(Domain, Proto), + Path = sc_rs_tcp_client_bind(Sock, Domain), sc_rs_tcp_client_announce_ready(Parent, init), sc_rs_tcp_client_await_continue(Parent, connect), sc_rs_tcp_client_connect(Sock, ServerSA), @@ -8521,7 +15895,7 @@ sc_rs_tcp_client(Parent, Send) -> sc_rs_tcp_client_shutdown(Sock), sc_rs_tcp_client_announce_ready(Parent, shutdown), sc_rs_tcp_client_await_continue(Parent, close), - sc_rs_tcp_client_close(Sock), + sc_rs_tcp_client_close(Sock, Path), sc_rs_tcp_client_announce_ready(Parent, close), Reason = sc_rs_tcp_client_await_terminate(Parent), ?SEV_IPRINT("terminate"), @@ -8537,9 +15911,9 @@ sc_rs_tcp_client_await_start(Parent) -> i("sc_rs_tcp_client_await_start -> entry"), ?SEV_AWAIT_START(Parent). -sc_rs_tcp_client_create(Domain) -> +sc_rs_tcp_client_create(Domain, Proto) -> i("sc_rs_tcp_client_create -> entry"), - case socket:open(Domain, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> Sock; {error, Reason} -> @@ -8548,12 +15922,17 @@ sc_rs_tcp_client_create(Domain) -> sc_rs_tcp_client_bind(Sock, Domain) -> i("sc_rs_tcp_client_bind -> entry"), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; {error, Reason} -> exit({bind, Reason}) end. @@ -8600,13 +15979,17 @@ sc_rs_tcp_client_shutdown(Sock) -> exit({shutdown, Reason}) end. -sc_rs_tcp_client_close(Sock) -> +sc_rs_tcp_client_close(Sock, Path) -> i("sc_rs_tcp_client_close -> entry"), case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. sc_rs_tcp_client_await_terminate(Parent) -> @@ -8711,12 +16094,11 @@ sc_rs_recvmsg_send_shutdown_receive_tcp4(_Config) when is_list(_Config) -> MsgHdr = #{iov => [Data]}, socket:sendmsg(Sock, MsgHdr) end, - InitState = #{domain => inet, - type => stream, - protocol => tcp, - recv => Recv, - send => Send, - data => MsgData}, + InitState = #{domain => inet, + proto => tcp, + recv => Recv, + send => Send, + data => MsgData}, ok = sc_rs_send_shutdown_receive_tcp(InitState) end). @@ -8748,20 +16130,2105 @@ sc_rs_recvmsg_send_shutdown_receive_tcp6(_Config) when is_list(_Config) -> end end, Send = fun(Sock, Data) when is_binary(Data) -> - MsgHdr = #{iov => [Data]}, - socket:sendmsg(Sock, MsgHdr) + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) end, - InitState = #{domain => inet6, - type => stream, - protocol => tcp, - recv => Recv, - send => Send, - data => MsgData}, + InitState = #{domain => inet6, + proto => tcp, + recv => Recv, + send => Send, + data => MsgData}, + ok = sc_rs_send_shutdown_receive_tcp(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. +%% The remote client sends data, then shutdown(write) and then the +%% reader attempts a recv. +%% Socket is UNix Domain (stream) socket. + +sc_rs_recvmsg_send_shutdown_receive_tcpL(suite) -> + []; +sc_rs_recvmsg_send_shutdown_receive_tcpL(doc) -> + []; +sc_rs_recvmsg_send_shutdown_receive_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_rs_recvmsg_send_shutdown_receive_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + {ok, CWD} = file:get_cwd(), + ?SEV_IPRINT("CWD: ~s", [CWD]), + MsgData = ?DATA, + Recv = fun(Sock) -> + case socket:recvmsg(Sock) of + %% On some platforms, the address + %% is *not* provided (e.g. FreeBSD) + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + %% On some platforms, the address + %% *is* provided (e.g. linux) + {ok, #{addr := #{family := local}, + iov := [Data]}} -> + {ok, Data}; + {error, _} = ERROR -> + ERROR + end + end, + Send = fun(Sock, Data) when is_binary(Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + InitState = #{domain => local, + proto => default, + recv => Recv, + send => Send, + data => MsgData}, ok = sc_rs_send_shutdown_receive_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use TCP on IPv4. + +traffic_send_and_recv_counters_tcp4(suite) -> + []; +traffic_send_and_recv_counters_tcp4(doc) -> + []; +traffic_send_and_recv_counters_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_send_and_recv_counters_tcp4, + fun() -> + InitState = #{domain => inet, + proto => tcp, + recv => fun(S) -> socket:recv(S) end, + send => fun(S, D) -> socket:send(S, D) end}, + ok = traffic_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use TCP on IPv6. + +traffic_send_and_recv_counters_tcp6(suite) -> + []; +traffic_send_and_recv_counters_tcp6(doc) -> + []; +traffic_send_and_recv_counters_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_send_and_recv_counters_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + InitState = #{domain => inet6, + proto => tcp, + recv => fun(S) -> socket:recv(S) end, + send => fun(S, D) -> socket:send(S, D) end}, + ok = traffic_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use default (TCP) on local. + +traffic_send_and_recv_counters_tcpL(suite) -> + []; +traffic_send_and_recv_counters_tcpL(doc) -> + []; +traffic_send_and_recv_counters_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_send_and_recv_counters_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, + recv => fun(S) -> socket:recv(S) end, + send => fun(S, D) -> socket:send(S, D) end}, + ok = traffic_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use TCP on IPv4. + +traffic_sendmsg_and_recvmsg_counters_tcp4(suite) -> + []; +traffic_sendmsg_and_recvmsg_counters_tcp4(doc) -> + []; +traffic_sendmsg_and_recvmsg_counters_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_sendmsg_and_recvmsg_counters_tcp4, + fun() -> + InitState = #{domain => inet, + proto => tcp, + recv => fun(S) -> + case socket:recvmsg(S) of + {ok, #{addr := _Source, + iov := [Data]}} -> + {ok, Data}; + {error, _} = ERROR -> + ERROR + end + end, + send => fun(S, Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(S, MsgHdr) + end}, + ok = traffic_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use TCP on IPv6. + +traffic_sendmsg_and_recvmsg_counters_tcp6(suite) -> + []; +traffic_sendmsg_and_recvmsg_counters_tcp6(doc) -> + []; +traffic_sendmsg_and_recvmsg_counters_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_sendmsg_and_recvmsg_counters_tcp6, + fun() -> has_support_ipv6() end, + fun() -> + InitState = #{domain => inet6, + proto => tcp, + recv => fun(S) -> + case socket:recvmsg(S) of + {ok, #{addr := _Source, + iov := [Data]}} -> + {ok, Data}; + {error, _} = ERROR -> + ERROR + end + end, + send => fun(S, Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(S, MsgHdr) + end}, + ok = traffic_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use default (TCP) on local. + +traffic_sendmsg_and_recvmsg_counters_tcpL(suite) -> + []; +traffic_sendmsg_and_recvmsg_counters_tcpL(doc) -> + []; +traffic_sendmsg_and_recvmsg_counters_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_sendmsg_and_recvmsg_counters_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, + recv => fun(S) -> + case socket:recvmsg(S) of + {ok, #{addr := _Source, + iov := [Data]}} -> + {ok, Data}; + {error, _} = ERROR -> + ERROR + end + end, + send => fun(S, Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(S, MsgHdr) + end}, + ok = traffic_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +traffic_send_and_recv_tcp(InitState) -> + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, eafnosupport = Reason} -> + {skip, Reason}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{domain := local, + lsock := LSock, + local_sa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, + local_sa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{domain := local, + tester := Tester, + local_sa := #{path := Path}}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, + lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "accept", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock) of + {ok, Sock} -> + {ok, State#{csock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "initial counter validation (=zero)", + cmd => fun(#{csock := Sock} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("Validate initial counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation(Counters) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept), + ok + end}, + + #{desc => "await continue (recv_and_validate 1)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate) + end}, + #{desc => "recv (1)", + cmd => fun(#{csock := Sock, + recv := Recv} = State) -> + case Recv(Sock) of + {ok, Data} -> + ?SEV_IPRINT("recv ~p bytes", [size(Data)]), + {ok, State#{read_pkg => 1, + read_byte => size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (recv 1)", + cmd => fun(#{csock := Sock, + read_pkg := Pkg, + read_byte := Byte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, Pkg}, + {read_byte, Byte}, + {read_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (recv_and_validate 1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_and_validate), + ok + end}, + + #{desc => "await continue (send_and_validate 1)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate) + end}, + #{desc => "send (1)", + cmd => fun(#{csock := Sock, + send := Send} = State) -> + Data = ?DATA, + case Send(Sock, Data) of + ok -> + ?SEV_IPRINT("sent ~p bytes", [size(Data)]), + {ok, State#{write_pkg => 1, + write_byte => size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (send 1)", + cmd => fun(#{csock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (send_and_validate 1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_and_validate), + ok + end}, + + #{desc => "await continue (recv_and_validate 2)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate) + end}, + #{desc => "recv (2)", + cmd => fun(#{csock := Sock, + recv := Recv, + read_pkg := Pkg, + read_byte := Byte} = State) -> + case Recv(Sock) of + {ok, Data} -> + ?SEV_IPRINT("recv ~p bytes", [size(Data)]), + {ok, State#{read_pkg => Pkg + 1, + read_byte => Byte + size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (recv 2)", + cmd => fun(#{csock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (recv_and_validate 2)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_and_validate), + ok + end}, + + #{desc => "await continue (send_and_validate 2)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate) + end}, + #{desc => "send (2)", + cmd => fun(#{csock := Sock, + send := Send, + write_pkg := Pkg, + write_byte := Byte} = State) -> + Data = ?DATA, + case Send(Sock, Data) of + ok -> + ?SEV_IPRINT("sent ~p bytes", [size(Data)]), + {ok, State#{write_pkg => Pkg + 1, + write_byte => Byte + size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (send 2)", + cmd => fun(#{csock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (send_and_validate 2)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_and_validate), + ok + end}, + + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close connection socket (just in case)", + cmd => fun(#{csock := Sock} = State) -> + (catch socket:close(Sock)), + {ok, maps:remove(csock, State)} + end}, + #{desc => "close listen socket", + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := Sock} = State) -> + (catch socket:close(Sock)), + {ok, maps:remove(lsock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + ClientSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(#{domain := local} = State) -> + {Tester, Path} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_path => Path}}; + (State) -> + {Tester, Port} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_port => Port}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which server (local) address", + cmd => fun(#{domain := local = Domain, + server_path := Path} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = #{family => Domain, path => Path}, + {ok, State#{local_sa => LSA, server_sa => SSA}}; + (#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, + {ok, State#{local_sa => LSA, server_sa => SSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, eafnosupport = Reason} -> + {skip, Reason}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (connect)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, connect), + ok + end}, + #{desc => "connect to server", + cmd => fun(#{sock := Sock, server_sa := SSA}) -> + socket:connect(Sock, SSA) + end}, + #{desc => "announce ready (connect)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect), + ok + end}, + + #{desc => "await continue (send_and_validate 1)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate) + end}, + #{desc => "send (1)", + cmd => fun(#{sock := Sock, + send := Send} = State) -> + Data = ?DATA, + case Send(Sock, Data) of + ok -> + ?SEV_IPRINT("sent ~p bytes", [size(Data)]), + {ok, State#{write_pkg => 1, + write_byte => size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (send 1)", + cmd => fun(#{sock := Sock, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{write_pkg, SPkg}, + {write_byte, SByte}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (send_and_validate 1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_and_validate), + ok + end}, + + #{desc => "await continue (recv_and_validate 1)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate) + end}, + #{desc => "recv (1)", + cmd => fun(#{sock := Sock, + recv := Recv} = State) -> + case Recv(Sock) of + {ok, Data} -> + ?SEV_IPRINT("recv ~p bytes", [size(Data)]), + {ok, State#{read_pkg => 1, + read_byte => size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (recv 1)", + cmd => fun(#{sock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (recv_and_validate 1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_and_validate), + ok + end}, + + #{desc => "await continue (send_and_validate 2)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate) + end}, + #{desc => "send (2)", + cmd => fun(#{sock := Sock, + send := Send, + write_pkg := SPkg, + write_byte := SByte} = State) -> + Data = ?DATA, + case Send(Sock, Data) of + ok -> + ?SEV_IPRINT("sent ~p bytes", [size(Data)]), + {ok, State#{write_pkg => SPkg + 1, + write_byte => SByte + size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (send 2)", + cmd => fun(#{sock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (send_and_validate 2)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_and_validate), + ok + end}, + + #{desc => "await continue (recv_and_validate 2)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate) + end}, + #{desc => "recv (2)", + cmd => fun(#{sock := Sock, + recv := Recv, + read_pkg := RPkg, + read_byte := RByte} = State) -> + case Recv(Sock) of + {ok, Data} -> + ?SEV_IPRINT("recv ~p bytes", [size(Data)]), + {ok, State#{read_pkg => RPkg + 1, + read_byte => RByte + size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (recv 2)", + cmd => fun(#{sock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (recv_and_validate 2)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_and_validate), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close connection socket", + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{domain := local, + server := Pid} = State) -> + {ok, Path} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{path => Path}}; + (#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{port => Port}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{domain := local, + client := Pid, + path := Path} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Path), + ok; + (#{client := Pid, + port := Port} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Port), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + %% *** The actual test *** + + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order client to continue (with connect)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, connect), + ok + end}, + #{desc => "await client ready (connect)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, connect) + end}, + #{desc => "await server ready (accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, accept) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order server to continue (recv_and_validate 1)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, recv_and_validate), + ok + end}, + #{desc => "order client to continue (send_and_validate 1)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, send_and_validate), + ok + end}, + #{desc => "await client ready (send_and_validate 1)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, send_and_validate) + end}, + #{desc => "await server ready (recv_and_validate 1)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, recv_and_validate) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order client to continue (recv_and_validate 1)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, recv_and_validate), + ok + end}, + #{desc => "order server to continue (send_and_validate 1)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, send_and_validate), + ok + end}, + #{desc => "await server ready (send_and_validate 1)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, send_and_validate) + end}, + #{desc => "await client ready (recv_and_validate 1)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, recv_and_validate) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order server to continue (recv_and_validate 2)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, recv_and_validate), + ok + end}, + #{desc => "order client to continue (send_and_validate 2)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, send_and_validate), + ok + end}, + #{desc => "await client ready (send_and_validate 2)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, send_and_validate) + end}, + #{desc => "await server ready (recv_and_validate 2)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, recv_and_validate) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order client to continue (recv_and_validate 2)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, recv_and_validate), + ok + end}, + #{desc => "order server to continue (send_and_validate 2)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, send_and_validate), + ok + end}, + #{desc => "await server ready (send_and_validate 2)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, send_and_validate) + end}, + #{desc => "await client ready (recv_and_validate 2)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, recv_and_validate) + end}, + + %% *** Termination *** + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Client), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + ?SEV_AWAIT_TERMINATION(Client), + State1 = maps:remove(client, State), + {ok, State1} + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + ServerInitState = InitState#{host => local_host()}, + Server = ?SEV_START("server", ServerSeq, ServerInitState), + + i("start client evaluator(s)"), + ClientInitState = InitState#{host => local_host()}, + Client = ?SEV_START("client", ClientSeq, ClientInitState), + + i("start 'tester' evaluator"), + TesterInitState = #{server => Server#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). + + + +traffic_sar_counters_validation(Counters) -> + traffic_sar_counters_validation(Counters, []). + +traffic_sar_counters_validation(Counters, []) -> + (catch lists:foreach( + fun({_Cnt, 0}) -> ok; + ({Cnt, Val}) -> throw({error, {invalid_counter, Cnt, Val}}) + end, + Counters)); +traffic_sar_counters_validation(Counters, [{Cnt, Val}|ValidateCounters]) -> + case lists:keysearch(Cnt, 1, Counters) of + {value, {Cnt, Val}} -> + Counters2 = lists:keydelete(Cnt, 1, Counters), + traffic_sar_counters_validation(Counters2, ValidateCounters); + {value, {Cnt, _Val}} when (Val =:= any) -> + Counters2 = lists:keydelete(Cnt, 1, Counters), + traffic_sar_counters_validation(Counters2, ValidateCounters); + {value, {Cnt, InvVal}} -> + {error, {invalid_counter, Cnt, InvVal, Val}}; + false -> + {error, {unknown_counter, Cnt, Counters}} + end. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use UDP on IPv4. + +traffic_sendto_and_recvfrom_counters_udp4(suite) -> + []; +traffic_sendto_and_recvfrom_counters_udp4(doc) -> + []; +traffic_sendto_and_recvfrom_counters_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_sendto_and_recvfrom_counters_udp4, + fun() -> + InitState = #{domain => inet, + proto => udp, + recv => fun(S) -> + socket:recvfrom(S) + end, + send => fun(S, Data, Dest) -> + socket:sendto(S, Data, Dest) + end}, + ok = traffic_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use UDP on IPv6. + +traffic_sendto_and_recvfrom_counters_udp6(suite) -> + []; +traffic_sendto_and_recvfrom_counters_udp6(doc) -> + []; +traffic_sendto_and_recvfrom_counters_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_sendto_and_recvfrom_counters_udp6, + fun() -> has_support_ipv6() end, + fun() -> + InitState = #{domain => inet6, + proto => udp, + recv => fun(S) -> + socket:recvfrom(S) + end, + send => fun(S, Data, Dest) -> + socket:sendto(S, Data, Dest) + end}, + ok = traffic_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use default (UDP) on local. + +traffic_sendto_and_recvfrom_counters_udpL(suite) -> + []; +traffic_sendto_and_recvfrom_counters_udpL(doc) -> + []; +traffic_sendto_and_recvfrom_counters_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_sendto_and_recvfrom_counters_udp4, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, + recv => fun(S) -> + socket:recvfrom(S) + end, + send => fun(S, Data, Dest) -> + socket:sendto(S, Data, Dest) + end}, + ok = traffic_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use UDP on IPv4. + +traffic_sendmsg_and_recvmsg_counters_udp4(suite) -> + []; +traffic_sendmsg_and_recvmsg_counters_udp4(doc) -> + []; +traffic_sendmsg_and_recvmsg_counters_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_sendmsg_and_recvmsg_counters_udp4, + fun() -> + InitState = #{domain => inet, + proto => udp, + recv => fun(S) -> + case socket:recvmsg(S) of + {ok, #{addr := Source, + iov := [Data]}} -> + {ok, {Source, Data}}; + {error, _} = ERROR -> + ERROR + end + end, + send => fun(S, Data, Dest) -> + MsgHdr = #{addr => Dest, + iov => [Data]}, + socket:sendmsg(S, MsgHdr) + end}, + ok = traffic_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use UDP on IPv6. + +traffic_sendmsg_and_recvmsg_counters_udp6(suite) -> + []; +traffic_sendmsg_and_recvmsg_counters_udp6(doc) -> + []; +traffic_sendmsg_and_recvmsg_counters_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_sendmsg_and_recvmsg_counters_udp6, + fun() -> has_support_ipv6() end, + fun() -> + InitState = #{domain => inet6, + proto => udp, + recv => fun(S) -> + case socket:recvmsg(S) of + {ok, #{addr := Source, + iov := [Data]}} -> + {ok, {Source, Data}}; + {error, _} = ERROR -> + ERROR + end + end, + send => fun(S, Data, Dest) -> + MsgHdr = #{addr => Dest, + iov => [Data]}, + socket:sendmsg(S, MsgHdr) + end}, + ok = traffic_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to (simply) test that the counters +%% for both read and write. +%% So that its easy to extend, we use fun's for read and write. +%% We use default (UDP) on local. + +traffic_sendmsg_and_recvmsg_counters_udpL(suite) -> + []; +traffic_sendmsg_and_recvmsg_counters_udpL(doc) -> + []; +traffic_sendmsg_and_recvmsg_counters_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + tc_try(traffic_sendmsg_and_recvmsg_counters_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, + recv => fun(S) -> + case socket:recvmsg(S) of + {ok, #{addr := Source, + iov := [Data]}} -> + {ok, {Source, Data}}; + {error, _} = ERROR -> + ERROR + end + end, + send => fun(S, Data, Dest) -> + MsgHdr = #{addr => Dest, + iov => [Data]}, + socket:sendmsg(S, MsgHdr) + end}, + ok = traffic_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +traffic_send_and_recv_udp(InitState) -> + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, dgram, Proto) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{domain := local, + sock := Sock, + local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{sock := LSock, + local_sa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "initial counter validation (=zero)", + cmd => fun(#{sock := Sock} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("Validate initial counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation(Counters) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{domain := local, + tester := Tester, + local_sa := #{path := Path}}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, + lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (recv_and_validate 1)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate) + end}, + #{desc => "recv (1)", + cmd => fun(#{sock := Sock, + recv := Recv} = State) -> + case Recv(Sock) of + {ok, {ClientSA, Data}} -> + ?SEV_IPRINT("recv ~p bytes", [size(Data)]), + {ok, State#{client_sa => ClientSA, + read_pkg => 1, + read_byte => size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (recv 1)", + cmd => fun(#{sock := Sock, + read_pkg := Pkg, + read_byte := Byte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, Pkg}, + {read_byte, Byte}, + {read_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (recv_and_validate 1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_and_validate), + ok + end}, + + #{desc => "await continue (send_and_validate 1)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate) + end}, + #{desc => "send (1)", + cmd => fun(#{sock := Sock, + send := Send, + client_sa := ClientSA} = State) -> + Data = ?DATA, + case Send(Sock, Data, ClientSA) of + ok -> + ?SEV_IPRINT("sent ~p bytes", [size(Data)]), + {ok, State#{write_pkg => 1, + write_byte => size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (send 1)", + cmd => fun(#{sock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (send_and_validate 1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_and_validate), + ok + end}, + + #{desc => "await continue (recv_and_validate 2)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate) + end}, + #{desc => "recv (2)", + cmd => fun(#{sock := Sock, + recv := Recv, + read_pkg := Pkg, + read_byte := Byte} = State) -> + case Recv(Sock) of + {ok, {Source, Data}} -> + ?SEV_IPRINT("recv ~p bytes", [size(Data)]), + {ok, State#{client_sa => Source, + read_pkg => Pkg + 1, + read_byte => Byte + size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (recv 2)", + cmd => fun(#{sock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (recv_and_validate 2)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_and_validate), + ok + end}, + + #{desc => "await continue (send_and_validate 2)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate) + end}, + #{desc => "send (2)", + cmd => fun(#{sock := Sock, + client_sa := ClientSA, + send := Send, + write_pkg := Pkg, + write_byte := Byte} = State) -> + Data = ?DATA, + case Send(Sock, Data, ClientSA) of + ok -> + ?SEV_IPRINT("sent ~p bytes", [size(Data)]), + {ok, State#{write_pkg => Pkg + 1, + write_byte => Byte + size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (send 2)", + cmd => fun(#{sock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (send_and_validate 2)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_and_validate), + ok + end}, + + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket (just in case)", + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{sock := Sock} = State) -> + (catch socket:close(Sock)), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + ClientSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start (from tester)", + cmd => fun(#{domain := local} = State) -> + {Tester, Path} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_path => Path}}; + (State) -> + {Tester, Port} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_port => Port}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which server (local) address", + cmd => fun(#{domain := local = Domain, + server_path := Path} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = #{family => Domain, path => Path}, + {ok, State#{local_sa => LSA, server_sa => SSA}}; + (#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, + {ok, State#{local_sa => LSA, server_sa => SSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + case socket:open(Domain, dgram, Proto) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "initial counter validation (=zero)", + cmd => fun(#{sock := Sock} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("Validate initial counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation(Counters) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (send_and_validate 1)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate) + end}, + #{desc => "send (1)", + cmd => fun(#{sock := Sock, + send := Send, + server_sa := ServerSA} = State) -> + Data = ?DATA, + case Send(Sock, Data, ServerSA) of + ok -> + ?SEV_IPRINT("sent ~p bytes", [size(Data)]), + {ok, State#{write_pkg => 1, + write_byte => size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (send 1)", + cmd => fun(#{sock := Sock, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{write_pkg, SPkg}, + {write_byte, SByte}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (send_and_validate 1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_and_validate), + ok + end}, + + #{desc => "await continue (recv_and_validate 1)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate) + end}, + #{desc => "recv (1)", + cmd => fun(#{sock := Sock, + recv := Recv, + server_sa := #{family := local} = ServerSA} = State) -> + case Recv(Sock) of + {ok, {ServerSA, Data}} -> + ?SEV_IPRINT("recv ~p bytes", [size(Data)]), + {ok, State#{read_pkg => 1, + read_byte => size(Data)}}; + {error, _} = ERROR -> + ERROR + end; + (#{sock := Sock, + recv := Recv, + server_sa := #{addr := Addr, port := Port}} = State) -> + case Recv(Sock) of + {ok, {#{addr := Addr, port := Port}, Data}} -> + ?SEV_IPRINT("recv ~p bytes", [size(Data)]), + {ok, State#{read_pkg => 1, + read_byte => size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (recv 1)", + cmd => fun(#{sock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (recv_and_validate 1)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_and_validate), + ok + end}, + + #{desc => "await continue (send_and_validate 2)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate) + end}, + #{desc => "send (2)", + cmd => fun(#{sock := Sock, + send := Send, + server_sa := ServerSA, + write_pkg := SPkg, + write_byte := SByte} = State) -> + Data = ?DATA, + case Send(Sock, Data, ServerSA) of + ok -> + ?SEV_IPRINT("sent ~p bytes", [size(Data)]), + {ok, State#{write_pkg => SPkg + 1, + write_byte => SByte + size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (send 2)", + cmd => fun(#{sock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (send_and_validate 2)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_and_validate), + ok + end}, + + #{desc => "await continue (recv_and_validate 2)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate) + end}, + #{desc => "recv (2)", + cmd => fun(#{sock := Sock, + server_sa := #{family := local} = ServerSA, + recv := Recv, + read_pkg := RPkg, + read_byte := RByte} = State) -> + case Recv(Sock) of + {ok, {ServerSA, Data}} -> + ?SEV_IPRINT("recv ~p bytes", [size(Data)]), + {ok, State#{read_pkg => RPkg + 1, + read_byte => RByte + size(Data)}}; + {error, _} = ERROR -> + ERROR + end; + (#{sock := Sock, + server_sa := #{addr := Addr, port := Port}, + recv := Recv, + read_pkg := RPkg, + read_byte := RByte} = State) -> + case Recv(Sock) of + {ok, {#{addr := Addr, port := Port}, Data}} -> + ?SEV_IPRINT("recv ~p bytes", [size(Data)]), + {ok, State#{read_pkg => RPkg + 1, + read_byte => RByte + size(Data)}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "validate (recv 2)", + cmd => fun(#{sock := Sock, + read_pkg := RPkg, + read_byte := RByte, + write_pkg := SPkg, + write_byte := SByte} = _State) -> + try socket:info(Sock) of + #{counters := Counters} -> + ?SEV_IPRINT("validate counters: " + "~n ~p", [Counters]), + traffic_sar_counters_validation( + Counters, + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}]) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, + #{desc => "announce ready (recv_and_validate 2)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_and_validate), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close connection socket", + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{domain := local, + server := Pid} = State) -> + {ok, Path} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{path => Path}}; + (#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{port => Port}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{domain := local, + client := Pid, + path := Path} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Path), + ok; + (#{client := Pid, + port := Port} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Port), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + %% *** The actual test *** + + #{desc => "order server to continue (recv_and_validate 1)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, recv_and_validate), + ok + end}, + #{desc => "order client to continue (send_and_validate 1)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, send_and_validate), + ok + end}, + #{desc => "await client ready (send_and_validate 1)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, send_and_validate) + end}, + #{desc => "await server ready (recv_and_validate 1)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, recv_and_validate) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order client to continue (recv_and_validate 1)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, recv_and_validate), + ok + end}, + #{desc => "order server to continue (send_and_validate 1)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, send_and_validate), + ok + end}, + #{desc => "await server ready (send_and_validate 1)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, send_and_validate) + end}, + #{desc => "await client ready (recv_and_validate 1)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, recv_and_validate) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order server to continue (recv_and_validate 2)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, recv_and_validate), + ok + end}, + #{desc => "order client to continue (send_and_validate 2)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, send_and_validate), + ok + end}, + #{desc => "await client ready (send_and_validate 2)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, send_and_validate) + end}, + #{desc => "await server ready (recv_and_validate 2)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, recv_and_validate) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order client to continue (recv_and_validate 2)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, recv_and_validate), + ok + end}, + #{desc => "order server to continue (send_and_validate 2)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, send_and_validate), + ok + end}, + #{desc => "await server ready (send_and_validate 2)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, send_and_validate) + end}, + #{desc => "await client ready (recv_and_validate 2)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, recv_and_validate) + end}, + + %% *** Termination *** + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Client), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + ?SEV_AWAIT_TERMINATION(Client), + State1 = maps:remove(client, State), + {ok, State1} + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + ServerInitState = InitState#{host => local_host()}, + Server = ?SEV_START("server", ServerSeq, ServerInitState), + + i("start client evaluator(s)"), + ClientInitState = InitState#{host => local_host()}, + Client = ?SEV_START("client", ClientSeq, ClientInitState), + + i("start 'tester' evaluator"), + TesterInitState = #{server => Server#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the send and recv functions %% behave as expected when sending and/or reading chunks. %% First send data in one "big" chunk, and read it in "small" chunks. @@ -8773,10 +18240,11 @@ traffic_send_and_recv_chunks_tcp4(suite) -> traffic_send_and_recv_chunks_tcp4(doc) -> []; traffic_send_and_recv_chunks_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(traffic_send_and_recv_chunks_tcp4, fun() -> - ?TT(?SECS(30)), - InitState = #{domain => inet}, + InitState = #{domain => inet, + proto => tcp}, ok = traffic_send_and_recv_chunks_tcp(InitState) end). @@ -8794,11 +18262,34 @@ traffic_send_and_recv_chunks_tcp6(suite) -> traffic_send_and_recv_chunks_tcp6(doc) -> []; traffic_send_and_recv_chunks_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(traffic_send_and_recv_chunks_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(30)), - InitState = #{domain => inet6}, + InitState = #{domain => inet6, + proto => tcp}, + ok = traffic_send_and_recv_chunks_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% behave as expected when sending and/or reading chunks. +%% First send data in one "big" chunk, and read it in "small" chunks. +%% Second, send in a bunch of "small" chunks, and read in one "big" chunk. +%% Socket is UNix Domain (Stream) socket. + +traffic_send_and_recv_chunks_tcpL(suite) -> + []; +traffic_send_and_recv_chunks_tcpL(doc) -> + []; +traffic_send_and_recv_chunks_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(traffic_send_and_recv_chunks_tcp6, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default}, ok = traffic_send_and_recv_chunks_tcp(InitState) end). @@ -8823,13 +18314,12 @@ traffic_send_and_recv_chunks_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -8837,7 +18327,17 @@ traffic_send_and_recv_chunks_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + local_sa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, + local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> {ok, State#{lport => Port}}; @@ -8850,7 +18350,14 @@ traffic_send_and_recv_chunks_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, + local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, + local_sa := LSA, + lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -9045,9 +18552,20 @@ traffic_send_and_recv_chunks_tcp(InitState) -> {ok, maps:remove(csock, State)} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := Sock} = State) -> + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := Sock} = State) -> (catch socket:close(Sock)), - {ok, maps:remove(lsock, State)} + {ok, maps:remove(lsock, State)} end}, %% *** We are done *** @@ -9098,8 +18616,10 @@ traffic_send_and_recv_chunks_tcp(InitState) -> ok end}, #{desc => "order remote client to start", - cmd => fun(#{rclient := Client, server_sa := ServerSA}) -> - ?SEV_ANNOUNCE_START(Client, ServerSA), + cmd => fun(#{rclient := Client, + server_sa := ServerSA, + proto := Proto}) -> + ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}), ok end}, #{desc => "await remote client ready", @@ -9654,14 +19174,14 @@ traffic_snr_tcp_client_start(Node) -> erlang:spawn(Node, Fun). traffic_snr_tcp_client(Parent) -> - {Sock, ServerSA} = traffic_snr_tcp_client_init(Parent), + {Sock, ServerSA, Path} = traffic_snr_tcp_client_init(Parent), traffic_snr_tcp_client_announce_ready(Parent, init), traffic_snr_tcp_client_await_continue(Parent, connect), traffic_snr_tcp_client_connect(Sock, ServerSA), traffic_snr_tcp_client_announce_ready(Parent, connect), traffic_snr_tcp_client_send_loop(Parent, Sock), Reason = traffic_snr_tcp_client_await_terminate(Parent), - traffic_snr_tcp_client_close(Sock), + traffic_snr_tcp_client_close(Sock, Path), exit(Reason). @@ -9687,19 +19207,19 @@ traffic_snr_tcp_client_init(Parent) -> put(sname, "rclient"), ?SEV_IPRINT("init"), _MRef = erlang:monitor(process, Parent), - ServerSA = traffic_snr_tcp_client_await_start(Parent), + {ServerSA, Proto} = traffic_snr_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = traffic_snr_tcp_client_create(Domain), - traffic_snr_tcp_client_bind(Sock, Domain), - {Sock, ServerSA}. + Sock = traffic_snr_tcp_client_create(Domain, Proto), + Path = traffic_snr_tcp_client_bind(Sock, Domain), + {Sock, ServerSA, Path}. traffic_snr_tcp_client_await_start(Parent) -> i("traffic_snr_tcp_client_await_start -> entry"), ?SEV_AWAIT_START(Parent). -traffic_snr_tcp_client_create(Domain) -> +traffic_snr_tcp_client_create(Domain, Proto) -> i("traffic_snr_tcp_client_create -> entry"), - case socket:open(Domain, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> Sock; {error, Reason} -> @@ -9708,12 +19228,17 @@ traffic_snr_tcp_client_create(Domain) -> traffic_snr_tcp_client_bind(Sock, Domain) -> i("traffic_snr_tcp_client_bind -> entry"), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; {error, Reason} -> exit({bind, Reason}) end. @@ -9734,13 +19259,17 @@ traffic_snr_tcp_client_connect(Sock, ServerSA) -> exit({connect, Reason}) end. -traffic_snr_tcp_client_close(Sock) -> +traffic_snr_tcp_client_close(Sock, Path) -> i("traffic_snr_tcp_client_close -> entry"), case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. traffic_snr_tcp_client_await_terminate(Parent) -> @@ -9768,12 +19297,13 @@ traffic_ping_pong_small_send_and_recv_tcp4(suite) -> traffic_ping_pong_small_send_and_recv_tcp4(doc) -> []; traffic_ping_pong_small_send_and_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), Msg = l2b(?TPP_SMALL), Num = ?TPP_SMALL_NUM, tc_try(traffic_ping_pong_small_send_and_recv_tcp4, fun() -> - ?TT(?SECS(15)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9795,13 +19325,42 @@ traffic_ping_pong_small_send_and_recv_tcp6(suite) -> traffic_ping_pong_small_send_and_recv_tcp6(doc) -> []; traffic_ping_pong_small_send_and_recv_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), Msg = l2b(?TPP_SMALL), Num = ?TPP_SMALL_NUM, tc_try(traffic_ping_pong_small_send_and_recv_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(15)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_small_send_and_recv_tcpL(suite) -> + []; +traffic_ping_pong_small_send_and_recv_tcpL(doc) -> + []; +traffic_ping_pong_small_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_send_and_recv_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9828,6 +19387,7 @@ traffic_ping_pong_medium_send_and_recv_tcp4(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(30)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9855,6 +19415,36 @@ traffic_ping_pong_medium_send_and_recv_tcp6(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(30)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_send_and_recv_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_medium_send_and_recv_tcpL(suite) -> + []; +traffic_ping_pong_medium_send_and_recv_tcpL(doc) -> + []; +traffic_ping_pong_medium_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_send_and_recv_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9876,12 +19466,13 @@ traffic_ping_pong_large_send_and_recv_tcp4(suite) -> traffic_ping_pong_large_send_and_recv_tcp4(doc) -> []; traffic_ping_pong_large_send_and_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(45)), Msg = l2b(?TPP_LARGE), Num = ?TPP_LARGE_NUM, tc_try(traffic_ping_pong_large_send_and_recv_tcp4, fun() -> - ?TT(?SECS(45)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9909,6 +19500,7 @@ traffic_ping_pong_large_send_and_recv_tcp6(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(45)), InitState = #{domain => inet6, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9917,6 +19509,47 @@ traffic_ping_pong_large_send_and_recv_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'large' message test case, for UNix Domain (stream) socket. + +traffic_ping_pong_large_send_and_recv_tcpL(suite) -> + []; +traffic_ping_pong_large_send_and_recv_tcpL(doc) -> + []; +traffic_ping_pong_large_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(45)), + Msg = l2b(?TPP_LARGE), + Num = ?TPP_LARGE_NUM, + tc_try(traffic_ping_pong_large_send_and_recv_tcpL, + fun() -> + has_support_unix_domain_socket(), + traffic_ping_pong_large_host_cond() + end, + fun() -> + InitState = #{domain => local, + proto => default, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_send_and_recv_tcp(InitState) + end). + +%% This test case is a bit extreme and fails on some hosts +%% (e.g. OpenIndiana Hipster), so exclude them. +traffic_ping_pong_large_host_cond() -> + traffic_ping_pong_large_host_cond(os:type(), os:version()). + +traffic_ping_pong_large_host_cond({unix, sunos}, _) -> + skip("TC does not work on platform"); +traffic_ping_pong_large_host_cond(_, _) -> + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the sendto and recvfrom %% functions by repeatedly sending a meassage between two entities. %% The same basic test case is used for two different message sizes; @@ -9936,6 +19569,7 @@ traffic_ping_pong_small_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(45)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -9959,9 +19593,40 @@ traffic_ping_pong_small_sendto_and_recvfrom_udp6(_Config) when is_list(_Config) Msg = l2b(?TPP_SMALL), Num = ?TPP_SMALL_NUM, tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udp6, + fun() -> has_support_ipv6() end, fun() -> ?TT(?SECS(45)), - InitState = #{domain => inet, + InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendto and recvfrom +%% functions by repeatedly sending a meassage between two entities. +%% The same basic test case is used for two different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_small_sendto_and_recvfrom_udpL(suite) -> + []; +traffic_ping_pong_small_sendto_and_recvfrom_udpL(doc) -> + []; +traffic_ping_pong_small_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(45)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -9989,6 +19654,7 @@ traffic_ping_pong_medium_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(45)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -10016,6 +19682,36 @@ traffic_ping_pong_medium_sendto_and_recvfrom_udp6(_Config) when is_list(_Config) fun() -> ?TT(?SECS(45)), InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendto and recvfrom +%% functions by repeatedly sending a meassage between two entities. +%% The same basic test case is used for two different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_medium_sendto_and_recvfrom_udpL(suite) -> + []; +traffic_ping_pong_medium_sendto_and_recvfrom_udpL(doc) -> + []; +traffic_ping_pong_medium_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_sendto_and_recvfrom_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(45)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -10043,6 +19739,7 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(20)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10070,6 +19767,35 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) fun() -> ?TT(?SECS(20)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL(suite) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL(doc) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(20)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10096,6 +19822,7 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(30)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10121,8 +19848,37 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(20)), - InitState = #{domain => ine6, + ?TT(?SECS(30)), + InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(suite) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(doc) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(30)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10146,15 +19902,27 @@ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) Msg = l2b(?TPP_LARGE), Num = ?TPP_LARGE_NUM, tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4, + fun() -> traffic_ping_pong_large_sendmsg_and_recvmsg_cond() end, fun() -> - ?TT(?SECS(30)), + ?TT(?SECS(60)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) end). +traffic_ping_pong_large_sendmsg_and_recvmsg_cond() -> + traffic_ping_pong_large_sendmsg_and_recvmsg_cond(os:type(), os:version()). + +traffic_ping_pong_large_sendmsg_and_recvmsg_cond({unix, linux}, {M, _, _}) + when (M < 3) -> + skip("TC may not work on this version"); +traffic_ping_pong_large_sendmsg_and_recvmsg_cond(_, _) -> + ok. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the sendmsg and recvmsg functions %% by repeatedly sending a meassage between two entities. @@ -10172,10 +19940,43 @@ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) Msg = l2b(?TPP_LARGE), Num = ?TPP_LARGE_NUM, tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6, - fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(30)), + has_support_ipv6(), + traffic_ping_pong_large_sendmsg_and_recvmsg_cond() + end, + fun() -> + ?TT(?SECS(60)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'large' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(suite) -> + []; +traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(doc) -> + []; +traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_LARGE), + Num = ?TPP_LARGE_NUM, + tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(60)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10203,6 +20004,7 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(60)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10228,8 +20030,37 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(30)), - InitState = #{domain => inet, + ?TT(?SECS(60)), + InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(suite) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(doc) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(60)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10254,8 +20085,9 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) Num = ?TPP_MEDIUM_NUM, tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4, fun() -> - ?TT(?SECS(30)), + ?TT(?SECS(60)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10281,8 +20113,38 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(20)), - InitState = #{domain => ine6, + ?TT(?SECS(60)), + InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg +%% functions by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(suite) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(doc) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(60)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10302,14 +20164,26 @@ traffic_ping_pong_send_and_recv_tcp(InitState) -> }, traffic_ping_pong_send_and_receive_tcp(InitState2). -traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) -> - Send = fun(Sock, Data) when is_binary(Data) -> - MsgHdr = #{iov => [Data]}, - socket:sendmsg(Sock, MsgHdr); - (Sock, Data) when is_list(Data) -> %% We assume iovec... - MsgHdr = #{iov => Data}, - socket:sendmsg(Sock, MsgHdr) +traffic_ping_pong_sendmsg_and_recvmsg_tcp(#{domain := local} = InitState) -> + Recv = fun(Sock, Sz) -> + case socket:recvmsg(Sock, Sz, 0) of + %% On some platforms, the address + %% is *not* provided (e.g. FreeBSD) + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + %% On some platforms, the address + %% *is* provided (e.g. linux) + {ok, #{addr := #{family := local}, + iov := [Data]}} -> + {ok, Data}; + {error, _} = ERROR -> + ERROR + end end, + InitState2 = InitState#{recv => Recv}, % Receive function + traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState2); +traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) -> Recv = fun(Sock, Sz) -> case socket:recvmsg(Sock, Sz, 0) of {ok, #{addr := undefined, @@ -10319,9 +20193,18 @@ traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) -> ERROR end end, - InitState2 = InitState#{send => Send, % Send function - recv => Recv % Receive function - }, + InitState2 = InitState#{recv => Recv}, % Receive function + traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState2). + +traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState) -> + Send = fun(Sock, Data) when is_binary(Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr); + (Sock, Data) when is_list(Data) -> %% We assume iovec... + MsgHdr = #{iov => Data}, + socket:sendmsg(Sock, MsgHdr) + end, + InitState2 = InitState#{send => Send}, % Send function traffic_ping_pong_send_and_receive_tcp(InitState2). @@ -10331,12 +20214,14 @@ traffic_ping_pong_send_and_receive_tcp(#{msg := Msg} = InitState) -> ?SEV_IPRINT("RcvBuf is ~p (needs atleast ~p)", [RcvSz, 16+size(Msg)]), if (RcvSz < size(Msg)) -> - case socket:setopt(Sock, - socket, rcvbuf, 1024+size(Msg)) of + NewRcvSz = 1024+size(Msg), + case socket:setopt(Sock, socket, rcvbuf, NewRcvSz) of ok -> ok; {error, enobufs} -> - skip({failed_change, rcvbuf}); + skip(?F("Change ~w buffer size (to ~w) " + "not allowed", + [rcvbuf, NewRcvSz])); {error, Reason1} -> ?FAIL({rcvbuf, Reason1}) end; @@ -10347,12 +20232,14 @@ traffic_ping_pong_send_and_receive_tcp(#{msg := Msg} = InitState) -> ?SEV_IPRINT("SndBuf is ~p (needs atleast ~p)", [SndSz, 16+size(Msg)]), if (SndSz < size(Msg)) -> - case socket:setopt(Sock, - socket, sndbuf, 1024+size(Msg)) of + NewSndSz = 1024+size(Msg), + case socket:setopt(Sock, socket, sndbuf, NewSndSz) of ok -> ok; {error, enobufs} -> - skip({failed_change, sndbuf}); + skip(?F("Change ~w buffer size (to ~w) " + "not allowed", + [sndbuf, NewSndSz])); {error, Reason2} -> ?FAIL({sndbuf, Reason2}) end; @@ -10381,13 +20268,12 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -10395,9 +20281,19 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -10412,7 +20308,11 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -10523,9 +20423,20 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> {ok, State1} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := Sock} = State) -> + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + (catch socket:close(Sock)), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := Sock} = State) -> (catch socket:close(Sock)), - {ok, maps:remove(lsock, State)} + {ok, maps:remove(lsock, State)} end}, %% *** We are done *** @@ -10577,12 +20488,14 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> end}, #{desc => "order remote client to start", cmd => fun(#{rclient := RClient, + proto := Proto, server_sa := ServerSA, buf_init := BufInit, send := Send, recv := Recv}) -> ?SEV_ANNOUNCE_START(RClient, - {ServerSA, BufInit, Send, Recv}), + {ServerSA, Proto, BufInit, + Send, Recv}), ok end}, #{desc => "await remote client ready", @@ -10883,6 +20796,7 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> i("start server evaluator"), ServerInitState = #{domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), recv => maps:get(recv, InitState), send => maps:get(send, InitState), buf_init => maps:get(buf_init, InitState)}, @@ -10976,14 +20890,6 @@ tpp_tcp_handler_msg_exchange_loop(Sock, Send, Recv, N, Sent, Received, Start) -> ?SEV_EPRINT("send (~w): ~p", [N, SReason]), exit({send, SReason, N}) end; - %% {error, timeout} -> - %% ?SEV_IPRINT("timeout(~w) - try again", [N]), - %% case Send(Sock, list_to_binary("ping")) of - %% ok -> - %% exit({'ping-send', ok, N}); - %% {error, Reason} -> - %% exit({'ping-send', Reason, N}) - %% end; {error, closed} -> ?SEV_IPRINT("closed - we are done: ~w, ~w, ~w", [N, Sent, Received]), Stop = ?LIB:timestamp(), @@ -11002,10 +20908,10 @@ tpp_tcp_client_create(Node) -> tpp_tcp_client(Parent) -> tpp_tcp_client_init(Parent), - {ServerSA, BufInit, Send, Recv} = tpp_tcp_client_await_start(Parent), + {ServerSA, Proto, BufInit, Send, Recv} = tpp_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = tpp_tcp_client_sock_open(Domain, BufInit), - tpp_tcp_client_sock_bind(Sock, Domain), + Sock = tpp_tcp_client_sock_open(Domain, Proto, BufInit), + Path = tpp_tcp_client_sock_bind(Sock, Domain), tpp_tcp_client_announce_ready(Parent, init), tpp_tcp_client_await_continue(Parent, connect), tpp_tcp_client_sock_connect(Sock, ServerSA), @@ -11014,7 +20920,7 @@ tpp_tcp_client(Parent) -> Result = tpp_tcp_client_msg_exchange(Sock, Send, Recv, InitMsg, Num), tpp_tcp_client_announce_ready(Parent, send, Result), Reason = tpp_tcp_client_await_terminate(Parent), - tpp_tcp_client_sock_close(Sock), + tpp_tcp_client_sock_close(Sock, Path), ?SEV_IPRINT("terminating"), exit(Reason). @@ -11054,8 +20960,10 @@ tpp_tcp_client_await_terminate(Parent) -> ?SEV_IPRINT("await terminate"), case ?SEV_AWAIT_TERMINATE(Parent, parent) of ok -> - ok; + ?SEV_IPRINT("termination received: normal"), + normal; {error, Reason} -> + ?SEV_IPRINT("termination received: ~w", [Reason]), Reason end. @@ -11099,8 +21007,8 @@ tpp_tcp_client_msg_exchange_loop(Sock, Send, Recv, Data, exit({send, SReason, N}) end. -tpp_tcp_client_sock_open(Domain, BufInit) -> - case socket:open(Domain, stream, tcp) of +tpp_tcp_client_sock_open(Domain, Proto, BufInit) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> ok = BufInit(Sock), Sock; @@ -11109,14 +21017,19 @@ tpp_tcp_client_sock_open(Domain, BufInit) -> end. tpp_tcp_client_sock_bind(Sock, Domain) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; - {error, Reason} -> - exit({bind, Reason}) + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; + {error, Reason2} -> + exit({bind, Reason2}) end. tpp_tcp_client_sock_connect(Sock, ServerSA) -> @@ -11127,14 +21040,20 @@ tpp_tcp_client_sock_connect(Sock, ServerSA) -> exit({connect, Reason}) end. -tpp_tcp_client_sock_close(Sock) -> +tpp_tcp_client_sock_close(Sock, Path) -> case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. + + -define(TPP_REQUEST, 1). -define(TPP_REPLY, 2). @@ -11300,13 +21219,12 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, - #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, dgram, udp) of + #{desc => "create socket", + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, dgram, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -11314,7 +21232,15 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{sock := Sock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _} -> + ok; + {error, _} = ERROR -> + ERROR + end; + (#{sock := Sock, local_sa := LSA} = State) -> case socket:bind(Sock, LSA) of {ok, Port} -> {ok, State#{port => Port}}; @@ -11357,7 +21283,11 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> end end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, port := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, port := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -11396,6 +21326,19 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> ERROR end end}, + #{desc => "(maybe) unlink socket", + cmd => fun(#{domain := local, + local_sa := #{path := Path}} = State) -> + unlink_path(Path, + fun() -> + {ok, maps:remove(local_sa, State)} + end, + fun() -> + ok + end); + (_) -> + ok + end}, #{desc => "announce ready (close)", cmd => fun(#{tester := Tester} = _State) -> ?SEV_ANNOUNCE_READY(Tester, close), @@ -11492,11 +21435,13 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> #{desc => "order remote handler to start", cmd => fun(#{handler := Handler, server_sa := ServerSA, + proto := Proto, buf_init := BufInit, send := Send, recv := Recv}) -> ?SEV_ANNOUNCE_START(Handler, - {ServerSA, BufInit, Send, Recv}), + {ServerSA, Proto, BufInit, + Send, Recv}), ok end}, #{desc => "await (remote) handler ready", @@ -11741,6 +21686,7 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> i("start server evaluator"), ServerInitState = #{domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), recv => maps:get(recv, InitState), send => maps:get(send, InitState), buf_init => maps:get(buf_init, InitState)}, @@ -11841,12 +21787,12 @@ tpp_udp_client_handler_create(Node) -> tpp_udp_client_handler(Parent) -> tpp_udp_client_handler_init(Parent), ?SEV_IPRINT("await start command"), - {ServerSA, BufInit, Send, Recv} = tpp_udp_handler_await_start(Parent), + {ServerSA, Proto, BufInit, Send, Recv} = tpp_udp_handler_await_start(Parent), ?SEV_IPRINT("start command with" "~n ServerSA: ~p", [ServerSA]), Domain = maps:get(family, ServerSA), - Sock = tpp_udp_sock_open(Domain, BufInit), - tpp_udp_sock_bind(Sock, Domain), + Sock = tpp_udp_sock_open(Domain, Proto, BufInit), + Path = tpp_udp_sock_bind(Sock, Domain), ?SEV_IPRINT("announce ready", []), tpp_udp_handler_announce_ready(Parent, init), {InitMsg, Num} = tpp_udp_handler_await_continue(Parent, send), @@ -11859,7 +21805,7 @@ tpp_udp_client_handler(Parent) -> ?SEV_IPRINT("await terminate"), Reason = tpp_udp_handler_await_terminate(Parent), ?SEV_IPRINT("terminate with ~p", [Reason]), - tpp_udp_sock_close(Sock), + tpp_udp_sock_close(Sock, Path), ?SEV_IPRINT("terminating"), exit(Reason). @@ -11881,7 +21827,9 @@ tpp_udp_client_handler_msg_exchange_loop(_Sock, _Dest, _Send, _Recv, _Msg, Start) -> Stop = ?LIB:timestamp(), {Sent, Received, Start, Stop}; -tpp_udp_client_handler_msg_exchange_loop(Sock, Dest, Send, Recv, Data, +tpp_udp_client_handler_msg_exchange_loop(Sock, + #{family := local} = Dest, + Send, Recv, Data, Num, N, Sent, Received, Start) -> case tpp_udp_send_req(Sock, Send, Data, Dest) of {ok, SendSz} -> @@ -11900,6 +21848,28 @@ tpp_udp_client_handler_msg_exchange_loop(Sock, Dest, Send, Recv, Data, {error, SReason} -> ?SEV_EPRINT("send (~w of ~w): ~p", [N, Num, SReason]), exit({send, SReason, N}) + end; +tpp_udp_client_handler_msg_exchange_loop(Sock, + #{addr := Addr, port := Port} = Dest0, + Send, Recv, Data, + Num, N, Sent, Received, Start) -> + case tpp_udp_send_req(Sock, Send, Data, Dest0) of + {ok, SendSz} -> + case tpp_udp_recv_rep(Sock, Recv) of + {ok, NewData, RecvSz, #{addr := Addr, port := Port} = Dest1} -> + tpp_udp_client_handler_msg_exchange_loop(Sock, Dest1, + Send, Recv, + NewData, Num, N+1, + Sent+SendSz, + Received+RecvSz, + Start); + {error, RReason} -> + ?SEV_EPRINT("recv (~w of ~w): ~p", [N, Num, RReason]), + exit({recv, RReason, N}) + end; + {error, SReason} -> + ?SEV_EPRINT("send (~w of ~w): ~p", [N, Num, SReason]), + exit({send, SReason, N}) end. @@ -11997,8 +21967,8 @@ tpp_udp_handler_await_terminate(Parent) -> end. -tpp_udp_sock_open(Domain, BufInit) -> - case socket:open(Domain, dgram, udp) of +tpp_udp_sock_open(Domain, Proto, BufInit) -> + case socket:open(Domain, dgram, Proto) of {ok, Sock} -> ok = BufInit(Sock), Sock; @@ -12007,9 +21977,7 @@ tpp_udp_sock_open(Domain, BufInit) -> end. tpp_udp_sock_bind(Sock, Domain) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> ok; @@ -12017,12 +21985,16 @@ tpp_udp_sock_bind(Sock, Domain) -> exit({bind, Reason}) end. -tpp_udp_sock_close(Sock) -> +tpp_udp_sock_close(Sock, Path) -> case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("Failed closing socket: " + "~n ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. @@ -15104,6 +25076,30 @@ ttest_ssockf_csockf_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = false +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockf_csockf_small_tcpL(suite) -> + []; +ttest_ssockf_csockf_small_tcpL(doc) -> + []; +ttest_ssockf_csockf_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_small_tcpL, + Runtime, + local, + sock, false, + sock, false, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = false %% Message Size: medium (=2) %% Domain: inet %% @@ -15152,6 +25148,30 @@ ttest_ssockf_csockf_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = false +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockf_csockf_medium_tcpL(suite) -> + []; +ttest_ssockf_csockf_medium_tcpL(doc) -> + []; +ttest_ssockf_csockf_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_medium_tcpL, + Runtime, + local, + sock, false, + sock, false, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = false %% Message Size: large (=3) %% Domain: inet %% @@ -15199,6 +25219,30 @@ ttest_ssockf_csockf_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = false +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockf_csockf_large_tcpL(suite) -> + []; +ttest_ssockf_csockf_large_tcpL(doc) -> + []; +ttest_ssockf_csockf_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_large_tcpL, + Runtime, + local, + sock, false, + sock, false, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = once %% Message Size: small (=1) %% Domain: inet @@ -15248,6 +25292,30 @@ ttest_ssockf_csocko_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = once +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockf_csocko_small_tcpL(suite) -> + []; +ttest_ssockf_csocko_small_tcpL(doc) -> + []; +ttest_ssockf_csocko_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_small_tcpL, + Runtime, + local, + sock, false, + sock, once, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = once %% Message Size: medium (=2) %% Domain: inet %% @@ -15296,6 +25364,30 @@ ttest_ssockf_csocko_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = once +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockf_csocko_medium_tcpL(suite) -> + []; +ttest_ssockf_csocko_medium_tcpL(doc) -> + []; +ttest_ssockf_csocko_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_medium_tcpL, + Runtime, + local, + sock, false, + sock, once, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = once %% Message Size: large (=3) %% Domain: inet %% @@ -15343,6 +25435,30 @@ ttest_ssockf_csocko_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = once +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockf_csocko_large_tcpL(suite) -> + []; +ttest_ssockf_csocko_large_tcpL(doc) -> + []; +ttest_ssockf_csocko_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_large_tcpL, + Runtime, + local, + sock, false, + sock, once, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = true %% Message Size: small (=1) %% Domain: inet @@ -15390,6 +25506,30 @@ ttest_ssockf_csockt_small_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = true +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockf_csockt_small_tcpL(suite) -> + []; +ttest_ssockf_csockt_small_tcpL(doc) -> + []; +ttest_ssockf_csockt_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_small_tcpL, + Runtime, + local, + sock, false, + sock, true, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = true %% Message Size: medium (=2) @@ -15440,6 +25580,30 @@ ttest_ssockf_csockt_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = true +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockf_csockt_medium_tcpL(suite) -> + []; +ttest_ssockf_csockt_medium_tcpL(doc) -> + []; +ttest_ssockf_csockt_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_medium_tcpL, + Runtime, + local, + sock, false, + sock, true, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = true %% Message Size: large (=3) %% Domain: inet %% @@ -15486,6 +25650,30 @@ ttest_ssockf_csockt_large_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = true +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockf_csockt_large_tcpL(suite) -> + []; +ttest_ssockf_csockt_large_tcpL(doc) -> + []; +ttest_ssockf_csockt_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_large_tcpL, + Runtime, + local, + sock, false, + sock, true, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = gen_tcp, Active = false %% Message Size: small (=1) @@ -15968,6 +26156,30 @@ ttest_ssocko_csockf_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = false +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssocko_csockf_small_tcpL(suite) -> + []; +ttest_ssocko_csockf_small_tcpL(doc) -> + []; +ttest_ssocko_csockf_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_small_tcpL, + Runtime, + local, + sock, once, + sock, false, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = false %% Message Size: medium (=2) %% Domain: inet %% @@ -16016,6 +26228,30 @@ ttest_ssocko_csockf_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = false +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssocko_csockf_medium_tcpL(suite) -> + []; +ttest_ssocko_csockf_medium_tcpL(doc) -> + []; +ttest_ssocko_csockf_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_medium_tcpL, + Runtime, + local, + sock, once, + sock, false, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = false %% Message Size: large (=3) %% Domain: inet %% @@ -16063,6 +26299,30 @@ ttest_ssocko_csockf_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = false +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssocko_csockf_large_tcpL(suite) -> + []; +ttest_ssocko_csockf_large_tcpL(doc) -> + []; +ttest_ssocko_csockf_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_large_tcpL, + Runtime, + local, + sock, once, + sock, false, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = once %% Message Size: small (=1) %% Domain: inet @@ -16112,6 +26372,30 @@ ttest_ssocko_csocko_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = once +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssocko_csocko_small_tcpL(suite) -> + []; +ttest_ssocko_csocko_small_tcpL(doc) -> + []; +ttest_ssocko_csocko_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_small_tcpL, + Runtime, + local, + sock, once, + sock, once, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = once %% Message Size: medium (=2) %% Domain: inet %% @@ -16160,6 +26444,30 @@ ttest_ssocko_csocko_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = once +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssocko_csocko_medium_tcpL(suite) -> + []; +ttest_ssocko_csocko_medium_tcpL(doc) -> + []; +ttest_ssocko_csocko_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_medium_tcpL, + Runtime, + local, + sock, once, + sock, once, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = once %% Message Size: large (=3) %% Domain: inet %% @@ -16207,6 +26515,30 @@ ttest_ssocko_csocko_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = once +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssocko_csocko_large_tcpL(suite) -> + []; +ttest_ssocko_csocko_large_tcpL(doc) -> + []; +ttest_ssocko_csocko_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_large_tcpL, + Runtime, + local, + sock, once, + sock, once, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = true %% Message Size: small (=1) %% Domain: inet @@ -16254,6 +26586,30 @@ ttest_ssocko_csockt_small_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = true +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssocko_csockt_small_tcpL(suite) -> + []; +ttest_ssocko_csockt_small_tcpL(doc) -> + []; +ttest_ssocko_csockt_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_small_tcpL, + Runtime, + local, + sock, once, + sock, true, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = true %% Message Size: medium (=2) @@ -16304,6 +26660,30 @@ ttest_ssocko_csockt_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = true +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssocko_csockt_medium_tcpL(suite) -> + []; +ttest_ssocko_csockt_medium_tcpL(doc) -> + []; +ttest_ssocko_csockt_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_medium_tcpL, + Runtime, + local, + sock, once, + sock, true, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = true %% Message Size: large (=3) %% Domain: inet %% @@ -16350,6 +26730,30 @@ ttest_ssocko_csockt_large_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = true +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssocko_csockt_large_tcpL(suite) -> + []; +ttest_ssocko_csockt_large_tcpL(doc) -> + []; +ttest_ssocko_csockt_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_large_tcpL, + Runtime, + local, + sock, once, + sock, true, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = gen_tcp, Active = false %% Message Size: small (=1) @@ -16832,6 +27236,30 @@ ttest_ssockt_csockf_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = false +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockt_csockf_small_tcpL(suite) -> + []; +ttest_ssockt_csockf_small_tcpL(doc) -> + []; +ttest_ssockt_csockf_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_small_tcpL, + Runtime, + local, + sock, true, + sock, false, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = false %% Message Size: medium (=2) %% Domain: inet %% @@ -16880,6 +27308,30 @@ ttest_ssockt_csockf_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = false +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockt_csockf_medium_tcpL(suite) -> + []; +ttest_ssockt_csockf_medium_tcpL(doc) -> + []; +ttest_ssockt_csockf_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_medium_tcpL, + Runtime, + local, + sock, true, + sock, false, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = false %% Message Size: large (=3) %% Domain: inet %% @@ -16927,6 +27379,30 @@ ttest_ssockt_csockf_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = false +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockt_csockf_large_tcpL(suite) -> + []; +ttest_ssockt_csockf_large_tcpL(doc) -> + []; +ttest_ssockt_csockf_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_large_tcpL, + Runtime, + local, + sock, true, + sock, false, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = once %% Message Size: small (=1) %% Domain: inet @@ -16976,6 +27452,30 @@ ttest_ssockt_csocko_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = once +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockt_csocko_small_tcpL(suite) -> + []; +ttest_ssockt_csocko_small_tcpL(doc) -> + []; +ttest_ssockt_csocko_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_small_tcpL, + Runtime, + local, + sock, true, + sock, once, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = once %% Message Size: medium (=2) %% Domain: inet %% @@ -17024,6 +27524,30 @@ ttest_ssockt_csocko_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = once +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockt_csocko_medium_tcpL(suite) -> + []; +ttest_ssockt_csocko_medium_tcpL(doc) -> + []; +ttest_ssockt_csocko_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_medium_tcpL, + Runtime, + local, + sock, true, + sock, once, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = once %% Message Size: large (=3) %% Domain: inet %% @@ -17071,6 +27595,30 @@ ttest_ssockt_csocko_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = once +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockt_csocko_large_tcpL(suite) -> + []; +ttest_ssockt_csocko_large_tcpL(doc) -> + []; +ttest_ssockt_csocko_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_large_tcpL, + Runtime, + local, + sock, true, + sock, once, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = true %% Message Size: small (=1) %% Domain: inet @@ -17118,6 +27666,30 @@ ttest_ssockt_csockt_small_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = true +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockt_csockt_small_tcpL(suite) -> + []; +ttest_ssockt_csockt_small_tcpL(doc) -> + []; +ttest_ssockt_csockt_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_small_tcpL, + Runtime, + local, + sock, true, + sock, true, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = true %% Message Size: medium (=2) @@ -17168,6 +27740,30 @@ ttest_ssockt_csockt_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = true +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockt_csockt_medium_tcpL(suite) -> + []; +ttest_ssockt_csockt_medium_tcpL(doc) -> + []; +ttest_ssockt_csockt_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_medium_tcpL, + Runtime, + local, + sock, true, + sock, true, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = true %% Message Size: large (=3) %% Domain: inet %% @@ -17212,6 +27808,30 @@ ttest_ssockt_csockt_large_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = true +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockt_csockt_large_tcpL(suite) -> + []; +ttest_ssockt_csockt_large_tcpL(doc) -> + []; +ttest_ssockt_csockt_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_large_tcpL, + Runtime, + local, + sock, true, + sock, true, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% which_ttest_runtime(Config) when is_list(Config) -> case lists:keysearch(esock_test_ttest_runtime, 1, Config) of @@ -17235,7 +27855,7 @@ which_ttest_runtime_env(false) -> %% ms: milliseconds %% s: seconds (default) %% m: minutes -which_ttest_runtime_env2([$m, $s | MS]) when (length(MS) > 0) -> +which_ttest_runtime_env2([$s, $m | MS]) when (length(MS) > 0) -> convert_time(MS, fun(X) -> X end); which_ttest_runtime_env2([$m | M]) when (length(M) > 0) -> convert_time(M, fun(X) -> ?MINS(X) end); @@ -17272,7 +27892,8 @@ ttest_tcp(TC, tc_try(TC, fun() -> if - (Domain =/= inet) -> has_support_ipv6(); + (Domain =:= local) -> has_support_unix_domain_socket(); + (Domain =:= inet6) -> has_support_ipv6(); true -> ok end end, @@ -17326,11 +27947,25 @@ ttest_tcp(InitState) -> ok end}, #{desc => "start ttest (remote) server", - cmd => fun(#{mod := Mod, + cmd => fun(#{domain := local = Domain, + mod := Mod, + active := Active, + node := Node} = State) -> + case ttest_tcp_server_start(Node, + Domain, Mod, Active) of + {ok, {{Pid, _}, Path}} -> + {ok, State#{rserver => Pid, + path => Path}}; + {error, _} = ERROR -> + ERROR + end; + (#{domain := Domain, + mod := Mod, active := Active, node := Node} = State) -> - case ttest_tcp_server_start(Node, Mod, Active) of - {ok, {{Pid, _MRef}, {Addr, Port}}} -> + case ttest_tcp_server_start(Node, + Domain, Mod, Active) of + {ok, {{Pid, _}, {Addr, Port}}} -> {ok, State#{rserver => Pid, addr => Addr, port => Port}}; @@ -17339,7 +27974,12 @@ ttest_tcp(InitState) -> end end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, + cmd => fun(#{domain := local, + tester := Tester, + path := Path}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, addr := Addr, port := Port}) -> ?SEV_ANNOUNCE_READY(Tester, init, {Addr, Port}), @@ -17394,8 +28034,14 @@ ttest_tcp(InitState) -> [ %% *** Wait for start order part *** #{desc => "await start", - cmd => fun(State) -> - {Tester, {ServerAddr, ServerPort}} = ?SEV_AWAIT_START(), + cmd => fun(#{domain := local} = State) -> + {Tester, ServerPath} = + ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, + server_path => ServerPath}}; + (State) -> + {Tester, {ServerAddr, ServerPort}} = + ?SEV_AWAIT_START(), {ok, State#{tester => Tester, server_addr => ServerAddr, server_port => ServerPort}} @@ -17436,7 +28082,32 @@ ttest_tcp(InitState) -> ok end}, #{desc => "start ttest (remote) client", - cmd => fun(#{node := Node, + cmd => fun(#{domain := local = Domain, + node := Node, + mod := Mod, + active := Active, + msg_id := MsgID, + max_outstanding := MaxOutstanding, + runtime := RunTime, + server_path := Path} = State) -> + Self = self(), + Notify = + fun(Result) -> + ?SEV_ANNOUNCE_READY(Self, ttest, Result) + end, + case ttest_tcp_client_start(Node, Notify, + Domain, Mod, + Path, + Active, + MsgID, MaxOutstanding, + RunTime) of + {ok, {Pid, _MRef}} -> + {ok, State#{rclient => Pid}}; + {error, _} = ERROR -> + ERROR + end; + (#{domain := Domain, + node := Node, mod := Mod, active := Active, msg_id := MsgID, @@ -17450,8 +28121,9 @@ ttest_tcp(InitState) -> ?SEV_ANNOUNCE_READY(Self, ttest, Result) end, case ttest_tcp_client_start(Node, Notify, - Mod, Active, - Addr, Port, + Domain, Mod, + {Addr, Port}, + Active, MsgID, MaxOutstanding, RunTime) of {ok, {Pid, _MRef}} -> @@ -17463,8 +28135,6 @@ ttest_tcp(InitState) -> #{desc => "await ttest ready", cmd => fun(#{tester := Tester, rclient := RClient} = State) -> - %% TTestResult = ?SEV_AWAIT_READY(RClient, rclient, ttest, - %% [{tester, Tester}]), case ?SEV_AWAIT_READY(RClient, rclient, ttest, [{tester, Tester}]) of {ok, Result} -> @@ -17535,8 +28205,13 @@ ttest_tcp(InitState) -> ok end}, #{desc => "await server ready (init)", - cmd => fun(#{server := Pid} = State) -> - {ok, {Addr, Port}} = ?SEV_AWAIT_READY(Pid, server, init), + cmd => fun(#{domain := local, + server := Pid} = State) -> + {ok, Path} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_path => Path}}; + (#{server := Pid} = State) -> + {ok, {Addr, Port}} = + ?SEV_AWAIT_READY(Pid, server, init), {ok, State#{server_addr => Addr, server_port => Port}} end}, @@ -17544,7 +28219,12 @@ ttest_tcp(InitState) -> %% Start the client #{desc => "order client start", - cmd => fun(#{client := Pid, + cmd => fun(#{domain := local, + client := Pid, + server_path := Path} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Path), + ok; + (#{client := Pid, server_addr := Addr, server_port := Port} = _State) -> ?SEV_ANNOUNCE_START(Pid, {Addr, Port}), @@ -17600,12 +28280,23 @@ ttest_tcp(InitState) -> %% Present the results #{desc => "present the results", - cmd => fun(#{result := Result} = State) -> + cmd => fun(#{result := Result, + domain := Domain, + server_mod := ServerTrans, + server_active := ServerActive, + client_mod := ClientTrans, + client_active := ClientActive, + msg_id := MsgID} = State) -> case Result of #{status := ok, runtime := RunTime, cnt := Cnt, bcnt := BCnt} -> + ttest_report(Domain, + ServerTrans, ServerActive, + ClientTrans, ClientActive, + MsgID, + RunTime, BCnt, Cnt), ?SEV_IPRINT( "TTest results: " "~n Run Time: ~s" @@ -17665,16 +28356,22 @@ ttest_tcp(InitState) -> ?SEV_FINISH_NORMAL ], + Domain = maps:get(domain, InitState), + LHost = local_host(), + LAddr = which_local_addr(Domain), + i("start server evaluator"), - ServerInitState = #{host => local_host(), - domain => maps:get(domain, InitState), + ServerInitState = #{host => LHost, + addr => LAddr, + domain => Domain, mod => maps:get(server_mod, InitState), active => maps:get(server_active, InitState)}, Server = ?SEV_START("server", ServerSeq, ServerInitState), i("start client evaluator"), - ClientInitState = #{host => local_host(), - domain => maps:get(domain, InitState), + ClientInitState = #{host => LHost, + addr => LAddr, + domain => Domain, mod => maps:get(client_mod, InitState), active => maps:get(client_active, InitState), msg_id => maps:get(msg_id, InitState), @@ -17683,8 +28380,14 @@ ttest_tcp(InitState) -> Client = ?SEV_START("client", ClientSeq, ClientInitState), i("start 'tester' evaluator"), - TesterInitState = #{server => Server#ev.pid, - client => Client#ev.pid}, + TesterInitState = #{domain => Domain, + msg_id => maps:get(msg_id, InitState), + client => Client#ev.pid, + client_mod => maps:get(client_mod, InitState), + client_active => maps:get(client_active, InitState), + server => Server#ev.pid, + server_mod => maps:get(server_mod, InitState), + server_active => maps:get(server_active, InitState)}, Tester = ?SEV_START("tester", TesterSeq, TesterInitState), i("await evaluator(s)"), @@ -17692,12 +28395,15 @@ ttest_tcp(InitState) -> -ttest_tcp_server_start(Node, gen, Active) -> - Transport = socket_test_ttest_tcp_gen, +ttest_tcp_server_start(Node, Domain, gen, Active) -> + TransportMod = socket_test_ttest_tcp_gen, + Transport = {TransportMod, #{domain => Domain}}, socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active); -ttest_tcp_server_start(Node, sock, Active) -> +ttest_tcp_server_start(Node, Domain, sock, Active) -> TransportMod = socket_test_ttest_tcp_socket, - Transport = {TransportMod, #{method => plain}}, + Transport = {TransportMod, #{domain => Domain, + async => true, + method => plain}}, socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active). ttest_tcp_server_stop(Pid) -> @@ -17705,31 +28411,211 @@ ttest_tcp_server_stop(Pid) -> ttest_tcp_client_start(Node, Notify, - gen, - Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> - Transport = socket_test_ttest_tcp_gen, + Domain, gen, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> + TransportMod = socket_test_ttest_tcp_gen, + Transport = {TransportMod, #{domain => Domain}}, socket_test_ttest_tcp_client:start_monitor(Node, Notify, Transport, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime); ttest_tcp_client_start(Node, Notify, - sock, - Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> + Domain, sock, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> TransportMod = socket_test_ttest_tcp_socket, - Transport = {TransportMod, #{method => plain}}, + Transport = {TransportMod, #{domain => Domain, + async => true, + method => plain}}, socket_test_ttest_tcp_client:start_monitor(Node, Notify, Transport, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-define(TTEST_MANAGER, esock_ttest_manager). + +-record(ttest_report_id, + {domain :: socket:domain(), + serv_trans :: gen | sock, + serv_active :: once | boolean(), + client_trans :: gen | sock, + client_active :: once | boolean(), + msg_id :: small | medium | large}). + +-record(ttest_report, {id :: #ttest_report_id{}, + time :: non_neg_integer(), + bytes :: non_neg_integer(), + msgs :: non_neg_integer()}). + +-spec ttest_report(Domain :: socket:domain(), + ServTrans :: gen | sock, ServActive :: once | boolean(), + ClientTrans :: gen | sock, ClientActive :: once | boolean(), + MsgID :: 1 | 2 | 3, + RunTime :: non_neg_integer(), + NumBytes :: non_neg_integer(), + NumMsgs :: non_neg_integer()) -> ok. + +ttest_report(Domain, + ServTrans, ServActive, + ClientTrans, ClientActive, + MsgID, + RunTime, + NumBytes, + NumMsgs) -> + ID = #ttest_report_id{domain = Domain, + serv_trans = ServTrans, + serv_active = ServActive, + client_trans = ClientTrans, + client_active = ClientActive, + msg_id = ttest_msg_id_num_to_name(MsgID)}, + Report = #ttest_report{id = ID, + time = RunTime, + bytes = NumBytes, + msgs = NumMsgs}, + %% If we run just one test case, the group init has never been run + %% and therefor the ttest manager is not running (we also don't actually + %% care about collecting reports in that case). + (catch global:send(?TTEST_MANAGER, Report)), + ok. + +ttest_msg_id_num_to_name(1) -> + small; +ttest_msg_id_num_to_name(2) -> + medium; +ttest_msg_id_num_to_name(3) -> + large. + +ttest_manager_start() -> + Self = self(), + {Pid, MRef} = spawn_monitor(fun() -> ttest_manager_init(Self) end), + receive + {ttest_manager_started, Pid} -> + erlang:demonitor(MRef, [flush]), + ok; + {'DOWN', MRef, process, Pid, Reason} -> + exit({failed_starting, ttest_manager, Reason}) + after 5000 -> + exit(Pid, kill), + exit({failed_starting, ttest_manager, timeout}) + end. + +ttest_manager_stop() -> + case global:whereis_name(?TTEST_MANAGER) of + Pid when is_pid(Pid) -> + erlang:monitor(process, Pid), + global:send(?TTEST_MANAGER, stop), + receive + {'DOWN', _MRef, process, Pid, _} -> + ok + after 10000 -> + exit(Pid, kill), + ok + end; + _ -> + ok + end. + +ttest_manager_init(Parent) -> + yes = global:register_name(?TTEST_MANAGER, self()), + ets:new(?TTEST_MANAGER, + [{keypos, #ttest_report.id}, named_table, protected, ordered_set]), + Parent ! {ttest_manager_started, self()}, + ttest_manager_loop(). + +ttest_manager_loop() -> + receive + stop -> + ?LOGGER:format("manager stopping~n", []), + ttest_manager_done(); + + #ttest_report{id = _ID, + time = _RunTime, + bytes = _NumBytes, + msgs = _NumMsgs} = Report -> + true = ets:insert_new(?TTEST_MANAGER, Report), + ttest_manager_loop() + end. + +%% We are supposed to pretty print the result here... +ttest_manager_done() -> + format_reports(inet), + %% format_reports(inet6), + ets:delete(?TTEST_MANAGER), + exit(normal). + +format_reports(Domain) -> + ?LOGGER:format("Domain ~w reports:~n~n", [Domain]), + format_reports(Domain, small), + format_reports(Domain, medium), + format_reports(Domain, large). + +format_reports(Domain, MsgID) when is_atom(MsgID) -> + case which_ttest_reports(Domain, MsgID) of + [] -> + ?LOGGER:format(" No ~w reports~n~n", [MsgID]); + Reports -> + ?LOGGER:format(" ~w reports: ~n", [MsgID]), + lists:foreach(fun(R) -> format_report(R) end, Reports) + end. + +%% This should really be a table like this: +%% +%% client +%% server gen(false) gen(once) gen(true) sock(false) sock(once) sock(true) +%% gen(false) nnn +%% gen(once) nnn +%% gen(true) nnn +%% sock(false) nnn +%% sock(once) nnn +%% sock(true) nnn +%% +format_report(#ttest_report{id = #ttest_report_id{serv_trans = STrans, + serv_active = SActive, + client_trans = CTrans, + client_active = CActive}, + time = RunTime, + bytes = BCnt, + msgs = MCnt}) -> + ?LOGGER:format(" server ~w[~w] - client ~w[~w] => " + "~n Run Time: ~s" + "~n Bytes: ~s" + "~n Messages: ~s" + "~n", [STrans, SActive, CTrans, CActive, + ?TTEST_LIB:format_time(RunTime), + if ((BCnt =:= 0) orelse (RunTime =:= 0)) -> + ?TTEST_LIB:format("~w, ~w", + [BCnt, RunTime]); + true -> + ?TTEST_LIB:format("~p => ~p byte / ms", + [BCnt, BCnt div RunTime]) + end, + if (RunTime =:= 0) -> + "-"; + true -> + ?TTEST_LIB:format("~p => ~p iterations / ms", + [MCnt, MCnt div RunTime]) + end]), + ok. + + +which_ttest_reports(Domain, all) -> + [R || R = #ttest_report{id = #ttest_report_id{domain = D}} <- + ets:tab2list(?TTEST_MANAGER), Domain =:= D]; +which_ttest_reports(Domain, MsgID) -> + [R || R = #ttest_report{id = #ttest_report_id{domain = D, msg_id = MID}} <- + ets:tab2list(?TTEST_MANAGER), (Domain =:= D) andalso (MsgID =:= MID)]. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% This mechanism has only one purpose: So that we are able to kill %% the node-starter process if it takes to long. The node-starter %% runs on the local node. @@ -17801,19 +28687,6 @@ sock_open(Domain, Type, Proto) -> end. -sock_bind(Sock, SockAddr) -> - try socket:bind(Sock, SockAddr) of - {ok, Port} -> - Port; - {error, Reason} -> - i("sock_bind -> error: ~p", [Reason]), - ?FAIL({bind, Reason}) - catch - C:E:S -> - i("sock_bind -> failed: ~p, ~p, ~p", [C, E, S]), - ?FAIL({bind, C, E, S}) - end. - sock_connect(Sock, SockAddr) -> try socket:connect(Sock, SockAddr) of ok -> @@ -17836,39 +28709,6 @@ sock_sockname(Sock) -> ?FAIL({sockname, C, E, S}) end. - -%% sock_listen(Sock) -> -%% sock_listen2(fun() -> socket:listen(Sock) end). - -%% sock_listen(Sock, BackLog) -> -%% sock_listen2(fun() -> socket:listen(Sock, BackLog) end). - -%% sock_listen2(Listen) -> -%% try Listen() of -%% ok -> -%% ok; -%% {error, Reason} -> -%% ?FAIL({listen, Reason}) -%% catch -%% C:E:S -> -%% ?FAIL({listen, C, E, S}) -%% end. - - -%% sock_accept(LSock) -> -%% try socket:accept(LSock) of -%% {ok, Sock} -> -%% Sock; -%% {error, Reason} -> -%% i("sock_accept -> error: ~p", [Reason]), -%% ?FAIL({accept, Reason}) -%% catch -%% C:E:S -> -%% i("sock_accept -> failed: ~p, ~p, ~p", [C, E, S]), -%% ?FAIL({accept, C, E, S}) -%% end. - - sock_close(Sock) -> try socket:close(Sock) of ok -> @@ -17899,64 +28739,213 @@ local_host() -> end. +%% The point of this is to "ensure" that paths from different test runs +%% don't clash. +mk_unique_path() -> + [NodeName | _] = string:tokens(atom_to_list(node()), [$@]), + Path = ?LIB:f("/tmp/esock_~s_~w", [NodeName, erlang:system_time(nanosecond)]), + ensure_unique_path(Path). + +ensure_unique_path(Path) -> + case file:read_file_info(Path) of + {ok, _} -> % Ouch, append a unique ID and try again + ensure_unique_path(Path, 1); + {error, _} -> + %% We assume this means it does not exist yet... + %% If we have several process in paralell trying to create + %% (unique) path's, then we are in trouble. To *really* be + %% on the safe side we should have a (central) path registry... + Path + end. + +ensure_unique_path(Path, ID) when (ID < 100) -> % If this is not enough... + NewPath = ?LIB:f("~s_~w", [Path, ID]), + case file:read_file_info(NewPath) of + {ok, _} -> % Ouch, this also existed, increment and try again + ensure_unique_path(Path, ID + 1); + {error, _} -> % We assume this means it does not exist yet... + NewPath + end; +ensure_unique_path(_, _) -> + skip("Could not create unique path"). + + +which_local_socket_addr(local = Domain) -> + #{family => Domain, + path => mk_unique_path()}; + +%% This gets the local socket address (not 127.0...) +%% We should really implement this using the (new) net module, +%% but until that gets the necessary functionality... +which_local_socket_addr(Domain) -> + case ?LIB:which_local_host_info(Domain) of + {ok, #{addr := Addr}} -> + #{family => Domain, + addr => Addr}; + {error, Reason} -> + ?FAIL(Reason) + end. + + + +which_local_addr(local = _Domain) -> + mk_unique_path(); + %% This gets the local address (not 127.0...) %% We should really implement this using the (new) net module, %% but until that gets the necessary functionality... which_local_addr(Domain) -> - case inet:getifaddrs() of - {ok, IFL} -> - which_addr(Domain, IFL); + ?LIB:which_local_addr(Domain). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Here are all the *general* test case condition functions. + +%% We also need (be able) to figure out the the multicast address, +%% which we only support for some platforms (linux and sunos). +%% We don't do that here, but since we can only do that (find a +%% multicast address) for specific platforms, we check that we are +%% on of those platforms here. +has_support_ip_multicast() -> + case os:type() of + {unix, OsName} when (OsName =:= linux) orelse + (OsName =:= sunos) -> + case ?LIB:which_local_host_info(inet) of + {ok, #{flags := Flags}} -> + case lists:member(multicast, Flags) of + true -> + ok; + false -> + not_supported(multicast) + end; + {error, Reason} -> + not_supported({multicast, Reason}) + end; + {unix, OsName} -> + skip(?F("Not Supported: platform ~w", [OsName])); + Type -> + skip(?F("Not Supported: platform ~p", [Type])) + end. + +has_support_sock_acceptconn() -> + has_support_socket_option_sock(acceptconn). + +has_support_sock_bindtodevice() -> + has_support_socket_option_sock(bindtodevice). + +has_support_sock_broadcast() -> + has_support_socket_option_sock(broadcast), + case ?LIB:which_local_host_info(inet) of + {ok, #{flags := Flags}} -> + case lists:member(broadcast, Flags) of + true -> + ok; + false -> + not_supported({broadcast, Flags}) + end; {error, Reason} -> - ?FAIL({inet, getifaddrs, Reason}) + not_supported({broadcast, Reason}) end. -which_addr(_Domain, []) -> - ?FAIL(no_address); -which_addr(Domain, [{"lo" ++ _, _}|IFL]) -> - which_addr(Domain, IFL); -which_addr(Domain, [{_Name, IFO}|IFL]) -> - case which_addr2(Domain, IFO) of - {ok, Addr} -> - Addr; - {error, no_address} -> - which_addr(Domain, IFL) - end; -which_addr(Domain, [_|IFL]) -> - which_addr(Domain, IFL). +has_support_sock_debug() -> + has_support_socket_option_sock(debug). -which_addr2(_Domain, []) -> - {error, no_address}; -which_addr2(inet = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 4) -> - {ok, Addr}; -which_addr2(inet6 = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 8) -> - {ok, Addr}; -which_addr2(Domain, [_|IFO]) -> - which_addr2(Domain, IFO). +has_support_sock_domain() -> + has_support_socket_option_sock(domain). +has_support_sock_dontroute() -> + has_support_socket_option_sock(dontroute). +has_support_sock_keepalive() -> + has_support_socket_option_sock(keepalive). +has_support_ip_add_membership() -> + has_support_socket_option_ip(add_membership). + +has_support_ip_drop_membership() -> + has_support_socket_option_ip(drop_membership). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Here are all the *general* test vase condition functions. +has_support_socket_option_ip(Opt) -> + has_support_socket_option(ip, Opt). + +has_support_socket_option_sock(Opt) -> + has_support_socket_option(socket, Opt). + +has_support_socket_option(Level, Option) -> + case socket:supports(options, Level, Option) of + true -> + ok; + false -> + skip(?F("Not Supported: ~w option ~w", [Level, Option])) + end. + + + + +unix_domain_socket_host_cond() -> + unix_domain_socket_host_cond(os:type(), os:version()). + +unix_domain_socket_host_cond({unix, linux}, {M, _, _}) when (M < 3) -> + skip("TC may not work on this version"); +unix_domain_socket_host_cond(_, _) -> + ok. + +has_support_unix_domain_socket() -> + case os:type() of + {win32, _} -> + skip("Not supported"); + _ -> + case socket:supports(local) of + true -> + ok; + false -> + skip("Not supported") + end + end. + %% The idea is that this function shall test if the test host has %% support for IPv6. If not, there is no point in running IPv6 tests. %% Currently we just skip. has_support_ipv6() -> - %% case socket:supports(ipv6) of - %% true -> - %% ok; - %% false -> - %% {error, not_supported} - %% end. - not_yet_implemented(). + ?LIB:has_support_ipv6(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +unlink_path(Path) -> + unlink_path(Path, fun() -> ok end, fun() -> ok end). + +unlink_path(Path, Success, Failure) when is_list(Path) andalso + is_function(Success, 0) andalso + is_function(Failure, 0) -> + ?SEV_IPRINT("try unlink path: " + "~n ~s", [Path]), + case os:cmd("unlink " ++ Path) of + "" -> + ?SEV_IPRINT("path unlinked: " + "~n Path: ~s", [Path]), + Success(); + Result -> + ?SEV_EPRINT("unlink maybe failed: " + "~n Path: ~s" + "~n Res: ~s", [Path, Result]), + Failure() + end; +unlink_path(_, _, _) -> + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +not_supported(What) -> + skip({not_supported, What}). + not_yet_implemented() -> skip("not yet implemented"). diff --git a/erts/emulator/test/socket_test_lib.erl b/erts/emulator/test/socket_test_lib.erl index 4e65c4f3c0..39cbf0c79f 100644 --- a/erts/emulator/test/socket_test_lib.erl +++ b/erts/emulator/test/socket_test_lib.erl @@ -32,6 +32,12 @@ %% String and format f/2, + %% Generic 'has support' test function(s) + has_support_ipv6/0, + + which_local_host_info/1, + which_local_addr/1, + %% Skipping not_yet_implemented/0, skip/1 @@ -40,6 +46,11 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-define(FAIL(R), exit(R)). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + pi(Item) when is_atom(Item) -> pi(self(), Item). @@ -88,6 +99,196 @@ f(F, A) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +has_support_ipv6() -> + case socket:supports(ipv6) of + true -> + ok; + false -> + skip("IPv6 Not Supported") + end, + Domain = inet6, + LocalAddr = + case which_local_addr(Domain) of + {ok, Addr} -> + Addr; + {error, R1} -> + skip(f("Local Address eval failed: ~p", [R1])) + end, + ServerSock = + case socket:open(Domain, dgram, udp) of + {ok, SS} -> + SS; + {error, R2} -> + skip(f("(server) socket open failed: ~p", [R2])) + end, + LocalSA = #{family => Domain, addr => LocalAddr}, + ServerPort = + case socket:bind(ServerSock, LocalSA) of + {ok, P1} -> + P1; + {error, R3} -> + socket:close(ServerSock), + skip(f("(server) socket bind failed: ~p", [R3])) + end, + ServerSA = LocalSA#{port => ServerPort}, + ClientSock = + case socket:open(Domain, dgram, udp) of + {ok, CS} -> + CS; + {error, R4} -> + skip(f("(client) socket open failed: ~p", [R4])) + end, + case socket:bind(ClientSock, LocalSA) of + {ok, _} -> + ok; + {error, R5} -> + socket:close(ServerSock), + socket:close(ClientSock), + skip(f("(client) socket bind failed: ~p", [R5])) + end, + case socket:sendto(ClientSock, <<"hejsan">>, ServerSA) of + ok -> + ok; + {error, R6} -> + socket:close(ServerSock), + socket:close(ClientSock), + skip(f("failed socket sendto test: ~p", [R6])) + end, + case socket:recvfrom(ServerSock) of + {ok, {_, <<"hejsan">>}} -> + socket:close(ServerSock), + socket:close(ClientSock), + ok; + {error, R7} -> + socket:close(ServerSock), + socket:close(ClientSock), + skip(f("failed socket recvfrom test: ~p", [R7])) + end. + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This gets the local address (not {127, _} or {0, ...} or {16#fe80, ...}) +%% We should really implement this using the (new) net module, +%% but until that gets the necessary functionality... +which_local_addr(Domain) -> + case which_local_host_info(Domain) of + {ok, #{addr := Addr}} -> + {ok, Addr}; + {error, _Reason} = ERROR -> + ERROR + end. + + +%% Returns the interface (name), flags and address (not 127...) +%% of the local host. +which_local_host_info(Domain) -> + case inet:getifaddrs() of + {ok, IFL} -> + which_local_host_info(Domain, IFL); + {error, _} = ERROR -> + ERROR + end. + +which_local_host_info(_Domain, []) -> + {error, no_address}; +which_local_host_info(Domain, [{"lo" ++ _, _}|IFL]) -> + which_local_host_info(Domain, IFL); +which_local_host_info(Domain, [{"docker" ++ _, _}|IFL]) -> + which_local_host_info(Domain, IFL); +which_local_host_info(Domain, [{"br-" ++ _, _}|IFL]) -> + which_local_host_info(Domain, IFL); +which_local_host_info(Domain, [{Name, IFO}|IFL]) -> + try which_local_host_info2(Domain, IFO) of + Info -> + {ok, Info#{name => Name}} + catch + throw:_:_ -> + which_local_host_info(Domain, IFL) + end; +which_local_host_info(Domain, [_|IFL]) -> + which_local_host_info(Domain, IFL). + +%% which_local_host_info2(Domain, IFO) -> +%% case lists:keysearch(flags, 1, IFO) of +%% {value, {flags, Flags}} -> +%% which_local_host_info2(Domain, IFO, Flags); +%% false -> +%% {error, no_flags} +%% end. + + +%% which_local_host_info2(_Domain, [], _Flags) -> +%% {error, no_address}; +%% which_local_host_info2(inet = _Domain, [{addr, Addr}|_IFO], Flags) +%% when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) -> +%% {ok, {Flags, Addr}}; +%% which_local_host_info2(inet6 = _Domain, [{addr, Addr}|_IFO], Flags) +%% when (size(Addr) =:= 8) andalso +%% (element(1, Addr) =/= 0) andalso +%% (element(1, Addr) =/= 16#fe80) -> +%% {ok, {Flags, Addr}}; +%% which_local_host_info2(Domain, [_|IFO], Flags) -> +%% which_local_host_info2(Domain, IFO, Flags). + +%% foo(Info, inet = Domain, IFO) -> +%% foo(Info, Domain, IFO, [flags, addr, netmask, broadaddr, hwaddr]); +%% foo(Info, inet6 = Domain, IFO) -> +%% foo(Info, Domain, IFO, [flags, addr, netmask, hwaddr]). + +which_local_host_info2(inet = _Domain, IFO) -> + Addr = which_local_host_info3(addr, IFO, + fun({A, _, _, _}) when (A =/= 127) -> true; + (_) -> false + end), + NetMask = which_local_host_info3(netmask, IFO, + fun({_, _, _, _}) -> true; + (_) -> false + end), + BroadAddr = which_local_host_info3(broadaddr, IFO, + fun({_, _, _, _}) -> true; + (_) -> false + end), + Flags = which_local_host_info3(flags, IFO, fun(_) -> true end), + #{flags => Flags, + addr => Addr, + broadaddr => BroadAddr, + netmask => NetMask}; +which_local_host_info2(inet6 = _Domain, IFO) -> + Addr = which_local_host_info3(addr, IFO, + fun({A, _, _, _, _, _, _, _}) + when (A =/= 0) andalso + (A =/= 16#fe80) -> true; + (_) -> false + end), + NetMask = which_local_host_info3(netmask, IFO, + fun({_, _, _, _, _, _, _, _}) -> true; + (_) -> false + end), + Flags = which_local_host_info3(flags, IFO, fun(_) -> true end), + #{flags => Flags, + addr => Addr, + netmask => NetMask}. + +which_local_host_info3(_Key, [], _) -> + throw({error, no_address}); +which_local_host_info3(Key, [{Key, Val}|IFO], Check) -> + case Check(Val) of + true -> + Val; + false -> + which_local_host_info3(Key, IFO, Check) + end; +which_local_host_info3(Key, [_|IFO], Check) -> + which_local_host_info3(Key, IFO, Check). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + not_yet_implemented() -> skip("not yet implemented"). diff --git a/erts/emulator/test/socket_test_logger.erl b/erts/emulator/test/socket_test_logger.erl index 26610e9ef3..f5d4c8c7b2 100644 --- a/erts/emulator/test/socket_test_logger.erl +++ b/erts/emulator/test/socket_test_logger.erl @@ -43,7 +43,7 @@ start(Quiet) -> ok; undefined -> Self = self(), - Pid = spawn_link(fun() -> init(Self, Quiet) end), + Pid = spawn(fun() -> init(Self, Quiet) end), yes = global:register_name(?LOGGER, Pid), ok end. diff --git a/erts/emulator/test/socket_test_ttest_tcp_client.erl b/erts/emulator/test/socket_test_ttest_tcp_client.erl index 5efa3fe491..f28819ca69 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_client.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_client.erl @@ -42,16 +42,16 @@ -export([ %% These are for the test suite - start_monitor/6, start_monitor/7, start_monitor/9, + start_monitor/5, start_monitor/6, start_monitor/8, %% These are for starting in a shell when run "manually" - start/4, start/5, start/7, start/8, + start/3, start/4, start/6, start/7, stop/1 ]). %% Internal exports -export([ - do_start/10 + do_start/9 ]). -include_lib("kernel/include/inet.hrl"). @@ -80,25 +80,25 @@ %% ========================================================================== -start_monitor(Node, Notify, Transport, Active, Addr, Port) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, ?MSG_ID_DEFAULT). +start_monitor(Node, Notify, Transport, ServerInfo, Active) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, ?MSG_ID_DEFAULT). -start_monitor(Node, Notify, Transport, Active, Addr, Port, 1 = MsgID) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, MsgID, +start_monitor(Node, Notify, Transport, ServerInfo, Active, 1 = MsgID) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_1, ?RUNTIME_DEFAULT); -start_monitor(Node, Notify, Transport, Active, Addr, Port, 2 = MsgID) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, MsgID, +start_monitor(Node, Notify, Transport, ServerInfo, Active, 2 = MsgID) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_2, ?RUNTIME_DEFAULT); -start_monitor(Node, Notify, Transport, Active, Addr, Port, 3 = MsgID) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, MsgID, +start_monitor(Node, Notify, Transport, ServerInfo, Active, 3 = MsgID) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_3, ?RUNTIME_DEFAULT). -start_monitor(Node, Notify, Transport, Active, Addr, Port, +start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) when (Node =/= node()) -> Args = [false, self(), Notify, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime], + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime], case rpc:call(Node, ?MODULE, do_start, Args) of {badrpc, _} = Reason -> {error, Reason}; @@ -108,11 +108,11 @@ start_monitor(Node, Notify, Transport, Active, Addr, Port, {error, _} = ERROR -> ERROR end; -start_monitor(_, Notify, Transport, Active, Addr, Port, +start_monitor(_, Notify, Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> case do_start(false, self(), Notify, - Transport, Active, Addr, Port, + Transport, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) of {ok, Pid} -> MRef = erlang:monitor(process, Pid), @@ -122,50 +122,48 @@ start_monitor(_, Notify, Transport, Active, Addr, Port, end. -start(Transport, Active, Addr, Port) -> - start(Transport, Active, Addr, Port, ?MSG_ID_DEFAULT). +start(Transport, ServerInfo, Active) -> + start(Transport, ServerInfo, Active, ?MSG_ID_DEFAULT). -start(Transport, Active, Addr, Port, 1 = MsgID) -> +start(Transport, ServerInfo, Active, 1 = MsgID) -> start(false, - Transport, Active, Addr, Port, MsgID, + Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_1, ?RUNTIME_DEFAULT); -start(Transport, Active, Addr, Port, 2 = MsgID) -> +start(Transport, ServerInfo, Active, 2 = MsgID) -> start(false, - Transport, Active, Addr, Port, MsgID, + Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_2, ?RUNTIME_DEFAULT); -start(Transport, Active, Addr, Port, 3 = MsgID) -> +start(Transport, ServerInfo, Active, 3 = MsgID) -> start(false, - Transport, Active, Addr, Port, MsgID, + Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_3, ?RUNTIME_DEFAULT). -start(Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> start(false, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime). + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -start(Quiet, Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Quiet, Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> Notify = fun(R) -> present_results(R) end, do_start(Quiet, self(), Notify, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime). + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -spec do_start(Quiet, Parent, Notify, Transport, + ServerInfo, Active, - Addr, - Port, MsgID, MaxOutstanding, RunTime) -> {ok, Pid} | {error, Reason} when - Quiet :: pid(), + Quiet :: boolean(), Parent :: pid(), Notify :: function(), Transport :: atom() | tuple(), + ServerInfo :: {inet:ip_address(), inet:port_number()} | string(), Active :: active(), - Addr :: inet:ip_address(), - Port :: inet:port_number(), MsgID :: msg_id(), MaxOutstanding :: max_outstanding(), RunTime :: runtime(), @@ -174,14 +172,13 @@ start(Quiet, Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> do_start(Quiet, Parent, Notify, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) when is_boolean(Quiet) andalso is_pid(Parent) andalso is_function(Notify) andalso (is_atom(Transport) orelse is_tuple(Transport)) andalso (is_boolean(Active) orelse (Active =:= once)) andalso - is_tuple(Addr) andalso - (is_integer(Port) andalso (Port > 0)) andalso + (is_tuple(ServerInfo) orelse is_list(ServerInfo)) andalso (is_integer(MsgID) andalso (MsgID >= 1) andalso (MsgID =< 3)) andalso (is_integer(MaxOutstanding) andalso (MaxOutstanding > 0)) andalso (is_integer(RunTime) andalso (RunTime > 0)) -> @@ -191,7 +188,7 @@ do_start(Quiet, Starter, Parent, Notify, - Transport, Active, Addr, Port, + Transport, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) end, {Pid, MRef} = spawn_monitor(Init), @@ -217,25 +214,30 @@ stop(Pid) when is_pid(Pid) -> init(Quiet, Starter, Parent, Notify, - Transport, Active, Addr, Port, + Transport, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) -> if not Quiet -> ?I("init with" "~n Transport: ~p" "~n Active: ~p" - "~n Addr: ~s" - "~n Port: ~p" + "~n ServerInfo: ~s" "~n Msg ID: ~p (=> 16 + ~w bytes)" "~n Max Outstanding: ~p" "~n (Suggested) Run Time: ~p ms", - [Transport, Active, inet:ntoa(Addr), Port, + [Transport, Active, + case ServerInfo of + {Addr, Port} -> + ?F("Addr: ~s, Port: ~w", [inet:ntoa(Addr), Port]); + Path -> + Path + end, MsgID, size(which_msg_data(MsgID)), MaxOutstanding, RunTime]); true -> ok end, {Mod, Connect} = process_transport(Transport), - case Connect(Addr, Port) of + case Connect(ServerInfo) of {ok, Sock} -> if not Quiet -> ?I("connected"); true -> ok @@ -264,14 +266,21 @@ init(Quiet, (catch Mod:close(Sock)), exit(normal); {error, Reason} -> - ?E("connect failed: ~p", [Reason]), - exit({connect, Reason}) + ?E("connect failed: ~p" + "~n ~p", [Reason, ServerInfo]), + exit({connect, Reason, ServerInfo}) end. process_transport(Mod) when is_atom(Mod) -> - {Mod, fun(A, P) -> Mod:connect(A, P) end}; -process_transport({Mod, Opts}) -> - {Mod, fun(A, P) -> Mod:connect(A, P, Opts) end}. + %% In this case we assume it to be a plain tcp socket + {Mod, fun({A, P}) -> Mod:connect(A, P) end}; +process_transport({Mod, #{domain := Domain} = Opts}) -> + Connect = + case Domain of + local -> fun(Path) -> Mod:connect(Path, Opts) end; + _ -> fun({A, P}) -> Mod:connect(A, P, Opts) end + end, + {Mod, Connect}. which_msg_data(1) -> ?MSG_DATA1; diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl index 0ec2e908d7..65a3a94d38 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl @@ -21,28 +21,28 @@ -module(socket_test_ttest_tcp_client_gen). -export([ - start/3, start/4, start/6, start/7, + start/2, start/3, start/5, start/6, stop/1 ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_gen). -start(Active, Addr, Port) -> - socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, Active, Addr, Port). +start(ServerInfo, Active) -> + socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, ServerInfo, Active). -start(Active, Addr, Port, MsgID) -> - socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, Active, Addr, Port, MsgID). +start(ServerInfo, Active, MsgID) -> + socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, ServerInfo, Active, MsgID). -start(Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> socket_test_ttest_tcp_client:start(false, ?TRANSPORT_MOD, - Active, Addr, Port, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -start(Quiet, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Quiet, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> socket_test_ttest_tcp_client:start(Quiet, ?TRANSPORT_MOD, - Active, Addr, Port, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime). stop(Pid) -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl index acf2556793..2fb1242028 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl @@ -26,25 +26,90 @@ ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_socket). --define(MOD(M), {?TRANSPORT_MOD, #{method => Method}}). +-define(MOD(D, A, M), {?TRANSPORT_MOD, #{domain => D, + async => A, + method => M}}). -start(Method, Active, Addr, Port) -> - socket_test_ttest_tcp_client:start_monitor(?MOD(Method), Active, Addr, Port). +start(Method, Async, Active, ServerInfo) + when is_list(ServerInfo) -> + Domain = local, + socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method), + ServerInfo, Active); +start(Method, Async, Active, ServerInfo = {Addr, _}) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + Domain = inet, + socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method), + ServerInfo, Active); +start(Method, Async, Active, ServerInfo = {Addr, _}) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, + socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method), + ServerInfo, Active). -start(Method, Active, Addr, Port, MsgID) -> - socket_test_ttest_tcp_client:start(?MOD(Method), - Active, Addr, Port, MsgID). +start(Method, Async, Active, ServerInfo, MsgID) + when is_list(ServerInfo) -> + %% This is just a simplification + Domain = local, + socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method), + ServerInfo, Active, MsgID); +start(Method, Async, Active, ServerInfo = {Addr, _}, MsgID) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + %% This is just a simplification + Domain = inet, + socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method), + Active, ServerInfo, MsgID); +start(Method, Async, Active, ServerInfo = {Addr, _}, MsgID) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, + socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method), + ServerInfo, Active, MsgID). -start(Method, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Method, Async, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) + when is_list(ServerInfo) -> + Domain = local, socket_test_ttest_tcp_client:start(false, - ?MOD(Method), - Active, Addr, Port, + ?MOD(Domain, Async, Method), + ServerInfo, Active, + MsgID, MaxOutstanding, RunTime); +start(Method, Async, Active, ServerInfo = {Addr, _}, + MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + Domain = inet, + socket_test_ttest_tcp_client:start(false, + ?MOD(Domain, Async, Method), + ServerInfo, Active, + MsgID, MaxOutstanding, RunTime); +start(Method, Async, Active, ServerInfo = {Addr, _}, + MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, + socket_test_ttest_tcp_client:start(false, + ?MOD(Domain, Async, Method), + ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -start(Quiet, Method, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Quiet, Async, Active, Method, ServerInfo, MsgID, MaxOutstanding, RunTime) + when is_list(ServerInfo) -> + Domain = local, + socket_test_ttest_tcp_client:start(Quiet, + ?MOD(Domain, Async, Method), + ServerInfo, Active, + MsgID, MaxOutstanding, RunTime); +start(Quiet, Async, Active, Method, ServerInfo = {Addr, _}, + MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + Domain = inet, + socket_test_ttest_tcp_client:start(Quiet, + ?MOD(Domain, Async, Method), + ServerInfo, Active, + MsgID, MaxOutstanding, RunTime); +start(Quiet, Async, Active, Method, ServerInfo = {Addr, _}, + MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, socket_test_ttest_tcp_client:start(Quiet, - ?MOD(Method), - Active, Addr, Port, + ?MOD(Domain, Async, Method), + ServerInfo, Active, MsgID, MaxOutstanding, RunTime). stop(Pid) -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_gen.erl index 604408c489..e59bd881e7 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_gen.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_gen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. 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. @@ -24,9 +24,9 @@ accept/1, accept/2, active/2, close/1, - connect/2, + connect/2, connect/3, controlling_process/2, - listen/0, listen/1, + listen/0, listen/1, listen/2, peername/1, port/1, recv/2, recv/3, @@ -36,19 +36,7 @@ ]). -%% ========================================================================== - -%% getopt(Sock, Opt) when is_atom(Opt) -> -%% case inet:getopts(Sock, [Opt]) of -%% {ok, [{Opt, Value}]} -> -%% {ok, Value}; -%% {error, _} = ERROR -> -%% ERROR -%% end. - -%% setopt(Sock, Opt, Value) when is_atom(Opt) -> -%% inet:setopts(Sock, [{Opt, Value}]). - +-define(LIB, socket_test_lib). %% ========================================================================== @@ -80,6 +68,13 @@ close(Sock) -> connect(Addr, Port) -> Opts = [binary, {packet, raw}, {active, false}, {buffer, 32*1024}], + do_connect(Addr, Port, Opts). + +connect(Addr, Port, #{domain := Domain}) -> + Opts = [Domain, binary, {packet, raw}, {active, false}, {buffer, 32*1024}], + do_connect(Addr, Port, Opts). + +do_connect(Addr, Port, Opts) -> case gen_tcp:connect(Addr, Port, Opts) of {ok, Sock} -> {ok, Sock}; @@ -95,12 +90,16 @@ controlling_process(Sock, NewPid) -> listen() -> listen(0). -listen(Port) when is_integer(Port) andalso (Port >= 0) -> - Opts = [binary, {ip, {0,0,0,0}}, {packet, raw}, {active, false}, - {buffer, 32*1024}], - case gen_tcp:listen(Port, Opts) of - {ok, Sock} -> - {ok, Sock}; +listen(Port) -> + listen(Port, #{domain => inet}). + +listen(Port, #{domain := Domain}) when is_integer(Port) andalso (Port >= 0) -> + case ?LIB:which_local_host_info(Domain) of + {ok, {_, _, Addr}} -> + Opts = [Domain, + binary, {ip, Addr}, {packet, raw}, {active, false}, + {buffer, 32*1024}], + gen_tcp:listen(Port, Opts); {error, _} = ERROR -> ERROR end. diff --git a/erts/emulator/test/socket_test_ttest_tcp_server.erl b/erts/emulator/test/socket_test_ttest_tcp_server.erl index e8d626e3d8..2394dc7924 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_server.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. 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. @@ -90,14 +90,17 @@ start_monitor(_, Transport, Active) -> start(Transport, Active) -> do_start(self(), Transport, Active). - +%% Note that the Async option is actually only "used" for the +%% socket transport module (it details how to implement the +%% active feature). do_start(Parent, Transport, Active) when is_pid(Parent) andalso (is_atom(Transport) orelse is_tuple(Transport)) andalso (is_boolean(Active) orelse (Active =:= once)) -> Starter = self(), - ServerInit = fun() -> put(sname, "server"), - server_init(Starter, Parent, Transport, Active) + ServerInit = fun() -> + put(sname, "server"), + server_init(Starter, Parent, Transport, Active) end, {Pid, MRef} = spawn_monitor(ServerInit), receive @@ -126,17 +129,35 @@ server_init(Starter, Parent, Transport, Active) -> case Listen(0) of {ok, LSock} -> case Mod:port(LSock) of - {ok, Port} -> - Addr = which_addr(), % This is just for convenience - ?I("listening on:" - "~n Addr: ~p (~s)" - "~n Port: ~w" - "~n", [Addr, inet:ntoa(Addr), Port]), - Starter ! {?MODULE, self(), {ok, {Addr, Port}}}, + {ok, PortOrPath} -> + Result = + if + is_integer(PortOrPath) -> + %% This is just for convenience + case Mod:sockname(LSock) of + {ok, {Addr, _}} -> + ?I("listening on:" + "~n Addr: ~p (~s)" + "~n Port: ~w" + "~n", [Addr, + inet:ntoa(Addr), + PortOrPath]), + {Addr, PortOrPath}; + {error, SNReason} -> + exit({sockname, SNReason}) + end; + is_list(PortOrPath) -> + ?I("listening on:" + "~n Path: ~s" + "~n", [PortOrPath]), + PortOrPath + end, + Starter ! {?MODULE, self(), {ok, Result}}, server_loop(#{parent => Parent, mod => Mod, active => Active, lsock => LSock, + port_or_path => PortOrPath, handlers => [], stats_interval => StatsInterval, %% Accumulation @@ -166,49 +187,68 @@ process_transport({Mod, Opts}, _Active) -> server_loop(State) -> - server_loop( server_handle_message( server_accept(State) ) ). + server_loop( server_handle_message( server_accept(State, ?ACC_TIMEOUT), 0) ). -server_accept(#{mod := Mod, - active := Active, - lsock := LSock, - handlers := Handlers} = State) -> - case Mod:accept(LSock, ?ACC_TIMEOUT) of +server_accept(#{mod := Mod, lsock := LSock} = State, Timeout) -> + case Mod:accept(LSock, Timeout) of {ok, Sock} -> - ?I("accepted connection from ~s", - [case Mod:peername(Sock) of - {ok, Peer} -> - format_peername(Peer); - {error, _} -> - "-" - end]), - {Pid, _} = handler_start(), - ?I("handler ~p started -> try transfer socket control", [Pid]), - case Mod:controlling_process(Sock, Pid) of - ok -> - maybe_start_stats_timer(State, Pid), - ?I("server-accept: handler ~p started", [Pid]), - handler_continue(Pid, Mod, Sock, Active), - Handlers2 = [Pid | Handlers], - State#{handlers => Handlers2}; - {error, CPReason} -> - (catch Mod:close(Sock)), - (catch Mod:close(LSock)), - exit({controlling_process, CPReason}) - end; - {error, timeout} -> + server_handle_accepted(State, Sock); + {error, timeout} when (Timeout =/= nowait) -> State; {error, AReason} -> (catch Mod:close(LSock)), exit({accept, AReason}) end. +%% server_accept(#{mod := Mod, +%% lsock := LSock} = State) -> +%% case Mod:accept(LSock, ?ACC_TIMEOUT) of +%% {ok, Sock} -> +%% server_handle_accepted(State, Sock); +%% {error, timeout} -> +%% State; +%% {error, AReason} -> +%% (catch Mod:close(LSock)), +%% exit({accept, AReason}) +%% end. + +server_handle_accepted(#{mod := Mod, + lsock := LSock, + active := Active, + handlers := Handlers} = State, + Sock) -> + ?I("accepted connection from ~s", + [case Mod:peername(Sock) of + {ok, Peer} -> + format_peername(Peer); + {error, _} -> + "-" + end]), + {Pid, _} = handler_start(), + ?I("handler ~p started -> try transfer socket control", [Pid]), + case Mod:controlling_process(Sock, Pid) of + ok -> + maybe_start_stats_timer(State, Pid), + ?I("server-accept: handler ~p started", [Pid]), + handler_continue(Pid, Mod, Sock, Active), + Handlers2 = [Pid | Handlers], + State#{handlers => Handlers2}; + {error, CPReason} -> + (catch Mod:close(Sock)), + (catch Mod:close(LSock)), + exit({controlling_process, CPReason}) + end. + + format_peername({Addr, Port}) -> case inet:gethostbyaddr(Addr) of {ok, #hostent{h_name = N}} -> ?F("~s (~s:~w)", [N, inet:ntoa(Addr), Port]); {error, _} -> ?F("~p, ~p", [Addr, Port]) - end. + end; +format_peername(Path) when is_list(Path) -> + Path. maybe_start_stats_timer(#{active := Active, stats_interval := Time}, Handler) when (Active =/= false) andalso (is_integer(Time) andalso (Time > 0)) -> @@ -219,7 +259,10 @@ maybe_start_stats_timer(_, _) -> start_stats_timer(Time, ProcStr, Pid) -> erlang:start_timer(Time, self(), {stats, Time, ProcStr, Pid}). -server_handle_message(#{parent := Parent, handlers := H} = State) -> +server_handle_message(#{mod := Mod, + lsock := LSock, + parent := Parent, + handlers := H} = State, Timeout) -> receive {timeout, _TRef, {stats, Interval, ProcStr, Pid}} -> case server_handle_stats(ProcStr, Pid) of @@ -229,16 +272,17 @@ server_handle_message(#{parent := Parent, handlers := H} = State) -> ok end, State; - + {?MODULE, Ref, Parent, stop} -> reply(Parent, Ref, ok), lists:foreach(fun(P) -> handler_stop(P) end, H), + (catch Mod:close(LSock)), exit(normal); {'DOWN', _MRef, process, Pid, Reason} -> server_handle_down(Pid, Reason, State) - after 0 -> + after Timeout -> State end. @@ -272,28 +316,26 @@ server_handle_handler_down(Pid, AccMCnt2 = AccMCnt + MCnt, AccBCnt2 = AccBCnt + BCnt, AccHCnt2 = AccHCnt + 1, - ?I("handler ~p (~w) done => accumulated results: " - "~n Run Time: ~s ms" + MsgCount2Str = + fun(RT, ART, MC, AMC) when (RT > 0) -> + ?F("~w => ~w (~w) msgs / ms", [MC, MC div RT, AMC div ART]); + (_, _, MC, AMC) -> + ?F("~w (~w)", [MC, AMC]) + end, + ByteCount2Str = + fun(RT, ART, BC, ABC) when (RT > 0) -> + ?F("~w => ~w (~w) bytes / ms", [BC, BC div RT, ABC div ART]); + (_, _, BC, ABC) -> + ?F("~w", [BC, ABC]) + end, + ?I("handler ~p (~w) done: " + "~n Run Time: ~s" "~n Message Count: ~s" "~n Byte Count: ~s", [Pid, AccHCnt2, - ?FORMAT_TIME(AccRunTime2), - if (AccRunTime2 > 0) -> - ?F("~w => ~w (~w) msgs / ms", - [AccMCnt2, - AccMCnt2 div AccRunTime2, - (AccMCnt2 div AccHCnt2) div AccRunTime2]); - true -> - ?F("~w", [AccMCnt2]) - end, - if (AccRunTime2 > 0) -> - ?F("~w => ~w (~w) bytes / ms", - [AccBCnt2, - AccBCnt2 div AccRunTime2, - (AccBCnt2 div AccHCnt2) div AccRunTime2]); - true -> - ?F("~w", [AccBCnt2]) - end]), + ?FORMAT_TIME(RunTime), + MsgCount2Str(RunTime, AccRunTime2, MCnt, AccMCnt2), + ByteCount2Str(RunTime, AccRunTime2, BCnt, AccBCnt2)]), State#{runtime => AccRunTime2, mcnt => AccMCnt2, bcnt => AccBCnt2, @@ -325,15 +367,15 @@ handler_init(Parent) -> ?I("received continue"), reply(Parent, Ref, ok), handler_initial_activation(Mod, Sock, Active), - handler_loop(#{parent => Parent, - mod => Mod, - sock => Sock, - active => Active, - start => ?T(), - mcnt => 0, - bcnt => 0, - last_reply => none, - acc => <<>>}) + handler_loop(#{parent => Parent, + mod => Mod, + sock => Sock, + active => Active, + start => ?T(), + mcnt => 0, + bcnt => 0, + last_reply => none, + acc => <<>>}) after 5000 -> ?I("timeout when message queue: " @@ -533,51 +575,51 @@ handler_maybe_activate(_, _, _) -> %% ========================================================================== -which_addr() -> - case inet:getifaddrs() of - {ok, IfAddrs} -> - which_addrs(inet, IfAddrs); - {error, Reason} -> - exit({getifaddrs, Reason}) - end. - -which_addrs(_Family, []) -> - exit({getifaddrs, not_found}); -which_addrs(Family, [{"lo", _} | IfAddrs]) -> - %% Skip - which_addrs(Family, IfAddrs); -which_addrs(Family, [{"docker" ++ _, _} | IfAddrs]) -> - %% Skip docker - which_addrs(Family, IfAddrs); -which_addrs(Family, [{"br-" ++ _, _} | IfAddrs]) -> - %% Skip docker - which_addrs(Family, IfAddrs); -which_addrs(Family, [{"en" ++ _, IfOpts} | IfAddrs]) -> - %% Maybe take this one - case which_addr(Family, IfOpts) of - {ok, Addr} -> - Addr; - error -> - which_addrs(Family, IfAddrs) - end; -which_addrs(Family, [{_IfName, IfOpts} | IfAddrs]) -> - case which_addr(Family, IfOpts) of - {ok, Addr} -> - Addr; - error -> - which_addrs(Family, IfAddrs) - end. - -which_addr(_, []) -> - error; -which_addr(inet, [{addr, Addr}|_]) - when is_tuple(Addr) andalso (size(Addr) =:= 4) -> - {ok, Addr}; -which_addr(inet6, [{addr, Addr}|_]) - when is_tuple(Addr) andalso (size(Addr) =:= 8) -> - {ok, Addr}; -which_addr(Family, [_|IfOpts]) -> - which_addr(Family, IfOpts). +%% which_addr() -> +%% case inet:getifaddrs() of +%% {ok, IfAddrs} -> +%% which_addrs(inet, IfAddrs); +%% {error, Reason} -> +%% exit({getifaddrs, Reason}) +%% end. + +%% which_addrs(_Family, []) -> +%% exit({getifaddrs, not_found}); +%% which_addrs(Family, [{"lo", _} | IfAddrs]) -> +%% %% Skip +%% which_addrs(Family, IfAddrs); +%% which_addrs(Family, [{"docker" ++ _, _} | IfAddrs]) -> +%% %% Skip docker +%% which_addrs(Family, IfAddrs); +%% which_addrs(Family, [{"br-" ++ _, _} | IfAddrs]) -> +%% %% Skip docker +%% which_addrs(Family, IfAddrs); +%% which_addrs(Family, [{"en" ++ _, IfOpts} | IfAddrs]) -> +%% %% Maybe take this one +%% case which_addr(Family, IfOpts) of +%% {ok, Addr} -> +%% Addr; +%% error -> +%% which_addrs(Family, IfAddrs) +%% end; +%% which_addrs(Family, [{_IfName, IfOpts} | IfAddrs]) -> +%% case which_addr(Family, IfOpts) of +%% {ok, Addr} -> +%% Addr; +%% error -> +%% which_addrs(Family, IfAddrs) +%% end. + +%% which_addr(_, []) -> +%% error; +%% which_addr(inet, [{addr, Addr}|_]) +%% when is_tuple(Addr) andalso (size(Addr) =:= 4) -> +%% {ok, Addr}; +%% which_addr(inet6, [{addr, Addr}|_]) +%% when is_tuple(Addr) andalso (size(Addr) =:= 8) -> +%% {ok, Addr}; +%% which_addr(Family, [_|IfOpts]) -> +%% which_addr(Family, IfOpts). %% ========================================================================== diff --git a/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl index b1b31f5158..fdf40f1369 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. 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. @@ -21,20 +21,18 @@ -module(socket_test_ttest_tcp_server_gen). -export([ - start/1, + start/1, start/2, stop/1 ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_gen). +-define(MOD(D), {?TRANSPORT_MOD, #{domain => D}}). start(Active) -> - socket_test_ttest_tcp_server:start(?TRANSPORT_MOD, Active). - %% {ok, {Pid, AddrPort}} -> - %% MRef = erlang:monitor(process, Pid), - %% {ok, {Pid, MRef, AddrPort}}; - %% {error, _} = ERROR -> - %% ERROR - %% end. + start(inet, Active). + +start(Domain, Active) -> + socket_test_ttest_tcp_server:start(?MOD(Domain), Active). stop(Pid) -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl index b7ea1e8e93..4045bf4e4e 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. 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. @@ -21,17 +21,20 @@ -module(socket_test_ttest_tcp_server_socket). -export([ - start/2, + start/4, stop/1 ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_socket). -%% -define(MOD(M), {?TRANSPORT_MOD, #{method => M, +%% -define(MOD(M), {?TRANSPORT_MOD, #{async => false, +%% method => M, %% stats_interval => 10000}}). --define(MOD(M), {?TRANSPORT_MOD, #{method => M}}). +-define(MOD(D,M,A), {?TRANSPORT_MOD, #{domain => D, + async => A, + method => M}}). -start(Method, Active) -> - socket_test_ttest_tcp_server:start(?MOD(Method), Active). +start(Method, Domain, Async, Active) -> + socket_test_ttest_tcp_server:start(?MOD(Domain, Method, Async), Active). %% {ok, {Pid, AddrPort}} -> %% MRef = erlang:monitor(process, Pid), %% {ok, {Pid, MRef, AddrPort}}; diff --git a/erts/emulator/test/socket_test_ttest_tcp_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_socket.erl index 0ae2412e4c..9112748b4c 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_socket.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_socket.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. 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. @@ -24,7 +24,7 @@ accept/1, accept/2, active/2, close/1, - connect/2, connect/3, + connect/1, connect/2, connect/3, controlling_process/2, listen/0, listen/1, listen/2, port/1, @@ -36,6 +36,8 @@ ]). +-define(LIB, socket_test_lib). + -define(READER_RECV_TIMEOUT, 1000). -define(DATA_MSG(Sock, Method, Data), @@ -55,7 +57,7 @@ %% ========================================================================== -%% This does not really work. Its just a placeholder for the time beeing... +%% This does not really work. Its just a placeholder for the time being... %% getopt(Sock, Opt) when is_atom(Opt) -> %% socket:getopt(Sock, socket, Opt). @@ -66,22 +68,32 @@ %% ========================================================================== -accept(#{sock := LSock, opts := #{method := Method} = Opts}) -> +%% The way we use server async its no point in doing a async accept call +%% (we do never actually run the test with more than one client). +accept(#{sock := LSock, opts := #{async := Async, + method := Method} = Opts}) -> case socket:accept(LSock) of - {ok, Sock} -> + {ok, Sock} -> Self = self(), - Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end), + Reader = spawn(fun() -> + reader_init(Self, Sock, Async, false, Method) + end), maybe_start_stats_timer(Opts, Reader), {ok, #{sock => Sock, reader => Reader, method => Method}}; {error, _} = ERROR -> ERROR end. -accept(#{sock := LSock, opts := #{method := Method} = Opts}, Timeout) -> +%% If a timeout has been explictly specified, then we do not use +%% async here. We will pass it on to the reader process. +accept(#{sock := LSock, opts := #{async := Async, + method := Method} = Opts}, Timeout) -> case socket:accept(LSock, Timeout) of {ok, Sock} -> Self = self(), - Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end), + Reader = spawn(fun() -> + reader_init(Self, Sock, Async, false, Method) + end), maybe_start_stats_timer(Opts, Reader), {ok, #{sock => Sock, reader => Reader, method => Method}}; {error, _} = ERROR -> @@ -97,41 +109,92 @@ active(#{reader := Pid}, NewActive) close(#{sock := Sock, reader := Pid}) -> Pid ! {?MODULE, stop}, - socket:close(Sock). + Unlink = case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + fun() -> os:cmd("unlink " ++ Path), ok end; + _ -> + fun() -> ok end + end, + Res = socket:close(Sock), + Unlink(), + Res. %% Create a socket and connect it to a peer -connect(Addr, Port) -> - connect(Addr, Port, #{method => plain}). - -connect(Addr, Port, #{method := Method} = Opts) -> +connect(ServerPath) when is_list(ServerPath) -> + Domain = local, + ClientPath = mk_unique_path(), + LocalSA = #{family => Domain, + path => ClientPath}, + ServerSA = #{family => Domain, path => ServerPath}, + Opts = #{domain => Domain, + proto => default, + method => plain}, + Cleanup = fun() -> os:cmd("unlink " ++ ClientPath), ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts). + +connect(Addr, Port) when is_tuple(Addr) andalso is_integer(Port) -> + Domain = inet, + LocalSA = any, + ServerSA = #{family => Domain, + addr => Addr, + port => Port}, + Opts = #{domain => Domain, + proto => tcp, + method => plain}, + Cleanup = fun() -> ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts); +connect(ServerPath, + #{domain := local = Domain} = Opts) + when is_list(ServerPath) -> + ClientPath = mk_unique_path(), + LocalSA = #{family => Domain, + path => ClientPath}, + ServerSA = #{family => Domain, + path => ServerPath}, + Cleanup = fun() -> os:cmd("unlink " ++ ClientPath), ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts#{proto => default}). + +connect(Addr, Port, #{domain := Domain} = Opts) -> + LocalSA = any, + ServerSA = #{family => Domain, + addr => Addr, + port => Port}, + Cleanup = fun() -> ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts#{proto => tcp}). + +do_connect(LocalSA, ServerSA, Cleanup, #{domain := Domain, + proto := Proto, + async := Async, + method := Method} = Opts) -> try begin Sock = - case socket:open(inet, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, S} -> S; {error, OReason} -> throw({error, {open, OReason}}) end, - case socket:bind(Sock, any) of + case socket:bind(Sock, LocalSA) of {ok, _} -> ok; {error, BReason} -> (catch socket:close(Sock)), + Cleanup(), throw({error, {bind, BReason}}) end, - SA = #{family => inet, - addr => Addr, - port => Port}, - case socket:connect(Sock, SA) of + case socket:connect(Sock, ServerSA) of ok -> ok; {error, CReason} -> (catch socket:close(Sock)), + Cleanup(), throw({error, {connect, CReason}}) end, Self = self(), - Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end), + Reader = spawn(fun() -> + reader_init(Self, Sock, Async, false, Method) + end), maybe_start_stats_timer(Opts, Reader), {ok, #{sock => Sock, reader => Reader, method => Method}} end @@ -140,6 +203,9 @@ connect(Addr, Port, #{method := Method} = Opts) -> ERROR end. +mk_unique_path() -> + [NodeName | _] = string:tokens(atom_to_list(node()), [$@]), + ?LIB:f("/tmp/esock_~s_~w", [NodeName, erlang:system_time(nanosecond)]). maybe_start_stats_timer(#{stats_to := Pid, stats_interval := T}, @@ -163,37 +229,65 @@ controlling_process(#{sock := Sock, reader := Pid}, NewPid) -> %% Create a listen socket listen() -> - listen(0, #{method => plain}). + listen(0). + +listen(Port) when is_integer(Port) -> + listen(Port, #{domain => inet, async => false, method => plain}); +listen(Path) when is_list(Path) -> + listen(Path, #{domain => local, async => false, method => plain}). + +listen(0, #{domain := local} = Opts) -> + listen(mk_unique_path(), Opts); +listen(Path, #{domain := local = Domain} = Opts) + when is_list(Path) andalso (Path =/= []) -> + SA = #{family => Domain, + path => Path}, + Cleanup = fun() -> os:cmd("unlink " ++ Path), ok end, + do_listen(SA, Cleanup, Opts#{proto => default}); +listen(Port, #{domain := Domain} = Opts) + when is_integer(Port) andalso (Port >= 0) -> + %% Bind fills in the rest + case ?LIB:which_local_host_info(Domain) of + {ok, {_, _, Addr}} -> + SA = #{family => Domain, + addr => Addr, + port => Port}, + Cleanup = fun() -> ok end, + do_listen(SA, Cleanup, Opts#{proto => tcp}); + {error, _} = ERROR -> + ERROR + end. -listen(Port) -> - listen(Port, #{method => plain}). -listen(Port, #{method := Method} = Opts) - when (is_integer(Port) andalso (Port >= 0)) andalso - ((Method =:= plain) orelse (Method =:= msg)) -> +do_listen(SA, + Cleanup, + #{domain := Domain, proto := Proto, + async := Async, method := Method} = Opts) + when (Method =:= plain) orelse (Method =:= msg) andalso + is_boolean(Async) -> try begin - Sock = case socket:open(inet, stream, tcp) of + Sock = case socket:open(Domain, stream, Proto) of {ok, S} -> S; {error, OReason} -> throw({error, {open, OReason}}) end, - SA = #{family => inet, - port => Port}, case socket:bind(Sock, SA) of {ok, _} -> ok; {error, BReason} -> (catch socket:close(Sock)), + Cleanup(), throw({error, {bind, BReason}}) end, case socket:listen(Sock) of ok -> - ok; - {error, LReason} -> + ok; + {error, LReason} -> (catch socket:close(Sock)), - throw({error, {listen, LReason}}) - end, + Cleanup(), + throw({error, {listen, LReason}}) + end, {ok, #{sock => Sock, opts => Opts}} end catch @@ -204,6 +298,8 @@ listen(Port, #{method := Method} = Opts) port(#{sock := Sock}) -> case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + {ok, Path}; {ok, #{port := Port}} -> {ok, Port}; {error, _} = ERROR -> @@ -213,6 +309,8 @@ port(#{sock := Sock}) -> peername(#{sock := Sock}) -> case socket:peername(Sock) of + {ok, #{family := local, path := Path}} -> + {ok, Path}; {ok, #{addr := Addr, port := Port}} -> {ok, {Addr, Port}}; {error, _} = ERROR -> @@ -262,13 +360,18 @@ sockname(#{sock := Sock}) -> %% ========================================================================== -reader_init(ControllingProcess, Sock, Active, Method) +reader_init(ControllingProcess, Sock, Async, Active, Method) when is_pid(ControllingProcess) andalso + is_boolean(Async) andalso (is_boolean(Active) orelse (Active =:= once)) andalso ((Method =:= plain) orelse (Method =:= msg)) -> + put(verbose, false), MRef = erlang:monitor(process, ControllingProcess), reader_loop(#{ctrl_proc => ControllingProcess, ctrl_proc_mref => MRef, + async => Async, + select_info => undefined, + select_num => 0, % Count the number of select messages active => Active, sock => Sock, method => Method}). @@ -279,11 +382,11 @@ reader_loop(#{active := false, ctrl_proc := Pid} = State) -> receive {?MODULE, stop} -> - exit(normal); + reader_exit(State, stop); {?MODULE, Pid, controlling_process, NewPid} -> - MRef = maps:get(ctrl_proc_mref, State), - erlang:demonitor(MRef, [flush]), + OldMRef = maps:get(ctrl_proc_mref, State), + erlang:demonitor(OldMRef, [flush]), NewMRef = erlang:monitor(process, NewPid), Pid ! {?MODULE, self(), controlling_process}, reader_loop(State#{ctrl_proc => NewPid, @@ -294,10 +397,8 @@ reader_loop(#{active := false, {'DOWN', MRef, process, Pid, Reason} -> case maps:get(ctrl_proc_mref, State) of - MRef when (Reason =:= normal) -> - exit(normal); MRef -> - exit({controlling_process, Reason}); + reader_exit(State, {ctrl_exit, Reason}); _ -> reader_loop(State) end @@ -305,7 +406,8 @@ reader_loop(#{active := false, %% Read *once* and then change to false reader_loop(#{active := once, - sock := Sock, + async := false, + sock := Sock, method := Method, ctrl_proc := Pid} = State) -> case do_recv(Method, Sock) of @@ -315,25 +417,23 @@ reader_loop(#{active := once, {error, timeout} -> receive {?MODULE, stop} -> - exit(normal); + reader_exit(State, stop); {?MODULE, Pid, controlling_process, NewPid} -> - MRef = maps:get(ctrl_proc_mref, State), - erlang:demonitor(MRef, [flush]), - MRef = erlang:monitor(process, NewPid), + OldMRef = maps:get(ctrl_proc_mref, State), + erlang:demonitor(OldMRef, [flush]), + NewMRef = erlang:monitor(process, NewPid), Pid ! {?MODULE, self(), controlling_process}, reader_loop(State#{ctrl_proc => NewPid, - ctrl_proc_mref => MRef}); + ctrl_proc_mref => NewMRef}); {?MODULE, active, NewActive} -> reader_loop(State#{active => NewActive}); {'DOWN', MRef, process, Pid, Reason} -> case maps:get(ctrl_proc_mref, State) of - MRef when (Reason =:= normal) -> - exit(normal); - MRef -> - exit({controlling_process, Reason}); + MRef -> + reader_exit(State, {ctrl_exit, Reason}); _ -> reader_loop(State) end @@ -341,17 +441,86 @@ reader_loop(#{active := once, reader_loop(State) end; - {error, closed} -> + {error, closed} = E1 -> + Pid ! ?CLOSED_MSG(Sock, Method), + reader_exit(State, E1); + + {error, Reason} = E2 -> + Pid ! ?ERROR_MSG(Sock, Method, Reason), + reader_exit(State, E2) + end; +reader_loop(#{active := once, + async := true, + select_info := undefined, + sock := Sock, + method := Method, + ctrl_proc := Pid} = State) -> + case do_recv(Method, Sock, nowait) of + {select, SelectInfo} -> + reader_loop(State#{select_info => SelectInfo}); + {ok, Data} -> + Pid ! ?DATA_MSG(Sock, Method, Data), + reader_loop(State#{active => false}); + + {error, closed} = E1 -> Pid ! ?CLOSED_MSG(Sock, Method), - exit(normal); + reader_exit(State, E1); - {error, Reason} -> + {error, Reason} = E2 -> Pid ! ?ERROR_MSG(Sock, Method, Reason), - exit(Reason) + reader_exit(State, E2) + end; +reader_loop(#{active := once, + async := true, + select_info := {select_info, _, Ref}, + select_num := N, + sock := Sock, + method := Method, + ctrl_proc := Pid} = State) -> + receive + {?MODULE, stop} -> + reader_exit(State, stop); + + {?MODULE, Pid, controlling_process, NewPid} -> + OldMRef = maps:get(ctrl_proc_mref, State), + erlang:demonitor(OldMRef, [flush]), + NewMRef = erlang:monitor(process, NewPid), + Pid ! {?MODULE, self(), controlling_process}, + reader_loop(State#{ctrl_proc => NewPid, + ctrl_proc_mref => NewMRef}); + + {?MODULE, active, NewActive} -> + reader_loop(State#{active => NewActive}); + + {'DOWN', MRef, process, Pid, Reason} -> + case maps:get(ctrl_proc_mref, State) of + MRef -> + reader_exit(State, {ctrl_exit, Reason}); + _ -> + reader_loop(State) + end; + + {'$socket', Sock, select, Ref} -> + case do_recv(Method, Sock, nowait) of + {ok, Data} when is_binary(Data) -> + Pid ! ?DATA_MSG(Sock, Method, Data), + reader_loop(State#{active => false, + select_info => undefined, + select_num => N+1}); + + {error, closed} = E1 -> + Pid ! ?CLOSED_MSG(Sock, Method), + reader_exit(State, E1); + + {error, Reason} = E2 -> + Pid ! ?ERROR_MSG(Sock, Method, Reason), + reader_exit(State, E2) + end end; %% Read and forward data continuously reader_loop(#{active := true, + async := false, sock := Sock, method := Method, ctrl_proc := Pid} = State) -> @@ -362,25 +531,23 @@ reader_loop(#{active := true, {error, timeout} -> receive {?MODULE, stop} -> - exit(normal); + reader_exit(State, stop); {?MODULE, Pid, controlling_process, NewPid} -> - MRef = maps:get(ctrl_proc_mref, State), - erlang:demonitor(MRef, [flush]), - MRef = erlang:monitor(process, NewPid), + OldMRef = maps:get(ctrl_proc_mref, State), + erlang:demonitor(OldMRef, [flush]), + NewMRef = erlang:monitor(process, NewPid), Pid ! {?MODULE, self(), controlling_process}, reader_loop(State#{ctrl_proc => NewPid, - ctrl_proc_mref => MRef}); + ctrl_proc_mref => NewMRef}); {?MODULE, active, NewActive} -> reader_loop(State#{active => NewActive}); {'DOWN', MRef, process, Pid, Reason} -> case maps:get(ctrl_proc_mref, State) of - MRef when (Reason =:= normal) -> - exit(normal); MRef -> - exit({controlling_process, Reason}); + reader_exit(State, {ctrl_exit, Reason}); _ -> reader_loop(State) end @@ -388,27 +555,170 @@ reader_loop(#{active := true, reader_loop(State) end; - {error, closed} -> + {error, closed} = E1 -> Pid ! ?CLOSED_MSG(Sock, Method), - exit(normal); + reader_exit(State, E1); - {error, Reason} -> + {error, Reason} = E2 -> Pid ! ?ERROR_MSG(Sock, Method, Reason), - exit(Reason) + reader_exit(State, E2) + end; +reader_loop(#{active := true, + async := true, + select_info := undefined, + sock := Sock, + method := Method, + ctrl_proc := Pid} = State) -> + case do_recv(Method, Sock) of + {select, SelectInfo} -> + reader_loop(State#{select_info => SelectInfo}); + {ok, Data} -> + Pid ! ?DATA_MSG(Sock, Method, Data), + reader_loop(State); + + {error, closed} = E1 -> + Pid ! ?CLOSED_MSG(Sock, Method), + reader_exit(State, E1); + + {error, Reason} = E2 -> + Pid ! ?ERROR_MSG(Sock, Method, Reason), + reader_exit(State, E2) + end; +reader_loop(#{active := true, + async := true, + select_info := {select_info, _, Ref}, + select_num := N, + sock := Sock, + method := Method, + ctrl_proc := Pid} = State) -> + receive + {?MODULE, stop} -> + reader_exit(State, stop); + + {?MODULE, Pid, controlling_process, NewPid} -> + OldMRef = maps:get(ctrl_proc_mref, State), + erlang:demonitor(OldMRef, [flush]), + NewMRef = erlang:monitor(process, NewPid), + Pid ! {?MODULE, self(), controlling_process}, + reader_loop(State#{ctrl_proc => NewPid, + ctrl_proc_mref => NewMRef}); + + {?MODULE, active, NewActive} -> + reader_loop(State#{active => NewActive}); + + {'DOWN', MRef, process, Pid, Reason} -> + case maps:get(ctrl_proc_mref, State) of + MRef -> + reader_exit(State, {ctrl_exit, Reason}); + _ -> + reader_loop(State) + end; + + {'$socket', Sock, select, Ref} -> + case do_recv(Method, Sock, nowait) of + {ok, Data} when is_binary(Data) -> + Pid ! ?DATA_MSG(Sock, Method, Data), + reader_loop(State#{select_info => undefined, + select_num => N+1}); + + {error, closed} = E1 -> + Pid ! ?CLOSED_MSG(Sock, Method), + reader_exit(State, E1); + + {error, Reason} = E2 -> + Pid ! ?ERROR_MSG(Sock, Method, Reason), + reader_exit(State, E2) + end end. -do_recv(plain, Sock) -> - socket:recv(Sock, 0, ?READER_RECV_TIMEOUT); -do_recv(msg, Sock) -> - case socket:recvmsg(Sock, 0, 0, [], ?READER_RECV_TIMEOUT) of +do_recv(Method, Sock) -> + do_recv(Method, Sock, ?READER_RECV_TIMEOUT). + +do_recv(plain, Sock, Timeout) -> + socket:recv(Sock, 0, Timeout); +do_recv(msg, Sock, Timeout) -> + case socket:recvmsg(Sock, 0, 0, [], Timeout) of {ok, #{iov := [Bin]}} -> {ok, Bin}; + {select, _} = SELECT -> + SELECT; {error, _} = ERROR -> ERROR end. - - + + +reader_exit(#{async := false, active := Active}, stop) -> + vp("reader stopped when active: ~w", [Active]), + exit(normal); +reader_exit(#{async := true, + active := Active, + select_info := SelectInfo, + select_num := N}, stop) -> + vp("reader stopped when active: ~w" + "~n Current select info: ~p" + "~n Number of select messages: ~p", [Active, SelectInfo, N]), + exit(normal); +reader_exit(#{async := false, active := Active}, {ctrl_exit, normal}) -> + vp("reader ctrl exit when active: ~w", [Active]), + exit(normal); +reader_exit(#{async := true, + active := Active, + select_info := SelectInfo, + select_num := N}, {ctrl_exit, normal}) -> + vp("reader ctrl exit when active: ~w" + "~n Current select info: ~p" + "~n Number of select messages: ~p", [Active, SelectInfo, N]), + exit(normal); +reader_exit(#{async := false, active := Active}, {ctrl_exit, Reason}) -> + vp("reader exit when ctrl crash when active: ~w", [Active]), + exit({controlling_process, Reason}); +reader_exit(#{async := true, + active := Active, + select_info := SelectInfo, + select_num := N}, {ctrl_exit, Reason}) -> + vp("reader exit when ctrl crash when active: ~w" + "~n Current select info: ~p" + "~n Number of select messages: ~p", [Active, SelectInfo, N]), + exit({controlling_process, Reason}); +reader_exit(#{async := false, active := Active}, {error, closed}) -> + vp("reader exit when socket closed when active: ~w", [Active]), + exit(normal); +reader_exit(#{async := true, + active := Active, + select_info := SelectInfo, + select_num := N}, {error, closed}) -> + vp("reader exit when socket closed when active: ~w " + "~n Current select info: ~p" + "~n Number of select messages: ~p", [Active, SelectInfo, N]), + exit(normal); +reader_exit(#{async := false, active := Active}, {error, Reason}) -> + vp("reader exit when socket error when active: ~w", [Active]), + exit(Reason); +reader_exit(#{async := true, + active := Active, + select_info := SelectInfo, + select_num := N}, {error, Reason}) -> + vp("reader exit when socket error when active: ~w: " + "~n Current select info: ~p" + "~n Number of select messages: ~p", [Active, SelectInfo, N]), + exit(Reason). + + + + + %% ========================================================================== +vp(F, A) -> + vp(get(verbose), F, A). + +vp(true, F, A) -> + p(F, A); +vp(_, _, _) -> + ok. + +p(F, A) -> + io:format(F ++ "~n", A). + diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index 55b1162cfb..b48be3dd04 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -37,10 +37,13 @@ -export([process_count/1, system_version/1, misc_smoke_tests/1, heap_size/1, wordsize/1, memory/1, ets_limit/1, atom_limit/1, + procs_bug/1, ets_count/1, atom_count/1, system_logger/1]). -export([init/1, handle_event/2, handle_call/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 2}}]. @@ -48,8 +51,20 @@ suite() -> all() -> [process_count, system_version, misc_smoke_tests, ets_count, heap_size, wordsize, memory, ets_limit, atom_limit, atom_count, + procs_bug, system_logger]. + +init_per_testcase(procs_bug, Config) -> + procs_bug(init_per_testcase, Config); +init_per_testcase(_, Config) -> + Config. + +end_per_testcase(procs_bug, Config) -> + procs_bug(end_per_testcase, Config); +end_per_testcase(_, _) -> + ok. + %%% %%% The test cases ------------------------------------------------------------- %%% @@ -654,3 +669,41 @@ handle_call(Msg, State) -> handle_event(Event, State) -> State ! {report_handler, Event}, {ok, State}. + + +%% OTP-15909: Provoke bug that would cause VM crash +%% if doing system_info(procs) when process have queued exit/down signals. +procs_bug(init_per_testcase, Config) -> + %% Use single scheduler and process prio to starve monitoring processes + %% from handling their received DOWN signals. + OldSchedOnline = erlang:system_flag(schedulers_online,1), + [{schedulers_online, OldSchedOnline} | Config]; +procs_bug(end_per_testcase, Config) -> + erlang:system_flag(schedulers_online, + proplists:get_value(schedulers_online, Config)), + ok. + +procs_bug(Config) when is_list(Config) -> + {Monee,_} = spawn_opt(fun () -> receive die -> ok end end, + [monitor,{priority,max}]), + Papa = self(), + Pids = [begin + P = spawn_opt(fun () -> + erlang:monitor(process, Monee), + Papa ! {self(),ready}, + receive "nada" -> no end + end, + [link, {priority,normal}]), + {P, ready} = receive M -> M end, + P + end + || _ <- lists:seq(1,10)], + process_flag(priority,high), + Monee ! die, + {'DOWN',_,process,Monee,normal} = receive M -> M end, + + %% This call did crash VM as Pids have pending DOWN signals. + erlang:system_info(procs), + process_flag(priority,normal), + [begin unlink(P), exit(P, kill) end || P <- Pids], + ok. |