aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test')
-rw-r--r--erts/emulator/test/Makefile36
-rw-r--r--erts/emulator/test/alloc_SUITE.erl3
-rw-r--r--erts/emulator/test/bif_SUITE.erl54
-rw-r--r--erts/emulator/test/binary_SUITE.erl178
-rw-r--r--erts/emulator/test/bs_construct_SUITE.erl14
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl2
-rw-r--r--erts/emulator/test/code_SUITE.erl26
-rw-r--r--erts/emulator/test/code_SUITE_data/literals.erl8
-rw-r--r--erts/emulator/test/distribution_SUITE.erl336
-rw-r--r--erts/emulator/test/driver_SUITE.erl19
-rw-r--r--erts/emulator/test/emulator_bench.spec1
-rw-r--r--erts/emulator/test/erts_debug_SUITE.erl31
-rw-r--r--erts/emulator/test/erts_test_utils.erl271
-rw-r--r--erts/emulator/test/esock_ttest/.gitignore0
-rwxr-xr-xerts/emulator/test/esock_ttest/esock-ttest352
-rwxr-xr-xerts/emulator/test/esock_ttest/esock-ttest-client72
-rwxr-xr-xerts/emulator/test/esock_ttest/esock-ttest-server-gen32
-rwxr-xr-xerts/emulator/test/esock_ttest/esock-ttest-server-sock32
-rw-r--r--erts/emulator/test/exception_SUITE.erl5
-rw-r--r--erts/emulator/test/fun_SUITE.erl26
-rw-r--r--erts/emulator/test/net_SUITE.erl481
-rw-r--r--erts/emulator/test/nif_SUITE.erl149
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c115
-rw-r--r--erts/emulator/test/node_container_SUITE.erl259
-rw-r--r--erts/emulator/test/process_SUITE.erl8
-rw-r--r--erts/emulator/test/smoke_test_SUITE.erl7
-rw-r--r--erts/emulator/test/socket_SUITE.erl17434
-rw-r--r--erts/emulator/test/socket_client.erl538
-rw-r--r--erts/emulator/test/socket_lib.erl133
-rw-r--r--erts/emulator/test/socket_server.erl954
-rw-r--r--erts/emulator/test/socket_test_evaluator.erl524
-rw-r--r--erts/emulator/test/socket_test_evaluator.hrl68
-rw-r--r--erts/emulator/test/socket_test_lib.erl98
-rw-r--r--erts/emulator/test/socket_test_logger.erl111
-rw-r--r--erts/emulator/test/socket_test_ttest.hrl32
-rw-r--r--erts/emulator/test/socket_test_ttest_client.hrl141
-rw-r--r--erts/emulator/test/socket_test_ttest_lib.erl127
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_client.erl678
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_client_gen.erl49
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_client_socket.erl51
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_gen.erl138
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_server.erl642
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_server_gen.erl41
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_server_socket.erl43
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_socket.erl414
-rw-r--r--erts/emulator/test/system_info_SUITE.erl13
-rw-r--r--erts/emulator/test/timer_bif_SUITE.erl4
47 files changed, 24406 insertions, 344 deletions
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 6a064ec8d4..9e9cc2ead8 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -28,6 +28,27 @@ EBIN = .
# Target Specs
# ----------------------------------------------------
+SOCKET_MODULES = \
+ socket_lib \
+ socket_server \
+ socket_client \
+ socket_test_lib \
+ socket_test_logger \
+ socket_test_evaluator \
+ socket_test_ttest_lib \
+ socket_test_ttest_tcp_gen \
+ socket_test_ttest_tcp_socket \
+ socket_test_ttest_tcp_client \
+ socket_test_ttest_tcp_client_gen \
+ socket_test_ttest_tcp_client_socket \
+ socket_test_ttest_tcp_server \
+ socket_test_ttest_tcp_server_gen \
+ socket_test_ttest_tcp_server_socket \
+ socket_SUITE
+
+NET_MODULES = \
+ net_SUITE
+
MODULES= \
a_SUITE \
after_SUITE \
@@ -84,6 +105,7 @@ MODULES= \
monitor_SUITE \
multi_load_SUITE \
nested_SUITE \
+ $(NET_MODULES) \
nif_SUITE \
node_container_SUITE \
nofrag_SUITE \
@@ -106,6 +128,7 @@ MODULES= \
sensitive_SUITE \
signal_SUITE \
smoke_test_SUITE \
+ $(SOCKET_MODULES) \
statistics_SUITE \
system_info_SUITE \
system_profile_SUITE \
@@ -130,6 +153,7 @@ MODULES= \
ignore_cores \
dgawd_handler \
random_iolist \
+ erts_test_utils \
crypto_reference
NO_OPT= bs_bincomp \
@@ -152,8 +176,14 @@ NATIVE_MODULES= $(NATIVE:%=%_native_SUITE)
NATIVE_ERL_FILES= $(NATIVE_MODULES:%=%.erl)
ERL_FILES= $(MODULES:%=%.erl)
+HRL_FILES= \
+ socket_test_evaluator.hrl \
+ socket_test_ttest.hrl \
+ socket_test_ttest_client.hrl
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+NET_TARGETS = $(NET_MODULES:%=$(EBIN)/%.$(EMULATOR))
+SOCKET_TARGETS = $(SOCKET_MODULES:%=$(EBIN)/%.$(EMULATOR))
EMAKEFILE=Emakefile
@@ -200,6 +230,10 @@ clean:
docs:
+targets: $(TARGET_FILES)
+socket_targets: $(SOCKET_TARGETS)
+net_targets: $(NET_TARGETS)
+
# ----------------------------------------------------
# Special targets
# ----------------------------------------------------
@@ -220,7 +254,7 @@ release_spec:
release_tests_spec: make_emakefile
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(EMAKEFILE) $(TEST_SPEC_FILES) \
- $(ERL_FILES) "$(RELSYSDIR)"
+ $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(NO_OPT_ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(NATIVE_ERL_FILES) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl
index 343afe85e6..4e0243c1cd 100644
--- a/erts/emulator/test/alloc_SUITE.erl
+++ b/erts/emulator/test/alloc_SUITE.erl
@@ -71,7 +71,8 @@ migration(Cfg) ->
%% Disable driver_alloc to avoid recursive alloc_util calls
%% through enif_mutex_create() in my_creating_mbc().
drv_case(Cfg, concurrent, "+MZe true +MRe false"),
- drv_case(Cfg, concurrent, "+MZe true +MRe false +MZas ageffcbf").
+ drv_case(Cfg, concurrent, "+MZe true +MRe false +MZas ageffcbf"),
+ drv_case(Cfg, concurrent, "+MZe true +MRe false +MZas chaosff").
erts_mmap(Config) when is_list(Config) ->
case {os:type(), mmsc_flags()} of
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index 9e7bcd5255..3eedf2f6a6 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -37,7 +37,8 @@
group_leader_prio/1, group_leader_prio_dirty/1,
is_process_alive/1,
process_info_blast/1,
- os_env_case_sensitivity/1]).
+ os_env_case_sensitivity/1,
+ test_length/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -52,7 +53,8 @@ all() ->
erl_crash_dump_bytes, min_max, erlang_halt, is_builtin,
error_stacktrace, error_stacktrace_during_call_trace,
group_leader_prio, group_leader_prio_dirty,
- is_process_alive, process_info_blast, os_env_case_sensitivity].
+ is_process_alive, process_info_blast, os_env_case_sensitivity,
+ test_length].
%% Uses erlang:display to test that erts_printf does not do deep recursion
display(Config) when is_list(Config) ->
@@ -1181,7 +1183,53 @@ consume_msgs() ->
after 0 ->
ok
end.
-
+
+%% Test that length/1 returns the correct result after trapping, and
+%% also that the argument is correct in the stacktrace for a badarg
+%% exception.
+
+test_length(_Config) ->
+ {Start,Inc} = case test_server:timetrap_scale_factor() of
+ 1 -> {16*4000,3977};
+ _ -> {100,1}
+ end,
+ Good = lists:reverse(lists:seq(1, Start)),
+ Bad = Good ++ [bad|cons],
+ test_length(Start, 10*Start, Inc, Good, Bad),
+
+ %% Test that calling length/1 from a match spec works.
+ MsList = lists:seq(1, 2*Start),
+ MsInput = [{tag,Good},{tag,MsList}],
+ Ms0 = [{{tag,'$1'},[{'>',{length,'$1'},Start}],['$1']}],
+ Ms = ets:match_spec_compile(Ms0),
+ [MsList] = ets:match_spec_run(MsInput, Ms),
+ ok.
+
+test_length(I, N, Inc, Good, Bad) when I < N ->
+ Length = id(length),
+ I = length(Good),
+ I = erlang:Length(Good),
+
+ %% Test length/1 in guards.
+ if
+ length(Good) =:= I ->
+ ok
+ end,
+ if
+ length(Bad) =:= I ->
+ error(should_fail);
+ true ->
+ ok
+ end,
+
+ {'EXIT',{badarg,[{erlang,length,[[I|_]],_}|_]}} = (catch length(Bad)),
+ {'EXIT',{badarg,[{erlang,length,[[I|_]],_}|_]}} = (catch erlang:Length(Bad)),
+ IncSeq = lists:seq(I + 1, I + Inc),
+ test_length(I+Inc, N, Inc,
+ lists:reverse(IncSeq, Good),
+ lists:reverse(IncSeq, Bad));
+test_length(_, _, _, _, _) -> ok.
+
%% helpers
id(I) -> I.
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 23c675733c..1406ddc9dc 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -40,6 +40,7 @@
%%
-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
@@ -50,6 +51,14 @@
terms/1, terms_float/1, float_middle_endian/1,
b2t_used_big/1,
external_size/1, t_iolist_size/1,
+ t_iolist_size_huge_list/1,
+ t_iolist_size_huge_bad_arg_list/1,
+ t_iolist_size_shallow_trapping/1,
+ t_iolist_size_shallow_short_lists/1,
+ t_iolist_size_shallow_tiny_lists/1,
+ t_iolist_size_deep_trapping/1,
+ t_iolist_size_deep_short_lists/1,
+ t_iolist_size_deep_tiny_lists/1,
t_hash/1,
bad_size/1,
bad_term_to_binary/1,
@@ -75,6 +84,9 @@ all() ->
t_split_binary, bad_split,
bad_list_to_binary, bad_binary_to_list, terms,
terms_float, float_middle_endian, external_size, t_iolist_size,
+ t_iolist_size_huge_list,
+ t_iolist_size_huge_bad_arg_list,
+ {group, iolist_size_benchmarks},
b2t_used_big,
bad_binary_to_term_2, safe_binary_to_term2,
bad_binary_to_term, bad_terms, t_hash, bad_size,
@@ -86,13 +98,36 @@ all() ->
error_after_yield, cmp_old_impl].
groups() ->
- [].
+ [
+ {
+ iolist_size_benchmarks,
+ [],
+ [t_iolist_size_shallow_trapping,
+ t_iolist_size_shallow_short_lists,
+ t_iolist_size_shallow_tiny_lists,
+ t_iolist_size_deep_trapping,
+ t_iolist_size_deep_short_lists,
+ t_iolist_size_deep_tiny_lists
+ ]
+ }
+ ].
init_per_suite(Config) ->
+ A0 = case application:start(sasl) of
+ ok -> [sasl];
+ _ -> []
+ end,
+ A = case application:start(os_mon) of
+ ok -> [os_mon|A0];
+ _ -> A0
+ end,
+ [{started_apps, A}|Config].
+
+end_per_suite(Config) ->
+ As = proplists:get_value(started_apps, Config),
+ lists:foreach(fun (A) -> application:stop(A) end, As),
Config.
-end_per_suite(_Config) ->
- ok.
init_per_group(_GroupName, Config) ->
Config.
@@ -615,6 +650,143 @@ build_iolist(N0, Base) ->
[47,L,L|Seq]
end.
+approx_4GB_bin() ->
+ Bin = lists:duplicate(4194304, 255),
+ BinRet = erlang:iolist_to_binary(lists:duplicate(1124, Bin)),
+ BinRet.
+
+duplicate_iolist(IOList, 0) ->
+ IOList;
+duplicate_iolist(IOList, NrOfTimes) ->
+ duplicate_iolist([IOList, IOList], NrOfTimes - 1).
+
+t_iolist_size_huge_list(Config) when is_list(Config) ->
+ run_when_enough_resources(
+ fun() ->
+ {TimeToCreateIOList, IOList} = timer:tc(fun()->duplicate_iolist(approx_4GB_bin(), 32) end),
+ {IOListSizeTime, CalculatedSize} = timer:tc(fun()->erlang:iolist_size(IOList) end),
+ 20248183924657750016 = CalculatedSize,
+ {comment, io_lib:format("Time to create iolist: ~f s. Time to calculate size: ~f s.",
+ [TimeToCreateIOList / 1000000, IOListSizeTime / 1000000])}
+ end).
+
+t_iolist_size_huge_bad_arg_list(Config) when is_list(Config) ->
+ run_when_enough_resources(
+ fun() ->
+ P = self(),
+ spawn_link(fun()-> IOListTmp = duplicate_iolist(approx_4GB_bin(), 32),
+ IOList = [IOListTmp, [badarg]],
+ {'EXIT',{badarg,_}} = (catch erlang:iolist_size(IOList)),
+ P ! ok
+ end),
+ receive ok -> ok end
+ end).
+
+%% iolist_size tests for shallow lists
+
+t_iolist_size_shallow_trapping(Config) when is_list(Config) ->
+ Lengths = [2000, 20000, 200000, 200000, 2000000, 20000000],
+ run_iolist_size_test_and_benchmark(Lengths, fun make_shallow_iolist/2).
+
+t_iolist_size_shallow_short_lists(Config) when is_list(Config) ->
+ Lengths = lists:duplicate(15000, 300),
+ run_iolist_size_test_and_benchmark(Lengths, fun make_shallow_iolist/2).
+
+t_iolist_size_shallow_tiny_lists(Config) when is_list(Config) ->
+ Lengths = lists:duplicate(250000, 18),
+ run_iolist_size_test_and_benchmark(Lengths, fun make_shallow_iolist/2).
+
+make_shallow_iolist(SizeDiv2, LastItem) ->
+ lists:map(
+ fun(I) ->
+ case I of
+ SizeDiv2 -> [1, LastItem];
+ _ -> [1, 1]
+ end
+ end,
+ lists:seq(1, SizeDiv2)).
+
+%% iolist_size tests for deep lists
+
+t_iolist_size_deep_trapping(Config) when is_list(Config) ->
+ Lengths = [2000, 20000, 200000, 200000, 2000000, 10000000],
+ run_iolist_size_test_and_benchmark(Lengths, fun make_deep_iolist/2).
+
+t_iolist_size_deep_short_lists(Config) when is_list(Config) ->
+ Lengths = lists:duplicate(10000, 300),
+ run_iolist_size_test_and_benchmark(Lengths, fun make_deep_iolist/2).
+
+t_iolist_size_deep_tiny_lists(Config) when is_list(Config) ->
+ Lengths = lists:duplicate(150000, 18),
+ run_iolist_size_test_and_benchmark(Lengths, fun make_deep_iolist/2).
+
+make_deep_iolist(1, LastItem) ->
+ [1, LastItem];
+make_deep_iolist(Depth, LastItem) ->
+ [[1, 1], make_deep_iolist(Depth - 1, LastItem)].
+
+% Helper functions for iolist_size tests
+
+run_iolist_size_test_and_benchmark(Lengths, ListGenerator) ->
+ run_when_enough_resources(
+ fun() ->
+ GoodListsWithSizes =
+ lists:map(fun(Length) -> {Length*2, ListGenerator(Length, 1)} end, Lengths),
+ BadListsWithSizes =
+ lists:map(fun(Length) -> {Length*2, ListGenerator(Length, bad)} end, Lengths),
+ erlang:garbage_collect(),
+ report_throughput(
+ fun() ->
+ lists:foreach(
+ fun(_)->
+ lists:foreach(
+ fun({Size, List}) -> Size = iolist_size(List) end,
+ GoodListsWithSizes),
+ lists:foreach(
+ fun({_, List}) -> {'EXIT',_} = (catch (iolist_size(List))) end,
+ BadListsWithSizes)
+ end,
+ lists:seq(1,3))
+ end,
+ lists:sum(Lengths)*4)
+ end).
+
+report_throughput(Fun, NrOfItems) ->
+ Parent = self(),
+ spawn(fun() -> Parent ! timer:tc(Fun) end),
+ {Time, _} = receive D -> D end,
+ ItemsPerMicrosecond = NrOfItems / Time,
+ ct_event:notify(#event{ name = benchmark_data, data = [{value, ItemsPerMicrosecond}]}),
+ {comment, io_lib:format("Items per microsecond: ~p, Nr of items: ~p, Benchmark time: ~p seconds)",
+ [ItemsPerMicrosecond, NrOfItems, Time/1000000])}.
+
+total_memory() ->
+ %% Total memory in GB.
+ try
+ MemoryData = memsup:get_system_memory_data(),
+ case lists:keysearch(total_memory, 1, MemoryData) of
+ {value, {total_memory, TM}} ->
+ TM div (1024*1024*1024);
+ false ->
+ {value, {system_total_memory, STM}} =
+ lists:keysearch(system_total_memory, 1, MemoryData),
+ STM div (1024*1024*1024)
+ end
+ catch
+ _ : _ ->
+ undefined
+ end.
+
+run_when_enough_resources(Fun) ->
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem >= 15 ->
+ Fun();
+ {Mem, WordSize} ->
+ {skipped,
+ io_lib:format("Not enough resources (System Memory >= ~p, Word Size = ~p)",
+ [Mem, WordSize])}
+ end.
+
%% OTP-4053
bad_binary_to_term_2(Config) when is_list(Config) ->
diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl
index ce50bcdd86..ad05cb3689 100644
--- a/erts/emulator/test/bs_construct_SUITE.erl
+++ b/erts/emulator/test/bs_construct_SUITE.erl
@@ -26,7 +26,7 @@
init_per_suite/1, end_per_suite/1,
test1/1, test2/1, test3/1, test4/1, test5/1, testf/1,
not_used/1, in_guard/1,
- mem_leak/1, coerce_to_float/1, bjorn/1,
+ mem_leak/1, coerce_to_float/1, bjorn/1, append_empty_is_same/1,
huge_float_field/1, huge_binary/1, system_limit/1, badarg/1,
copy_writable_binary/1, kostis/1, dynamic/1, bs_add/1,
otp_7422/1, zero_width/1, bad_append/1, bs_add_overflow/1]).
@@ -39,7 +39,7 @@ suite() ->
all() ->
[test1, test2, test3, test4, test5, testf, not_used,
- in_guard, mem_leak, coerce_to_float, bjorn,
+ in_guard, mem_leak, coerce_to_float, bjorn, append_empty_is_same,
huge_float_field, huge_binary, system_limit, badarg,
copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width,
bad_append, bs_add_overflow].
@@ -520,6 +520,16 @@ do_more(Bin, Sz) ->
do_something() ->
throw(blurf).
+append_empty_is_same(Config) when is_list(Config) ->
+ NonWritableBin = <<"123">>,
+ true = erts_debug:same(NonWritableBin, append(NonWritableBin, <<>>)),
+ WritableBin = <<(id(<<>>))/binary,0,1,2,3,4,5,6,7>>,
+ true = erts_debug:same(WritableBin, append(WritableBin, <<>>)),
+ ok.
+
+append(A, B) ->
+ <<A/binary, B/binary>>.
+
huge_float_field(Config) when is_list(Config) ->
{'EXIT',{badarg,_}} = (catch <<0.0:9/float-unit:8>>),
huge_float_check(catch <<0.0:67108865/float-unit:64>>),
diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index d19f7f81ad..742592f88e 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -1395,7 +1395,7 @@ seq(M, N, R) when M =< N ->
seq(M, N-1, [N|R]);
seq(_, _, R) -> R.
-%% lists:reverse can not be called since it is traced
+%% lists:reverse cannot be called since it is traced
reverse(L) ->
reverse(L, []).
%%
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index 0444ba4f89..493c6ebe99 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -332,6 +332,7 @@ constant_pools(Config) when is_list(Config) ->
A = literals:a(),
B = literals:b(),
C = literals:huge_bignum(),
+ D = literals:funs(),
process_flag(trap_exit, true),
Self = self(),
@@ -345,7 +346,7 @@ constant_pools(Config) when is_list(Config) ->
true = erlang:purge_module(literals),
NoOldHeap ! done,
receive
- {'EXIT',NoOldHeap,{A,B,C}} ->
+ {'EXIT',NoOldHeap,{A,B,C,D}} ->
ok;
Other ->
ct:fail({unexpected,Other})
@@ -362,7 +363,7 @@ constant_pools(Config) when is_list(Config) ->
erlang:purge_module(literals),
OldHeap ! done,
receive
- {'EXIT',OldHeap,{A,B,C,[1,2,3|_]=Seq}} when length(Seq) =:= 16 ->
+ {'EXIT',OldHeap,{A,B,C,D,[1,2,3|_]=Seq}} when length(Seq) =:= 16 ->
ok
end,
@@ -390,7 +391,7 @@ constant_pools(Config) when is_list(Config) ->
{'DOWN', Mon, process, Hib, Reason} ->
{undef, [{no_module,
no_function,
- [{A,B,C,[1,2,3|_]=Seq}], _}]} = Reason,
+ [{A,B,C,D,[1,2,3|_]=Seq}], _}]} = Reason,
16 = length(Seq)
end,
HeapSz = TotHeapSz, %% Ensure restored to hibernated state...
@@ -400,7 +401,9 @@ constant_pools(Config) when is_list(Config) ->
no_old_heap(Parent) ->
A = literals:a(),
B = literals:b(),
- Res = {A,B,literals:huge_bignum()},
+ C = literals:huge_bignum(),
+ D = literals:funs(),
+ Res = {A,B,C,D},
Parent ! go,
receive
done ->
@@ -410,7 +413,9 @@ no_old_heap(Parent) ->
old_heap(Parent) ->
A = literals:a(),
B = literals:b(),
- Res = {A,B,literals:huge_bignum(),lists:seq(1, 16)},
+ C = literals:huge_bignum(),
+ D = literals:funs(),
+ Res = {A,B,C,D,lists:seq(1, 16)},
create_old_heap(),
Parent ! go,
receive
@@ -421,7 +426,9 @@ old_heap(Parent) ->
hibernated(Parent) ->
A = literals:a(),
B = literals:b(),
- Res = {A,B,literals:huge_bignum(),lists:seq(1, 16)},
+ C = literals:huge_bignum(),
+ D = literals:funs(),
+ Res = {A,B,C,D,lists:seq(1, 16)},
Parent ! go,
erlang:hibernate(no_module, no_function, [Res]).
@@ -755,7 +762,8 @@ t_copy_literals_frags(Config) when is_list(Config) ->
0, 1, 2, 3, 4, 5, 6, 7,
8, 9,10,11,12,13,14,15,
0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9,10,11,12,13,14,15>>}]),
+ 8, 9,10,11,12,13,14,15>>},
+ {f, fun ?MODULE:all/0}]),
{module, ?mod} = erlang:load_module(?mod, Bin),
N = 6000,
@@ -796,6 +804,7 @@ literal_receiver() ->
C = ?mod:c(),
D = ?mod:d(),
E = ?mod:e(),
+ F = ?mod:f(),
literal_receiver();
{Pid, sender_confirm} ->
io:format("sender confirm ~w~n", [Pid]),
@@ -811,7 +820,8 @@ literal_sender(N, Recv) ->
?mod:b(),
?mod:c(),
?mod:d(),
- ?mod:e()]},
+ ?mod:e(),
+ ?mod:f()]},
literal_sender(N - 1, Recv).
literal_switcher() ->
diff --git a/erts/emulator/test/code_SUITE_data/literals.erl b/erts/emulator/test/code_SUITE_data/literals.erl
index 7c3b0ebe73..13c8b412b0 100644
--- a/erts/emulator/test/code_SUITE_data/literals.erl
+++ b/erts/emulator/test/code_SUITE_data/literals.erl
@@ -19,7 +19,8 @@
%%
-module(literals).
--export([a/0,b/0,huge_bignum/0,binary/0,unused_binaries/0,bits/0]).
+-export([a/0,b/0,huge_bignum/0,funs/0,
+ binary/0,unused_binaries/0,bits/0]).
-export([msg1/0,msg2/0,msg3/0,msg4/0,msg5/0]).
a() ->
@@ -108,3 +109,8 @@ msg2() -> {"hello","world"}.
msg3() -> <<"halloj">>.
msg4() -> #{ 1=> "hello", b => "world"}.
msg5() -> {1,2,3,4,5,6}.
+
+funs() ->
+ %% Literal funs (in a non-literal list).
+ [fun ?MODULE:a/0,
+ fun() -> ok end]. %No environment.
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 885c66331c..4f70b51aa0 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -62,7 +62,12 @@
bad_dist_ext_control/1,
bad_dist_ext_connection_id/1,
bad_dist_ext_size/1,
- start_epmd_false/1, epmd_module/1]).
+ start_epmd_false/1, epmd_module/1,
+ bad_dist_fragments/1,
+ message_latency_large_message/1,
+ message_latency_large_link_exit/1,
+ message_latency_large_monitor_exit/1,
+ message_latency_large_exit2/1]).
%% Internal exports.
-export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
@@ -90,7 +95,8 @@ all() ->
dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip,
atom_roundtrip_r16b,
contended_atom_cache_entry, contended_unicode_atom_cache_entry,
- bad_dist_structure, {group, bad_dist_ext},
+ {group, message_latency},
+ {group, bad_dist}, {group, bad_dist_ext},
start_epmd_false, epmd_module].
groups() ->
@@ -100,10 +106,18 @@ groups() ->
{trap_bif, [], [trap_bif_1, trap_bif_2, trap_bif_3]},
{dist_auto_connect, [],
[dist_auto_connect_never, dist_auto_connect_once]},
+ {bad_dist, [],
+ [bad_dist_structure, bad_dist_fragments]},
{bad_dist_ext, [],
[bad_dist_ext_receive, bad_dist_ext_process_info,
bad_dist_ext_size,
- bad_dist_ext_control, bad_dist_ext_connection_id]}].
+ bad_dist_ext_control, bad_dist_ext_connection_id]},
+ {message_latency, [],
+ [message_latency_large_message,
+ message_latency_large_link_exit,
+ message_latency_large_monitor_exit,
+ message_latency_large_exit2]}
+ ].
%% Tests pinging a node in different ways.
ping(Config) when is_list(Config) ->
@@ -568,10 +582,20 @@ do_busy_test(Node, Fun) ->
%% Don't match arity; it is different in debug and
%% optimized emulator
[{status, suspended},
- {current_function, {erlang, bif_return_trap, _}}] = Pinfo,
+ {current_function, {Mod, Func, _}}] = Pinfo,
+ if
+ Mod =:= erlang andalso Func =:= bif_return_trap ->
+ true;
+ Mod =:= erts_internal andalso Func =:= dsend_continue_trap ->
+ true;
+ true ->
+ ct:fail({incorrect, pinfo, Pinfo})
+ end,
receive
{'DOWN', M, process, P, Reason} ->
io:format("~p died with exit reason ~p~n", [P, Reason]),
+ verify_nc(node()),
+ verify_nc(Node),
normal = Reason
end
end.
@@ -931,7 +955,9 @@ dist_auto_connect_never(Config) when is_list(Config) ->
ok;
{do_dist_auto_connect, Error} ->
{error, Error};
- Other ->
+ %% The io:formats in dos_dist_auto_connect will
+ %% generate port output messages that are ok
+ Other when not is_port(element(1, Other))->
{error, Other}
after 32000 ->
timeout
@@ -1364,6 +1390,131 @@ get_conflicting_unicode_atoms(CIX, N) ->
get_conflicting_unicode_atoms(CIX, N)
end.
+
+%% 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.
+message_latency_large_message(Config) when is_list(Config) ->
+ measure_latency_large_message(?FUNCTION_NAME, fun(Dropper, Payload) -> Dropper ! Payload end).
+
+message_latency_large_exit2(Config) when is_list(Config) ->
+ measure_latency_large_message(?FUNCTION_NAME, fun erlang:exit/2).
+
+message_latency_large_link_exit(Config) when is_list(Config) ->
+ message_latency_large_exit(?FUNCTION_NAME, fun erlang:link/1).
+
+message_latency_large_monitor_exit(Config) when is_list(Config) ->
+ message_latency_large_exit(?FUNCTION_NAME, fun(Dropper) ->
+ Dropper ! {monitor, self()},
+ receive ok -> ok end
+ end).
+
+message_latency_large_exit(Nodename, ReasonFun) ->
+ measure_latency_large_message(
+ Nodename,
+ fun(Dropper, Payload) ->
+ Pid = spawn(fun() ->
+ receive go -> ok end,
+ ReasonFun(Dropper),
+ exit(Payload)
+ end),
+
+ FlushTrace = fun F() ->
+ receive
+ {trace, Pid, _, _} = M ->
+ F()
+ after 0 ->
+ ok
+ end
+ end,
+
+ erlang:trace(Pid, true, [exiting]),
+ Pid ! go,
+ receive
+ {trace, Pid, out_exited, 0} ->
+ FlushTrace()
+ end
+ end).
+
+measure_latency_large_message(Nodename, DataFun) ->
+
+ erlang:system_monitor(self(), [busy_dist_port]),
+
+ {ok, N} = start_node(Nodename),
+
+ Dropper = spawn(N, fun F() ->
+ process_flag(trap_exit, true),
+ receive
+ {monitor,Pid} ->
+ erlang:monitor(process, Pid),
+ Pid ! ok;
+ _ -> ok
+ end,
+ F()
+ end),
+
+ Echo = spawn(N, fun F() -> receive {From, Msg} -> From ! Msg, F() end end),
+
+ %% Test 32 MB and 320 MB and test the latency difference of sent messages
+ Payloads = [{I, <<0:(I * 32 * 1024 * 1024 * 8)>>} || I <- [1,10]],
+
+ IndexTimes = [{I, measure_latency(DataFun, Dropper, Echo, P)}
+ || {I, P} <- Payloads],
+
+ Times = [ Time || {_I, Time} <- IndexTimes],
+
+ ct:pal("~p",[IndexTimes]),
+
+ case {lists:max(Times), lists:min(Times)} of
+ {Max, Min} when Max * 0.25 > Min ->
+ ct:fail({incorrect_latency, IndexTimes});
+ _ ->
+ ok
+ end.
+
+measure_latency(DataFun, Dropper, Echo, Payload) ->
+
+ flush(),
+
+ Senders = [spawn_monitor(
+ fun F() ->
+ DataFun(Dropper, Payload),
+ receive
+ die -> ok
+ after 0 ->
+ F()
+ end
+ end) || _ <- lists:seq(1,2)],
+
+ [receive
+ {monitor, _Sender, busy_dist_port, _Info} = M ->
+ ok
+ end || _ <- lists:seq(1,10)],
+
+ {TS, _} =
+ timer:tc(fun() ->
+ [begin
+ Echo ! {self(), hello},
+ receive hello -> ok end
+ end || _ <- lists:seq(1,100)]
+ end),
+ [begin
+ Sender ! die,
+ receive
+ {'DOWN', Ref, process, _, _} ->
+ ok
+ end
+ end || {Sender, Ref} <- Senders],
+ TS.
+
+flush() ->
+ receive
+ _ ->
+ flush()
+ after 0 ->
+ ok
+ end.
+
-define(COOKIE, '').
-define(DOP_LINK, 1).
-define(DOP_SEND, 2).
@@ -1382,6 +1533,15 @@ get_conflicting_unicode_atoms(CIX, N) ->
-define(DOP_DEMONITOR_P, 20).
-define(DOP_MONITOR_P_EXIT, 21).
+-define(DOP_SEND_SENDER, 22).
+-define(DOP_SEND_SENDER_TT, 23).
+
+-define(DOP_PAYLOAD_EXIT, 24).
+-define(DOP_PAYLOAD_EXIT_TT, 25).
+-define(DOP_PAYLOAD_EXIT2, 26).
+-define(DOP_PAYLOAD_EXIT2_TT, 27).
+-define(DOP_PAYLOAD_MONITOR_P_EXIT, 28).
+
start_monitor(Offender,P) ->
Parent = self(),
Q = spawn(Offender,
@@ -1515,7 +1675,145 @@ bad_dist_structure(Config) when is_list(Config) ->
stop_node(Victim),
ok.
+%% Test various dist fragmentation errors
+bad_dist_fragments(Config) when is_list(Config) ->
+ ct:timetrap({seconds, 15}),
+
+ {ok, Offender} = start_node(bad_dist_fragment_offender),
+ {ok, Victim} = start_node(bad_dist_fragment_victim),
+
+ Msg = iolist_to_binary(dmsg_ext(lists:duplicate(255,255))),
+
+ start_node_monitors([Offender,Victim]),
+ Parent = self(),
+ P = spawn(Victim,
+ fun () ->
+ process_flag(trap_exit,true),
+ Parent ! {self(), started},
+ receive check_msgs -> ok end,
+ bad_dist_struct_check_msgs([one,
+ two]),
+ Parent ! {self(), messages_checked},
+ receive done -> ok end
+ end),
+ receive {P, started} -> ok end,
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
+ start_monitor(Offender,P),
+ P ! one,
+
+ start_monitor(Offender,P),
+ send_bad_fragments(Offender, Victim, P,{?DOP_SEND,?COOKIE,P},3,
+ [{frg, 1, binary:part(Msg, 10,byte_size(Msg)-10)}]),
+ start_monitor(Offender,P),
+ send_bad_fragments(Offender, Victim, P,{?DOP_SEND,?COOKIE,P},3,
+ [{hdr, 3, binary:part(Msg, 0,10)},
+ {frg, 1, binary:part(Msg, 10,byte_size(Msg)-10)}]),
+
+ start_monitor(Offender,P),
+ send_bad_fragments(Offender, Victim, P,{?DOP_SEND,?COOKIE,P},3,
+ [{hdr, 3, binary:part(Msg, 0,10)},
+ {hdr, 3, binary:part(Msg, 0,10)}]),
+
+ start_monitor(Offender,P),
+ send_bad_fragments(Offender, Victim, P,{?DOP_SEND,?COOKIE,P,broken},3,
+ [{hdr, 1, binary:part(Msg, 10,byte_size(Msg)-10)}]),
+
+ start_monitor(Offender,P),
+ send_bad_fragments(Offender, Victim, P,{?DOP_SEND,?COOKIE,P},3,
+ [{hdr, 3, binary:part(Msg, 10,byte_size(Msg)-10)},
+ close]),
+
+ start_monitor(Offender,P),
+ ExitVictim = spawn(Victim, fun() -> receive ok -> ok end end),
+ send_bad_fragments(Offender, Victim, P,{?DOP_PAYLOAD_EXIT,P,ExitVictim},2,
+ [{hdr, 1, [131]}]),
+
+ start_monitor(Offender,P),
+ Exit2Victim = spawn(Victim, fun() -> receive ok -> ok end end),
+ send_bad_fragments(Offender, Victim, P,{?DOP_PAYLOAD_EXIT2,P,ExitVictim},2,
+ [{hdr, 1, [132]}]),
+
+ start_monitor(Offender,P),
+ DownVictim = spawn(Victim, fun() -> receive ok -> ok end end),
+ DownRef = erlang:monitor(process, DownVictim),
+ send_bad_fragments(Offender, Victim, P,{?DOP_PAYLOAD_MONITOR_P_EXIT,P,DownVictim,DownRef},2,
+ [{hdr, 1, [133]}]),
+
+ P ! two,
+ P ! check_msgs,
+ receive
+ {P, messages_checked} -> ok
+ after 5000 ->
+ exit(victim_is_dead)
+ end,
+
+ {message_queue_len, 0}
+ = rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
+
+ unlink(P),
+ P ! done,
+ stop_node(Offender),
+ stop_node(Victim),
+ ok.
+
+dmsg_frag_hdr(Frag) ->
+ dmsg_frag_hdr(erlang:phash2(self()), Frag).
+dmsg_frag_hdr(Seq, Frag) ->
+ [131, $E, uint64_be(Seq), uint64_be(Frag), 0].
+
+dmsg_frag(Frag) ->
+ dmsg_frag(erlang:phash2(self()), Frag).
+dmsg_frag(Seq, Frag) ->
+ [131, $F, uint64_be(Seq), uint64_be(Frag)].
+
+send_bad_fragments(Offender,VictimNode,Victim,Ctrl,WhereToPutSelf,Fragments) ->
+ Parent = self(),
+ Done = make_ref(),
+ ct:pal("Send: ~p",[Fragments]),
+ spawn_link(Offender,
+ fun () ->
+ Node = node(Victim),
+ pong = net_adm:ping(Node),
+ erlang:monitor_node(Node, true),
+ DCtrl = dctrl(Node),
+ Ctrl1 = case WhereToPutSelf of
+ 0 ->
+ Ctrl;
+ N when N > 0 ->
+ setelement(N,Ctrl,self())
+ end,
+
+ FragData = [case Type of
+ hdr ->
+ [dmsg_frag_hdr(FragId),
+ dmsg_ext(Ctrl1), FragPayload];
+ frg ->
+ [dmsg_frag(FragId), FragPayload]
+ end || {Type, FragId, FragPayload} <- Fragments],
+
+ receive {nodedown, Node} -> exit("premature nodedown")
+ after 10 -> ok
+ end,
+
+ [ dctrl_send(DCtrl, D) || D <- FragData ],
+ [ erlang:port_close(DCtrl) || close <- Fragments],
+
+ receive {nodedown, Node} -> ok
+ after 5000 -> exit("missing nodedown")
+ end,
+ Parent ! {FragData,Done}
+ end),
+ receive
+ {WhatSent,Done} ->
+ io:format("Offender sent ~p~n",[WhatSent]),
+ verify_nc(VictimNode),
+ ok
+ after 7000 ->
+ exit(unable_to_send)
+ end.
bad_dist_ext_receive(Config) when is_list(Config) ->
{ok, Offender} = start_node(bad_dist_ext_receive_offender),
@@ -2124,8 +2422,25 @@ start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) ->
start_node(Name, Args, Rel).
stop_node(Node) ->
+ verify_nc(Node),
test_server:stop_node(Node).
+verify_nc(Node) ->
+ P = self(),
+ Ref = make_ref(),
+ spawn(Node,
+ fun() ->
+ R = erts_test_utils:check_node_dist(fun(E) -> E end),
+ P ! {Ref, R}
+ end),
+ receive
+ {Ref, ok} ->
+ ok;
+ {Ref, Error} ->
+ ct:log("~s",[Error]),
+ ct:fail(failed_nc_refc_check)
+ end.
+
freeze_node(Node, MS) ->
Own = 300,
DoingIt = make_ref(),
@@ -2485,6 +2800,17 @@ mk_ref({NodeNameExt, Creation}, Numbers) when is_integer(Creation),
exit({unexpected_binary_to_term_result, Other})
end.
+uint64_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 64 ->
+ [(Uint bsr 56) band 16#ff,
+ (Uint bsr 48) band 16#ff,
+ (Uint bsr 40) band 16#ff,
+ (Uint bsr 32) band 16#ff,
+ (Uint bsr 24) band 16#ff,
+ (Uint bsr 16) band 16#ff,
+ (Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint64_be(Uint) ->
+ exit({badarg, uint64_be, [Uint]}).
uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
[(Uint bsr 24) band 16#ff,
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index 1d2ae4fb51..bb0f3498ab 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -2665,24 +2665,7 @@ wait_deallocations() ->
driver_alloc_size() ->
wait_deallocations(),
- case erlang:system_info({allocator_sizes, driver_alloc}) of
- false ->
- undefined;
- MemInfo ->
- CS = lists:foldl(
- fun ({instance, _, L}, Acc) ->
- {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L),
- {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L),
- [MBCS,SBCS | Acc]
- end,
- [],
- MemInfo),
- lists:foldl(
- fun(L, Sz0) ->
- {value,{_,Sz,_,_}} = lists:keysearch(blocks_size, 1, L),
- Sz0+Sz
- end, 0, CS)
- end.
+ erts_debug:alloc_blocks_size(driver_alloc).
rpc(Config, Fun) ->
case proplists:get_value(node, Config) of
diff --git a/erts/emulator/test/emulator_bench.spec b/erts/emulator/test/emulator_bench.spec
index f709d913b7..2a180b440c 100644
--- a/erts/emulator/test/emulator_bench.spec
+++ b/erts/emulator/test/emulator_bench.spec
@@ -1 +1,2 @@
{groups,"../emulator_test",estone_SUITE,[estone_bench]}.
+{groups,"../emulator_test",binary_SUITE,[iolist_size_benchmarks]}.
diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl
index 6aa7a445b5..f39dbedd8f 100644
--- a/erts/emulator/test/erts_debug_SUITE.erl
+++ b/erts/emulator/test/erts_debug_SUITE.erl
@@ -22,8 +22,10 @@
-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,
- test_size/1,flat_size_big/1,df/1,term_type/1,
- instructions/1, stack_check/1]).
+ test_size/1,flat_size_big/1,df/1,term_type/1,
+ instructions/1, stack_check/1, alloc_blocks_size/1]).
+
+-export([do_alloc_blocks_size/0]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -31,7 +33,7 @@ suite() ->
all() ->
[test_size, flat_size_big, df, instructions, term_type,
- stack_check].
+ stack_check, alloc_blocks_size].
test_size(Config) when is_list(Config) ->
ConsCell1 = id([a|b]),
@@ -210,5 +212,28 @@ instructions(Config) when is_list(Config) ->
_ = [list_to_atom(I) || I <- Is],
ok.
+alloc_blocks_size(Config) when is_list(Config) ->
+ F = fun(Args) ->
+ Node = start_slave(Args),
+ ok = rpc:call(Node, ?MODULE, do_alloc_blocks_size, []),
+ true = test_server:stop_node(Node)
+ end,
+ F("+Meamax"),
+ F("+Meamin"),
+ F(""),
+ ok.
+
+do_alloc_blocks_size() ->
+ _ = erts_debug:alloc_blocks_size(binary_alloc),
+ ok.
+
+start_slave(Args) ->
+ Name = ?MODULE_STRING ++ "_slave",
+ Pa = filename:dirname(code:which(?MODULE)),
+ {ok, Node} = test_server:start_node(list_to_atom(Name),
+ slave,
+ [{args, "-pa " ++ Pa ++ " " ++ Args}]),
+ Node.
+
id(I) ->
I.
diff --git a/erts/emulator/test/erts_test_utils.erl b/erts/emulator/test/erts_test_utils.erl
new file mode 100644
index 0000000000..0c3ef3e0fc
--- /dev/null
+++ b/erts/emulator/test/erts_test_utils.erl
@@ -0,0 +1,271 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erts_test_utils).
+
+%%
+%% THIS MODULE IS ALSO USED BY *OTHER* APPLICATIONS TEST CODE
+%%
+
+-export([mk_ext_pid/3,
+ mk_ext_port/2,
+ mk_ext_ref/2,
+ available_internal_state/1,
+ check_node_dist/0, check_node_dist/1, check_node_dist/3]).
+
+
+
+-define(VERSION_MAGIC, 131).
+
+-define(ATOM_EXT, 100).
+-define(REFERENCE_EXT, 101).
+-define(PORT_EXT, 102).
+-define(PID_EXT, 103).
+-define(NEW_REFERENCE_EXT, 114).
+-define(NEW_PID_EXT, $X).
+-define(NEW_PORT_EXT, $Y).
+-define(NEWER_REFERENCE_EXT, $Z).
+
+uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
+ [(Uint bsr 24) band 16#ff,
+ (Uint bsr 16) band 16#ff,
+ (Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint32_be(Uint) ->
+ exit({badarg, uint32_be, [Uint]}).
+
+
+uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 ->
+ [(Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint16_be(Uint) ->
+ exit({badarg, uint16_be, [Uint]}).
+
+uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
+ Uint band 16#ff;
+uint8(Uint) ->
+ exit({badarg, uint8, [Uint]}).
+
+pid_tag(bad_creation) -> ?PID_EXT;
+pid_tag(Creation) when Creation =< 3 -> ?PID_EXT;
+pid_tag(_Creation) -> ?NEW_PID_EXT.
+
+enc_creation(bad_creation) -> uint8(4);
+enc_creation(Creation) when Creation =< 3 -> uint8(Creation);
+enc_creation(Creation) -> uint32_be(Creation).
+
+mk_ext_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
+ mk_ext_pid({atom_to_list(NodeName), Creation}, Number, Serial);
+mk_ext_pid({NodeName, Creation}, Number, Serial) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ pid_tag(Creation),
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint32_be(Serial),
+ enc_creation(Creation)])) of
+ Pid when is_pid(Pid) ->
+ Pid;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+port_tag(bad_creation) -> ?PORT_EXT;
+port_tag(Creation) when Creation =< 3 -> ?PORT_EXT;
+port_tag(_Creation) -> ?NEW_PORT_EXT.
+
+mk_ext_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
+ mk_ext_port({atom_to_list(NodeName), Creation}, Number);
+mk_ext_port({NodeName, Creation}, Number) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ port_tag(Creation),
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ enc_creation(Creation)])) of
+ Port when is_port(Port) ->
+ Port;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_port, [{NodeName, Creation}, Number]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+ref_tag(bad_creation) -> ?NEW_REFERENCE_EXT;
+ref_tag(Creation) when Creation =< 3 -> ?NEW_REFERENCE_EXT;
+ref_tag(_Creation) -> ?NEWER_REFERENCE_EXT.
+
+mk_ext_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
+ is_list(Numbers) ->
+ mk_ext_ref({atom_to_list(NodeName), Creation}, Numbers);
+mk_ext_ref({NodeName, Creation}, [Number]) when is_list(NodeName),
+ Creation =< 3,
+ is_integer(Number) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?REFERENCE_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end;
+mk_ext_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
+ is_list(Numbers) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ref_tag(Creation),
+ uint16_be(length(Numbers)),
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ enc_creation(Creation),
+ lists:map(fun (N) ->
+ uint32_be(N)
+ end,
+ Numbers)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+
+available_internal_state(Bool) when Bool == true; Bool == false ->
+ case {Bool,
+ (catch erts_debug:get_internal_state(available_internal_state))} of
+ {true, true} ->
+ true;
+ {false, true} ->
+ erts_debug:set_internal_state(available_internal_state, false),
+ true;
+ {true, _} ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ false;
+ {false, _} ->
+ false
+ end.
+
+
+%%
+%% Check reference counters for node- and dist entries.
+%%
+check_node_dist() ->
+ check_node_dist(fun(ErrMsg) ->
+ io:format("check_node_dist ERROR:\n~p\n", [ErrMsg]),
+ error
+ end).
+
+check_node_dist(Fail) ->
+ AIS = available_internal_state(true),
+ [erlang:garbage_collect(P) || P <- erlang:processes()],
+ {{node_references, NodeRefs},
+ {dist_references, DistRefs}} =
+ erts_debug:get_internal_state(node_and_dist_references),
+ R = check_node_dist(Fail, NodeRefs, DistRefs),
+ available_internal_state(AIS),
+ R.
+
+check_node_dist(Fail, NodeRefs, DistRefs) ->
+ AIS = available_internal_state(true),
+ R = check_nd_refc({node(),erlang:system_info(creation)},
+ NodeRefs, DistRefs, Fail),
+ available_internal_state(AIS),
+ R.
+
+
+check_nd_refc({ThisNodeName, ThisCreation}, NodeRefs, DistRefs, Fail) ->
+ case catch begin
+ check_refc(ThisNodeName,ThisCreation,"node table",NodeRefs),
+ check_refc(ThisNodeName,ThisCreation,"dist table",DistRefs),
+ ok
+ end of
+ ok ->
+ ok;
+ {'EXIT', Reason} ->
+ {Y,Mo,D} = date(),
+ {H,Mi,S} = time(),
+ ErrMsg = io_lib:format("~n"
+ "*** Reference count check of node ~w "
+ "failed (~p) at ~w~w~w ~w:~w:~w~n"
+ "*** Node table references:~n ~p~n"
+ "*** Dist table references:~n ~p~n",
+ [node(), Reason, Y, Mo, D, H, Mi, S,
+ NodeRefs, DistRefs]),
+ Fail(lists:flatten(ErrMsg))
+ end.
+
+
+check_refc(ThisNodeName,ThisCreation,Table,EntryList) when is_list(EntryList) ->
+ lists:foreach(
+ fun ({Entry, Refc, ReferrerList}) ->
+ {DelayedDeleteTimer,
+ FoundRefs} =
+ lists:foldl(
+ fun ({Referrer, ReferencesList}, {DDT, A1}) ->
+ {case Referrer of
+ {system,delayed_delete_timer} ->
+ true;
+ {system,thread_progress_delete_timer} ->
+ true;
+ _ ->
+ DDT
+ end,
+ A1 + lists:foldl(fun ({_T,Rs},A2) ->
+ A2+Rs
+ end,
+ 0,
+ ReferencesList)}
+ end,
+ {false, 0},
+ ReferrerList),
+
+ %% Reference count equals found references?
+ case {Refc, FoundRefs, DelayedDeleteTimer} of
+ {X, X, _} ->
+ ok;
+ {0, 1, true} ->
+ ok;
+ _ ->
+ exit({invalid_reference_count, Table, Entry})
+ end,
+
+ %% All entries in table referred to?
+ case {Entry, Refc} of
+ {ThisNodeName, 0} -> ok;
+ {{ThisNodeName, ThisCreation}, 0} -> ok;
+ {_, 0} when DelayedDeleteTimer == false ->
+ exit({not_referred_entry_in_table, Table, Entry});
+ {_, _} -> ok
+ end
+
+ end,
+ EntryList),
+ ok.
diff --git a/erts/emulator/test/esock_ttest/.gitignore b/erts/emulator/test/esock_ttest/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/erts/emulator/test/esock_ttest/.gitignore
diff --git a/erts/emulator/test/esock_ttest/esock-ttest b/erts/emulator/test/esock_ttest/esock-ttest
new file mode 100755
index 0000000000..f0d363ab30
--- /dev/null
+++ b/erts/emulator/test/esock_ttest/esock-ttest
@@ -0,0 +1,352 @@
+#!/usr/bin/env escript
+
+%%
+%% %CopyrightBegin%
+%%
+%% 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.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% ==========================================================================
+%%
+%% This is a simple wrapper escript on top of the socket ttest program(s).
+%% The idea is to make it simple to run in a normal shell (bash).
+%%
+%% ==========================================================================
+
+-define(SECS(I), timer:seconds(I)).
+
+-define(CLIENT_MSG_1_MAX_OUTSTANDING, 100).
+-define(CLIENT_MSG_2_MAX_OUTSTANDING, 10).
+-define(CLIENT_MSG_3_MAX_OUTSTANDING, 1).
+
+main(Args) ->
+ State = process_args(Args),
+ exec(State),
+ ok.
+
+usage(ErrorString) when is_list(ErrorString) ->
+ eprint(ErrorString),
+ usage(),
+ erlang:halt(0).
+
+usage() ->
+ io:format("usage: ~s [options]"
+ "~n"
+ "~n This erlang script is used to start the (e)socket ttest "
+ "~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"
+ "~n"
+ "~n",
+ [scriptname()]),
+ ok.
+
+process_args(["--help"|_]) ->
+ usage();
+process_args(["--server"|ServerArgs]) ->
+ process_server_args(ServerArgs);
+process_args(["--client"|ClientArgs]) ->
+ process_client_args(ClientArgs);
+process_args(Args) ->
+ usage(f("Invalid Args: "
+ "~n ~p", [Args])).
+
+
+process_server_args(Args) ->
+ Defaults = #{role => server,
+ active => false,
+ transport => {sock, plain}},
+ process_server_args(Args, Defaults).
+
+process_server_args([], State) ->
+ State;
+
+process_server_args(["--active", Active|Args], State)
+ when ((Active =:= "false") orelse
+ (Active =:= "once") orelse
+ (Active =:= "true")) ->
+ process_server_args(Args, State#{active => list_to_atom(Active)});
+
+process_server_args(["--transport", "gen" | Args], State) ->
+ process_server_args(Args, State#{transport => gen});
+process_server_args(["--transport", "sock" | Args], State) ->
+ process_server_args(Args, State#{transport => {sock, plain}});
+process_server_args(["--transport", "sock:plain" | Args], State) ->
+ process_server_args(Args, State#{transport => {sock, plain}});
+process_server_args(["--transport", "sock:msg" | Args], State) ->
+ process_server_args(Args, State#{transport => {sock, msg}});
+
+process_server_args([Arg|_], _State) ->
+ usage(f("Invalid Server arg: ~s", [Arg])).
+
+
+process_client_args(Args) ->
+ Defaults = #{role => client,
+ active => false,
+ transport => {sock, plain},
+ %% Will cause error if not provided
+ %% Should be "addr:port"
+ server => undefined,
+ msg_id => 1,
+ %% Will be filled in based on msg_id if not provided
+ max_outstanding => undefined,
+ runtime => ?SECS(60)},
+ process_client_args(Args, Defaults).
+
+process_client_args([], State) ->
+ process_client_args_ensure_max_outstanding(State);
+
+process_client_args(["--active", Active|Args], State)
+ 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) ->
+ process_client_args(Args, State#{transport => gen});
+process_client_args(["--transport", "sock" | Args], State) ->
+ process_client_args(Args, State#{transport => {sock, plain}});
+process_client_args(["--transport", "sock:plain" | Args], State) ->
+ process_client_args(Args, State#{transport => {sock, plain}});
+process_client_args(["--transport", "sock:msg" | Args], State) ->
+ process_client_args(Args, State#{transport => {sock, msg}});
+
+process_client_args(["--msg-id", MsgID|Args], State)
+ when ((MsgID =:= "1") orelse
+ (MsgID =:= "2") orelse
+ (MsgID =:= "3")) ->
+ process_client_args(Args, State#{msg_id => list_to_integer(MsgID)});
+
+process_client_args(["--max-outstanding", Max|Args], State) ->
+ try list_to_integer(Max) of
+ I when (I > 0) ->
+ process_client_args(Args, State#{max_outstanding => I});
+ _ ->
+ usage(f("Invalid Max Outstanding: ~s", [Max]))
+ catch
+ _:_:_ ->
+ usage(f("Invalid Max Outstanding: ~s", [Max]))
+ end;
+
+process_client_args(["--scon", Server|Args], State) ->
+ case string:tokens(Server, [$:]) of
+ [AddrStr,PortStr] ->
+ Addr = case inet:parse_address(AddrStr) of
+ {ok, A} ->
+ A;
+ {error, _} ->
+ usage(f("Invalid Server Address: ~s", [AddrStr]))
+ end,
+ Port = try list_to_integer(PortStr) of
+ I when (I > 0) ->
+ I;
+ _ ->
+ usage(f("Invalid Server Port: ~s", [PortStr]))
+ catch
+ _:_:_ ->
+ usage(f("Invalid Server Port: ~s", [PortStr]))
+ end,
+ process_client_args(Args, State#{server => {Addr, Port}});
+ _ ->
+ usage(f("Invalid Server: ~s", [Server]))
+ end;
+
+process_client_args(["--runtime", T|Args], State) ->
+ try list_to_integer(T) of
+ I when (I > 0) ->
+ process_client_args(Args, State#{runtime => ?SECS(I)});
+ _ ->
+ usage(f("Invalid Run Time: ~s", [T]))
+ catch
+ _:_:_ ->
+ usage(f("Invalid Run Time: ~s", [T]))
+ end;
+
+process_client_args([Arg|_], _State) ->
+ usage(f("Invalid Client arg: ~s", [Arg])).
+
+
+process_client_args_ensure_max_outstanding(
+ #{msg_id := 1,
+ max_outstanding := undefined} = State) ->
+ State#{max_outstanding => ?CLIENT_MSG_1_MAX_OUTSTANDING};
+process_client_args_ensure_max_outstanding(
+ #{msg_id := 2,
+ max_outstanding := undefined} = State) ->
+ State#{max_outstanding => ?CLIENT_MSG_2_MAX_OUTSTANDING};
+process_client_args_ensure_max_outstanding(
+ #{msg_id := 3,
+ max_outstanding := undefined} = State) ->
+ State#{max_outstanding => ?CLIENT_MSG_3_MAX_OUTSTANDING};
+process_client_args_ensure_max_outstanding(
+ #{msg_id := MsgID,
+ max_outstanding := MaxOutstanding} = State)
+ when ((MsgID =:= 1) orelse
+ (MsgID =:= 2) orelse
+ (MsgID =:= 3)) andalso
+ (is_integer(MaxOutstanding) andalso (MaxOutstanding > 0)) ->
+ State;
+process_client_args_ensure_max_outstanding(
+ #{msg_id := MsgID,
+ max_outstanding := MaxOutstanding}) ->
+ usage(f("Invalid Msg ID (~w) and Max Outstanding (~w)",
+ [MsgID, MaxOutstanding])).
+
+
+
+%% ==========================================================================
+
+exec(#{role := server,
+ active := Active,
+ transport := gen}) ->
+ case socket_test_ttest_tcp_server_gen:start(Active) of
+ {ok, {Pid, _}} ->
+ MRef = erlang:monitor(process, Pid),
+ receive
+ {'DOWN', MRef, process, Pid, Info} ->
+ Info
+ end;
+ {error, Reason} ->
+ eprint(f("Failed starting server: "
+ "~n ~p", [Reason])),
+ error
+ end;
+exec(#{role := server,
+ active := Active,
+ transport := {sock, Method}}) ->
+ case socket_test_ttest_tcp_server_socket:start(Method, Active) of
+ {ok, {Pid, _}} ->
+ MRef = erlang:monitor(process, Pid),
+ receive
+ {'DOWN', MRef, process, Pid, Info} ->
+ Info
+ end;
+ {error, Reason} ->
+ eprint(f("Failed starting server: "
+ "~n ~p", [Reason])),
+ error
+ end;
+
+exec(#{role := client,
+ server := undefined}) ->
+ usage("Mandatory option 'server' not provided");
+exec(#{role := client,
+ server := {Addr, Port},
+ active := Active,
+ transport := gen,
+ msg_id := MsgID,
+ max_outstanding := MaxOutstanding,
+ runtime := RunTime}) ->
+ case socket_test_ttest_tcp_client_gen:start(true,
+ Active,
+ Addr, Port,
+ MsgID, MaxOutstanding,
+ RunTime) of
+ {ok, Pid} ->
+ MRef = erlang:monitor(process, Pid),
+ receive
+ {'DOWN', MRef, process, Pid, Info} ->
+ Info
+ end;
+ {error, Reason} ->
+ eprint(f("Failed starting server: "
+ "~n ~p", [Reason])),
+ error
+ end;
+exec(#{role := client,
+ server := {Addr, Port},
+ active := Active,
+ transport := {sock, Method},
+ msg_id := MsgID,
+ max_outstanding := MaxOutstanding,
+ runtime := RunTime}) ->
+ case socket_test_ttest_tcp_client_socket:start(true,
+ Method,
+ Active,
+ Addr, Port,
+ MsgID, MaxOutstanding,
+ RunTime) of
+ {ok, Pid} ->
+ MRef = erlang:monitor(process, Pid),
+ receive
+ {'DOWN', MRef, process, Pid, Info} ->
+ Info
+ end;
+ {error, Reason} ->
+ eprint(f("Failed starting server: "
+ "~n ~p", [Reason])),
+ error
+ end;
+exec(_) ->
+ usage("Unexpected option combo"),
+ ok.
+
+
+
+%% ==========================================================================
+
+f(F, A) ->
+ socket_test_ttest_lib:format(F, A).
+
+eprint(ErrorString) when is_list(ErrorString) ->
+ print("<ERROR> " ++ ErrorString ++ "~n", []).
+
+print(F, A) ->
+ io:format(F ++ "~n", A).
+
+scriptname() ->
+ FullName = escript:script_name(),
+ filename:basename(FullName).
+
diff --git a/erts/emulator/test/esock_ttest/esock-ttest-client b/erts/emulator/test/esock_ttest/esock-ttest-client
new file mode 100755
index 0000000000..1ab56f2d44
--- /dev/null
+++ b/erts/emulator/test/esock_ttest/esock-ttest-client
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2019-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.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+
+EMU=$ERL_TOP/erts/emulator
+EMU_TEST=$EMU/test
+ESOCK_TTEST=$EMU_TEST/esock_ttest
+
+RUNTIME=30
+
+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
+#
+
+# ---------------------------------------------------------------------------
+
+echo "$ITERATIONS" |
+ while read TRANSPORT ACTIVE MSG_ID; 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
+ echo ""
+
+ done
+
+
diff --git a/erts/emulator/test/esock_ttest/esock-ttest-server-gen b/erts/emulator/test/esock_ttest/esock-ttest-server-gen
new file mode 100755
index 0000000000..c29184772e
--- /dev/null
+++ b/erts/emulator/test/esock_ttest/esock-ttest-server-gen
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2019-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.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+
+EMU=$ERL_TOP/erts/emulator
+EMU_TEST=$EMU/test
+ESOCK_TTEST=$EMU_TEST/esock_ttest
+
+if [ $# = 1 ]; then
+ ACTIVE="--active $1"
+fi
+
+$ESOCK_TTEST/esock-ttest --server --transport gen $ACTIVE
+
diff --git a/erts/emulator/test/esock_ttest/esock-ttest-server-sock b/erts/emulator/test/esock_ttest/esock-ttest-server-sock
new file mode 100755
index 0000000000..4ec0d335d9
--- /dev/null
+++ b/erts/emulator/test/esock_ttest/esock-ttest-server-sock
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2019-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.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+
+EMU=$ERL_TOP/erts/emulator
+EMU_TEST=$EMU/test
+ESOCK_TTEST=$EMU_TEST/esock_ttest
+
+if [ $# = 1 ]; then
+ ACTIVE="--active $1"
+fi
+
+$ESOCK_TTEST/esock-ttest --server --transport sock $ACTIVE
+
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index aec66cb9a3..c4d9ea515a 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -36,6 +36,11 @@
%% during compilation instead of at runtime, so do not perform this analysis.
-compile([{hipe, [no_icode_range]}]).
+%% Module-level type optimization propagates the constants used when testing
+%% increment1/1 and increment2/1, which makes it test something completely
+%% different, so we're turning it off.
+-compile(no_module_opt).
+
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 1}}].
diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl
index 73fe9b0d8f..4042b58ff2 100644
--- a/erts/emulator/test/fun_SUITE.erl
+++ b/erts/emulator/test/fun_SUITE.erl
@@ -576,7 +576,7 @@ refc_dist(Config) when is_list(Config) ->
process_flag(trap_exit, true),
Pid = spawn_link(Node, fun() -> receive
Fun when is_function(Fun) ->
- 2 = fun_refc(Fun),
+ 3 = fun_refc(Fun),
exit({normal,Fun}) end
end),
F = fun() -> 42 end,
@@ -598,7 +598,7 @@ refc_dist_send(Node, F) ->
Pid = spawn_link(Node, fun() -> receive
{To,Fun} when is_function(Fun) ->
wait_until(fun () ->
- 2 =:= fun_refc(Fun)
+ 3 =:= fun_refc(Fun)
end),
To ! Fun
end
@@ -626,7 +626,7 @@ refc_dist_reg_send(Node, F) ->
Me ! Ref,
receive
{Me,Fun} when is_function(Fun) ->
- 2 = fun_refc(Fun),
+ 3 = fun_refc(Fun),
Me ! Fun
end
end),
@@ -710,6 +710,16 @@ t_is_function2(Config) when is_list(Config) ->
bad_arity({}),
bad_arity({a,b}),
bad_arity(self()),
+
+ %% Bad arity argument in guard test.
+ Fun = fun erlang:abs/1,
+ ok = if
+ is_function(Fun, -1) -> error;
+ is_function(Fun, 256) -> error;
+ is_function(Fun, a) -> error;
+ is_function(Fun, Fun) -> error;
+ true -> ok
+ end,
ok.
bad_arity(A) ->
@@ -806,11 +816,13 @@ verify_not_undef(Fun, Tag) ->
ct:fail("tag ~w not defined in fun_info", [Tag]);
{Tag,_} -> ok
end.
-
+
id(X) ->
X.
spawn_call(Node, AFun) ->
+ Parent = self(),
+ Init = erlang:whereis(init),
Pid = spawn_link(Node,
fun() ->
receive
@@ -821,8 +833,10 @@ spawn_call(Node, AFun) ->
_ -> lists:seq(0, Arity-1)
end,
Res = apply(Fun, Args),
- {pid,Creator} = erlang:fun_info(Fun, pid),
- Creator ! {result,Res}
+ case erlang:fun_info(Fun, pid) of
+ {pid,Init} -> Parent ! {result,Res};
+ {pid,Creator} -> Creator ! {result,Res}
+ end
end
end),
Pid ! {AFun,AFun,AFun},
diff --git a/erts/emulator/test/net_SUITE.erl b/erts/emulator/test/net_SUITE.erl
new file mode 100644
index 0000000000..1a973cacb2
--- /dev/null
+++ b/erts/emulator/test/net_SUITE.erl
@@ -0,0 +1,481 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-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.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% This test suite is basically a "placeholder" for a proper test suite...
+%%
+
+%% Run the entire test suite:
+%% ts:run(emulator, net_SUITE, [batch]).
+%%
+%% Run a specific group:
+%% ts:run(emulator, net_SUITE, {group, foo}, [batch]).
+%%
+%% Run a specific test case:
+%% ts:run(emulator, net_SUITE, foo, [batch]).
+
+-module(net_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+%% Suite exports
+-export([suite/0, all/0, groups/0]).
+-export([init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([
+ %% *** API Basic ***
+ api_b_gethostname/1,
+ api_b_name_and_addr_info/1,
+
+ api_b_name_and_index/1
+
+ %% Tickets
+ ]).
+
+
+%% -include("socket_test_evaluator.hrl").
+
+%% Internal exports
+%% -export([]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(SLEEP(T), receive after T -> ok end).
+
+-define(FAIL(R), exit(R)).
+
+-define(MINS(M), timer:minutes(M)).
+-define(SECS(S), timer:seconds(S)).
+
+-define(TT(T), ct:timetrap(T)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,1}}].
+
+all() ->
+ Groups = [{api, "ENET_TEST_API", include}],
+ [use_group(Group, Env, Default) || {Group, Env, Default} <- Groups].
+
+use_group(Group, Env, Default) ->
+ case os:getenv(Env) of
+ false when (Default =:= include) ->
+ [{group, Group}];
+ false ->
+ [];
+ Val ->
+ case list_to_atom(string:to_lower(Val)) of
+ Use when (Use =:= include) orelse
+ (Use =:= enable) orelse
+ (Use =:= true) ->
+ [{group, Group}];
+ _ ->
+ []
+ end
+ end.
+
+
+groups() ->
+ [{api, [], api_cases()},
+ {api_basic, [], api_basic_cases()}
+
+ %% {tickets, [], ticket_cases()}
+ ].
+
+api_cases() ->
+ [
+ {group, api_basic}
+ ].
+
+api_basic_cases() ->
+ [
+ api_b_gethostname,
+ api_b_name_and_addr_info,
+ api_b_name_and_index
+ ].
+
+%% ticket_cases() ->
+%% [].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init_per_suite(Config) ->
+ case os:type() of
+ {win32, _} ->
+ not_yet_implemented();
+ _ ->
+ %% ?LOGGER:start(),
+ Config
+ end.
+
+end_per_suite(_) ->
+ %% ?LOGGER:stop(),
+ ok.
+
+init_per_group(_Group, Config) ->
+ Config.
+
+end_per_group(_Group, Config) ->
+ Config.
+
+
+init_per_testcase(_TC, Config) ->
+ Config.
+
+end_per_testcase(_TC, Config) ->
+ Config.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% API BASIC %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Get the hostname of the host.
+api_b_gethostname(suite) ->
+ [];
+api_b_gethostname(doc) ->
+ [];
+api_b_gethostname(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_b_gethostname,
+ fun() ->
+ ok = api_b_gethostname()
+ end).
+
+
+api_b_gethostname() ->
+ case net:gethostname() of
+ {ok, Hostname} ->
+ i("hostname: ~s", [Hostname]),
+ ok;
+ {error, Reason} ->
+ ?FAIL(Reason)
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Get name and address info.
+api_b_name_and_addr_info(suite) ->
+ [];
+api_b_name_and_addr_info(doc) ->
+ [];
+api_b_name_and_addr_info(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_b_name_and_addr_info,
+ fun() ->
+ ok = api_b_name_and_addr_info()
+ end).
+
+
+api_b_name_and_addr_info() ->
+ Domain = inet,
+ Addr = which_local_addr(Domain),
+ SA = #{family => Domain, addr => Addr},
+ Hostname =
+ case net:getnameinfo(SA) of
+ {ok, #{host := Name, service := Service} = NameInfo}
+ when is_list(Name) andalso is_list(Service) ->
+ i("getnameinfo: "
+ "~n ~p", [NameInfo]),
+ Name;
+ {ok, BadNameInfo} ->
+ ?FAIL({getnameinfo, SA, BadNameInfo});
+ {error, Reason1} ->
+ ?FAIL({getnameinfo, SA, Reason1})
+ end,
+ case net:getaddrinfo(Hostname) of
+ {ok, AddrInfos} when is_list(AddrInfos) ->
+ i("getaddrinfo: "
+ "~n ~p", [AddrInfos]),
+ verify_addr_info(AddrInfos, Domain);
+ {ok, BadAddrInfo} ->
+ ?FAIL({getaddrinfo, Hostname, BadAddrInfo});
+ {error, Reason2} ->
+ ?FAIL({getaddrinfo, Hostname, Reason2})
+ end.
+
+
+verify_addr_info(AddrInfos, Domain) when (AddrInfos =/= []) ->
+ verify_addr_info2(AddrInfos, Domain).
+
+verify_addr_info2([], _Domain) ->
+ ok;
+verify_addr_info2([#{addr := #{addr := Addr,
+ family := Domain,
+ port := Port},
+ family := Domain,
+ type := _Type,
+ protocol := _Proto}|T], Domain)
+ when is_integer(Port) andalso
+ (((Domain =:= inet) andalso is_tuple(Addr) andalso (size(Addr) =:= 4)) orelse
+ ((Domain =:= inet6) andalso is_tuple(Addr) andalso (size(Addr) =:= 8))) ->
+ verify_addr_info2(T, Domain);
+verify_addr_info2([#{family := DomainA}|T], DomainB)
+ when (DomainA =/= DomainB) ->
+ %% Ignore entries for other domains
+ verify_addr_info2(T, DomainB);
+verify_addr_info2([BadAddrInfo|_], Domain) ->
+ ?FAIL({bad_address_info, BadAddrInfo, Domain}).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Verify (interface) name and index functions.
+%% if_names/0,
+%% if_name2index/1
+%% if_index2name/1
+api_b_name_and_index(suite) ->
+ [];
+api_b_name_and_index(doc) ->
+ [];
+api_b_name_and_index(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_b_name_and_index,
+ fun() ->
+ ok = api_b_name_and_index()
+ end).
+
+
+api_b_name_and_index() ->
+ Names =
+ case net:if_names() of
+ {ok, N} when is_list(N) andalso (N =/= []) ->
+ N;
+ {error, Reason} ->
+ ?FAIL({if_names, Reason})
+ end,
+ verify_if_names(Names).
+
+verify_if_names([]) ->
+ ok;
+verify_if_names([{Index, Name}|T]) ->
+ case net:if_name2index(Name) of
+ {ok, Index} ->
+ ok;
+ {ok, BadIndex} ->
+ ?FAIL({name2index, Name, Index, BadIndex});
+ {error, ReasonN2I} ->
+ ?FAIL({name2index, Name, ReasonN2I})
+ end,
+ case net:if_index2name(Index) of
+ {ok, Name} ->
+ ok;
+ {ok, BadName} ->
+ ?FAIL({index2name, Index, Name, BadName});
+ {error, ReasonI2N} ->
+ ?FAIL({index2name, Index, ReasonI2N})
+ end,
+ verify_if_names(T).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% local_host() ->
+%% try net_adm:localhost() of
+%% Host when is_list(Host) ->
+%% %% Convert to shortname if long
+%% case string:tokens(Host, [$.]) of
+%% [H|_] ->
+%% list_to_atom(H)
+%% end
+%% catch
+%% C:E:S ->
+%% erlang:raise(C, E, S)
+%% end.
+
+
+%% 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);
+ {error, Reason} ->
+ ?FAIL({inet, getifaddrs, Reason})
+ end.
+
+which_addr(_Domain, []) ->
+ skip(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).
+
+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).
+
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+not_yet_implemented() ->
+ skip("not yet implemented").
+
+skip(Reason) ->
+ throw({skip, Reason}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% t() ->
+%% os:timestamp().
+
+
+%% tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) ->
+%% T1 = A1*1000000000+B1*1000+(C1 div 1000),
+%% T2 = A2*1000000000+B2*1000+(C2 div 1000),
+%% T2 - T1.
+
+
+formated_timestamp() ->
+ format_timestamp(os:timestamp()).
+
+format_timestamp({_N1, _N2, _N3} = TS) ->
+ {_Date, Time} = calendar:now_to_local_time(TS),
+ %% {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ %% FormatTS =
+ %% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w.~w",
+ %% [YYYY, MM, DD, Hour, Min, Sec, N3]),
+ FormatTS = io_lib:format("~.2.0w:~.2.0w:~.2.0w", [Hour, Min, Sec]),
+ lists:flatten(FormatTS).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+set_tc_name(N) when is_atom(N) ->
+ set_tc_name(atom_to_list(N));
+set_tc_name(N) when is_list(N) ->
+ put(tc_name, N).
+
+%% get_tc_name() ->
+%% get(tc_name).
+
+tc_begin(TC) ->
+ set_tc_name(TC),
+ tc_print("begin ***",
+ "~n----------------------------------------------------~n", "").
+
+tc_end(Result) when is_list(Result) ->
+ tc_print("done: ~s", [Result],
+ "", "----------------------------------------------------~n~n"),
+ ok.
+
+
+tc_try(Case, Fun) when is_atom(Case) andalso is_function(Fun, 0) ->
+ tc_begin(Case),
+ try
+ begin
+ Fun(),
+ ?SLEEP(?SECS(1)),
+ tc_end("ok")
+ end
+ catch
+ throw:{skip, _} = SKIP ->
+ tc_end("skipping"),
+ SKIP;
+ Class:Error:Stack ->
+ tc_end("failed"),
+ erlang:raise(Class, Error, Stack)
+ end.
+
+
+tc_print(F, Before, After) ->
+ tc_print(F, [], Before, After).
+
+tc_print(F, A, Before, After) ->
+ Name = tc_which_name(),
+ FStr = f("*** [~s][~s][~p] " ++ F ++ "~n",
+ [formated_timestamp(),Name,self()|A]),
+ io:format(user, Before ++ FStr ++ After, []).
+
+tc_which_name() ->
+ case get(tc_name) of
+ undefined ->
+ case get(sname) of
+ undefined ->
+ "";
+ SName when is_list(SName) ->
+ SName
+ end;
+ Name when is_list(Name) ->
+ Name
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% l2a(S) when is_list(S) ->
+%% list_to_atom(S).
+
+%% l2b(L) when is_list(L) ->
+%% list_to_binary(L).
+
+%% b2l(B) when is_binary(B) ->
+%% binary_to_list(B).
+
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+
+%% i(F) ->
+%% i(F, []).
+
+i(F, A) ->
+ FStr = f("[~s] " ++ F, [formated_timestamp()|A]),
+ io:format(user, FStr ++ "~n", []),
+ io:format(FStr, []).
+
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index ca5f90621f..4a0ad9c1d5 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -64,7 +64,8 @@
nif_phash2/1,
nif_whereis/1, nif_whereis_parallel/1,
nif_whereis_threaded/1, nif_whereis_proxy/1,
- nif_ioq/1
+ nif_ioq/1,
+ pid/1
]).
-export([many_args_100/100]).
@@ -103,7 +104,8 @@ all() ->
nif_internal_hash_salted,
nif_phash2,
nif_whereis, nif_whereis_parallel, nif_whereis_threaded,
- nif_ioq].
+ nif_ioq,
+ pid].
groups() ->
[{G, [], api_repeaters()} || G <- api_groups()]
@@ -485,91 +487,118 @@ t_on_load(Config) when is_list(Config) ->
-define(ERL_NIF_SELECT_READ, (1 bsl 0)).
-define(ERL_NIF_SELECT_WRITE, (1 bsl 1)).
-define(ERL_NIF_SELECT_STOP, (1 bsl 2)).
+-define(ERL_NIF_SELECT_CANCEL, (1 bsl 3)).
+-define(ERL_NIF_SELECT_CUSTOM_MSG, (1 bsl 4)).
-define(ERL_NIF_SELECT_STOP_CALLED, (1 bsl 0)).
-define(ERL_NIF_SELECT_STOP_SCHEDULED, (1 bsl 1)).
-define(ERL_NIF_SELECT_INVALID_EVENT, (1 bsl 2)).
-define(ERL_NIF_SELECT_FAILED, (1 bsl 3)).
-
+-define(ERL_NIF_SELECT_READ_CANCELLED, (1 bsl 4)).
+-define(ERL_NIF_SELECT_WRITE_CANCELLED, (1 bsl 5)).
select(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
+ select_do(0, make_ref(), make_ref(), null),
+
+ RefBin = list_to_binary(lists:duplicate(100, $x)),
+ [select_do(?ERL_NIF_SELECT_CUSTOM_MSG,
+ small, {a, tuple, with, "some", RefBin}, MSG_ENV)
+ || MSG_ENV <- [null, alloc_env]],
+ ok.
+
+select_do(Flag, Ref, Ref2, MSG_ENV) ->
+ io:format("select_do(~p, ~p, ~p)\n", [Ref, Ref2, MSG_ENV]),
- Ref = make_ref(),
- Ref2 = make_ref(),
{{R, R_ptr}, {W, W_ptr}} = pipe_nif(),
ok = write_nif(W, <<"hej">>),
<<"hej">> = read_nif(R, 3),
%% Wait for read
eagain = read_nif(R, 3),
- 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,null,Ref),
+ 0 = select_nif(R,?ERL_NIF_SELECT_READ bor Flag, R,null,Ref,MSG_ENV),
[] = flush(0),
ok = write_nif(W, <<"hej">>),
- [{select, R, Ref, ready_input}] = flush(),
- 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,self(),Ref2),
- [{select, R, Ref2, ready_input}] = flush(),
+ receive_ready(R, Ref, ready_input),
+ 0 = select_nif(R,?ERL_NIF_SELECT_READ bor Flag,R,self(),Ref2,MSG_ENV),
+ receive_ready(R, Ref2, ready_input),
Papa = self(),
Pid = spawn_link(fun() ->
- [{select, R, Ref, ready_input}] = flush(),
+ receive_ready(R, Ref, ready_input),
Papa ! {self(), done}
end),
- 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,Pid,Ref),
+ 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, Pid, Ref,MSG_ENV),
{Pid, done} = receive_any(1000),
+
+ %% Cancel read
+ 0 = select_nif(R,?ERL_NIF_SELECT_READ bor ?ERL_NIF_SELECT_CANCEL,R,null,Ref,null),
<<"hej">> = read_nif(R, 3),
+ 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref, MSG_ENV),
+ ?ERL_NIF_SELECT_READ_CANCELLED =
+ select_nif(R,?ERL_NIF_SELECT_READ bor ?ERL_NIF_SELECT_CANCEL,R,null,Ref,null),
+ ok = write_nif(W, <<"hej again">>),
+ [] = flush(0),
+ <<"hej again">> = read_nif(R, 9),
%% Wait for write
Written = write_full(W, $a),
- 0 = select_nif(W,?ERL_NIF_SELECT_WRITE,W,self(),Ref),
+ 0 = select_nif(W, ?ERL_NIF_SELECT_WRITE bor Flag, W, self(), Ref, MSG_ENV),
[] = flush(0),
Written = read_nif(R,byte_size(Written)),
- [{select, W, Ref, ready_output}] = flush(),
+ receive_ready(W, Ref, ready_output),
+
+ %% Cancel write
+ 0 = select_nif(W, ?ERL_NIF_SELECT_WRITE bor ?ERL_NIF_SELECT_CANCEL, W, null, Ref, null),
+ Written2 = write_full(W, $b),
+ 0 = select_nif(W, ?ERL_NIF_SELECT_WRITE bor Flag, W, null, Ref, MSG_ENV),
+ ?ERL_NIF_SELECT_WRITE_CANCELLED =
+ select_nif(W, ?ERL_NIF_SELECT_WRITE bor ?ERL_NIF_SELECT_CANCEL, W, null, Ref, null),
+ Written2 = read_nif(R,byte_size(Written2)),
+ [] = flush(0),
%% Close write and wait for EOF
eagain = read_nif(R, 1),
- check_stop_ret(select_nif(W,?ERL_NIF_SELECT_STOP,W,null,Ref)),
+ check_stop_ret(select_nif(W, ?ERL_NIF_SELECT_STOP, W, null, Ref, null)),
[{fd_resource_stop, W_ptr, _}] = flush(),
{1, {W_ptr,_}} = last_fd_stop_call(),
true = is_closed_nif(W),
[] = flush(0),
- 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,self(),Ref),
- [{select, R, Ref, ready_input}] = flush(),
+ 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, self(), Ref, MSG_ENV),
+ receive_ready(R, Ref, ready_input),
eof = read_nif(R,1),
- check_stop_ret(select_nif(R,?ERL_NIF_SELECT_STOP,R,null,Ref)),
+ check_stop_ret(select_nif(R, ?ERL_NIF_SELECT_STOP, R, null, Ref, null)),
[{fd_resource_stop, R_ptr, _}] = flush(),
{1, {R_ptr,_}} = last_fd_stop_call(),
true = is_closed_nif(R),
- select_2(Config).
+ select_2(Flag, Ref, Ref2, MSG_ENV).
-select_2(Config) ->
+select_2(Flag, Ref1, Ref2, MSG_ENV) ->
erlang:garbage_collect(),
{_,_,2} = last_resource_dtor_call(),
- Ref1 = make_ref(),
- Ref2 = make_ref(),
{{R, R_ptr}, {W, W_ptr}} = pipe_nif(),
%% Change ref
eagain = read_nif(R, 1),
- 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,null,Ref1),
- 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,self(),Ref2),
+ 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref1, MSG_ENV),
+ 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, self(), Ref2, MSG_ENV),
[] = flush(0),
ok = write_nif(W, <<"hej">>),
- [{select, R, Ref2, ready_input}] = flush(),
+ receive_ready(R, Ref2, ready_input),
<<"hej">> = read_nif(R, 3),
%% Change pid
eagain = read_nif(R, 1),
- 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,null,Ref1),
+ 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref1, MSG_ENV),
Papa = self(),
spawn_link(fun() ->
- 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,null,Ref1),
+ 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref1, MSG_ENV),
[] = flush(0),
Papa ! sync,
- [{select, R, Ref1, ready_input}] = flush(),
+ receive_ready(R, Ref1, ready_input),
<<"hej">> = read_nif(R, 3),
Papa ! done
end),
@@ -578,24 +607,30 @@ select_2(Config) ->
done = receive_any(),
[] = flush(0),
- check_stop_ret(select_nif(R,?ERL_NIF_SELECT_STOP,R,null,Ref1)),
+ check_stop_ret(select_nif(R,?ERL_NIF_SELECT_STOP,R,null,Ref1, null)),
[{fd_resource_stop, R_ptr, _}] = flush(),
{1, {R_ptr,_}} = last_fd_stop_call(),
true = is_closed_nif(R),
%% Stop without previous read/write select
- ?ERL_NIF_SELECT_STOP_CALLED = select_nif(W,?ERL_NIF_SELECT_STOP,W,null,Ref1),
+ ?ERL_NIF_SELECT_STOP_CALLED = select_nif(W,?ERL_NIF_SELECT_STOP,W,null,Ref1,null),
[{fd_resource_stop, W_ptr, 1}] = flush(),
{1, {W_ptr,1}} = last_fd_stop_call(),
true = is_closed_nif(W),
- select_3(Config).
+ select_3().
-select_3(_Config) ->
+select_3() ->
erlang:garbage_collect(),
{_,_,2} = last_resource_dtor_call(),
ok.
+receive_ready(R, Ref, IOatom) when is_reference(Ref) ->
+ [{select, R, Ref, IOatom}] = flush();
+receive_ready(_, Msg, _) ->
+ [Got] = flush(),
+ {true,_,_} = {Got=:=Msg, Got, Msg}.
+
%% @doc The stealing child process for the select_steal test. Duplicates given
%% W/RFds and runs select on them to steal
select_steal_child_process(Parent, RFd) ->
@@ -604,7 +639,7 @@ select_steal_child_process(Parent, RFd) ->
Ref2 = make_ref(),
%% Try to select from the child pid (steal from parent)
- ?assertEqual(0, select_nif(R2Fd, ?ERL_NIF_SELECT_READ, R2Fd, null, Ref2)),
+ ?assertEqual(0, select_nif(R2Fd, ?ERL_NIF_SELECT_READ, R2Fd, null, Ref2, null)),
?assertEqual([], flush(0)),
?assertEqual(eagain, read_nif(R2Fd, 1)),
@@ -612,7 +647,7 @@ select_steal_child_process(Parent, RFd) ->
Parent ! {self(), stage1}, % signal parent to send the <<"stolen1">>
%% Receive <<"stolen1">> via enif_select
- ?assertEqual(0, select_nif(R2Fd, ?ERL_NIF_SELECT_READ, R2Fd, null, Ref2)),
+ ?assertEqual(0, select_nif(R2Fd, ?ERL_NIF_SELECT_READ, R2Fd, null, Ref2, null)),
?assertMatch([{select, R2Fd, Ref2, ready_input}], flush()),
?assertEqual(<<"stolen1">>, read_nif(R2Fd, 7)),
@@ -630,7 +665,7 @@ select_steal(Config) when is_list(Config) ->
{{RFd, RPtr}, {WFd, WPtr}} = pipe_nif(),
%% Bind the socket to current pid in enif_select
- ?assertEqual(0, select_nif(RFd, ?ERL_NIF_SELECT_READ, RFd, null, Ref)),
+ ?assertEqual(0, select_nif(RFd, ?ERL_NIF_SELECT_READ, RFd, null, Ref, null)),
?assertEqual([], flush(0)),
%% Spawn a process and do some stealing
@@ -644,15 +679,15 @@ select_steal(Config) when is_list(Config) ->
?assertMatch([{Pid, done}], flush(1)), % synchronize with the child
%% Try to select from the parent pid (steal back)
- ?assertEqual(0, select_nif(RFd, ?ERL_NIF_SELECT_READ, RFd, Pid, Ref)),
+ ?assertEqual(0, select_nif(RFd, ?ERL_NIF_SELECT_READ, RFd, Pid, Ref, null)),
%% Ensure that no data is hanging and close.
%% Rfd is stolen at this point.
- check_stop_ret(select_nif(WFd, ?ERL_NIF_SELECT_STOP, WFd, null, Ref)),
+ check_stop_ret(select_nif(WFd, ?ERL_NIF_SELECT_STOP, WFd, null, Ref, null)),
?assertMatch([{fd_resource_stop, WPtr, _}], flush()),
{1, {WPtr, 1}} = last_fd_stop_call(),
- check_stop_ret(select_nif(RFd, ?ERL_NIF_SELECT_STOP, RFd, null, Ref)),
+ check_stop_ret(select_nif(RFd, ?ERL_NIF_SELECT_STOP, RFd, null, Ref, null)),
?assertMatch([{fd_resource_stop, RPtr, _}], flush()),
{1, {RPtr, _DirectCall}} = last_fd_stop_call(),
@@ -789,8 +824,11 @@ demonitor_process(Config) ->
end),
R_ptr = alloc_monitor_resource_nif(),
{0,MonBin1} = monitor_process_nif(R_ptr, Pid, true, self()),
+ MonTerm1 = make_monitor_term_nif(MonBin1),
[R_ptr] = monitored_by(Pid),
{0,MonBin2} = monitor_process_nif(R_ptr, Pid, true, self()),
+ MonTerm2 = make_monitor_term_nif(MonBin2),
+ true = (MonTerm1 =/= MonTerm2),
[R_ptr, R_ptr] = monitored_by(Pid),
0 = demonitor_process_nif(R_ptr, MonBin1),
[R_ptr] = monitored_by(Pid),
@@ -804,6 +842,10 @@ demonitor_process(Config) ->
{R_ptr, _, 1} = last_resource_dtor_call(),
[] = monitored_by(Pid),
Pid ! return,
+
+ erlang:garbage_collect(),
+ true = (MonTerm1 =/= MonTerm2),
+ io:format("MonTerm1 = ~p\nMonTerm2 = ~p\n", [MonTerm1, MonTerm2]),
ok.
@@ -3327,6 +3369,30 @@ make_unaligned_binary(Bin0) ->
<<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>),
Bin.
+pid(Config) ->
+ ensure_lib_loaded(Config),
+ Self = self(),
+ {true, ErlNifPid} = get_local_pid_nif(Self),
+ false = is_pid_undefined_nif(ErlNifPid),
+ Self = make_pid_nif(ErlNifPid),
+
+ UndefPid = set_pid_undefined_nif(),
+ true = is_pid_undefined_nif(UndefPid),
+ undefined = make_pid_nif(UndefPid),
+ 0 = send_term(UndefPid, message),
+
+ Other = spawn(fun() -> ok end),
+ {true,OtherNifPid} = get_local_pid_nif(Other),
+ Cmp = compare_pids_nif(ErlNifPid, OtherNifPid),
+ true = if Cmp < 0 -> Self < Other;
+ Cmp > 0 -> Self > Other
+ end,
+ 0 = compare_pids_nif(ErlNifPid, ErlNifPid),
+
+ {false, _} = get_local_pid_nif(undefined),
+ ok.
+
+
id(I) -> I.
%% The NIFs:
@@ -3392,7 +3458,7 @@ term_to_binary_nif(_, _) -> ?nif_stub.
binary_to_term_nif(_, _, _) -> ?nif_stub.
port_command_nif(_, _) -> ?nif_stub.
format_term_nif(_,_) -> ?nif_stub.
-select_nif(_,_,_,_,_) -> ?nif_stub.
+select_nif(_,_,_,_,_,_) -> ?nif_stub.
dupe_resource_nif(_) -> ?nif_stub.
pipe_nif() -> ?nif_stub.
write_nif(_,_) -> ?nif_stub.
@@ -3404,6 +3470,7 @@ alloc_monitor_resource_nif() -> ?nif_stub.
monitor_process_nif(_,_,_,_) -> ?nif_stub.
demonitor_process_nif(_,_) -> ?nif_stub.
compare_monitors_nif(_,_) -> ?nif_stub.
+make_monitor_term_nif(_) -> ?nif_stub.
monitor_frenzy_nif(_,_,_,_) -> ?nif_stub.
ioq_nif(_) -> ?nif_stub.
ioq_nif(_,_) -> ?nif_stub.
@@ -3434,5 +3501,11 @@ convert_time_unit(_,_,_) -> ?nif_stub.
now_time() -> ?nif_stub.
cpu_time() -> ?nif_stub.
+get_local_pid_nif(_) -> ?nif_stub.
+make_pid_nif(_) -> ?nif_stub.
+set_pid_undefined_nif() -> ?nif_stub.
+is_pid_undefined_nif(_) -> ?nif_stub.
+compare_pids_nif(_, _) -> ?nif_stub.
+
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index f2ce6dbe67..0d5d900d31 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1882,12 +1882,23 @@ static ERL_NIF_TERM copy_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return enif_make_copy(env, mti.p->blob);
}
+static int get_pidbin(ErlNifEnv* env, ERL_NIF_TERM pidbin, ErlNifPid* pid)
+{
+ ErlNifBinary bin;
+
+ if (!enif_inspect_binary(env, pidbin, &bin) || bin.size != sizeof(ErlNifPid))
+ return 0;
+
+ memcpy(pid, bin.data, bin.size);
+ return 1;
+}
+
static ERL_NIF_TERM send_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifEnv* menv;
ErlNifPid pid;
int ret;
- if (!enif_get_local_pid(env, argv[0], &pid)) {
+ if (!enif_get_local_pid(env, argv[0], &pid) && !get_pidbin(env, argv[0], &pid)) {
return enif_make_badarg(env);
}
menv = enif_alloc_env();
@@ -2486,7 +2497,8 @@ static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
enum ErlNifSelectFlags mode;
void* obj;
ErlNifPid nifpid, *pid = NULL;
- ERL_NIF_TERM ref;
+ ERL_NIF_TERM ref_or_msg;
+ ErlNifEnv* msg_env = NULL;
int retval;
if (!get_fd(env, argv[0], &fdr)
@@ -2501,11 +2513,27 @@ static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
return enif_make_badarg(env);
pid = &nifpid;
}
- ref = argv[4];
+ ref_or_msg = argv[4];
+ if (argv[5] != atom_null) {
+ msg_env = enif_alloc_env();
+ ref_or_msg = enif_make_copy(msg_env, ref_or_msg);
+ }
fdr->was_selected = 1;
enif_self(env, &fdr->pid);
- retval = enif_select(env, fdr->fd, mode, obj, pid, ref);
+ switch (mode) {
+ case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_READ:
+ retval = enif_select_read(env, fdr->fd, obj, pid, ref_or_msg, msg_env);
+ break;
+ case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_WRITE:
+ retval = enif_select_write(env, fdr->fd, obj, pid, ref_or_msg, msg_env);
+ break;
+ default:
+ retval = enif_select(env, fdr->fd, mode, obj, pid, ref_or_msg);
+ }
+
+ if (msg_env)
+ enif_free_env(msg_env);
return enif_make_int(env, retval);
}
@@ -2830,6 +2858,16 @@ static ERL_NIF_TERM compare_monitors_nif(ErlNifEnv* env, int argc, const ERL_NIF
return enif_make_int(env, enif_compare_monitors(&m1, &m2));
}
+static ERL_NIF_TERM make_monitor_term_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifMonitor m;
+ if (!get_monitor(env, argv[0], &m)) {
+ return enif_make_badarg(env);
+ }
+
+ return enif_make_monitor_term(env, &m);
+}
+
/*********** monitor_frenzy ************/
@@ -3486,6 +3524,65 @@ static ERL_NIF_TERM ioq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
return enif_make_badarg(env);
}
+static ERL_NIF_TERM make_bool(ErlNifEnv* env, int bool)
+{
+ return bool ? atom_true : atom_false;
+}
+
+static ERL_NIF_TERM get_local_pid_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid pid;
+ ERL_NIF_TERM pid_bin;
+ int ret = enif_get_local_pid(env, argv[0], &pid);
+
+ memcpy(enif_make_new_binary(env, sizeof(ErlNifPid), &pid_bin),
+ &pid, sizeof(ErlNifPid));
+
+ return enif_make_tuple2(env, make_bool(env, ret), pid_bin);
+}
+
+static ERL_NIF_TERM make_pid_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid pid;
+
+ if (!get_pidbin(env, argv[0], &pid))
+ return enif_make_badarg(env);
+
+ return enif_make_pid(env, &pid);
+}
+
+static ERL_NIF_TERM set_pid_undefined_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid pid;
+ ERL_NIF_TERM pid_bin;
+
+ enif_set_pid_undefined(&pid);
+ memcpy(enif_make_new_binary(env, sizeof(ErlNifPid), &pid_bin),
+ &pid, sizeof(ErlNifPid));
+
+ return pid_bin;
+}
+
+static ERL_NIF_TERM is_pid_undefined_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid pid;
+
+ if (!get_pidbin(env, argv[0], &pid))
+ return enif_make_badarg(env);
+
+ return make_bool(env, enif_is_pid_undefined(&pid));
+}
+
+static ERL_NIF_TERM compare_pids_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifPid a, b;
+
+ if (!get_pidbin(env, argv[0], &a) || !get_pidbin(env, argv[1], &b))
+ return enif_make_badarg(env);
+
+ return enif_make_int(env, enif_compare_pids(&a, &b));
+}
+
static ErlNifFunc nif_funcs[] =
{
{"lib_version", 0, lib_version},
@@ -3565,7 +3662,7 @@ static ErlNifFunc nif_funcs[] =
{"binary_to_term_nif", 3, binary_to_term},
{"port_command_nif", 2, port_command},
{"format_term_nif", 2, format_term},
- {"select_nif", 5, select_nif},
+ {"select_nif", 6, select_nif},
#ifndef __WIN32__
{"pipe_nif", 0, pipe_nif},
{"write_nif", 2, write_nif},
@@ -3579,6 +3676,7 @@ static ErlNifFunc nif_funcs[] =
{"monitor_process_nif", 4, monitor_process_nif},
{"demonitor_process_nif", 2, demonitor_process_nif},
{"compare_monitors_nif", 2, compare_monitors_nif},
+ {"make_monitor_term_nif", 1, make_monitor_term_nif},
{"monitor_frenzy_nif", 4, monitor_frenzy_nif},
{"whereis_send", 3, whereis_send},
{"whereis_term", 2, whereis_term},
@@ -3587,7 +3685,12 @@ static ErlNifFunc nif_funcs[] =
{"ioq_nif", 1, ioq},
{"ioq_nif", 2, ioq},
{"ioq_nif", 3, ioq},
- {"ioq_nif", 4, ioq}
+ {"ioq_nif", 4, ioq},
+ {"get_local_pid_nif", 1, get_local_pid_nif},
+ {"make_pid_nif", 1, make_pid_nif},
+ {"set_pid_undefined_nif", 0, set_pid_undefined_nif},
+ {"is_pid_undefined_nif", 1, is_pid_undefined_nif},
+ {"compare_pids_nif", 2, compare_pids_nif}
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,NULL,upgrade,unload)
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
index 300b4ed036..ef4635a6f5 100644
--- a/erts/emulator/test/node_container_SUITE.erl
+++ b/erts/emulator/test/node_container_SUITE.erl
@@ -71,25 +71,10 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
erts_debug:set_internal_state(available_internal_state, true),
erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value
- available_internal_state(false).
-
-available_internal_state(Bool) when Bool == true; Bool == false ->
- case {Bool,
- (catch erts_debug:get_internal_state(available_internal_state))} of
- {true, true} ->
- true;
- {false, true} ->
- erts_debug:set_internal_state(available_internal_state, false),
- true;
- {true, _} ->
- erts_debug:set_internal_state(available_internal_state, true),
- false;
- {false, _} ->
- false
- end.
+ erts_test_utils:available_internal_state(false).
init_per_testcase(_Case, Config) when is_list(Config) ->
- available_internal_state(true),
+ erts_test_utils:available_internal_state(true),
Config.
end_per_testcase(_Case, Config) when is_list(Config) ->
@@ -928,9 +913,9 @@ id(X) ->
-define(ND_REFS, erts_debug:get_internal_state(node_and_dist_references)).
node_container_refc_check(Node) when is_atom(Node) ->
- AIS = available_internal_state(true),
+ AIS = erts_test_utils:available_internal_state(true),
nc_refc_check(Node),
- available_internal_state(AIS).
+ erts_test_utils:available_internal_state(AIS).
nc_refc_check(Node) when is_atom(Node) ->
Ref = make_ref(),
@@ -938,15 +923,11 @@ nc_refc_check(Node) when is_atom(Node) ->
io:format("Starting reference count check of node ~w~n", [Node]),
spawn_link(Node,
fun () ->
- {{node_references, NodeRefs},
- {dist_references, DistRefs}} = ?ND_REFS,
- check_nd_refc({node(), erlang:system_info(creation)},
- NodeRefs,
- DistRefs,
- fun (ErrMsg) ->
- Self ! {Ref, ErrMsg, failed},
- exit(normal)
- end),
+ erts_test_utils:check_node_dist(
+ fun (ErrMsg) ->
+ Self ! {Ref, ErrMsg, failed},
+ exit(normal)
+ end),
Self ! {Ref, succeded}
end),
receive
@@ -958,98 +939,26 @@ nc_refc_check(Node) when is_atom(Node) ->
ok
end.
-check_nd_refc({ThisNodeName, ThisCreation}, NodeRefs, DistRefs, Fail) ->
- case catch begin
- check_refc(ThisNodeName,ThisCreation,"node table",NodeRefs),
- check_refc(ThisNodeName,ThisCreation,"dist table",DistRefs),
- ok
- end of
- ok ->
- ok;
- {'EXIT', Reason} ->
- {Y,Mo,D} = date(),
- {H,Mi,S} = time(),
- ErrMsg = io_lib:format("~n"
- "*** Reference count check of node ~w "
- "failed (~p) at ~w~w~w ~w:~w:~w~n"
- "*** Node table references:~n ~p~n"
- "*** Dist table references:~n ~p~n",
- [node(), Reason, Y, Mo, D, H, Mi, S,
- NodeRefs, DistRefs]),
- Fail(lists:flatten(ErrMsg))
- end.
-
-
-check_refc(ThisNodeName,ThisCreation,Table,EntryList) when is_list(EntryList) ->
- lists:foreach(
- fun ({Entry, Refc, ReferrerList}) ->
- {DelayedDeleteTimer,
- FoundRefs} =
- lists:foldl(
- fun ({Referrer, ReferencesList}, {DDT, A1}) ->
- {case Referrer of
- {system,delayed_delete_timer} ->
- true;
- {system,thread_progress_delete_timer} ->
- true;
- _ ->
- DDT
- end,
- A1 + lists:foldl(fun ({_T,Rs},A2) ->
- A2+Rs
- end,
- 0,
- ReferencesList)}
- end,
- {false, 0},
- ReferrerList),
-
- %% Reference count equals found references?
- case {Refc, FoundRefs, DelayedDeleteTimer} of
- {X, X, _} ->
- ok;
- {0, 1, true} ->
- ok;
- _ ->
- exit({invalid_reference_count, Table, Entry})
- end,
-
- %% All entries in table referred to?
- case {Entry, Refc} of
- {ThisNodeName, 0} -> ok;
- {{ThisNodeName, ThisCreation}, 0} -> ok;
- {_, 0} when DelayedDeleteTimer == false ->
- exit({not_referred_entry_in_table, Table, Entry});
- {_, _} -> ok
- end
-
- end,
- EntryList),
- ok.
-
get_node_references({NodeName, Creation} = Node) when is_atom(NodeName),
is_integer(Creation) ->
{{node_references, NodeRefs},
{dist_references, DistRefs}} = ?ND_REFS,
- check_nd_refc({node(), erlang:system_info(creation)},
- NodeRefs,
- DistRefs,
- fun (ErrMsg) ->
- io:format("~s", [ErrMsg]),
- ct:fail(reference_count_check_failed)
- end),
+ erts_test_utils:check_node_dist(
+ fun (ErrMsg) ->
+ io:format("~s", [ErrMsg]),
+ ct:fail(reference_count_check_failed)
+ end,
+ NodeRefs, DistRefs),
find_references(Node, NodeRefs).
get_dist_references(NodeName) when is_atom(NodeName) ->
{{node_references, NodeRefs},
{dist_references, DistRefs}} = ?ND_REFS,
- check_nd_refc({node(), erlang:system_info(creation)},
- NodeRefs,
- DistRefs,
- fun (ErrMsg) ->
- io:format("~s", [ErrMsg]),
- ct:fail(reference_count_check_failed)
- end),
+ erts_test_utils:check_node_dist(fun (ErrMsg) ->
+ io:format("~s", [ErrMsg]),
+ ct:fail(reference_count_check_failed)
+ end,
+ NodeRefs, DistRefs),
find_references(NodeName, DistRefs).
find_references(N, NRefList) ->
@@ -1138,133 +1047,15 @@ get_nodename() ->
++ "@"
++ hostname()).
-
-
--define(VERSION_MAGIC, 131).
-
--define(ATOM_EXT, 100).
--define(REFERENCE_EXT, 101).
--define(PORT_EXT, 102).
--define(PID_EXT, 103).
--define(NEW_REFERENCE_EXT, 114).
--define(NEW_PID_EXT, $X).
--define(NEW_PORT_EXT, $Y).
--define(NEWER_REFERENCE_EXT, $Z).
-
-uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
- [(Uint bsr 24) band 16#ff,
- (Uint bsr 16) band 16#ff,
- (Uint bsr 8) band 16#ff,
- Uint band 16#ff];
-uint32_be(Uint) ->
- exit({badarg, uint32_be, [Uint]}).
-
-
-uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 ->
- [(Uint bsr 8) band 16#ff,
- Uint band 16#ff];
-uint16_be(Uint) ->
- exit({badarg, uint16_be, [Uint]}).
-
-uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
- Uint band 16#ff;
-uint8(Uint) ->
- exit({badarg, uint8, [Uint]}).
-
-
-pid_tag(bad_creation) -> ?PID_EXT;
-pid_tag(Creation) when Creation =< 3 -> ?PID_EXT;
-pid_tag(_Creation) -> ?NEW_PID_EXT.
-
-enc_creation(bad_creation) -> uint8(4);
-enc_creation(Creation) when Creation =< 3 -> uint8(Creation);
-enc_creation(Creation) -> uint32_be(Creation).
-
-mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
- mk_pid({atom_to_list(NodeName), Creation}, Number, Serial);
mk_pid({NodeName, Creation}, Number, Serial) ->
- case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- pid_tag(Creation),
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint32_be(Number),
- uint32_be(Serial),
- enc_creation(Creation)])) of
- Pid when is_pid(Pid) ->
- Pid;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
- end.
+ erts_test_utils:mk_ext_pid({NodeName, Creation}, Number, Serial).
-port_tag(bad_creation) -> ?PORT_EXT;
-port_tag(Creation) when Creation =< 3 -> ?PORT_EXT;
-port_tag(_Creation) -> ?NEW_PORT_EXT.
-
-mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
- mk_port({atom_to_list(NodeName), Creation}, Number);
mk_port({NodeName, Creation}, Number) ->
- case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- port_tag(Creation),
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint32_be(Number),
- enc_creation(Creation)])) of
- Port when is_port(Port) ->
- Port;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_port, [{NodeName, Creation}, Number]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
- end.
+ erts_test_utils:mk_ext_port({NodeName, Creation}, Number).
+
+mk_ref({NodeName, Creation}, Numbers) ->
+ erts_test_utils:mk_ext_ref({NodeName, Creation}, Numbers).
-ref_tag(bad_creation) -> ?NEW_REFERENCE_EXT;
-ref_tag(Creation) when Creation =< 3 -> ?NEW_REFERENCE_EXT;
-ref_tag(_Creation) -> ?NEWER_REFERENCE_EXT.
-
-mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
- is_list(Numbers) ->
- mk_ref({atom_to_list(NodeName), Creation}, Numbers);
-mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName),
- Creation =< 3,
- is_integer(Number) ->
- case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?REFERENCE_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- uint32_be(Number),
- uint8(Creation)])) of
- Ref when is_reference(Ref) ->
- Ref;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
- end;
-mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
- is_list(Numbers) ->
- case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ref_tag(Creation),
- uint16_be(length(Numbers)),
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
- enc_creation(Creation),
- lists:map(fun (N) ->
- uint32_be(N)
- end,
- Numbers)])) of
- Ref when is_reference(Ref) ->
- Ref;
- {'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]});
- Other ->
- exit({unexpected_binary_to_term_result, Other})
- end.
exec_loop() ->
receive
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index b23f77a0b2..edf08ce0bd 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -2247,8 +2247,8 @@ processes_term_proc_list(Config) when is_list(Config) ->
%% We have to run this test case with +S1 since instrument:allocations()
%% will report a free()'d block as present until it's actually deallocated
%% by its employer.
- Run("+MSe true +MSatags false +S1"),
- Run("+MSe true +MSatags true +S1"),
+ Run("+MSe true +Muatags false +S1"),
+ Run("+MSe true +Muatags true +S1"),
ok.
@@ -2256,10 +2256,12 @@ processes_term_proc_list(Config) when is_list(Config) ->
chk_term_proc_list(?LINE, MC, XB)).
chk_term_proc_list(Line, MustChk, ExpectBlks) ->
- Allocs = instrument:allocations(#{ allocator_types => [sl_alloc] }),
+ Allocs = instrument:allocations(),
case {MustChk, Allocs} of
{false, {error, not_enabled}} ->
not_enabled;
+ {false, {ok, {_Shift, _Unscanned, ByOrigin}}} when ByOrigin =:= #{} ->
+ not_enabled;
{_, {ok, {_Shift, _Unscanned, ByOrigin}}} ->
ByType = maps:get(system, ByOrigin, #{}),
Hist = maps:get(ptab_list_deleted_el, ByType, {}),
diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl
index 26c610e3a8..5b46342127 100644
--- a/erts/emulator/test/smoke_test_SUITE.erl
+++ b/erts/emulator/test/smoke_test_SUITE.erl
@@ -56,7 +56,7 @@ end_per_testcase(_Case, Config) when is_list(Config) ->
%%%
boot_combo(Config) when is_list(Config) ->
- ZFlags = os:getenv("ERL_ZFLAGS"),
+ ZFlags = os:getenv("ERL_ZFLAGS", ""),
NOOP = fun () -> ok end,
A42 = fun () ->
case erlang:system_info(threads) of
@@ -87,10 +87,7 @@ boot_combo(Config) when is_list(Config) ->
%% A lot more combos could be implemented...
ok
after
- os:putenv("ERL_ZFLAGS", case ZFlags of
- false -> "";
- _ -> ZFlags
- end)
+ os:putenv("ERL_ZFLAGS", ZFlags)
end.
native_atomics(Config) when is_list(Config) ->
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
new file mode 100644
index 0000000000..f799186675
--- /dev/null
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -0,0 +1,17434 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% Run the entire test suite:
+%% ts:run(emulator, socket_SUITE, [batch]).
+%%
+%% Run a specific group:
+%% ts:run(emulator, socket_SUITE, {group, foo}, [batch]).
+%%
+%% Run a specific test case:
+%% ts:run(emulator, socket_SUITE, foo, [batch]).
+
+-module(socket_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+%% Suite exports
+-export([suite/0, all/0, groups/0]).
+-export([init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([
+ %% *** API Basic ***
+ api_b_open_and_close_udp4/1,
+ api_b_open_and_close_tcp4/1,
+ api_b_sendto_and_recvfrom_udp4/1,
+ api_b_sendmsg_and_recvmsg_udp4/1,
+ api_b_send_and_recv_tcp4/1,
+ api_b_sendmsg_and_recvmsg_tcp4/1,
+
+ %% *** API Options ***
+ api_opt_simple_otp_options/1,
+ api_opt_simple_otp_rcvbuf_option/1,
+ api_opt_simple_otp_controlling_process/1,
+
+ %% *** API Operation Timeout ***
+ api_to_connect_tcp4/1,
+ api_to_connect_tcp6/1,
+ api_to_accept_tcp4/1,
+ api_to_accept_tcp6/1,
+ api_to_maccept_tcp4/1,
+ api_to_maccept_tcp6/1,
+ api_to_send_tcp4/1,
+ api_to_send_tcp6/1,
+ api_to_sendto_udp4/1,
+ api_to_sendto_udp6/1,
+ api_to_sendmsg_tcp4/1,
+ api_to_sendmsg_tcp6/1,
+ api_to_recv_udp4/1,
+ api_to_recv_udp6/1,
+ api_to_recv_tcp4/1,
+ api_to_recv_tcp6/1,
+ api_to_recvfrom_udp4/1,
+ api_to_recvfrom_udp6/1,
+ api_to_recvmsg_udp4/1,
+ api_to_recvmsg_udp6/1,
+ api_to_recvmsg_tcp4/1,
+ api_to_recvmsg_tcp6/1,
+
+ %% *** Socket Closure ***
+ sc_cpe_socket_cleanup_tcp4/1,
+ sc_cpe_socket_cleanup_tcp6/1,
+ sc_cpe_socket_cleanup_udp4/1,
+ sc_cpe_socket_cleanup_udp6/1,
+
+ sc_lc_recv_response_tcp4/1,
+ sc_lc_recv_response_tcp6/1,
+ sc_lc_recvfrom_response_udp4/1,
+ sc_lc_recvfrom_response_udp6/1,
+ sc_lc_recvmsg_response_tcp4/1,
+ sc_lc_recvmsg_response_tcp6/1,
+ sc_lc_recvmsg_response_udp4/1,
+ sc_lc_recvmsg_response_udp6/1,
+ sc_lc_acceptor_response_tcp4/1,
+ sc_lc_acceptor_response_tcp6/1,
+
+ sc_rc_recv_response_tcp4/1,
+ sc_rc_recv_response_tcp6/1,
+ sc_rc_recvmsg_response_tcp4/1,
+ sc_rc_recvmsg_response_tcp6/1,
+
+ sc_rs_recv_send_shutdown_receive_tcp4/1,
+ sc_rs_recv_send_shutdown_receive_tcp6/1,
+ sc_rs_recvmsg_send_shutdown_receive_tcp4/1,
+ sc_rs_recvmsg_send_shutdown_receive_tcp6/1,
+
+ %% *** Traffic ***
+ traffic_send_and_recv_chunks_tcp4/1,
+ traffic_send_and_recv_chunks_tcp6/1,
+
+ traffic_ping_pong_small_send_and_recv_tcp4/1,
+ traffic_ping_pong_small_send_and_recv_tcp6/1,
+ traffic_ping_pong_medium_send_and_recv_tcp4/1,
+ traffic_ping_pong_medium_send_and_recv_tcp6/1,
+ traffic_ping_pong_large_send_and_recv_tcp4/1,
+ traffic_ping_pong_large_send_and_recv_tcp6/1,
+
+ traffic_ping_pong_small_sendto_and_recvfrom_udp4/1,
+ traffic_ping_pong_small_sendto_and_recvfrom_udp6/1,
+ traffic_ping_pong_medium_sendto_and_recvfrom_udp4/1,
+ traffic_ping_pong_medium_sendto_and_recvfrom_udp6/1,
+
+ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4/1,
+ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6/1,
+ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4/1,
+ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6/1,
+ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4/1,
+ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6/1,
+
+ traffic_ping_pong_small_sendmsg_and_recvmsg_udp4/1,
+ traffic_ping_pong_small_sendmsg_and_recvmsg_udp6/1,
+ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4/1,
+ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6/1,
+
+ %% *** Time Test ***
+ %% Server: transport = gen_tcp, active = false
+ %% Client: transport = gen_tcp
+ ttest_sgenf_cgenf_small_tcp4/1,
+ ttest_sgenf_cgenf_small_tcp6/1,
+ ttest_sgenf_cgenf_medium_tcp4/1,
+ ttest_sgenf_cgenf_medium_tcp6/1,
+ ttest_sgenf_cgenf_large_tcp4/1,
+ ttest_sgenf_cgenf_large_tcp6/1,
+
+ ttest_sgenf_cgeno_small_tcp4/1,
+ ttest_sgenf_cgeno_small_tcp6/1,
+ ttest_sgenf_cgeno_medium_tcp4/1,
+ ttest_sgenf_cgeno_medium_tcp6/1,
+ ttest_sgenf_cgeno_large_tcp4/1,
+ ttest_sgenf_cgeno_large_tcp6/1,
+
+ ttest_sgenf_cgent_small_tcp4/1,
+ ttest_sgenf_cgent_small_tcp6/1,
+ ttest_sgenf_cgent_medium_tcp4/1,
+ ttest_sgenf_cgent_medium_tcp6/1,
+ ttest_sgenf_cgent_large_tcp4/1,
+ ttest_sgenf_cgent_large_tcp6/1,
+
+ %% Server: transport = gen_tcp, active = false
+ %% Client: transport = socket(tcp)
+ ttest_sgenf_csockf_small_tcp4/1,
+ ttest_sgenf_csockf_small_tcp6/1,
+ ttest_sgenf_csockf_medium_tcp4/1,
+ ttest_sgenf_csockf_medium_tcp6/1,
+ ttest_sgenf_csockf_large_tcp4/1,
+ ttest_sgenf_csockf_large_tcp6/1,
+
+ ttest_sgenf_csocko_small_tcp4/1,
+ ttest_sgenf_csocko_small_tcp6/1,
+ ttest_sgenf_csocko_medium_tcp4/1,
+ ttest_sgenf_csocko_medium_tcp6/1,
+ ttest_sgenf_csocko_large_tcp4/1,
+ ttest_sgenf_csocko_large_tcp6/1,
+
+ ttest_sgenf_csockt_small_tcp4/1,
+ ttest_sgenf_csockt_small_tcp6/1,
+ ttest_sgenf_csockt_medium_tcp4/1,
+ ttest_sgenf_csockt_medium_tcp6/1,
+ ttest_sgenf_csockt_large_tcp4/1,
+ ttest_sgenf_csockt_large_tcp6/1,
+
+ %% Server: transport = gen_tcp, active = once
+ %% Client: transport = gen_tcp
+ ttest_sgeno_cgenf_small_tcp4/1,
+ ttest_sgeno_cgenf_small_tcp6/1,
+ ttest_sgeno_cgenf_medium_tcp4/1,
+ ttest_sgeno_cgenf_medium_tcp6/1,
+ ttest_sgeno_cgenf_large_tcp4/1,
+ ttest_sgeno_cgenf_large_tcp6/1,
+
+ ttest_sgeno_cgeno_small_tcp4/1,
+ ttest_sgeno_cgeno_small_tcp6/1,
+ ttest_sgeno_cgeno_medium_tcp4/1,
+ ttest_sgeno_cgeno_medium_tcp6/1,
+ ttest_sgeno_cgeno_large_tcp4/1,
+ ttest_sgeno_cgeno_large_tcp6/1,
+
+ ttest_sgeno_cgent_small_tcp4/1,
+ ttest_sgeno_cgent_small_tcp6/1,
+ ttest_sgeno_cgent_medium_tcp4/1,
+ ttest_sgeno_cgent_medium_tcp6/1,
+ ttest_sgeno_cgent_large_tcp4/1,
+ ttest_sgeno_cgent_large_tcp6/1,
+
+ %% Server: transport = gen_tcp, active = once
+ %% Client: transport = socket(tcp)
+ ttest_sgeno_csockf_small_tcp4/1,
+ ttest_sgeno_csockf_small_tcp6/1,
+ ttest_sgeno_csockf_medium_tcp4/1,
+ ttest_sgeno_csockf_medium_tcp6/1,
+ ttest_sgeno_csockf_large_tcp4/1,
+ ttest_sgeno_csockf_large_tcp6/1,
+
+ ttest_sgeno_csocko_small_tcp4/1,
+ ttest_sgeno_csocko_small_tcp6/1,
+ ttest_sgeno_csocko_medium_tcp4/1,
+ ttest_sgeno_csocko_medium_tcp6/1,
+ ttest_sgeno_csocko_large_tcp4/1,
+ ttest_sgeno_csocko_large_tcp6/1,
+
+ ttest_sgeno_csockt_small_tcp4/1,
+ ttest_sgeno_csockt_small_tcp6/1,
+ ttest_sgeno_csockt_medium_tcp4/1,
+ ttest_sgeno_csockt_medium_tcp6/1,
+ ttest_sgeno_csockt_large_tcp4/1,
+ ttest_sgeno_csockt_large_tcp6/1,
+
+ %% Server: transport = gen_tcp, active = true
+ %% Client: transport = gen_tcp
+ ttest_sgent_cgenf_small_tcp4/1,
+ ttest_sgent_cgenf_small_tcp6/1,
+ ttest_sgent_cgenf_medium_tcp4/1,
+ ttest_sgent_cgenf_medium_tcp6/1,
+ ttest_sgent_cgenf_large_tcp4/1,
+ ttest_sgent_cgenf_large_tcp6/1,
+
+ ttest_sgent_cgeno_small_tcp4/1,
+ ttest_sgent_cgeno_small_tcp6/1,
+ ttest_sgent_cgeno_medium_tcp4/1,
+ ttest_sgent_cgeno_medium_tcp6/1,
+ ttest_sgent_cgeno_large_tcp4/1,
+ ttest_sgent_cgeno_large_tcp6/1,
+
+ ttest_sgent_cgent_small_tcp4/1,
+ ttest_sgent_cgent_small_tcp6/1,
+ ttest_sgent_cgent_medium_tcp4/1,
+ ttest_sgent_cgent_medium_tcp6/1,
+ ttest_sgent_cgent_large_tcp4/1,
+ ttest_sgent_cgent_large_tcp6/1,
+
+ %% Server: transport = gen_tcp, active = true
+ %% Client: transport = socket(tcp)
+ ttest_sgent_csockf_small_tcp4/1,
+ ttest_sgent_csockf_small_tcp6/1,
+ ttest_sgent_csockf_medium_tcp4/1,
+ ttest_sgent_csockf_medium_tcp6/1,
+ ttest_sgent_csockf_large_tcp4/1,
+ ttest_sgent_csockf_large_tcp6/1,
+
+ ttest_sgent_csocko_small_tcp4/1,
+ ttest_sgent_csocko_small_tcp6/1,
+ ttest_sgent_csocko_medium_tcp4/1,
+ ttest_sgent_csocko_medium_tcp6/1,
+ ttest_sgent_csocko_large_tcp4/1,
+ ttest_sgent_csocko_large_tcp6/1,
+
+ ttest_sgent_csockt_small_tcp4/1,
+ ttest_sgent_csockt_small_tcp6/1,
+ ttest_sgent_csockt_medium_tcp4/1,
+ ttest_sgent_csockt_medium_tcp6/1,
+ ttest_sgent_csockt_large_tcp4/1,
+ ttest_sgent_csockt_large_tcp6/1,
+
+ %% Server: transport = socket(tcp), active = false
+ %% Client: transport = gen_tcp
+ ttest_ssockf_cgenf_small_tcp4/1,
+ ttest_ssockf_cgenf_small_tcp6/1,
+ ttest_ssockf_cgenf_medium_tcp4/1,
+ ttest_ssockf_cgenf_medium_tcp6/1,
+ ttest_ssockf_cgenf_large_tcp4/1,
+ ttest_ssockf_cgenf_large_tcp6/1,
+
+ ttest_ssockf_cgeno_small_tcp4/1,
+ ttest_ssockf_cgeno_small_tcp6/1,
+ ttest_ssockf_cgeno_medium_tcp4/1,
+ ttest_ssockf_cgeno_medium_tcp6/1,
+ ttest_ssockf_cgeno_large_tcp4/1,
+ ttest_ssockf_cgeno_large_tcp6/1,
+
+ ttest_ssockf_cgent_small_tcp4/1,
+ ttest_ssockf_cgent_small_tcp6/1,
+ ttest_ssockf_cgent_medium_tcp4/1,
+ ttest_ssockf_cgent_medium_tcp6/1,
+ ttest_ssockf_cgent_large_tcp4/1,
+ ttest_ssockf_cgent_large_tcp6/1,
+
+ %% Server: transport = socket(tcp), active = false
+ %% Client: transport = socket(tcp)
+ ttest_ssockf_csockf_small_tcp4/1,
+ ttest_ssockf_csockf_small_tcp6/1,
+ ttest_ssockf_csockf_medium_tcp4/1,
+ ttest_ssockf_csockf_medium_tcp6/1,
+ ttest_ssockf_csockf_large_tcp4/1,
+ ttest_ssockf_csockf_large_tcp6/1,
+
+ ttest_ssockf_csocko_small_tcp4/1,
+ ttest_ssockf_csocko_small_tcp6/1,
+ ttest_ssockf_csocko_medium_tcp4/1,
+ ttest_ssockf_csocko_medium_tcp6/1,
+ ttest_ssockf_csocko_large_tcp4/1,
+ ttest_ssockf_csocko_large_tcp6/1,
+
+ ttest_ssockf_csockt_small_tcp4/1,
+ ttest_ssockf_csockt_small_tcp6/1,
+ ttest_ssockf_csockt_medium_tcp4/1,
+ ttest_ssockf_csockt_medium_tcp6/1,
+ ttest_ssockf_csockt_large_tcp4/1,
+ ttest_ssockf_csockt_large_tcp6/1,
+
+ %% Server: transport = socket(tcp), active = once
+ %% Client: transport = gen_tcp
+ ttest_ssocko_cgenf_small_tcp4/1,
+ ttest_ssocko_cgenf_small_tcp6/1,
+ ttest_ssocko_cgenf_medium_tcp4/1,
+ ttest_ssocko_cgenf_medium_tcp6/1,
+ ttest_ssocko_cgenf_large_tcp4/1,
+ ttest_ssocko_cgenf_large_tcp6/1,
+
+ ttest_ssocko_cgeno_small_tcp4/1,
+ ttest_ssocko_cgeno_small_tcp6/1,
+ ttest_ssocko_cgeno_medium_tcp4/1,
+ ttest_ssocko_cgeno_medium_tcp6/1,
+ ttest_ssocko_cgeno_large_tcp4/1,
+ ttest_ssocko_cgeno_large_tcp6/1,
+
+ ttest_ssocko_cgent_small_tcp4/1,
+ ttest_ssocko_cgent_small_tcp6/1,
+ ttest_ssocko_cgent_medium_tcp4/1,
+ ttest_ssocko_cgent_medium_tcp6/1,
+ ttest_ssocko_cgent_large_tcp4/1,
+ ttest_ssocko_cgent_large_tcp6/1,
+
+ %% Server: transport = socket(tcp), active = once
+ %% Client: transport = socket(tcp)
+ ttest_ssocko_csockf_small_tcp4/1,
+ ttest_ssocko_csockf_small_tcp6/1,
+ ttest_ssocko_csockf_medium_tcp4/1,
+ ttest_ssocko_csockf_medium_tcp6/1,
+ ttest_ssocko_csockf_large_tcp4/1,
+ ttest_ssocko_csockf_large_tcp6/1,
+
+ ttest_ssocko_csocko_small_tcp4/1,
+ ttest_ssocko_csocko_small_tcp6/1,
+ ttest_ssocko_csocko_medium_tcp4/1,
+ ttest_ssocko_csocko_medium_tcp6/1,
+ ttest_ssocko_csocko_large_tcp4/1,
+ ttest_ssocko_csocko_large_tcp6/1,
+
+ ttest_ssocko_csockt_small_tcp4/1,
+ ttest_ssocko_csockt_small_tcp6/1,
+ ttest_ssocko_csockt_medium_tcp4/1,
+ ttest_ssocko_csockt_medium_tcp6/1,
+ ttest_ssocko_csockt_large_tcp4/1,
+ ttest_ssocko_csockt_large_tcp6/1,
+
+ %% Server: transport = socket(tcp), active = true
+ %% Client: transport = gen_tcp
+ ttest_ssockt_cgenf_small_tcp4/1,
+ ttest_ssockt_cgenf_small_tcp6/1,
+ ttest_ssockt_cgenf_medium_tcp4/1,
+ ttest_ssockt_cgenf_medium_tcp6/1,
+ ttest_ssockt_cgenf_large_tcp4/1,
+ ttest_ssockt_cgenf_large_tcp6/1,
+
+ ttest_ssockt_cgeno_small_tcp4/1,
+ ttest_ssockt_cgeno_small_tcp6/1,
+ ttest_ssockt_cgeno_medium_tcp4/1,
+ ttest_ssockt_cgeno_medium_tcp6/1,
+ ttest_ssockt_cgeno_large_tcp4/1,
+ ttest_ssockt_cgeno_large_tcp6/1,
+
+ ttest_ssockt_cgent_small_tcp4/1,
+ ttest_ssockt_cgent_small_tcp6/1,
+ ttest_ssockt_cgent_medium_tcp4/1,
+ ttest_ssockt_cgent_medium_tcp6/1,
+ ttest_ssockt_cgent_large_tcp4/1,
+ ttest_ssockt_cgent_large_tcp6/1,
+
+ %% Server: transport = socket(tcp), active = true
+ %% Client: transport = socket(tcp)
+ ttest_ssockt_csockf_small_tcp4/1,
+ ttest_ssockt_csockf_small_tcp6/1,
+ ttest_ssockt_csockf_medium_tcp4/1,
+ ttest_ssockt_csockf_medium_tcp6/1,
+ ttest_ssockt_csockf_large_tcp4/1,
+ ttest_ssockt_csockf_large_tcp6/1,
+
+ ttest_ssockt_csocko_small_tcp4/1,
+ ttest_ssockt_csocko_small_tcp6/1,
+ ttest_ssockt_csocko_medium_tcp4/1,
+ ttest_ssockt_csocko_medium_tcp6/1,
+ ttest_ssockt_csocko_large_tcp4/1,
+ ttest_ssockt_csocko_large_tcp6/1,
+
+ ttest_ssockt_csockt_small_tcp4/1,
+ ttest_ssockt_csockt_small_tcp6/1,
+ ttest_ssockt_csockt_medium_tcp4/1,
+ ttest_ssockt_csockt_medium_tcp6/1,
+ ttest_ssockt_csockt_large_tcp4/1,
+ ttest_ssockt_csockt_large_tcp6/1
+
+ %% Tickets
+ ]).
+
+
+-include("socket_test_evaluator.hrl").
+
+%% Internal exports
+%% -export([]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(BASIC_REQ, <<"hejsan">>).
+-define(BASIC_REP, <<"hoppsan">>).
+
+-define(DATA, <<"HOPPSAN">>). % Temporary
+-define(FAIL(R), exit(R)).
+
+-define(SLEEP(T), receive after T -> ok end).
+
+-define(MINS(M), timer:minutes(M)).
+-define(SECS(S), timer:seconds(S)).
+
+-define(TT(T), ct:timetrap(T)).
+
+-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))).
+-define(TPP_LARGE, lists:flatten(lists:duplicate(1024, ?TPP_MEDIUM))).
+
+-define(TPP_SMALL_NUM, 10000).
+-define(TPP_MEDIUM_NUM, 1000).
+-define(TPP_LARGE_NUM, 100).
+
+-define(TTEST_RUNTIME, ?SECS(10)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {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}],
+ [use_group(Group, Env, Default) || {Group, Env, Default} <- Groups].
+
+use_group(Group, Env, Default) ->
+ case os:getenv(Env) of
+ false when (Default =:= include) ->
+ [{group, Group}];
+ false ->
+ [];
+ Val ->
+ case list_to_atom(string:to_lower(Val)) of
+ Use when (Use =:= include) orelse
+ (Use =:= enable) orelse
+ (Use =:= true) ->
+ [{group, Group}];
+ _ ->
+ []
+ end
+ end.
+
+
+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()}
+
+ %% {tickets, [], ticket_cases()}
+ ].
+
+api_cases() ->
+ [
+ {group, api_basic},
+ {group, api_options},
+ {group, api_op_with_timeout}
+ ].
+
+api_basic_cases() ->
+ [
+ api_b_open_and_close_udp4,
+ api_b_open_and_close_tcp4,
+ api_b_sendto_and_recvfrom_udp4,
+ api_b_sendmsg_and_recvmsg_udp4,
+ api_b_send_and_recv_tcp4,
+ api_b_sendmsg_and_recvmsg_tcp4
+ ].
+
+api_options_cases() ->
+ [
+ api_opt_simple_otp_options,
+ api_opt_simple_otp_rcvbuf_option,
+ api_opt_simple_otp_controlling_process
+ ].
+
+api_op_with_timeout_cases() ->
+ [
+ api_to_connect_tcp4,
+ api_to_connect_tcp6,
+ api_to_accept_tcp4,
+ api_to_accept_tcp6,
+ api_to_maccept_tcp4,
+ api_to_maccept_tcp6,
+ api_to_send_tcp4,
+ api_to_send_tcp6,
+ api_to_sendto_udp4,
+ api_to_sendto_udp6,
+ api_to_sendmsg_tcp4,
+ api_to_sendmsg_tcp6,
+ api_to_recv_udp4,
+ api_to_recv_udp6,
+ api_to_recv_tcp4,
+ api_to_recv_tcp6,
+ api_to_recvfrom_udp4,
+ api_to_recvfrom_udp6,
+ api_to_recvmsg_udp4,
+ api_to_recvmsg_udp6,
+ api_to_recvmsg_tcp4,
+ api_to_recvmsg_tcp6
+ ].
+
+%% These cases tests what happens when the socket is closed/shutdown,
+%% locally or remotely.
+socket_closure_cases() ->
+ [
+ {group, sc_ctrl_proc_exit},
+ {group, sc_local_close},
+ {group, sc_remote_close},
+ {group, sc_remote_shutdown}
+ ].
+
+%% These cases are all about socket cleanup after the controlling process
+%% exits *without* calling socket:close/1.
+sc_cp_exit_cases() ->
+ [
+ sc_cpe_socket_cleanup_tcp4,
+ sc_cpe_socket_cleanup_tcp6,
+ sc_cpe_socket_cleanup_udp4,
+ sc_cpe_socket_cleanup_udp6
+ ].
+
+%% These cases tests what happens when the socket is closed locally.
+sc_lc_cases() ->
+ [
+ sc_lc_recv_response_tcp4,
+ sc_lc_recv_response_tcp6,
+
+ sc_lc_recvfrom_response_udp4,
+ sc_lc_recvfrom_response_udp6,
+
+ sc_lc_recvmsg_response_tcp4,
+ sc_lc_recvmsg_response_tcp6,
+ sc_lc_recvmsg_response_udp4,
+ sc_lc_recvmsg_response_udp6,
+
+ sc_lc_acceptor_response_tcp4,
+ sc_lc_acceptor_response_tcp6
+ ].
+
+%% These cases tests what happens when the socket is closed remotely.
+sc_rc_cases() ->
+ [
+ sc_rc_recv_response_tcp4,
+ sc_rc_recv_response_tcp6,
+
+ sc_rc_recvmsg_response_tcp4,
+ sc_rc_recvmsg_response_tcp6
+ ].
+
+%% These cases tests what happens when the socket is shutdown/closed remotely
+%% after writing and reading is ongoing.
+sc_rs_cases() ->
+ [
+ sc_rs_recv_send_shutdown_receive_tcp4,
+ sc_rs_recv_send_shutdown_receive_tcp6,
+
+ sc_rs_recvmsg_send_shutdown_receive_tcp4,
+ sc_rs_recvmsg_send_shutdown_receive_tcp6
+ ].
+
+
+traffic_cases() ->
+ [
+ traffic_send_and_recv_chunks_tcp4,
+ traffic_send_and_recv_chunks_tcp6,
+
+ traffic_ping_pong_small_send_and_recv_tcp4,
+ traffic_ping_pong_small_send_and_recv_tcp6,
+ traffic_ping_pong_medium_send_and_recv_tcp4,
+ traffic_ping_pong_medium_send_and_recv_tcp6,
+ traffic_ping_pong_large_send_and_recv_tcp4,
+ traffic_ping_pong_large_send_and_recv_tcp6,
+
+ traffic_ping_pong_small_sendto_and_recvfrom_udp4,
+ traffic_ping_pong_small_sendto_and_recvfrom_udp6,
+ traffic_ping_pong_medium_sendto_and_recvfrom_udp4,
+ traffic_ping_pong_medium_sendto_and_recvfrom_udp6,
+
+ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4,
+ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6,
+ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4,
+ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6,
+ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4,
+ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6,
+
+ traffic_ping_pong_small_sendmsg_and_recvmsg_udp4,
+ traffic_ping_pong_small_sendmsg_and_recvmsg_udp6,
+ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4,
+ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6
+ ].
+
+
+ttest_cases() ->
+ [
+ %% Server: transport = gen_tcp, active = false
+ {group, ttest_sgenf},
+
+ %% Server: transport = gen_tcp, active = once
+ {group, ttest_sgeno},
+
+ %% Server: transport = gen_tcp, active = true
+ {group, ttest_sgent},
+
+ %% Server: transport = socket(tcp), active = false
+ {group, ttest_ssockf},
+
+ %% Server: transport = socket(tcp), active = once
+ {group, ttest_ssocko},
+
+ %% Server: transport = socket(tcp), active = true
+ {group, ttest_ssockt}
+
+ ].
+
+
+%% Server: transport = gen_tcp, active = false
+ttest_sgenf_cases() ->
+ [
+ {group, ttest_sgenf_cgen},
+ {group, ttest_sgenf_csock}
+ ].
+
+%% Server: transport = gen_tcp, active = false
+%% Client: transport = gen_tcp
+ttest_sgenf_cgen_cases() ->
+ [
+ {group, ttest_sgenf_cgenf},
+ {group, ttest_sgenf_cgeno},
+ {group, ttest_sgenf_cgent}
+ ].
+
+%% Server: transport = gen_tcp, active = false
+%% Client: transport = gen_tcp, active = false
+ttest_sgenf_cgenf_cases() ->
+ [
+ ttest_sgenf_cgenf_small_tcp4,
+ ttest_sgenf_cgenf_small_tcp6,
+
+ ttest_sgenf_cgenf_medium_tcp4,
+ ttest_sgenf_cgenf_medium_tcp6,
+
+ ttest_sgenf_cgenf_large_tcp4,
+ ttest_sgenf_cgenf_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = false
+%% Client: transport = gen_tcp, active = once
+ttest_sgenf_cgeno_cases() ->
+ [
+ ttest_sgenf_cgeno_small_tcp4,
+ ttest_sgenf_cgeno_small_tcp6,
+
+ ttest_sgenf_cgeno_medium_tcp4,
+ ttest_sgenf_cgeno_medium_tcp6,
+
+ ttest_sgenf_cgeno_large_tcp4,
+ ttest_sgenf_cgeno_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = false
+%% Client: transport = gen_tcp, active = true
+ttest_sgenf_cgent_cases() ->
+ [
+ ttest_sgenf_cgent_small_tcp4,
+ ttest_sgenf_cgent_small_tcp6,
+
+ ttest_sgenf_cgent_medium_tcp4,
+ ttest_sgenf_cgent_medium_tcp6,
+
+ ttest_sgenf_cgent_large_tcp4,
+ ttest_sgenf_cgent_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = false
+%% Client: transport = socket(tcp)
+ttest_sgenf_csock_cases() ->
+ [
+ {group, ttest_sgenf_csockf},
+ {group, ttest_sgenf_csocko},
+ {group, ttest_sgenf_csockt}
+ ].
+
+ttest_sgenf_csockf_cases() ->
+ [
+ ttest_sgenf_csockf_small_tcp4,
+ ttest_sgenf_csockf_small_tcp6,
+
+ ttest_sgenf_csockf_medium_tcp4,
+ ttest_sgenf_csockf_medium_tcp6,
+
+ ttest_sgenf_csockf_large_tcp4,
+ ttest_sgenf_csockf_large_tcp6
+ ].
+
+ttest_sgenf_csocko_cases() ->
+ [
+ ttest_sgenf_csocko_small_tcp4,
+ ttest_sgenf_csocko_small_tcp6,
+
+ ttest_sgenf_csocko_medium_tcp4,
+ ttest_sgenf_csocko_medium_tcp6,
+
+ ttest_sgenf_csocko_large_tcp4,
+ ttest_sgenf_csocko_large_tcp6
+ ].
+
+ttest_sgenf_csockt_cases() ->
+ [
+ ttest_sgenf_csockt_small_tcp4,
+ ttest_sgenf_csockt_small_tcp6,
+
+ ttest_sgenf_csockt_medium_tcp4,
+ ttest_sgenf_csockt_medium_tcp6,
+
+ ttest_sgenf_csockt_large_tcp4,
+ ttest_sgenf_csockt_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = once
+ttest_sgeno_cases() ->
+ [
+ {group, ttest_sgeno_cgen},
+ {group, ttest_sgeno_csock}
+ ].
+
+%% Server: transport = gen_tcp, active = once
+%% Client: transport = gen_tcp
+ttest_sgeno_cgen_cases() ->
+ [
+ {group, ttest_sgeno_cgenf},
+ {group, ttest_sgeno_cgeno},
+ {group, ttest_sgeno_cgent}
+ ].
+
+%% Server: transport = gen_tcp, active = once
+%% Client: transport = gen_tcp, active = false
+ttest_sgeno_cgenf_cases() ->
+ [
+ ttest_sgeno_cgenf_small_tcp4,
+ ttest_sgeno_cgenf_small_tcp6,
+
+ ttest_sgeno_cgenf_medium_tcp4,
+ ttest_sgeno_cgenf_medium_tcp6,
+
+ ttest_sgeno_cgenf_large_tcp4,
+ ttest_sgeno_cgenf_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = once
+%% Client: transport = gen_tcp, active = once
+ttest_sgeno_cgeno_cases() ->
+ [
+ ttest_sgeno_cgeno_small_tcp4,
+ ttest_sgeno_cgeno_small_tcp6,
+
+ ttest_sgeno_cgeno_medium_tcp4,
+ ttest_sgeno_cgeno_medium_tcp6,
+
+ ttest_sgeno_cgeno_large_tcp4,
+ ttest_sgeno_cgeno_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = once
+%% Client: transport = gen_tcp, active = true
+ttest_sgeno_cgent_cases() ->
+ [
+ ttest_sgeno_cgent_small_tcp4,
+ ttest_sgeno_cgent_small_tcp6,
+
+ ttest_sgeno_cgent_medium_tcp4,
+ ttest_sgeno_cgent_medium_tcp6,
+
+ ttest_sgeno_cgent_large_tcp4,
+ ttest_sgeno_cgent_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = once
+%% Client: transport = socket(tcp)
+ttest_sgeno_csock_cases() ->
+ [
+ {group, ttest_sgeno_csockf},
+ {group, ttest_sgeno_csocko},
+ {group, ttest_sgeno_csockt}
+ ].
+
+ttest_sgeno_csockf_cases() ->
+ [
+ ttest_sgeno_csockf_small_tcp4,
+ ttest_sgeno_csockf_small_tcp6,
+
+ ttest_sgeno_csockf_medium_tcp4,
+ ttest_sgeno_csockf_medium_tcp6,
+
+ ttest_sgeno_csockf_large_tcp4,
+ ttest_sgeno_csockf_large_tcp6
+ ].
+
+ttest_sgeno_csocko_cases() ->
+ [
+ ttest_sgeno_csocko_small_tcp4,
+ ttest_sgeno_csocko_small_tcp6,
+
+ ttest_sgeno_csocko_medium_tcp4,
+ ttest_sgeno_csocko_medium_tcp6,
+
+ ttest_sgeno_csocko_large_tcp4,
+ ttest_sgeno_csocko_large_tcp6
+ ].
+
+ttest_sgeno_csockt_cases() ->
+ [
+ ttest_sgeno_csockt_small_tcp4,
+ ttest_sgeno_csockt_small_tcp6,
+
+ ttest_sgeno_csockt_medium_tcp4,
+ ttest_sgeno_csockt_medium_tcp6,
+
+ ttest_sgeno_csockt_large_tcp4,
+ ttest_sgeno_csockt_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = true
+ttest_sgent_cases() ->
+ [
+ {group, ttest_sgent_cgen},
+ {group, ttest_sgent_csock}
+ ].
+
+%% Server: transport = gen_tcp, active = true
+%% Client: transport = gen_tcp
+ttest_sgent_cgen_cases() ->
+ [
+ {group, ttest_sgent_cgenf},
+ {group, ttest_sgent_cgeno},
+ {group, ttest_sgent_cgent}
+ ].
+
+%% Server: transport = gen_tcp, active = true
+%% Client: transport = gen_tcp, active = false
+ttest_sgent_cgenf_cases() ->
+ [
+ ttest_sgent_cgenf_small_tcp4,
+ ttest_sgent_cgenf_small_tcp6,
+
+ ttest_sgent_cgenf_medium_tcp4,
+ ttest_sgent_cgenf_medium_tcp6,
+
+ ttest_sgent_cgenf_large_tcp4,
+ ttest_sgent_cgenf_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = true
+%% Client: transport = gen_tcp, active = once
+ttest_sgent_cgeno_cases() ->
+ [
+ ttest_sgent_cgeno_small_tcp4,
+ ttest_sgent_cgeno_small_tcp6,
+
+ ttest_sgent_cgeno_medium_tcp4,
+ ttest_sgent_cgeno_medium_tcp6,
+
+ ttest_sgent_cgeno_large_tcp4,
+ ttest_sgent_cgeno_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = true
+%% Client: transport = gen_tcp, active = true
+ttest_sgent_cgent_cases() ->
+ [
+ ttest_sgent_cgent_small_tcp4,
+ ttest_sgent_cgent_small_tcp6,
+
+ ttest_sgent_cgent_medium_tcp4,
+ ttest_sgent_cgent_medium_tcp6,
+
+ ttest_sgent_cgent_large_tcp4,
+ ttest_sgent_cgent_large_tcp6
+ ].
+
+%% Server: transport = gen_tcp, active = true
+%% Client: transport = socket(tcp)
+ttest_sgent_csock_cases() ->
+ [
+ {group, ttest_sgent_csockf},
+ {group, ttest_sgent_csocko},
+ {group, ttest_sgent_csockt}
+ ].
+
+ttest_sgent_csockf_cases() ->
+ [
+ ttest_sgent_csockf_small_tcp4,
+ ttest_sgent_csockf_small_tcp6,
+
+ ttest_sgent_csockf_medium_tcp4,
+ ttest_sgent_csockf_medium_tcp6,
+
+ ttest_sgent_csockf_large_tcp4,
+ ttest_sgent_csockf_large_tcp6
+ ].
+
+ttest_sgent_csocko_cases() ->
+ [
+ ttest_sgent_csocko_small_tcp4,
+ ttest_sgent_csocko_small_tcp6,
+
+ ttest_sgent_csocko_medium_tcp4,
+ ttest_sgent_csocko_medium_tcp6,
+
+ ttest_sgent_csocko_large_tcp4,
+ ttest_sgent_csocko_large_tcp6
+ ].
+
+ttest_sgent_csockt_cases() ->
+ [
+ ttest_sgent_csockt_small_tcp4,
+ ttest_sgent_csockt_small_tcp6,
+
+ ttest_sgent_csockt_medium_tcp4,
+ ttest_sgent_csockt_medium_tcp6,
+
+ ttest_sgent_csockt_large_tcp4,
+ ttest_sgent_csockt_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = false
+ttest_ssockf_cases() ->
+ [
+ {group, ttest_ssockf_cgen},
+ {group, ttest_ssockf_csock}
+ ].
+
+%% Server: transport = socket(tcp), active = false
+%% Client: transport = gen_tcp
+ttest_ssockf_cgen_cases() ->
+ [
+ {group, ttest_ssockf_cgenf},
+ {group, ttest_ssockf_cgeno},
+ {group, ttest_ssockf_cgent}
+ ].
+
+%% Server: transport = socket(tcp), active = false
+%% Client: transport = gen_tcp, active = false
+ttest_ssockf_cgenf_cases() ->
+ [
+ ttest_ssockf_cgenf_small_tcp4,
+ ttest_ssockf_cgenf_small_tcp6,
+
+ ttest_ssockf_cgenf_medium_tcp4,
+ ttest_ssockf_cgenf_medium_tcp6,
+
+ ttest_ssockf_cgenf_large_tcp4,
+ ttest_ssockf_cgenf_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = false
+%% Client: transport = gen_tcp, active = once
+ttest_ssockf_cgeno_cases() ->
+ [
+ ttest_ssockf_cgeno_small_tcp4,
+ ttest_ssockf_cgeno_small_tcp6,
+
+ ttest_ssockf_cgeno_medium_tcp4,
+ ttest_ssockf_cgeno_medium_tcp6,
+
+ ttest_ssockf_cgeno_large_tcp4,
+ ttest_ssockf_cgeno_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = false
+%% Client: transport = gen_tcp, active = true
+ttest_ssockf_cgent_cases() ->
+ [
+ ttest_ssockf_cgent_small_tcp4,
+ ttest_ssockf_cgent_small_tcp6,
+
+ ttest_ssockf_cgent_medium_tcp4,
+ ttest_ssockf_cgent_medium_tcp6,
+
+ ttest_ssockf_cgent_large_tcp4,
+ ttest_ssockf_cgent_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = false
+%% Client: transport = socket(tcp)
+ttest_ssockf_csock_cases() ->
+ [
+ {group, ttest_ssockf_csockf},
+ {group, ttest_ssockf_csocko},
+ {group, ttest_ssockf_csockt}
+ ].
+
+%% Server: transport = socket(tcp), active = false
+%% Client: transport = socket(tcp), active = false
+ttest_ssockf_csockf_cases() ->
+ [
+ ttest_ssockf_csockf_small_tcp4,
+ ttest_ssockf_csockf_small_tcp6,
+
+ ttest_ssockf_csockf_medium_tcp4,
+ ttest_ssockf_csockf_medium_tcp6,
+
+ ttest_ssockf_csockf_large_tcp4,
+ ttest_ssockf_csockf_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = false
+%% Client: transport = socket(tcp), active = once
+ttest_ssockf_csocko_cases() ->
+ [
+ ttest_ssockf_csocko_small_tcp4,
+ ttest_ssockf_csocko_small_tcp6,
+
+ ttest_ssockf_csocko_medium_tcp4,
+ ttest_ssockf_csocko_medium_tcp6,
+
+ ttest_ssockf_csocko_large_tcp4,
+ ttest_ssockf_csocko_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = false
+%% Client: transport = socket(tcp), active = true
+ttest_ssockf_csockt_cases() ->
+ [
+ ttest_ssockf_csockt_small_tcp4,
+ ttest_ssockf_csockt_small_tcp6,
+
+ ttest_ssockf_csockt_medium_tcp4,
+ ttest_ssockf_csockt_medium_tcp6,
+
+ ttest_ssockf_csockt_large_tcp4,
+ ttest_ssockf_csockt_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = once
+ttest_ssocko_cases() ->
+ [
+ {group, ttest_ssocko_cgen},
+ {group, ttest_ssocko_csock}
+ ].
+
+%% Server: transport = socket(tcp), active = once
+%% Client: transport = gen_tcp
+ttest_ssocko_cgen_cases() ->
+ [
+ {group, ttest_ssocko_cgenf},
+ {group, ttest_ssocko_cgeno},
+ {group, ttest_ssocko_cgent}
+ ].
+
+%% Server: transport = socket(tcp), active = once
+%% Client: transport = gen_tcp, active = false
+ttest_ssocko_cgenf_cases() ->
+ [
+ ttest_ssocko_cgenf_small_tcp4,
+ ttest_ssocko_cgenf_small_tcp6,
+
+ ttest_ssocko_cgenf_medium_tcp4,
+ ttest_ssocko_cgenf_medium_tcp6,
+
+ ttest_ssocko_cgenf_large_tcp4,
+ ttest_ssocko_cgenf_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = once
+%% Client: transport = gen_tcp, active = once
+ttest_ssocko_cgeno_cases() ->
+ [
+ ttest_ssocko_cgeno_small_tcp4,
+ ttest_ssocko_cgeno_small_tcp6,
+
+ ttest_ssocko_cgeno_medium_tcp4,
+ ttest_ssocko_cgeno_medium_tcp6,
+
+ ttest_ssocko_cgeno_large_tcp4,
+ ttest_ssocko_cgeno_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = once
+%% Client: transport = gen_tcp, active = true
+ttest_ssocko_cgent_cases() ->
+ [
+ ttest_ssocko_cgent_small_tcp4,
+ ttest_ssocko_cgent_small_tcp6,
+
+ ttest_ssocko_cgent_medium_tcp4,
+ ttest_ssocko_cgent_medium_tcp6,
+
+ ttest_ssocko_cgent_large_tcp4,
+ ttest_ssocko_cgent_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = once
+%% Client: transport = socket(tcp)
+ttest_ssocko_csock_cases() ->
+ [
+ {group, ttest_ssocko_csockf},
+ {group, ttest_ssocko_csocko},
+ {group, ttest_ssocko_csockt}
+ ].
+
+%% Server: transport = socket(tcp), active = once
+%% Client: transport = socket(tcp), active = false
+ttest_ssocko_csockf_cases() ->
+ [
+ ttest_ssocko_csockf_small_tcp4,
+ ttest_ssocko_csockf_small_tcp6,
+
+ ttest_ssocko_csockf_medium_tcp4,
+ ttest_ssocko_csockf_medium_tcp6,
+
+ ttest_ssocko_csockf_large_tcp4,
+ ttest_ssocko_csockf_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = once
+%% Client: transport = socket(tcp), active = once
+ttest_ssocko_csocko_cases() ->
+ [
+ ttest_ssocko_csocko_small_tcp4,
+ ttest_ssocko_csocko_small_tcp6,
+
+ ttest_ssocko_csocko_medium_tcp4,
+ ttest_ssocko_csocko_medium_tcp6,
+
+ ttest_ssocko_csocko_large_tcp4,
+ ttest_ssocko_csocko_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = once
+%% Client: transport = socket(tcp), active = true
+ttest_ssocko_csockt_cases() ->
+ [
+ ttest_ssocko_csockt_small_tcp4,
+ ttest_ssocko_csockt_small_tcp6,
+
+ ttest_ssocko_csockt_medium_tcp4,
+ ttest_ssocko_csockt_medium_tcp6,
+
+ ttest_ssocko_csockt_large_tcp4,
+ ttest_ssocko_csockt_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = true
+ttest_ssockt_cases() ->
+ [
+ {group, ttest_ssockt_cgen},
+ {group, ttest_ssockt_csock}
+ ].
+
+%% Server: transport = socket(tcp), active = true
+%% Client: transport = gen_tcp
+ttest_ssockt_cgen_cases() ->
+ [
+ {group, ttest_ssockt_cgenf},
+ {group, ttest_ssockt_cgeno},
+ {group, ttest_ssockt_cgent}
+ ].
+
+%% Server: transport = socket(tcp), active = true
+%% Client: transport = gen_tcp, active = false
+ttest_ssockt_cgenf_cases() ->
+ [
+ ttest_ssockt_cgenf_small_tcp4,
+ ttest_ssockt_cgenf_small_tcp6,
+
+ ttest_ssockt_cgenf_medium_tcp4,
+ ttest_ssockt_cgenf_medium_tcp6,
+
+ ttest_ssockt_cgenf_large_tcp4,
+ ttest_ssockt_cgenf_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = true
+%% Client: transport = gen_tcp, active = once
+ttest_ssockt_cgeno_cases() ->
+ [
+ ttest_ssockt_cgeno_small_tcp4,
+ ttest_ssockt_cgeno_small_tcp6,
+
+ ttest_ssockt_cgeno_medium_tcp4,
+ ttest_ssockt_cgeno_medium_tcp6,
+
+ ttest_ssockt_cgeno_large_tcp4,
+ ttest_ssockt_cgeno_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = true
+%% Client: transport = gen_tcp, active = true
+ttest_ssockt_cgent_cases() ->
+ [
+ ttest_ssockt_cgent_small_tcp4,
+ ttest_ssockt_cgent_small_tcp6,
+
+ ttest_ssockt_cgent_medium_tcp4,
+ ttest_ssockt_cgent_medium_tcp6,
+
+ ttest_ssockt_cgent_large_tcp4,
+ ttest_ssockt_cgent_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = true
+%% Client: transport = socket(tcp)
+ttest_ssockt_csock_cases() ->
+ [
+ {group, ttest_ssockt_csockf},
+ {group, ttest_ssockt_csocko},
+ {group, ttest_ssockt_csockt}
+ ].
+
+%% Server: transport = socket(tcp), active = true
+%% Client: transport = socket(tcp), active = false
+ttest_ssockt_csockf_cases() ->
+ [
+ ttest_ssockt_csockf_small_tcp4,
+ ttest_ssockt_csockf_small_tcp6,
+
+ ttest_ssockt_csockf_medium_tcp4,
+ ttest_ssockt_csockf_medium_tcp6,
+
+ ttest_ssockt_csockf_large_tcp4,
+ ttest_ssockt_csockf_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = true
+%% Client: transport = socket(tcp), active = once
+ttest_ssockt_csocko_cases() ->
+ [
+ ttest_ssockt_csocko_small_tcp4,
+ ttest_ssockt_csocko_small_tcp6,
+
+ ttest_ssockt_csocko_medium_tcp4,
+ ttest_ssockt_csocko_medium_tcp6,
+
+ ttest_ssockt_csocko_large_tcp4,
+ ttest_ssockt_csocko_large_tcp6
+ ].
+
+%% Server: transport = socket(tcp), active = true
+%% Client: transport = socket(tcp), active = true
+ttest_ssockt_csockt_cases() ->
+ [
+ ttest_ssockt_csockt_small_tcp4,
+ ttest_ssockt_csockt_small_tcp6,
+
+ ttest_ssockt_csockt_medium_tcp4,
+ ttest_ssockt_csockt_medium_tcp6,
+
+ ttest_ssockt_csockt_large_tcp4,
+ ttest_ssockt_csockt_large_tcp6
+ ].
+
+%% ticket_cases() ->
+%% [].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init_per_suite(Config) ->
+ case os:type() of
+ {win32, _} ->
+ not_yet_implemented();
+ _ ->
+ ?LOGGER:start(),
+ Config
+ end.
+
+end_per_suite(_) ->
+ ?LOGGER:stop(),
+ ok.
+
+init_per_testcase(_TC, Config) ->
+ ?LOGGER:start(),
+ Config.
+
+end_per_testcase(_TC, Config) ->
+ ?LOGGER:stop(),
+ Config.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% API BASIC %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) and close an IPv4 UDP (dgram) socket.
+%% With some extra checks...
+api_b_open_and_close_udp4(suite) ->
+ [];
+api_b_open_and_close_udp4(doc) ->
+ [];
+api_b_open_and_close_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_b_open_and_close_udp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp},
+ ok = api_b_open_and_close(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically open (create) and close an IPv4 TCP (stream) socket.
+%% With some extra checks...
+api_b_open_and_close_tcp4(suite) ->
+ [];
+api_b_open_and_close_tcp4(doc) ->
+ [];
+api_b_open_and_close_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_b_open_and_close_tcp4,
+ fun() ->
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp},
+ ok = api_b_open_and_close(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_b_open_and_close(InitState) ->
+ Seq =
+ [
+ #{desc => "open",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Protocol} = S) ->
+ Res = socket:open(Domain, Type, Protocol),
+ {ok, {S, Res}}
+ end},
+ #{desc => "validate open",
+ cmd => fun({S, {ok, Sock}}) ->
+ NewS = S#{socket => Sock},
+ {ok, NewS};
+ ({_, {error, _} = ERROR}) ->
+ ERROR
+ end},
+ #{desc => "get domain (maybe)",
+ cmd => fun(#{socket := Sock} = S) ->
+ Res = socket:getopt(Sock, socket, domain),
+ {ok, {S, Res}}
+ end},
+ #{desc => "validate domain (maybe)",
+ cmd => fun({#{domain := Domain} = S, {ok, Domain}}) ->
+ {ok, S};
+ ({#{domain := ExpDomain}, {ok, Domain}}) ->
+ {error, {unexpected_domain, ExpDomain, Domain}};
+ %% Some platforms do not support this option
+ ({S, {error, einval}}) ->
+ {ok, S};
+ ({_, {error, _} = ERROR}) ->
+ ERROR
+ end},
+ #{desc => "get type",
+ cmd => fun(#{socket := Sock} = State) ->
+ Res = socket:getopt(Sock, socket, type),
+ {ok, {State, Res}}
+ end},
+ #{desc => "validate type",
+ cmd => fun({#{type := Type} = State, {ok, Type}}) ->
+ {ok, State};
+ ({#{type := ExpType}, {ok, Type}}) ->
+ {error, {unexpected_type, ExpType, Type}};
+ ({_, {error, _} = ERROR}) ->
+ ERROR
+ end},
+ #{desc => "get protocol",
+ cmd => fun(#{socket := Sock} = State) ->
+ case socket:supports(options, socket, protocol) of
+ true ->
+ Res = socket:getopt(Sock, socket, protocol),
+ {ok, {State, Res}};
+ false ->
+ {ok, {State, not_supported}}
+ end
+ end},
+ #{desc => "validate protocol",
+ cmd => fun({State, not_supported}) ->
+ ?SEV_IPRINT("socket option 'protocol' "
+ "not supported"),
+ {ok, State};
+ ({#{protocol := Protocol} = State, {ok, Protocol}}) ->
+ {ok, State};
+ ({#{protocol := ExpProtocol}, {ok, Protocol}}) ->
+ {error, {unexpected_type, ExpProtocol, Protocol}};
+ ({_, {error, _} = ERROR}) ->
+ ERROR
+ end},
+ #{desc => "get controlling-process",
+ cmd => fun(#{socket := Sock} = State) ->
+ Res = socket:getopt(Sock, otp, controlling_process),
+ {ok, {State, Res}}
+ end},
+ #{desc => "validate controlling-process",
+ cmd => fun({State, {ok, Pid}}) ->
+ case self() of
+ Pid ->
+ {ok, State};
+ _ ->
+ {error, {unexpected_owner, Pid}}
+ end;
+ ({_, {error, _} = ERROR}) ->
+ ERROR
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{socket := Sock} = State) ->
+ Res = socket:close(Sock),
+ {ok, {State, Res}}
+ end},
+ #{desc => "validate socket close",
+ cmd => fun({_, ok}) ->
+ ok;
+ ({_, {error, _} = ERROR}) ->
+ ERROR
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+ Evaluator = ?SEV_START("tester", Seq, InitState),
+ ok = ?SEV_AWAIT_FINISH([Evaluator]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically send and receive on an IPv4 UDP (dgram) socket using
+%% sendto and recvfrom..
+api_b_sendto_and_recvfrom_udp4(suite) ->
+ [];
+api_b_sendto_and_recvfrom_udp4(doc) ->
+ [];
+api_b_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_b_sendto_and_recvfrom_udp4,
+ fun() ->
+ Send = fun(Sock, Data, Dest) ->
+ socket:sendto(Sock, Data, Dest)
+ end,
+ Recv = fun(Sock) ->
+ socket:recvfrom(Sock)
+ end,
+ InitState = #{domain => inet,
+ 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_udp4(suite) ->
+ [];
+api_b_sendmsg_and_recvmsg_udp4(doc) ->
+ [];
+api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_b_sendmsg_and_recvmsg_udp4,
+ fun() ->
+ Send = fun(Sock, Data, Dest) ->
+ %% CMsgHdr = #{level => ip,
+ %% type => tos,
+ %% data => reliability},
+ %% CMsgHdrs = [CMsgHdr],
+ MsgHdr = #{addr => Dest,
+ %% ctrl => CMsgHdrs,
+ iov => [Data]},
+ socket:sendmsg(Sock, MsgHdr)
+ end,
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock) of
+ {ok, #{addr := Source,
+ iov := [Data]}} ->
+ {ok, {Source, Data}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet,
+ send => Send,
+ recv => Recv},
+ ok = api_b_send_and_recv_udp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+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}}
+ 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}}
+ end},
+ #{desc => "bind src",
+ cmd => fun(#{sock_src := Sock, lsa := LSA}) ->
+ sock_bind(Sock, LSA),
+ ok
+ end},
+ #{desc => "sockname src socket",
+ cmd => fun(#{sock_src := Sock} = State) ->
+ SASrc = sock_sockname(Sock),
+ %% ei("src sockaddr: ~p", [SASrc]),
+ {ok, State#{sa_src => SASrc}}
+ end},
+ #{desc => "open dst socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ Sock = sock_open(Domain, dgram, udp),
+ {ok, State#{sock_dst => Sock}}
+ end},
+ #{desc => "bind dst",
+ cmd => fun(#{sock_dst := Sock, lsa := LSA}) ->
+ sock_bind(Sock, LSA),
+ ok
+ end},
+ #{desc => "sockname dst socket",
+ cmd => fun(#{sock_dst := Sock} = State) ->
+ SADst = sock_sockname(Sock),
+ %% ei("dst sockaddr: ~p", [SADst]),
+ {ok, State#{sa_dst => SADst}}
+ end},
+ #{desc => "send req (to dst)",
+ cmd => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
+ ok = Send(Sock, ?BASIC_REQ, Dst)
+ end},
+ #{desc => "recv req (from src)",
+ cmd => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
+ {ok, {Src, ?BASIC_REQ}} = Recv(Sock),
+ ok
+ end},
+ #{desc => "send rep (to src)",
+ cmd => fun(#{sock_dst := Sock, sa_src := Src, send := Send}) ->
+ ok = Send(Sock, ?BASIC_REP, Src)
+ end},
+ #{desc => "recv rep (from dst)",
+ cmd => fun(#{sock_src := Sock, sa_dst := Dst, recv := Recv}) ->
+ {ok, {Dst, ?BASIC_REP}} = Recv(Sock),
+ ok
+ end},
+ #{desc => "close src socket",
+ cmd => fun(#{sock_src := Sock}) ->
+ ok = socket:close(Sock)
+ end},
+ #{desc => "close dst socket",
+ cmd => fun(#{sock_dst := Sock}) ->
+ ok = socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+ Evaluator = ?SEV_START("tester", Seq, InitState),
+ ok = ?SEV_AWAIT_FINISH([Evaluator]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically send and receive using the "common" functions (send and recv)
+%% on an IPv4 TCP (stream) socket.
+api_b_send_and_recv_tcp4(suite) ->
+ [];
+api_b_send_and_recv_tcp4(doc) ->
+ [];
+api_b_send_and_recv_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_b_send_and_recv_tcp4,
+ fun() ->
+ Send = fun(Sock, Data) ->
+ socket:send(Sock, Data)
+ end,
+ Recv = fun(Sock) ->
+ socket:recv(Sock)
+ end,
+ InitState = #{domain => inet,
+ 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 IPv4 TCP (stream) socket.
+api_b_sendmsg_and_recvmsg_tcp4(suite) ->
+ [];
+api_b_sendmsg_and_recvmsg_tcp4(doc) ->
+ [];
+api_b_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_b_sendmsg_and_recvmsg_tcp4,
+ fun() ->
+ Send = fun(Sock, Data) ->
+ MsgHdr = #{iov => [Data]},
+ socket:sendmsg(Sock, MsgHdr)
+ end,
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock) of
+ {ok, #{addr := undefined,
+ iov := [Data]}} ->
+ {ok, Data};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet,
+ send => Send,
+ recv => Recv},
+ ok = api_b_send_and_recv_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_b_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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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 (recv) request",
+ cmd => fun(#{csock := Sock, recv := Recv}) ->
+ 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 (with send reply)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
+ 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_reply),
+ 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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ 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}) ->
+ Send(Sock, ?BASIC_REQ)
+ end},
+ #{desc => "announce ready (send request)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
+ ok
+ end},
+ #{desc => "await recv reply (from server)",
+ 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_reply),
+ 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 => "sleep",
+ cmd => fun(_) ->
+ ?SLEEP(?SECS(1)),
+ ok
+ end},
+ #{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},
+ #{desc => "order client to continue (with send request)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
+ ok
+ end},
+ #{desc => "await client ready (with send request)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, send_req)
+ end},
+ #{desc => "await server ready (request recv)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_req)
+ end},
+ #{desc => "order server to continue (with send reply)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
+ ok
+ end},
+ #{desc => "await server ready (with reply sent)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, send_reply)
+ end},
+ #{desc => "await client ready (reply recv)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, recv_reply)
+ 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]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% API OPTIONS %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Perform some simple getopt and setopt with the level = otp options
+api_opt_simple_otp_options(suite) ->
+ [];
+api_opt_simple_otp_options(doc) ->
+ [];
+api_opt_simple_otp_options(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_opt_simple_otp_options,
+ fun() -> api_opt_simple_otp_options() end).
+
+api_opt_simple_otp_options() ->
+ Get = fun(S, Key) ->
+ socket:getopt(S, otp, Key)
+ end,
+ Set = fun(S, Key, Val) ->
+ socket:setopt(S, otp, Key, Val)
+ end,
+
+ Seq =
+ [
+ %% *** Init part ***
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Protocol} = State) ->
+ Sock = sock_open(Domain, Type, Protocol),
+ {ok, State#{sock => Sock}}
+ end},
+ #{desc => "create dummy process",
+ cmd => fun(State) ->
+ Pid = spawn_link(fun() ->
+ put(sname, "dummy"),
+ receive
+ die ->
+ exit(normal)
+ end
+ end),
+ {ok, State#{dummy => Pid}}
+ end},
+
+ %% *** Check iow part ***
+ #{desc => "get iow",
+ cmd => fun(#{sock := Sock} = State) ->
+ case Get(Sock, iow) of
+ {ok, IOW} when is_boolean(IOW) ->
+ {ok, State#{iow => IOW}};
+ {ok, InvalidIOW} ->
+ {error, {invalid, InvalidIOW}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% #{desc => "enable debug",
+ %% cmd => fun(#{sock := Sock}) ->
+ %% ok = socket:setopt(Sock, otp, debug, true)
+ %% end},
+
+ #{desc => "set (new) iow",
+ cmd => fun(#{sock := Sock, iow := OldIOW} = State) ->
+ NewIOW = not OldIOW,
+ case Set(Sock, iow, NewIOW) of
+ ok ->
+ {ok, State#{iow => NewIOW}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "get (new) iow",
+ cmd => fun(#{sock := Sock, iow := IOW}) ->
+ case Get(Sock, iow) of
+ {ok, IOW} ->
+ ok;
+ {ok, InvalidIOW} ->
+ {error, {invalid, InvalidIOW}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** Check rcvbuf part ***
+ #{desc => "get rcvbuf",
+ cmd => fun(#{sock := Sock} = State) ->
+ case Get(Sock, rcvbuf) of
+ {ok, RcvBuf} when is_integer(RcvBuf) ->
+ {ok, State#{rcvbuf => RcvBuf}};
+ {ok, {N, RcvBuf} = V} when is_integer(N) andalso
+ is_integer(RcvBuf) ->
+ {ok, State#{rcvbuf => V}};
+ {ok, InvalidRcvBuf} ->
+ {error, {invalid, InvalidRcvBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "set (new) rcvbuf",
+ cmd => fun(#{sock := Sock, rcvbuf := {OldN, OldRcvBuf}} = State) ->
+ NewRcvBuf = {OldN+2, OldRcvBuf + 1024},
+ case Set(Sock, rcvbuf, NewRcvBuf) of
+ ok ->
+ {ok, State#{rcvbuf => NewRcvBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+ (#{sock := Sock, rcvbuf := OldRcvBuf} = State) when is_integer(OldRcvBuf) ->
+ NewRcvBuf = 2 * OldRcvBuf,
+ case Set(Sock, rcvbuf, NewRcvBuf) of
+ ok ->
+ {ok, State#{rcvbuf => NewRcvBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+ (#{sock := Sock, rcvbuf := OldRcvBuf,
+ type := stream,
+ protocol := tcp} = State) when is_integer(OldRcvBuf) ->
+ NewRcvBuf = {2, OldRcvBuf},
+ case Set(Sock, rcvbuf, NewRcvBuf) of
+ ok ->
+ {ok, State#{rcvbuf => NewRcvBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "get (new) rcvbuf",
+ cmd => fun(#{sock := Sock, rcvbuf := RcvBuf}) ->
+ case Get(Sock, rcvbuf) of
+ {ok, RcvBuf} ->
+ ok;
+ {ok, InvalidRcvBuf} ->
+ {error, {invalid, InvalidRcvBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** Check rcvctrlbuf part ***
+ #{desc => "get rcvctrlbuf",
+ cmd => fun(#{sock := Sock} = State) ->
+ case Get(Sock, rcvctrlbuf) of
+ {ok, RcvCtrlBuf} when is_integer(RcvCtrlBuf) ->
+ {ok, State#{rcvctrlbuf => RcvCtrlBuf}};
+ {ok, InvalidRcvCtrlBuf} ->
+ {error, {invalid, InvalidRcvCtrlBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "set (new) rcvctrlbuf",
+ cmd => fun(#{sock := Sock, rcvctrlbuf := OldRcvCtrlBuf} = State) ->
+ NewRcvCtrlBuf = 2 * OldRcvCtrlBuf,
+ case Set(Sock, rcvctrlbuf, NewRcvCtrlBuf) of
+ ok ->
+ {ok, State#{rcvctrlbuf => NewRcvCtrlBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "get (new) rcvctrlbuf",
+ cmd => fun(#{sock := Sock, rcvctrlbuf := RcvCtrlBuf}) ->
+ case Get(Sock, rcvctrlbuf) of
+ {ok, RcvCtrlBuf} ->
+ ok;
+ {ok, InvalidRcvCtrlBuf} ->
+ {error, {invalid, InvalidRcvCtrlBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ %% *** Check rcvctrlbuf part ***
+ #{desc => "get rcvctrlbuf",
+ cmd => fun(#{sock := Sock} = State) ->
+ case Get(Sock, rcvctrlbuf) of
+ {ok, RcvCtrlBuf} when is_integer(RcvCtrlBuf) ->
+ {ok, State#{rcvctrlbuf => RcvCtrlBuf}};
+ {ok, InvalidRcvCtrlBuf} ->
+ {error, {invalid, InvalidRcvCtrlBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "set (new) rcvctrlbuf",
+ cmd => fun(#{sock := Sock, rcvctrlbuf := OldRcvCtrlBuf} = State) ->
+ NewRcvCtrlBuf = 2 * OldRcvCtrlBuf,
+ case Set(Sock, rcvctrlbuf, NewRcvCtrlBuf) of
+ ok ->
+ {ok, State#{rcvctrlbuf => NewRcvCtrlBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "get (new) rcvctrlbuf",
+ cmd => fun(#{sock := Sock, rcvctrlbuf := RcvCtrlBuf}) ->
+ case Get(Sock, rcvctrlbuf) of
+ {ok, RcvCtrlBuf} ->
+ ok;
+ {ok, InvalidRcvCtrlBuf} ->
+ {error, {invalid, InvalidRcvCtrlBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% *** Check sndctrlbuf part ***
+ #{desc => "get sndctrlbuf",
+ cmd => fun(#{sock := Sock} = State) ->
+ case Get(Sock, sndctrlbuf) of
+ {ok, SndCtrlBuf} when is_integer(SndCtrlBuf) ->
+ {ok, State#{sndctrlbuf => SndCtrlBuf}};
+ {ok, InvalidSndCtrlBuf} ->
+ {error, {invalid, InvalidSndCtrlBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "set (new) sndctrlbuf",
+ cmd => fun(#{sock := Sock, sndctrlbuf := OldSndCtrlBuf} = State) ->
+ NewSndCtrlBuf = 2 * OldSndCtrlBuf,
+ case Set(Sock, sndctrlbuf, NewSndCtrlBuf) of
+ ok ->
+ {ok, State#{sndctrlbuf => NewSndCtrlBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "get (new) sndctrlbuf",
+ cmd => fun(#{sock := Sock, sndctrlbuf := SndCtrlBuf}) ->
+ case Get(Sock, sndctrlbuf) of
+ {ok, SndCtrlBuf} ->
+ ok;
+ {ok, InvalidSndCtrlBuf} ->
+ {error, {invalid, InvalidSndCtrlBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** Check controlling-process part ***
+ #{desc => "verify self as controlling-process",
+ cmd => fun(#{sock := Sock}) ->
+ Self = self(),
+ case Get(Sock, controlling_process) of
+ {ok, Self} ->
+ ok;
+ {ok, InvalidPid} ->
+ {error, {invalid, InvalidPid}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "set dummy as controlling-process",
+ cmd => fun(#{sock := Sock, dummy := Dummy}) ->
+ Set(Sock, controlling_process, Dummy)
+ end},
+ #{desc => "verify dummy as controlling-process",
+ cmd => fun(#{sock := Sock, dummy := Dummy}) ->
+ case Get(Sock, controlling_process) of
+ {ok, Dummy} ->
+ ok;
+ {ok, InvalidPid} ->
+ {error, {invalid, InvalidPid}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ #{desc => "finish",
+ cmd => fun(_) ->
+ {ok, normal}
+ end}
+ ],
+
+ i("start tcp (stream) evaluator"),
+ InitState1 = #{domain => inet, type => stream, protocol => tcp},
+ Tester1 = ?SEV_START("tcp-tester", Seq, InitState1),
+ i("await tcp evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Tester1]),
+
+ i("start udp (dgram) socket"),
+ InitState2 = #{domain => inet, type => dgram, protocol => udp},
+ Tester2 = ?SEV_START("udp-tester", Seq, InitState2),
+ i("await udp evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Tester2]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Perform some simple operations with the rcvbuf otp option
+%% The operations we test here are only for type = stream and
+%% protocol = tcp.
+api_opt_simple_otp_rcvbuf_option(suite) ->
+ [];
+api_opt_simple_otp_rcvbuf_option(doc) ->
+ [];
+api_opt_simple_otp_rcvbuf_option(_Config) when is_list(_Config) ->
+ ?TT(?SECS(15)),
+ tc_try(api_opt_simple_otp_rcvbuf_option,
+ fun() -> api_opt_simple_otp_rcvbuf_option() end).
+
+api_opt_simple_otp_rcvbuf_option() ->
+ Get = fun(S) ->
+ socket:getopt(S, otp, rcvbuf)
+ end,
+ Set = fun(S, Val) ->
+ socket:setopt(S, otp, rcvbuf, Val)
+ end,
+
+ ServerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start (from tester)",
+ 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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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 => "bind to local address",
+ cmd => fun(#{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(#{tester := Tester,
+ local_sa := LocalSA,
+ lport := Port}) ->
+ ServerSA = LocalSA#{port => Port},
+ ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
+ ok
+ end},
+
+
+ %% *** The actual test part ***
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "attempt to accept",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+
+ %% Recv with default size for (otp) rcvbuf
+ #{desc => "await continue (recv initial)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, recv) of
+ {ok, MsgSz} ->
+ ?SEV_IPRINT("MsgSz: ~p", [MsgSz]),
+ {ok, State#{msg_sz => MsgSz}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt to recv",
+ cmd => fun(#{sock := Sock, msg_sz := MsgSz} = _State) ->
+ ?SEV_IPRINT("try recv ~w bytes when rcvbuf is ~s",
+ [MsgSz,
+ case Get(Sock) of
+ {ok, RcvBuf} -> f("~w", [RcvBuf]);
+ {error, _} -> "-"
+ end]),
+ case socket:recv(Sock) of
+ {ok, Data} when (size(Data) =:= MsgSz) ->
+ ok;
+ {ok, Data} ->
+ {error, {invalid_msg_sz, MsgSz, size(Data)}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv initial)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv),
+ ok
+ end},
+
+ %% Recv with new size (1) for (otp) rcvbuf
+ #{desc => "await continue (recv 1)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, recv) of
+ {ok, NewRcvBuf} ->
+ ?SEV_IPRINT("set new rcvbuf: ~p", [NewRcvBuf]),
+ {ok, State#{rcvbuf => NewRcvBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt to setopt rcvbuf",
+ cmd => fun(#{sock := Sock, rcvbuf := NewRcvBuf} = _State) ->
+ case Set(Sock, NewRcvBuf) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt to recv",
+ cmd => fun(#{sock := Sock, msg_sz := MsgSz} = _State) ->
+ case socket:recv(Sock) of
+ {ok, Data} when (size(Data) =:= MsgSz) ->
+ ok;
+ {ok, Data} ->
+ {error, {invalid_msg_sz, MsgSz, size(Data)}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv 1)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv),
+ ok
+ end},
+
+ %% Recv with new size (2) for (otp) rcvbuf
+ #{desc => "await continue (recv 2)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, recv) of
+ {ok, NewRcvBuf} ->
+ ?SEV_IPRINT("set new rcvbuf: ~p", [NewRcvBuf]),
+ {ok, State#{rcvbuf => NewRcvBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt to setopt rcvbuf",
+ cmd => fun(#{sock := Sock, rcvbuf := NewRcvBuf} = _State) ->
+ case Set(Sock, NewRcvBuf) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt to recv",
+ cmd => fun(#{sock := Sock, msg_sz := MsgSz} = _State) ->
+ case socket:recv(Sock) of
+ {ok, Data} when (size(Data) =:= MsgSz) ->
+ ok;
+ {ok, Data} ->
+ {error, {invalid_msg_sz, MsgSz, size(Data)}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv 2)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv),
+ ok
+ end},
+
+ %% Recv with new size (3) for (otp) rcvbuf
+ #{desc => "await continue (recv 3, truncated)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, recv) of
+ {ok, {ExpSz, NewRcvBuf}} ->
+ {ok, State#{msg_sz => ExpSz,
+ rcvbuf => NewRcvBuf}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt to setopt rcvbuf",
+ cmd => fun(#{sock := Sock, rcvbuf := NewRcvBuf} = _State) ->
+ case Set(Sock, NewRcvBuf) of
+ ok ->
+ ?SEV_IPRINT("set new rcvbuf: ~p", [NewRcvBuf]),
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt to recv",
+ cmd => fun(#{sock := Sock, msg_sz := MsgSz} = _State) ->
+ ?SEV_IPRINT("try recv ~w bytes of data", [MsgSz]),
+ case socket:recv(Sock) of
+ {ok, Data} when (size(Data) =:= MsgSz) ->
+ ok;
+ {ok, Data} ->
+ {error, {invalid_msg_sz, MsgSz, size(Data)}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv),
+ 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(s)",
+ cmd => fun(#{lsock := LSock, sock := Sock} = State) ->
+ sock_close(Sock),
+ sock_close(LSock),
+ State1 = maps:remove(sock, State),
+ State2 = maps:remove(lport, State1),
+ State3 = maps:remove(lsock, State2),
+ {ok, State3}
+ 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 => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{local_sa => LSA}}
+ 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 initial)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, send) of
+ {ok, Data} ->
+ {ok, State#{data => Data}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "send (initial) data to server",
+ cmd => fun(#{sock := Sock, data := Data} = _State) ->
+ ?SEV_IPRINT("try send ~w bytes", [size(Data)]),
+ socket:send(Sock, Data)
+ end},
+ #{desc => "announce ready (send initial)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send),
+ ok
+ end},
+
+ #{desc => "await continue (send 1)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send)
+ end},
+ #{desc => "send (1) data to server",
+ cmd => fun(#{sock := Sock, data := Data}) ->
+ ?SEV_IPRINT("try send ~w bytes", [size(Data)]),
+ socket:send(Sock, Data)
+ end},
+ #{desc => "announce ready (send 1)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send),
+ ok
+ end},
+
+ #{desc => "await continue (send 2)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send)
+ end},
+ #{desc => "send (2) data to server",
+ cmd => fun(#{sock := Sock, data := Data}) ->
+ ?SEV_IPRINT("try send ~w bytes", [size(Data)]),
+ socket:send(Sock, Data)
+ end},
+ #{desc => "announce ready (send 2)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send),
+ ok
+ end},
+
+ #{desc => "await continue (send 3)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send)
+ end},
+ #{desc => "send (3) data to server",
+ cmd => fun(#{sock := Sock, data := Data}) ->
+ ?SEV_IPRINT("try send ~w bytes", [size(Data)]),
+ socket:send(Sock, Data)
+ end},
+ #{desc => "announce ready (send 3)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send),
+ 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 := Server} = _State) ->
+ _MRef = erlang:monitor(process, Server),
+ ok
+ end},
+ #{desc => "monitor client",
+ cmd => fun(#{client := Client} = _State) ->
+ _MRef = erlang:monitor(process, Client),
+ ok
+ end},
+ #{desc => "order server start",
+ cmd => fun(#{server := Server}) ->
+ ?SEV_ANNOUNCE_START(Server)
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Server} = State) ->
+ {ok, ServerSA} = ?SEV_AWAIT_READY(Server, server, init),
+ {ok, State#{server_sa => ServerSA}}
+ end},
+ #{desc => "order client start",
+ cmd => fun(#{client := Client,
+ server_sa := ServerSA}) ->
+ ?SEV_ANNOUNCE_START(Client, ServerSA),
+ ok
+ end},
+ #{desc => "await client ready (init)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, init)
+ end},
+
+
+ %% The actual test (connecting)
+ #{desc => "order server accept (accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, accept),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client continue (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},
+
+ %% The actual test (initial part)
+ #{desc => "order client continue (send initial)",
+ cmd => fun(#{client := Client, data := Data} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Data),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order server continue (recv initial)",
+ cmd => fun(#{server := Server, data := Data} = _State) ->
+ ExpMsgSz = size(Data),
+ ?SEV_ANNOUNCE_CONTINUE(Server, recv, ExpMsgSz),
+ ok
+ end},
+ #{desc => "await client ready (send initial)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, client, send,
+ [{server, Server}]) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await server ready (recv initial)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Server, client, recv,
+ [{client, Client}]) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% The actual test (part 1)
+ #{desc => "order client continue (send 1)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order server continue (recv 1)",
+ cmd => fun(#{server := Server, data := Data} = _State) ->
+ MsgSz = size(Data),
+ NewRcvBuf = {2 + (MsgSz div 1024), 1024},
+ ?SEV_ANNOUNCE_CONTINUE(Server, recv, NewRcvBuf),
+ ok
+ end},
+ #{desc => "await client ready (send 1)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, client, send,
+ [{server, Server}]) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await server ready (recv 1)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Server, client, recv,
+ [{client, Client}]) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% The actual test (part 2)
+ #{desc => "order client continue (send 2)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order server continue (recv 2)",
+ cmd => fun(#{server := Server, data := Data} = _State) ->
+ MsgSz = size(Data),
+ NewRcvBuf = {2 + (MsgSz div 2048), 2048},
+ ?SEV_ANNOUNCE_CONTINUE(Server, recv, NewRcvBuf),
+ ok
+ end},
+ #{desc => "await client ready (send 2)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, client, send,
+ [{server, Server}]) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await server ready (recv 2)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Server, client, recv,
+ [{client, Client}]) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% The actual test (part 3)
+ #{desc => "order client continue (send 3)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order server continue (recv 3)",
+ cmd => fun(#{server := Server, data := Data} = _State) ->
+ MsgSz = size(Data),
+ BufSz = 2048,
+ N = MsgSz div BufSz - 1,
+ NewRcvBuf = {N, BufSz},
+ ?SEV_ANNOUNCE_CONTINUE(Server, recv,
+ {N*BufSz, NewRcvBuf})
+ end},
+ #{desc => "await client ready (send 3)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, client, send,
+ [{server, Server}]) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await server ready (recv 3)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Server, client, recv,
+ [{client, Client}]) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ %% *** Terminate server ***
+ #{desc => "order client terminate",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client down",
+ cmd => fun(#{client := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client, State),
+ {ok, State1}
+ end},
+ #{desc => "order server terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server down",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ State2 = maps:remove(server_sa, State1),
+ {ok, State2}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ %% Create a data binary of 6*1024 bytes
+ Data = list_to_binary(lists:duplicate(6*4, lists:seq(0, 255))),
+ InitState = #{domain => inet,
+ data => Data},
+
+ i("create server evaluator"),
+ ServerInitState = #{domain => maps:get(domain, InitState)},
+ Server = ?SEV_START("server", ServerSeq, ServerInitState),
+
+ i("create client evaluator"),
+ ClientInitState = #{host => local_host(),
+ domain => maps:get(domain, InitState)},
+ Client = ?SEV_START("client", ClientSeq, ClientInitState),
+
+ i("create tester evaluator"),
+ TesterInitState = InitState#{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]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Perform some simple getopt and setopt with the level = otp options
+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)),
+ tc_try(api_opt_simple_otp_controlling_process,
+ fun() -> api_opt_simple_otp_controlling_process() end).
+
+api_opt_simple_otp_controlling_process() ->
+ Get = fun(S, Key) ->
+ socket:getopt(S, otp, Key)
+ end,
+ Set = fun(S, Key, Val) ->
+ socket:setopt(S, otp, Key, Val)
+ end,
+
+ ClientSeq =
+ [
+ %% *** Init 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}) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "verify tester as controlling-process",
+ cmd => fun(#{tester := Tester, sock := Sock} = _State) ->
+ case Get(Sock, controlling_process) of
+ {ok, Tester} ->
+ ok;
+ {ok, InvalidPid} ->
+ {error, {invalid, InvalidPid}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt invalid controlling-process transfer (to self)",
+ cmd => fun(#{sock := Sock} = _State) ->
+ case Set(Sock, controlling_process, self()) of
+ {error, not_owner} ->
+ ok;
+ ok ->
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (not owner)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, not_owner),
+ ok
+ end},
+ #{desc => "await continue (owner)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, owner)
+ end},
+ #{desc => "verify self as controlling-process",
+ cmd => fun(#{sock := Sock} = _State) ->
+ Self = self(),
+ case Get(Sock, controlling_process) of
+ {ok, Self} ->
+ ok;
+ {ok, InvalidPid} ->
+ {error, {invalid, InvalidPid}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt controlling-process transfer to tester",
+ cmd => fun(#{tester := Tester, sock := Sock} = _State) ->
+ Set(Sock, controlling_process, Tester)
+ end},
+ #{desc => "attempt invalid controlling-process transfer (to self)",
+ cmd => fun(#{sock := Sock} = _State) ->
+ case Set(Sock, controlling_process, self()) of
+ {error, not_owner} ->
+ ok;
+ ok ->
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (owner)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, owner),
+ ok
+
+ end},
+
+ %% *** Termination ***
+ #{desc => "await termination",
+ cmd => fun(#{tester := Tester} = State) ->
+ ?SEV_AWAIT_TERMINATE(Tester, tester),
+ State1 = maps:remove(tester, State),
+ State2 = maps:remove(sock, State1),
+ {ok, State2}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Protocol} = State) ->
+ Sock = sock_open(Domain, Type, Protocol),
+ {ok, State#{sock => Sock}}
+ end},
+ #{desc => "monitor client",
+ cmd => fun(#{client := Client} = _State) ->
+ _MRef = erlang:monitor(process, Client),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "verify self as controlling-process",
+ cmd => fun(#{sock := Sock} = _State) ->
+ Self = self(),
+ case Get(Sock, controlling_process) of
+ {ok, Self} ->
+ ok;
+ {ok, InvalidPid} ->
+ {error, {invalid, InvalidPid}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order (client) start",
+ cmd => fun(#{client := Client, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Client, Sock),
+ ok
+ end},
+ #{desc => "await (client) ready (not owner)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, not_owner)
+ end},
+ #{desc => "attempt controlling-process transfer to client",
+ cmd => fun(#{client := Client, sock := Sock} = _State) ->
+ Set(Sock, controlling_process, Client)
+ end},
+ #{desc => "verify client as controlling-process",
+ cmd => fun(#{client := Client, sock := Sock} = _State) ->
+ case Get(Sock, controlling_process) of
+ {ok, Client} ->
+ ok;
+ {ok, InvalidPid} ->
+ {error, {invalid, InvalidPid}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt invalid controlling-process transfer (to self)",
+ cmd => fun(#{sock := Sock} = _State) ->
+ case Set(Sock, controlling_process, self()) of
+ {error, not_owner} ->
+ ok;
+ ok ->
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order (client) continue (owner)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, owner),
+ ok
+ end},
+ #{desc => "await (client) ready (2)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, owner),
+ ok
+ end},
+ #{desc => "verify self as controlling-process",
+ cmd => fun(#{sock := Sock} = _State) ->
+ Self = self(),
+ case Get(Sock, controlling_process) of
+ {ok, Self} ->
+ ok;
+ {ok, InvalidPid} ->
+ {error, {invalid, InvalidPid}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** Termination ***
+ #{desc => "order (client) 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),
+ {ok, maps:remove(client, State)}
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ sock_close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start tcp (stream) client evaluator"),
+ ClientInitState1 = #{},
+ Client1 = ?SEV_START("tcp-client", ClientSeq, ClientInitState1),
+
+ i("start tcp (stream) tester evaluator"),
+ TesterInitState1 = #{domain => inet,
+ type => stream,
+ protocol => tcp,
+ client => Client1#ev.pid},
+ Tester1 = ?SEV_START("tcp-tester", TesterSeq, TesterInitState1),
+
+ i("await tcp evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester1, Client1]),
+
+ i("start udp (dgram) client evaluator"),
+ ClientInitState2 = #{},
+ Client2 = ?SEV_START("udp-client", ClientSeq, ClientInitState2),
+
+ i("start udp (dgram) tester evaluator"),
+ TesterInitState2 = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ client => Client2#ev.pid},
+ Tester2 = ?SEV_START("udp-tester", TesterSeq, TesterInitState2),
+
+ i("await udp evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester2, Client2]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% API OPERATIONS WITH TIMEOUT %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the connect timeout option
+%% on an IPv4 TCP (stream) socket.
+api_to_connect_tcp4(suite) ->
+ [];
+api_to_connect_tcp4(doc) ->
+ [];
+api_to_connect_tcp4(_Config) when is_list(_Config) ->
+ tc_try(api_to_connect_tcp4,
+ fun() ->
+ ?TT(?SECS(10)),
+ InitState = #{domain => inet,
+ backlog => 1,
+ timeout => 5000,
+ connect_limit => 3},
+ ok = api_to_connect_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the connect timeout option
+%% on an IPv6 TCP (stream) socket.
+api_to_connect_tcp6(suite) ->
+ [];
+api_to_connect_tcp6(doc) ->
+ [];
+api_to_connect_tcp6(_Config) when is_list(_Config) ->
+ tc_try(api_to_connect_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(10)),
+ InitState = #{domain => inet6,
+ backlog => 1,
+ timeout => 5000,
+ connect_limit => 3},
+ ok = api_to_connect_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% We use the backlog (listen) argument to test this.
+%% Note that the behaviour of the TCP "server side" can vary when
+%% a client connect to a "busy" server (full backlog).
+%% For instance, on FreeBSD (11.2) the reponse when the backlog is full
+%% is a econreset.
+
+api_to_connect_tcp(InitState) ->
+ process_flag(trap_exit, true),
+
+ ServerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, Backlog} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester,
+ backlog => Backlog}}
+ 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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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 => "bind to local address",
+ cmd => fun(#{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 (with backlog = 1)",
+ cmd => fun(#{lsock := LSock, backlog := Backlog}) ->
+ socket:listen(LSock, Backlog)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, lport := Port}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ 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(#{lsock := Sock} = State) ->
+ sock_close(Sock),
+ State1 = maps:remove(lport, State),
+ State2 = maps:remove(sock, State1),
+ {ok, State2}
+ 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 => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{local_sa => LSA}}
+ end},
+ #{desc => "create node",
+ cmd => fun(#{host := Host} = State) ->
+ ?SEV_IPRINT("try create node on ~p", [Host]),
+ case start_node(Host, client) of
+ {ok, Node} ->
+ ?SEV_IPRINT("client node ~p started",
+ [Node]),
+ {ok, State#{node => Node}};
+ {error, Reason, _} ->
+ {error, Reason}
+ end
+ end},
+ #{desc => "monitor client node",
+ cmd => fun(#{node := Node} = _State) ->
+ true = erlang:monitor_node(Node, true),
+ ok
+ end},
+ #{desc => "start remote client on client node",
+ cmd => fun(#{node := Node} = State) ->
+ Pid = api_toc_tcp_client_start(Node),
+ ?SEV_IPRINT("remote client ~p started", [Pid]),
+ {ok, State#{rclient => Pid}}
+ end},
+ #{desc => "monitor remote client",
+ cmd => fun(#{rclient := Pid}) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "order remote client to start",
+ cmd => fun(#{rclient := Client,
+ server_sa := ServerSA}) ->
+ ?SEV_ANNOUNCE_START(Client, ServerSA),
+ ok
+ end},
+ #{desc => "await remote client ready",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, init,
+ [{tester, Tester}])
+ 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,
+ rclient := Client} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, connect,
+ [{rclient, Client}]) of
+ {ok, {ConTimeout, ConLimit}} ->
+ {ok, State#{connect_timeout => ConTimeout,
+ connect_limit => ConLimit}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (connect)",
+ cmd => fun(#{rclient := RClient,
+ connect_timeout := ConTimeout,
+ connect_limit := ConLimit}) ->
+ ?SEV_ANNOUNCE_CONTINUE(RClient, connect,
+ {ConTimeout, ConLimit}),
+ ok
+ end},
+ #{desc => "await remote client ready (connect)",
+ cmd => fun(#{tester := Tester,
+ rclient := RClient} = State) ->
+ case ?SEV_AWAIT_READY(RClient, rclient, connect,
+ [{tester, Tester}]) of
+ {ok, ok = _Result} ->
+ {ok, maps:remove(connect_limit, State)};
+ {ok, {error, {connect_limit_reached,R,L}}} ->
+ {skip,
+ ?LIB:f("Connect limit reached ~w: ~w",
+ [L, R])};
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester,
+ rclient := RClient} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester,
+ [{rclient, RClient}]) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "kill remote client",
+ cmd => fun(#{rclient := Client}) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await remote client termination",
+ cmd => fun(#{rclient := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(rclient, State),
+ {ok, State1}
+ end},
+ #{desc => "stop client node",
+ cmd => fun(#{node := Node} = _State) ->
+ stop_node(Node)
+ end},
+ #{desc => "await client node termination",
+ cmd => fun(#{node := Node} = State) ->
+ receive
+ {nodedown, Node} ->
+ State1 = maps:remove(node_id, State),
+ State2 = maps:remove(node, State1),
+ {ok, State2}
+ end
+ 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},
+ #{desc => "monitor client",
+ cmd => fun(#{client := Client} = _State) ->
+ _MRef = erlang:monitor(process, Client),
+ ok
+ end},
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{local_sa => LSA}}
+ end},
+ #{desc => "order server start",
+ cmd => fun(#{server := Server,
+ backlog := Backlog}) ->
+ ?SEV_ANNOUNCE_START(Server, Backlog),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Server, local_sa := LSA} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Server, server, init),
+ ServerSA = LSA#{port => Port},
+ {ok, State#{server_sa => ServerSA}}
+ end},
+ #{desc => "order client start",
+ cmd => fun(#{client := Client,
+ server_sa := ServerSA}) ->
+ ?SEV_ANNOUNCE_START(Client, ServerSA),
+ ok
+ end},
+ #{desc => "await client ready (init)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, init),
+ ok
+ end},
+
+ %% The actual test
+ %% The server does nothing (this is the point), no accept,
+ %% the client tries to connect.
+ #{desc => "order client continue (connect)",
+ cmd => fun(#{client := Client,
+ timeout := Timeout,
+ connect_limit := ConLimit} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, connect,
+ {Timeout, ConLimit}),
+ ok
+ end},
+ #{desc => "await client ready (connect)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, client, connect,
+ [{server, Server}]) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% *** Terminate server ***
+ #{desc => "order client terminate",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client down",
+ cmd => fun(#{client := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client, State),
+ {ok, State1}
+ end},
+ #{desc => "order server terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server down",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ State1 = maps:remove(server, State),
+ State2 = maps:remove(server_sa, State1),
+ {ok, State2}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("create server evaluator"),
+ ServerInitState = #{domain => maps:get(domain, InitState)},
+ Server = ?SEV_START("server", ServerSeq, ServerInitState),
+
+ i("create client evaluator"),
+ ClientInitState = #{host => local_host(),
+ domain => maps:get(domain, InitState)},
+ Client = ?SEV_START("client", ClientSeq, ClientInitState),
+
+ i("create tester evaluator"),
+ TesterInitState = InitState#{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]).
+
+
+api_toc_tcp_client_start(Node) ->
+ Self = self(),
+ Fun = fun() -> api_toc_tcp_client(Self) end,
+ erlang:spawn(Node, Fun).
+
+api_toc_tcp_client(Parent) ->
+ api_toc_tcp_client_init(Parent),
+ ServerSA = api_toc_tcp_client_await_start(Parent),
+ Domain = maps:get(family, ServerSA),
+ api_toc_tcp_client_announce_ready(Parent, init),
+ {To, ConLimit} = api_toc_tcp_client_await_continue(Parent, connect),
+ Result = api_to_connect_tcp_await_timeout(To, ServerSA, Domain, ConLimit),
+ ?SEV_IPRINT("result: ~p", [Result]),
+ api_toc_tcp_client_announce_ready(Parent, connect, Result),
+ Reason = api_toc_tcp_client_await_terminate(Parent),
+ exit(Reason).
+
+api_toc_tcp_client_init(Parent) ->
+ put(sname, "rclient"),
+ %% i("api_toc_tcp_client_init -> entry"),
+ _MRef = erlang:monitor(process, Parent),
+ ok.
+
+api_toc_tcp_client_await_start(Parent) ->
+ %% i("api_toc_tcp_client_await_start -> entry"),
+ ?SEV_AWAIT_START(Parent).
+
+api_toc_tcp_client_announce_ready(Parent, Slogan) ->
+ ?SEV_ANNOUNCE_READY(Parent, Slogan).
+api_toc_tcp_client_announce_ready(Parent, Slogan, Result) ->
+ ?SEV_ANNOUNCE_READY(Parent, Slogan, Result).
+
+api_toc_tcp_client_await_continue(Parent, Slogan) ->
+ %% i("api_toc_tcp_client_await_continue -> entry"),
+ case ?SEV_AWAIT_CONTINUE(Parent, parent, Slogan) of
+ ok ->
+ ok;
+ {ok, Extra} ->
+ Extra;
+ {error, Reason} ->
+ exit({await_continue, Slogan, Reason})
+ end.
+
+api_toc_tcp_client_await_terminate(Parent) ->
+ %% i("api_toc_tcp_client_await_terminate -> entry"),
+ case ?SEV_AWAIT_TERMINATE(Parent, parent) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ Reason
+ end.
+
+api_to_connect_tcp_await_timeout(To, ServerSA, Domain, ConLimit) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ NewSock = fun() ->
+ S = case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ Sock;
+ {error, OReason} ->
+ ?FAIL({open, OReason})
+ end,
+ case socket:bind(S, LSA) of
+ {ok, _} ->
+ S;
+ {error, BReason} ->
+ ?FAIL({bind, BReason})
+ end
+ end,
+ api_to_connect_tcp_await_timeout(1, ConLimit, To, ServerSA, NewSock, []).
+
+api_to_connect_tcp_await_timeout(ID, ConLimit, _To, _ServerSA, _NewSock, Acc)
+ when (ID > ConLimit) ->
+ api_to_connect_tcp_await_timeout3(Acc),
+ {error, {connect_limit_reached, ID, ConLimit}};
+api_to_connect_tcp_await_timeout(ID, ConLimit, To, ServerSA, NewSock, Acc) ->
+ case api_to_connect_tcp_await_timeout2(ID, To, ServerSA, NewSock) of
+ ok ->
+ %% ?SEV_IPRINT("success when number of socks: ~w", [length(Acc)]),
+ api_to_connect_tcp_await_timeout3(Acc),
+ ok;
+ {ok, Sock} ->
+ %% ?SEV_IPRINT("~w: unexpected success (connect)", [ID]),
+ api_to_connect_tcp_await_timeout(ID+1, ConLimit,
+ To, ServerSA, NewSock,
+ [Sock|Acc]);
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+api_to_connect_tcp_await_timeout2(_ID, To, ServerSA, NewSock) ->
+ Sock = NewSock(),
+ %% ?SEV_IPRINT("~w: try connect", [ID]),
+ Start = t(),
+ case socket:connect(Sock, ServerSA, To) of
+ {error, timeout} ->
+ Stop = t(),
+ TDiff = tdiff(Start, Stop),
+ if
+ (TDiff >= To) ->
+ (catch socket:close(Sock)),
+ ok;
+ true ->
+ (catch socket:close(Sock)),
+ ?FAIL({unexpected_timeout, TDiff, To})
+ end;
+ {error, econnreset = _Reason} ->
+ (catch socket:close(Sock)),
+ ok;
+ {error, Reason} ->
+ (catch socket:close(Sock)),
+ ?FAIL({connect, Reason});
+ ok ->
+ {ok, Sock}
+ end.
+
+api_to_connect_tcp_await_timeout3([]) ->
+ ok;
+api_to_connect_tcp_await_timeout3([Sock|Socka]) ->
+ (catch socket:close(Sock)),
+ api_to_connect_tcp_await_timeout3(Socka).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the accept timeout option
+%% on an IPv4 TCP (stream) socket.
+api_to_accept_tcp4(suite) ->
+ [];
+api_to_accept_tcp4(doc) ->
+ [];
+api_to_accept_tcp4(_Config) when is_list(_Config) ->
+ tc_try(api_to_accept_tcp4,
+ fun() ->
+ ?TT(?SECS(10)),
+ InitState = #{domain => inet, timeout => 5000},
+ ok = api_to_accept_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the accept timeout option
+%% on an IPv6 TCP (stream) socket.
+api_to_accept_tcp6(suite) ->
+ [];
+api_to_accept_tcp6(doc) ->
+ [];
+api_to_accept_tcp6(_Config) when is_list(_Config) ->
+ tc_try(api_to_accept_tcp4,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(10)),
+ InitState = #{domain => inet6, timeout => 5000},
+ ok = api_to_accept_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_to_accept_tcp(InitState) ->
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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, _} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+
+ %% *** The actual test part ***
+ #{desc => "attempt to accept (without success)",
+ cmd => fun(#{lsock := LSock, timeout := To} = State) ->
+ Start = t(),
+ case socket:accept(LSock, To) of
+ {error, timeout} ->
+ {ok, State#{start => Start, stop => t()}};
+ {ok, Sock} ->
+ (catch socket:close(Sock)),
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "validate timeout time",
+ cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) ->
+ TDiff = tdiff(Start, Stop),
+ if
+ (TDiff >= To) ->
+ ok;
+ true ->
+ {error, {unexpected_timeout, TDiff, To}}
+ end
+ end},
+
+ %% *** Close (listen) socket ***
+ #{desc => "close (listen) socket",
+ cmd => fun(#{lsock := LSock} = State) ->
+ sock_close(LSock),
+ {ok, maps:remove(sock3, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("create tester evaluator"),
+ Tester = ?SEV_START("tester", TesterSeq, InitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the multi accept timeout option
+%% on an IPv4 TCP (stream) socket with multiple acceptor processes
+%% (three in this case).
+api_to_maccept_tcp4(suite) ->
+ [];
+api_to_maccept_tcp4(doc) ->
+ [];
+api_to_maccept_tcp4(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_to_maccept_tcp4,
+ fun() ->
+ InitState = #{domain => inet, timeout => 5000},
+ ok = api_to_maccept_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the accept timeout option
+%% on an IPv6 TCP (stream) socket.
+api_to_maccept_tcp6(suite) ->
+ [];
+api_to_maccept_tcp6(doc) ->
+ [];
+api_to_maccept_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_to_maccept_tcp4,
+ fun() ->
+ not_yet_implemented(),
+ InitState = #{domain => inet6, timeout => 5000},
+ ok = api_to_maccept_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_to_maccept_tcp(InitState) ->
+ PrimAcceptorSeq =
+ [
+ %% *** Init part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ Tester = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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, _} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{lsock := LSock, tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, LSock),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "attempt to accept (without success)",
+ cmd => fun(#{lsock := LSock, timeout := To} = State) ->
+ Start = t(),
+ case socket:accept(LSock, To) of
+ {error, timeout} ->
+ {ok, State#{start => Start, stop => t()}};
+ {ok, Sock} ->
+ ?SEV_EPRINT("Unexpected accept success: "
+ "~n ~p", [Sock]),
+ (catch socket:close(Sock)),
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "validate timeout time",
+ cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) ->
+ TDiff = tdiff(Start, Stop),
+ if
+ (TDiff >= To) ->
+ ok;
+ true ->
+ {error, {unexpected_timeout, TDiff, To}}
+ end
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+
+ %% *** Terminate ***
+ #{desc => "await terminate",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_TERMINATE(Tester, tester),
+ ok
+ end},
+ %% *** Close (listen) socket ***
+ #{desc => "close (listen) socket",
+ cmd => fun(#{lsock := LSock} = State) ->
+ sock_close(LSock),
+ {ok, maps:remove(lsock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ SecAcceptorSeq =
+ [
+ %% *** Init part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ {Tester, LSock} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester,
+ lsock => LSock}}
+
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% *** The actual test part ***
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "attempt to accept (without success)",
+ cmd => fun(#{lsock := LSock, timeout := To} = State) ->
+ Start = t(),
+ case socket:accept(LSock, To) of
+ {error, timeout} ->
+ {ok, State#{start => Start, stop => t()}};
+ {ok, Sock} ->
+ (catch socket:close(Sock)),
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "validate timeout time",
+ cmd => fun(#{start := Start, stop := Stop, timeout := To} = State) ->
+ TDiff = tdiff(Start, Stop),
+ if
+ (TDiff >= To) ->
+ State1 = maps:remove(start, State),
+ State2 = maps:remove(stop, State1),
+ {ok, State2};
+ true ->
+ {error, {unexpected_timeout, TDiff, To}}
+ end
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+
+ %% *** Terminate ***
+ #{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
+ ],
+
+
+ TesterSeq =
+ [
+ %% Init part
+ #{desc => "monitor prim-acceptor",
+ cmd => fun(#{prim_acceptor := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor sec-acceptor 1",
+ cmd => fun(#{sec_acceptor1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor sec-acceptor 2",
+ cmd => fun(#{sec_acceptor2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+
+ %% Start the prim-acceptor
+ #{desc => "start prim-acceptor",
+ cmd => fun(#{prim_acceptor := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await prim-acceptor ready (init)",
+ cmd => fun(#{prim_acceptor := Pid} = State) ->
+ {ok, Sock} = ?SEV_AWAIT_READY(Pid, prim_acceptor, init),
+ {ok, State#{lsock => Sock}}
+ end},
+
+ %% Start sec-acceptor-1
+ #{desc => "start sec-acceptor 1",
+ cmd => fun(#{sec_acceptor1 := Pid, lsock := LSock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, LSock),
+ ok
+ end},
+ #{desc => "await sec-acceptor 1 ready (init)",
+ cmd => fun(#{sec_acceptor1 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, sec_acceptor1, init)
+ end},
+
+ %% Start sec-acceptor-2
+ #{desc => "start sec-acceptor 2",
+ cmd => fun(#{sec_acceptor2 := Pid, lsock := LSock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, LSock),
+ ok
+ end},
+ #{desc => "await sec-acceptor 2 ready (init)",
+ cmd => fun(#{sec_acceptor2 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, sec_acceptor2, init)
+ end},
+
+ %% Activate the acceptor(s)
+ #{desc => "active prim-acceptor",
+ cmd => fun(#{prim_acceptor := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ #{desc => "active sec-acceptor 1",
+ cmd => fun(#{sec_acceptor1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ #{desc => "active sec-acceptor 2",
+ cmd => fun(#{sec_acceptor2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+
+ %% Await acceptor(s) completions
+ #{desc => "await prim-acceptor ready (accept)",
+ cmd => fun(#{prim_acceptor := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, prim_acceptor, accept)
+ end},
+ #{desc => "await sec-acceptor 1 ready (accept)",
+ cmd => fun(#{sec_acceptor1 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, sec_acceptor1, accept)
+ end},
+ #{desc => "await sec-acceptor 2 ready (accept)",
+ cmd => fun(#{sec_acceptor2 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, sec_acceptor2, accept)
+ end},
+
+ %% Terminate
+ #{desc => "order prim-acceptor to terminate",
+ cmd => fun(#{prim_acceptor := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await prim-acceptor termination",
+ cmd => fun(#{prim_acceptor := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(prim_acceptor, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order sec-acceptor 1 to terminate",
+ cmd => fun(#{sec_acceptor1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await sec-acceptor 1 termination",
+ cmd => fun(#{sec_acceptor1 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(sec_acceptor1, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order sec-acceptor 2 to terminate",
+ cmd => fun(#{sec_acceptor2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await sec-acceptor 2 termination",
+ cmd => fun(#{sec_acceptor2 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(sec_acceptor2, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("create prim-acceptor evaluator"),
+ PrimAInitState = InitState,
+ PrimAcceptor = ?SEV_START("prim-acceptor", PrimAcceptorSeq, PrimAInitState),
+
+ i("create sec-acceptor 1 evaluator"),
+ SecAInitState1 = maps:remove(domain, InitState),
+ SecAcceptor1 = ?SEV_START("sec-acceptor-1", SecAcceptorSeq, SecAInitState1),
+
+ i("create sec-acceptor 2 evaluator"),
+ SecAInitState2 = SecAInitState1,
+ SecAcceptor2 = ?SEV_START("sec-acceptor-2", SecAcceptorSeq, SecAInitState2),
+
+ i("create tester evaluator"),
+ TesterInitState = #{prim_acceptor => PrimAcceptor#ev.pid,
+ sec_acceptor1 => SecAcceptor1#ev.pid,
+ sec_acceptor2 => SecAcceptor2#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([PrimAcceptor, SecAcceptor1, SecAcceptor2, Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the send timeout option
+%% on an IPv4 TCP (stream) socket.
+api_to_send_tcp4(suite) ->
+ [];
+api_to_send_tcp4(doc) ->
+ [];
+api_to_send_tcp4(_Config) when is_list(_Config) ->
+ tc_try(api_to_send_tcp4,
+ fun() ->
+ not_yet_implemented()%% ,
+ %% ok = api_to_send_tcp(inet)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the send timeout option
+%% on an IPv6 TCP (stream) socket.
+api_to_send_tcp6(suite) ->
+ [];
+api_to_send_tcp6(doc) ->
+ [];
+api_to_send_tcp6(_Config) when is_list(_Config) ->
+ tc_try(api_to_send_tcp6,
+ fun() ->
+ not_yet_implemented()%% ,
+ %% ok = api_to_send_tcp(inet6)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the sendto timeout option
+%% on an IPv4 UDP (dgram) socket.
+api_to_sendto_udp4(suite) ->
+ [];
+api_to_sendto_udp4(doc) ->
+ [];
+api_to_sendto_udp4(_Config) when is_list(_Config) ->
+ tc_try(api_to_sendto_udp4,
+ fun() ->
+ not_yet_implemented()%% ,
+ %% ok = api_to_sendto_to_udp(inet)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the sendto timeout option
+%% on an IPv6 UDP (dgram) socket.
+api_to_sendto_udp6(suite) ->
+ [];
+api_to_sendto_udp6(doc) ->
+ [];
+api_to_sendto_udp6(_Config) when is_list(_Config) ->
+ tc_try(api_to_sendto_udp6,
+ fun() ->
+ not_yet_implemented()%% ,
+ %% ok = api_to_sendto_to_udp(inet6)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the sendmsg timeout option
+%% on an IPv4 TCP (stream) socket.
+api_to_sendmsg_tcp4(suite) ->
+ [];
+api_to_sendmsg_tcp4(doc) ->
+ [];
+api_to_sendmsg_tcp4(_Config) when is_list(_Config) ->
+ tc_try(api_to_sendmsg_tcp4,
+ fun() ->
+ not_yet_implemented()%% ,
+ %% ok = api_to_sendmsg_tcp(inet)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the sendmsg timeout option
+%% on an IPv6 TCP (stream) socket.
+api_to_sendmsg_tcp6(suite) ->
+ [];
+api_to_sendmsg_tcp6(doc) ->
+ [];
+api_to_sendmsg_tcp6(_Config) when is_list(_Config) ->
+ tc_try(api_to_sendmsg_tcp6,
+ fun() ->
+ not_yet_implemented()%% ,
+ %% ok = api_to_sendmsg_tcp(inet6)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the recv timeout option
+%% on an IPv4 UDP (dgram) socket. To test this we must connect
+%% the socket.
+api_to_recv_udp4(suite) ->
+ [];
+api_to_recv_udp4(doc) ->
+ [];
+api_to_recv_udp4(_Config) when is_list(_Config) ->
+ tc_try(api_to_recv_udp4,
+ fun() ->
+ not_yet_implemented()%%,
+ %%ok = api_to_recv_udp(inet)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the recv timeout option
+%% on an IPv6 UDP (dgram) socket. To test this we must connect
+%% the socket.
+api_to_recv_udp6(suite) ->
+ [];
+api_to_recv_udp6(doc) ->
+ [];
+api_to_recv_udp6(_Config) when is_list(_Config) ->
+ tc_try(api_to_recv_udp6,
+ fun() ->
+ not_yet_implemented()%% ,
+ %% ok = api_to_recv_udp(inet6)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the recv timeout option
+%% on an IPv4 TCP (stream) socket.
+api_to_recv_tcp4(suite) ->
+ [];
+api_to_recv_tcp4(doc) ->
+ [];
+api_to_recv_tcp4(_Config) when is_list(_Config) ->
+ 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,
+ timeout => 5000},
+ ok = api_to_receive_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the recv timeout option
+%% on an IPv6 TCP (stream) socket.
+api_to_recv_tcp6(suite) ->
+ [];
+api_to_recv_tcp6(doc) ->
+ [];
+api_to_recv_tcp6(_Config) when is_list(_Config) ->
+ tc_try(api_to_recv_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ case socket:supports(ipv6) of
+ true ->
+ ?TT(?SECS(10)),
+ Recv = fun(Sock, To) ->
+ socket:recv(Sock, 0, To)
+ end,
+ InitState = #{domain => inet6,
+ recv => Recv,
+ timeout => 5000},
+ ok = api_to_receive_tcp(InitState);
+ false ->
+ skip("ipv6 not supported")
+ end
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_to_receive_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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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 => "bind to local address",
+ cmd => fun(#{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 (with backlog = 1)",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock, 1)
+ 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 and recv)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept_recv)
+ end},
+ #{desc => "attempt accept",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "attempt to recv (without success)",
+ cmd => fun(#{sock := Sock, recv := Recv, timeout := To} = State) ->
+ Start = t(),
+ case Recv(Sock, To) of
+ {error, timeout} ->
+ {ok, State#{start => Start, stop => t()}};
+ {ok, _Data} ->
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "validate timeout time",
+ cmd => fun(#{start := Start, stop := Stop, timeout := To} = State) ->
+ TDiff = tdiff(Start, Stop),
+ if
+ (TDiff >= To) ->
+ State1 = maps:remove(start, State),
+ State2 = maps:remove(stop, State1),
+ {ok, State2};
+ true ->
+ {error, {unexpected_timeout, TDiff, To}}
+ end
+ end},
+ #{desc => "announce ready (recv timeout success)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept_recv),
+ 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 (traffic) socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ sock_close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+ #{desc => "close (listen) socket",
+ cmd => fun(#{lsock := LSock} = State) ->
+ sock_close(LSock),
+ {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(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 local address",
+ cmd => fun(#{domain := Domain, server_port := Port} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ 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, _} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% *** The actual test ***
+ #{desc => "await continue (with connect)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect)
+ end},
+ #{desc => "connect",
+ cmd => fun(#{sock := Sock, server_sa := SSA}) ->
+ sock_connect(Sock, SSA),
+ 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) ->
+ sock_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},
+ #{desc => "monitor client",
+ cmd => fun(#{client := Client} = _State) ->
+ _MRef = erlang:monitor(process, Client),
+ ok
+ end},
+
+ %% *** Activate server ***
+ #{desc => "start server",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_START(Server),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Server} = State) ->
+ {ok, Port} = ?SEV_AWAIT_READY(Server, server, init),
+ {ok, State#{server_port => Port}}
+ end},
+ #{desc => "order server to continue (with accept)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, accept_recv),
+ ok
+ end},
+
+ %% *** Activate client ***
+ #{desc => "start client",
+ cmd => fun(#{client := Client, server_port := Port} = _State) ->
+ ?SEV_ANNOUNCE_START(Client, Port),
+ ok
+ end},
+ #{desc => "await client ready (init)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, init)
+ end},
+
+ %% *** The actual test ***
+ #{desc => "order client to continue (with connect)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, connect),
+ ok
+ end},
+ #{desc => "await server ready (accept/recv)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, accept_recv)
+ 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) ->
+ case ?SEV_AWAIT_TERMINATION(Client) of
+ ok ->
+ State1 = maps:remove(client, 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) ->
+ case ?SEV_AWAIT_TERMINATION(Server) of
+ ok ->
+ State1 = maps:remove(server, State),
+ State2 = maps:remove(server_port, State1),
+ {ok, State2};
+ {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"),
+ 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(s)"),
+ ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the recvfrom timeout option
+%% on an IPv4 UDP (dgram) socket.
+api_to_recvfrom_udp4(suite) ->
+ [];
+api_to_recvfrom_udp4(doc) ->
+ [];
+api_to_recvfrom_udp4(_Config) when is_list(_Config) ->
+ 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,
+ timeout => 5000},
+ ok = api_to_receive_udp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the recvfrom timeout option
+%% on an IPv6 UDP (dgram) socket.
+api_to_recvfrom_udp6(suite) ->
+ [];
+api_to_recvfrom_udp6(doc) ->
+ [];
+api_to_recvfrom_udp6(_Config) when is_list(_Config) ->
+ tc_try(api_to_recvfrom_udp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(10)),
+ Recv = fun(Sock, To) -> socket:recvfrom(Sock, 0, To) end,
+ InitState = #{domain => inet6,
+ recv => Recv,
+ timeout => 5000},
+ ok = api_to_receive_udp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+api_to_receive_udp(InitState) ->
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{lsa => 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 to local address",
+ cmd => fun(#{sock := Sock, lsa := LSA} = _State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** The actual test ***
+ #{desc => "attempt to read (without success)",
+ cmd => fun(#{sock := Sock, recv := Recv, timeout := To} = State) ->
+ Start = t(),
+ case Recv(Sock, To) of
+ {error, timeout} ->
+ {ok, State#{start => Start, stop => t()}};
+ {ok, _} ->
+ {error, unexpected_sucsess};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "validate timeout time",
+ cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) ->
+ TDiff = tdiff(Start, Stop),
+ if
+ (TDiff >= To) ->
+ ok;
+ true ->
+ {error, {unexpected_timeout, TDiff, To}}
+ end
+ end},
+
+ %% *** Termination ***
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = _State) ->
+ sock_close(Sock),
+ ok
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start tester evaluator"),
+ Tester = ?SEV_START("tester", TesterSeq, InitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the recvmsg timeout option
+%% on an IPv4 UDP (dgram) socket.
+api_to_recvmsg_udp4(suite) ->
+ [];
+api_to_recvmsg_udp4(doc) ->
+ [];
+api_to_recvmsg_udp4(_Config) when is_list(_Config) ->
+ tc_try(api_to_recvmsg_udp4,
+ fun() ->
+ ?TT(?SECS(10)),
+ Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end,
+ InitState = #{domain => inet,
+ recv => Recv,
+ timeout => 5000},
+ ok = api_to_receive_udp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the recvmsg timeout option
+%% on an IPv6 UDP (dgram) socket.
+api_to_recvmsg_udp6(suite) ->
+ [];
+api_to_recvmsg_udp6(doc) ->
+ [];
+api_to_recvmsg_udp6(_Config) when is_list(_Config) ->
+ tc_try(api_to_recvmsg_udp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(10)),
+ Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end,
+ InitState = #{domain => inet6,
+ recv => Recv,
+ timeout => 5000},
+ ok = api_to_receive_udp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the recvmsg timeout option
+%% on an IPv4 TCP (stream) socket.
+api_to_recvmsg_tcp4(suite) ->
+ [];
+api_to_recvmsg_tcp4(doc) ->
+ [];
+api_to_recvmsg_tcp4(_Config) when is_list(_Config) ->
+ tc_try(api_to_recvmsg_tcp4,
+ fun() ->
+ ?TT(?SECS(10)),
+ Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end,
+ InitState = #{domain => inet,
+ recv => Recv,
+ timeout => 5000},
+ ok = api_to_receive_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This test case is intended to test the recvmsg timeout option
+%% on an IPv6 TCP (stream) socket.
+api_to_recvmsg_tcp6(suite) ->
+ [];
+api_to_recvmsg_tcp6(doc) ->
+ [];
+api_to_recvmsg_tcp6(_Config) when is_list(_Config) ->
+ tc_try(api_to_recvmsg_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(10)),
+ Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end,
+ InitState = #{domain => inet6,
+ recv => Recv,
+ timeout => 5000},
+ ok = api_to_receive_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% SOCKET CLOSURE %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 TCP (stream) socket.
+
+sc_cpe_socket_cleanup_tcp4(suite) ->
+ [];
+sc_cpe_socket_cleanup_tcp4(doc) ->
+ [];
+sc_cpe_socket_cleanup_tcp4(_Config) when is_list(_Config) ->
+ tc_try(sc_cpe_socket_cleanup_tcp4,
+ fun() ->
+ %% not_yet_implemented(),
+ ?TT(?SECS(5)),
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp},
+ 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 IPv6 TCP (stream) socket.
+
+sc_cpe_socket_cleanup_tcp6(suite) ->
+ [];
+sc_cpe_socket_cleanup_tcp6(doc) ->
+ [];
+sc_cpe_socket_cleanup_tcp6(_Config) when is_list(_Config) ->
+ tc_try(sc_cpe_socket_cleanup_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(5)),
+ InitState = #{domain => inet6,
+ type => stream,
+ protocol => tcp},
+ 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) ->
+ [];
+sc_cpe_socket_cleanup_udp4(doc) ->
+ [];
+sc_cpe_socket_cleanup_udp4(_Config) when is_list(_Config) ->
+ tc_try(sc_cpe_socket_cleanup_udp4,
+ fun() ->
+ ?TT(?SECS(5)),
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp},
+ 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 IPv6 UDP (dgram) socket.
+
+sc_cpe_socket_cleanup_udp6(suite) ->
+ [];
+sc_cpe_socket_cleanup_udp6(doc) ->
+ [];
+sc_cpe_socket_cleanup_udp6(_Config) when is_list(_Config) ->
+ tc_try(sc_cpe_socket_cleanup_udp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(5)),
+ InitState = #{domain => inet6,
+ type => dgram,
+ protocol => udp},
+ ok = sc_cpe_socket_cleanup(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+sc_cpe_socket_cleanup(InitState) ->
+ OwnerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start (from tester)",
+ 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 => "create socket",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Proto} = State) ->
+ case socket:open(Domain, Type, Proto) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Sock),
+ ok
+ end},
+
+ %% *** The actual test ***
+ %% We *intentially* leave the socket "as is", no explicit close
+ #{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},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor owner",
+ cmd => fun(#{owner := Owner} = _State) ->
+ _MRef = erlang:monitor(process, Owner),
+ ok
+ end},
+ #{desc => "order (owner) start",
+ cmd => fun(#{owner := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await (owner) ready",
+ cmd => fun(#{owner := Pid} = State) ->
+ {ok, Sock} = ?SEV_AWAIT_READY(Pid, owner, init),
+ {ok, State#{sock => Sock}}
+ end},
+ #{desc => "verify owner as controlling-process",
+ cmd => fun(#{owner := Pid, sock := Sock} = _State) ->
+ case socket:getopt(Sock, otp, controlling_process) of
+ {ok, Pid} ->
+ ok;
+ {ok, Other} ->
+ {error, {unexpected_owner, Other}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order (owner) terminate",
+ cmd => fun(#{owner := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await (owner) termination",
+ cmd => fun(#{owner := Pid} = _State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ %% 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.
+ #{desc => "verify no socket (closed)",
+ cmd => fun(#{owner := Pid, sock := Sock} = _State) ->
+ case socket:getopt(Sock, otp, controlling_process) of
+ {ok, OtherPid} ->
+ {error, {unexpected_success, Pid, OtherPid}};
+ {error, closed} ->
+ ok;
+ {error, Reason} ->
+ ?SEV_IPRINT("expected failure: ~p", [Reason]),
+ {error, {unexpected_failure, Reason}}
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start (socket) owner evaluator"),
+ Owner = ?SEV_START("owner", OwnerSeq, InitState),
+
+ i("start tester evaluator"),
+ TesterInitState = #{owner => Owner#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Owner, Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to test what happens when a socket is
+%% locally closed while a process is calling the recv function.
+%% Socket is IPv4.
+%%
+%% <KOLLA>
+%%
+%% We should really have a similar test cases for when the controlling
+%% process exits and there are other processes in recv, accept, and
+%% all the other functions.
+%%
+%% </KOLLA>
+
+sc_lc_recv_response_tcp4(suite) ->
+ [];
+sc_lc_recv_response_tcp4(doc) ->
+ [];
+sc_lc_recv_response_tcp4(_Config) when is_list(_Config) ->
+ 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)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to test what happens when a socket is
+%% locally closed while the process is calling the recv function.
+%% Socket is IPv6.
+
+sc_lc_recv_response_tcp6(suite) ->
+ [];
+sc_lc_recv_response_tcp6(doc) ->
+ [];
+sc_lc_recv_response_tcp6(_Config) when is_list(_Config) ->
+ tc_try(sc_lc_recv_response_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?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)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+sc_lc_receive_response_tcp(InitState) ->
+ %% This (acceptor) is the server that accepts connections.
+ %% But it is also suppose to close the connection socket,
+ %% and trigger the read failure (=closed) for the handler process.
+ AcceptorSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start (from tester)",
+ 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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{local_sa => LSA}}
+ end},
+ #{desc => "create (listen) socket",
+ cmd => fun(#{domain := Domain,
+ type := Type,
+ protocol := Proto} = State) ->
+ case socket:open(Domain, Type, Proto) of
+ {ok, Sock} ->
+ {ok, State#{lsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{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(#{tester := Tester, lport := Port}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, accept) of
+ {ok, {H1, H2, H3}} ->
+ {ok, State#{handler1 => H1,
+ handler2 => H2,
+ handler3 => H3}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await accept",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ ?SEV_IPRINT("connection accepted"),
+ {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 => "transfer connection to handler 1",
+ cmd => fun(#{handler1 := Handler, csock := Sock}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Handler, transfer, Sock),
+ ok
+ end},
+ #{desc => "transfer connection to handler 2",
+ cmd => fun(#{handler2 := Handler, csock := Sock}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Handler, transfer, Sock),
+ ok
+ end},
+ #{desc => "transfer connection to handler 3",
+ cmd => fun(#{handler3 := Handler, csock := Sock}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Handler, transfer, Sock),
+ ok
+ end},
+ #{desc => "await continue (close)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, close),
+ ok
+ end},
+ #{desc => "close the 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)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (close)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, close),
+ ok
+ end},
+
+ %% *** Terminate ***
+ #{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(#{lsock := Sock} = State) ->
+ case socket:close(Sock) of
+ ok ->
+ State1 = maps:remove(lsock, State),
+ State2 = maps:remove(lport, State1),
+ {ok, State2};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ %% The point of this is to perform the recv for which
+ %% we are testing the reponse.
+ HandlerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start (from tester)",
+ cmd => fun(State) ->
+ {Tester, Acceptor} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester,
+ acceptor => Acceptor}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+ #{desc => "monitor acceptor",
+ cmd => fun(#{acceptor := Acceptor} = _State) ->
+ _MRef = erlang:monitor(process, Acceptor),
+ ok
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (transfer)",
+ cmd => fun(#{acceptor := Pid} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Pid, acceptor, transfer) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (transfer)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, transfer),
+ ok
+ end},
+ #{desc => "attempt recv (=> closed)",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock) of
+ {ok, _Data} ->
+ ?SEV_EPRINT("Unexpected data received"),
+ {error, unexpected_success};
+ {error, closed} ->
+ ?SEV_IPRINT("received expected 'closed' "
+ "result"),
+ State1 = maps:remove(sock, State),
+ {ok, State1};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected read faulure: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv closed)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_closed),
+ ok
+ end},
+
+ %% *** Terminate ***
+ #{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
+ ],
+
+ %% The point of this is basically just to create the connection.
+ ClientSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start (from tester)",
+ 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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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
+ {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, _} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (connect)",
+ cmd => fun(#{tester := Tester, local_sa := LSA} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, connect) of
+ {ok, Port} ->
+ ServerSA = LSA#{port => Port},
+ {ok, State#{server_sa => ServerSA}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "connect to server",
+ cmd => fun(#{sock := Sock, server_sa := ServerSA}) ->
+ socket:connect(Sock, ServerSA)
+ end},
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect),
+ ok
+ end},
+
+ %% *** Terminate ***
+ #{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) ->
+ sock_close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor acceptor",
+ cmd => fun(#{acceptor := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor handler 1",
+ cmd => fun(#{handler1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor handler 2",
+ cmd => fun(#{handler2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor handler 3",
+ cmd => fun(#{handler3 := 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 acceptor
+ #{desc => "order acceptor start",
+ cmd => fun(#{acceptor := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await acceptor ready (init)",
+ cmd => fun(#{acceptor := Pid} = State) ->
+ case ?SEV_AWAIT_READY(Pid, acceptor, init) of
+ {ok, Port} ->
+ {ok, State#{lport => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% Start the handler(s)
+ #{desc => "order handler 1 start",
+ cmd => fun(#{acceptor := Acceptor, handler1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Acceptor),
+ ok
+ end},
+ #{desc => "await handler 1 ready (init)",
+ cmd => fun(#{handler1 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, handler1, init)
+ end},
+ #{desc => "order handler 2 start",
+ cmd => fun(#{acceptor := Acceptor, handler2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Acceptor),
+ ok
+ end},
+ #{desc => "await handler 2 ready (init)",
+ cmd => fun(#{handler2 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, handler2, init)
+ end},
+ #{desc => "order handler 3 start",
+ cmd => fun(#{acceptor := Acceptor, handler3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Acceptor),
+ ok
+ end},
+ #{desc => "await handler 3 ready (init)",
+ cmd => fun(#{handler3 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, handler3, init)
+ end},
+
+ %% Start the client
+ #{desc => "order client start",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await client ready (init)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, client, init)
+ end},
+
+ %% The actual test
+ #{desc => "order acceptor to continue (accept)",
+ cmd => fun(#{acceptor := Pid,
+ handler1 := H1,
+ handler2 := H2,
+ handler3 := H3} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept, {H1, H2, H3}),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client to continue (connect)",
+ cmd => fun(#{client := Pid, lport := Port} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, connect, Port),
+ ok
+ end},
+ #{desc => "await acceptor ready (accept)",
+ cmd => fun(#{acceptor := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, acceptor, accept)
+ end},
+ #{desc => "await client ready (connect)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client, connect)
+ end},
+ #{desc => "await handler 1 ready (transfer)",
+ cmd => fun(#{handler1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, handler1, transfer)
+ end},
+ #{desc => "await handler 2 ready (transfer)",
+ cmd => fun(#{handler2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, handler2, transfer)
+ end},
+ #{desc => "await handler 3 ready (transfer)",
+ cmd => fun(#{handler3 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, handler3, transfer)
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order acceptor to continue (close connection socket)",
+ cmd => fun(#{acceptor := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, close),
+ ok
+ end},
+ #{desc => "await acceptor ready (close)",
+ cmd => fun(#{acceptor := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, acceptor, close)
+ end},
+ #{desc => "await handler 1 ready (recv closed)",
+ cmd => fun(#{handler1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, handler1, recv_closed)
+ end},
+ #{desc => "await handler 2 ready (recv closed)",
+ cmd => fun(#{handler2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, handler2, recv_closed)
+ end},
+ #{desc => "await handler 3 ready (recv closed)",
+ cmd => fun(#{handler3 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, handler3, recv_closed)
+ 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 ->
+ {ok, maps:remove(client, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order handler 1 to terminate",
+ cmd => fun(#{handler1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await handler 1 termination",
+ cmd => fun(#{handler1 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(handler1, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order handler 2 to terminate",
+ cmd => fun(#{handler2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await handler 2 termination",
+ cmd => fun(#{handler2 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(handler2, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order handler 3 to terminate",
+ cmd => fun(#{handler3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await handler 3 termination",
+ cmd => fun(#{handler3 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(handler3, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order acceptor to terminate",
+ cmd => fun(#{acceptor := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await acceptor termination",
+ cmd => fun(#{acceptor := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(acceptor, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start acceptor evaluator"),
+ AccInitState = InitState,
+ Acceptor = ?SEV_START("acceptor", AcceptorSeq, AccInitState),
+
+ i("start handler 1 evaluator"),
+ HandlerInitState = #{recv => maps:get(recv, InitState)},
+ Handler1 = ?SEV_START("handler-1", HandlerSeq, HandlerInitState),
+
+ i("start handler 2 evaluator"),
+ Handler2 = ?SEV_START("handler-2", HandlerSeq, HandlerInitState),
+
+ i("start handler 3 evaluator"),
+ Handler3 = ?SEV_START("handler-3", HandlerSeq, HandlerInitState),
+
+ i("start client evaluator"),
+ ClientInitState = InitState,
+ Client = ?SEV_START("client", ClientSeq, ClientInitState),
+
+ i("start tester evaluator"),
+ TesterInitState = #{acceptor => Acceptor#ev.pid,
+ handler1 => Handler1#ev.pid,
+ handler2 => Handler2#ev.pid,
+ handler3 => Handler3#ev.pid,
+ client => Client#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Acceptor,
+ Handler1, Handler2, Handler3,
+ Client, Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to test what happens when a socket is
+%% locally closed while a process is calling the recvfrom function.
+%% Socket is IPv4.
+%%
+
+sc_lc_recvfrom_response_udp4(suite) ->
+ [];
+sc_lc_recvfrom_response_udp4(doc) ->
+ [];
+sc_lc_recvfrom_response_udp4(_Config) when is_list(_Config) ->
+ tc_try(sc_lc_recvfrom_response_udp4,
+ fun() ->
+ ?TT(?SECS(30)),
+ Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end,
+ InitState = #{domain => inet,
+ type => dgram,
+ protocol => udp,
+ recv => Recv},
+ ok = sc_lc_receive_response_udp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to test what happens when a socket is
+%% locally closed while the process is calling the recv function.
+%% Socket is IPv6.
+
+sc_lc_recvfrom_response_udp6(suite) ->
+ [];
+sc_lc_recvfrom_response_udp6(doc) ->
+ [];
+sc_lc_recvfrom_response_udp6(_Config) when is_list(_Config) ->
+ tc_try(sc_lc_recvfrom_response_udp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(30)),
+ Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end,
+ InitState = #{domain => inet6,
+ recv => Recv},
+ ok = sc_lc_receive_response_udp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+sc_lc_receive_response_udp(InitState) ->
+ PrimServerSeq =
+ [
+ %% *** 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 => "local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{local_sa => LSA}}
+ end},
+ #{desc => "open socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ Sock = sock_open(Domain, dgram, udp),
+ SA = sock_sockname(Sock),
+ {ok, State#{sock => Sock, sa => SA}}
+ end},
+ #{desc => "bind socket",
+ cmd => fun(#{sock := Sock, local_sa := LSA}) ->
+ sock_bind(Sock, LSA),
+ ok
+ 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, with timeout)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, recv) of
+ {ok, Timeout} ->
+ {ok, State#{timeout => Timeout}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "receive, with timeout",
+ cmd => fun(#{sock := Sock, recv := Recv, timeout := Timeout}) ->
+ case Recv(Sock, Timeout) of
+ {error, timeout} ->
+ ok;
+ {ok, _} ->
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv, with timeout)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv),
+ ok
+ end},
+ #{desc => "await continue (close)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ok = ?SEV_AWAIT_CONTINUE(Tester, tester, close)
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ case socket:close(Sock) of
+ ok ->
+ {ok, maps:remove(sock, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (close)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, close),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, terminate) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ SecServerSeq =
+ [
+ %% *** Init 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) ->
+ ok = ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+
+ end},
+ #{desc => "receive",
+ cmd => fun(#{sock := Sock, recv := Recv} = State) ->
+ case Recv(Sock, infinity) of
+ {error, closed} ->
+ {ok, maps:remove(sock, State)};
+ {ok, _} ->
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv closed)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_closed),
+ 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},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor primary server",
+ cmd => fun(#{prim_server := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor secondary server 1",
+ cmd => fun(#{sec_server1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor secondary server 2",
+ cmd => fun(#{sec_server2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor secondary server 3",
+ cmd => fun(#{sec_server3 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the primary server
+ #{desc => "order 'primary server' start",
+ cmd => fun(#{prim_server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await 'primary server' ready (init)",
+ cmd => fun(#{prim_server := Pid} = State) ->
+ {ok, Sock} = ?SEV_AWAIT_READY(Pid, prim_server, init),
+ {ok, State#{sock => Sock}}
+ end},
+
+ %% Start the secondary server 1
+ #{desc => "order 'secondary server 1' start",
+ cmd => fun(#{sec_server1 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await 'secondary server 1' ready (init)",
+ cmd => fun(#{sec_server1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, sec_server1, init)
+ end},
+
+ %% Start the secondary server 2
+ #{desc => "order 'secondary server 2' start",
+ cmd => fun(#{sec_server2 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await 'secondary server 2' ready (init)",
+ cmd => fun(#{sec_server2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, sec_server2, init)
+ end},
+
+ %% Start the secondary server 3
+ #{desc => "order 'secondary server 3' start",
+ cmd => fun(#{sec_server3 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await 'secondary server 3' ready (init)",
+ cmd => fun(#{sec_server3 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, sec_server3, init)
+ end},
+
+
+ %% The actual test
+ %% Make all the seondary servers continue, with an infinit recvfrom
+ %% and then the prim-server with a timed recvfrom.
+ %% After the prim server notifies us (about the timeout) we order it
+ %% to close the socket, which should cause the all the secondary
+ %% server to return with error-closed.
+
+ #{desc => "order 'secondary server 1' to continue (recv)",
+ cmd => fun(#{sec_server1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order 'secondary server 2' to continue (recv)",
+ cmd => fun(#{sec_server2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order 'secondary server 3' to continue (recv)",
+ cmd => fun(#{sec_server3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order 'primary server' to continue (recv, with timeout)",
+ cmd => fun(#{prim_server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv, ?SECS(5)),
+ ok
+ end},
+ #{desc => "await 'primary server' ready (recv, with timeout)",
+ cmd => fun(#{prim_server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, prim_server, recv)
+ end},
+ #{desc => "order 'primary server' to continue (close)",
+ cmd => fun(#{prim_server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, close),
+ ok
+ end},
+ #{desc => "await 'primary server' ready (close)",
+ cmd => fun(#{prim_server := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, prim_server, close)
+ end},
+ #{desc => "await 'secondary server 1' ready (closed)",
+ cmd => fun(#{sec_server1 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, sec_server1, recv_closed)
+ end},
+ #{desc => "await 'secondary server 2' ready (closed)",
+ cmd => fun(#{sec_server2 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, sec_server2, recv_closed)
+ end},
+ #{desc => "await 'secondary server 3' ready (closed)",
+ cmd => fun(#{sec_server3 := Pid} = _State) ->
+ ?SEV_AWAIT_READY(Pid, sec_server3, recv_closed)
+ end},
+
+ %% Terminations
+ #{desc => "order 'secondary server 3' to terminate",
+ cmd => fun(#{sec_server3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await 'secondary server 3' termination",
+ cmd => fun(#{sec_server3 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(sec_server3, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order 'secondary server 2' to terminate",
+ cmd => fun(#{sec_server2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await 'secondary server 2' termination",
+ cmd => fun(#{sec_server2 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(sec_server2, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order 'secondary server 1' to terminate",
+ cmd => fun(#{sec_server1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await 'secondary server 1' termination",
+ cmd => fun(#{sec_server1 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(sec_server1, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order 'primary server' to terminate",
+ cmd => fun(#{prim_server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await 'primary server' termination",
+ cmd => fun(#{prim_server := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(prim_server, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ i("start 'primary server' evaluator"),
+ PrimSrvInitState = InitState,
+ PrimServer = ?SEV_START("prim-server", PrimServerSeq, PrimSrvInitState),
+
+ i("start 'secondary server 1' evaluator"),
+ SecSrvInitState = #{recv => maps:get(recv, InitState)},
+ SecServer1 = ?SEV_START("sec-server-1", SecServerSeq, SecSrvInitState),
+
+ i("start 'secondary server 2' evaluator"),
+ SecServer2 = ?SEV_START("sec-server-2", SecServerSeq, SecSrvInitState),
+
+ i("start 'secondary server 3' evaluator"),
+ SecServer3 = ?SEV_START("sec-server-3", SecServerSeq, SecSrvInitState),
+
+ i("start 'tester' evaluator"),
+ TesterInitState = #{prim_server => PrimServer#ev.pid,
+ sec_server1 => SecServer1#ev.pid,
+ sec_server2 => SecServer2#ev.pid,
+ sec_server3 => SecServer3#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([PrimServer,
+ SecServer1, SecServer2, SecServer3,
+ Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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_tcp4(suite) ->
+ [];
+sc_lc_recvmsg_response_tcp4(doc) ->
+ [];
+sc_lc_recvmsg_response_tcp4(_Config) when is_list(_Config) ->
+ 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)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to test what happens when a socket is
+%% locally closed while the process is calling the recvmsg function.
+%% Socket is IPv6.
+
+sc_lc_recvmsg_response_tcp6(suite) ->
+ [];
+sc_lc_recvmsg_response_tcp6(doc) ->
+ [];
+sc_lc_recvmsg_response_tcp6(_Config) when is_list(_Config) ->
+ tc_try(sc_recvmsg_response_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?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)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to test what happens when a socket is
+%% locally closed while the process is calling the recvmsg function.
+%% Socket is IPv4.
+
+sc_lc_recvmsg_response_udp4(suite) ->
+ [];
+sc_lc_recvmsg_response_udp4(doc) ->
+ [];
+sc_lc_recvmsg_response_udp4(_Config) when is_list(_Config) ->
+ tc_try(sc_lc_recvmsg_response_udp4,
+ fun() ->
+ ?TT(?SECS(10)),
+ Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end,
+ InitState = #{domain => inet,
+ recv => Recv},
+ ok = sc_lc_receive_response_udp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to test what happens when a socket is
+%% locally closed while the process is calling the recvmsg function.
+%% Socket is IPv6.
+
+sc_lc_recvmsg_response_udp6(suite) ->
+ [];
+sc_lc_recvmsg_response_udp6(doc) ->
+ [];
+sc_lc_recvmsg_response_udp6(_Config) when is_list(_Config) ->
+ tc_try(sc_recvmsg_response_udp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(10)),
+ Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end,
+ InitState = #{domain => inet6,
+ recv => Recv},
+ ok = sc_lc_receive_response_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to test what happens when a socket is
+%% 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 IPv4.
+
+sc_lc_acceptor_response_tcp4(suite) ->
+ [];
+sc_lc_acceptor_response_tcp4(doc) ->
+ [];
+sc_lc_acceptor_response_tcp4(_Config) when is_list(_Config) ->
+ 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).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 IPv6.
+
+sc_lc_acceptor_response_tcp6(suite) ->
+ [];
+sc_lc_acceptor_response_tcp6(doc) ->
+ [];
+sc_lc_acceptor_response_tcp6(_Config) when is_list(_Config) ->
+ tc_try(sc_lc_acceptor_response_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(10)),
+ InitState = #{domain => inet,
+ type => stream,
+ protocol => tcp},
+ ok = sc_lc_acceptor_response_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+sc_lc_acceptor_response_tcp(InitState) ->
+ PrimAcceptorSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start (from tester)",
+ 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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "bind to local address",
+ cmd => fun(#{sock := Sock, lsa := LSA} = _State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{sock := Sock}) ->
+ socket:listen(Sock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, Sock),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, accept) of
+ {ok, Timeout} ->
+ {ok, State#{timeout => Timeout}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await connection",
+ cmd => fun(#{sock := Sock, timeout := Timeout} = _State) ->
+ case socket:accept(Sock, Timeout) of
+ {error, timeout} ->
+ ok;
+ {ok, Sock} ->
+ ?SEV_EPRINT("unexpected success"),
+ (catch socket:close(Sock)),
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept timeout)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept_timeout),
+ ok
+ end},
+ #{desc => "await continue (close)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ok = ?SEV_AWAIT_CONTINUE(Tester, tester, close)
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ case socket:close(Sock) of
+ ok ->
+ {ok, maps:remove(sock, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (close)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, close),
+ 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
+ ],
+
+ SecAcceptorSeq =
+ [
+ %% *** Init 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)
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ok = ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "accept",
+ cmd => fun(#{sock := Sock} = State) ->
+ case socket:accept(Sock) of
+ {error, closed} ->
+ {ok, maps:remove(sock, State)};
+ {ok, _} ->
+ {error, unexpected_success};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept closed)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept_closed)
+ 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},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor 'primary acceptor'",
+ cmd => fun(#{prim_acc := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor 'secondary acceptor 1'",
+ cmd => fun(#{sec_acc1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor secondary acceptor 2",
+ cmd => fun(#{sec_acc2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor secondary acceptor 3",
+ cmd => fun(#{sec_acc3 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+
+ %% Start the primary server
+ #{desc => "order 'primary acceptor' start",
+ cmd => fun(#{prim_acc := Pid} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid),
+ ok
+ end},
+ #{desc => "await 'primary acceptor' ready (init)",
+ cmd => fun(#{prim_acc := Pid} = State) ->
+ {ok, Sock} = ?SEV_AWAIT_READY(Pid, prim_acc, init),
+ {ok, State#{sock => Sock}}
+ end},
+
+ %% Start the secondary acceptor 1
+ #{desc => "order 'secondary acceptor 1' start",
+ cmd => fun(#{sec_acc1 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await 'secondary acceptor 1' ready (init)",
+ cmd => fun(#{sec_acc1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, sec_acc1, init)
+ end},
+
+ %% Start the secondary acceptor 2
+ #{desc => "order 'secondary acceptor 2' start",
+ cmd => fun(#{sec_acc2 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await 'secondary acceptor 2' ready (init)",
+ cmd => fun(#{sec_acc2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, sec_acc2, init)
+ end},
+
+ %% Start the secondary acceptor 3
+ #{desc => "order 'secondary acceptor 3' start",
+ cmd => fun(#{sec_acc3 := Pid, sock := Sock} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, Sock),
+ ok
+ end},
+ #{desc => "await 'secondary acceptor 3' ready (init)",
+ cmd => fun(#{sec_acc3 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, sec_acc3, init)
+ end},
+
+
+ %% The actual test
+ %% Make all the seondary servers continue, with an infinit recvfrom
+ %% and then the prim-server with a timed recvfrom.
+ %% After the prim server notifies us (about the timeout) we order it
+ %% to close the socket, which should cause the all the secondary
+ %% server to return with error-closed.
+
+ #{desc => "order 'secondary acceptor 1' to continue (accept)",
+ cmd => fun(#{sec_acc1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order 'secondary acceptor 2' to continue (accept)",
+ cmd => fun(#{sec_acc2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order 'secondary acceptor 3' to continue (accept)",
+ cmd => fun(#{sec_acc3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order 'primary acceptor' to continue",
+ cmd => fun(#{prim_acc := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept, ?SECS(5)),
+ ok
+ end},
+ #{desc => "await 'primary acceptor' ready (accept timeout)",
+ cmd => fun(#{prim_acc := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, prim_acc, accept_timeout)
+ end},
+ #{desc => "order 'primary acceptor' to continue (close)",
+ cmd => fun(#{prim_acc := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, close),
+ ok
+ end},
+ #{desc => "await 'primary acceptor' ready (close)",
+ cmd => fun(#{prim_acc := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, prim_acc, close)
+ end},
+ #{desc => "await 'secondary acceptor 1' ready (accept closed)",
+ cmd => fun(#{sec_acc1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, sec_acc1, accept_closed)
+ end},
+ #{desc => "await 'secondary acceptor 2' ready (accept closed)",
+ cmd => fun(#{sec_acc2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, sec_acc2, accept_closed)
+ end},
+ #{desc => "await 'secondary acceptor 3' ready (accept closed)",
+ cmd => fun(#{sec_acc3 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, sec_acc3, accept_closed)
+ end},
+
+
+ %% Terminations
+ #{desc => "order 'secondary acceptor 3' to terminate",
+ cmd => fun(#{sec_acc3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await 'secondary acceptor 3' termination",
+ cmd => fun(#{sec_acc3 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(sec_acc3, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order 'secondary acceptor 2' to terminate",
+ cmd => fun(#{sec_acc2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await 'secondary acceptor 2' termination",
+ cmd => fun(#{sec_acc2 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(sec_acc2, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order 'secondary acceptor 1' to terminate",
+ cmd => fun(#{sec_acc1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await 'secondary acceptor 1' termination",
+ cmd => fun(#{sec_acc1 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(sec_acc1, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order 'primary acceptor' to terminate",
+ cmd => fun(#{prim_acc := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await 'primary acceptor' termination",
+ cmd => fun(#{prim_acc := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ {ok, maps:remove(prim_acc, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ i("start 'primary acceptor' evaluator"),
+ PrimAccInitState = InitState,
+ PrimAcc = ?SEV_START("prim-acceptor", PrimAcceptorSeq, PrimAccInitState),
+
+ i("start 'secondary acceptor 1' evaluator"),
+ SecAccInitState = #{},
+ SecAcc1 = ?SEV_START("sec-acceptor-1", SecAcceptorSeq, SecAccInitState),
+
+ i("start 'secondary acceptor 2' evaluator"),
+ SecAcc2 = ?SEV_START("sec-acceptor-2", SecAcceptorSeq, SecAccInitState),
+
+ i("start 'secondary acceptor 3' evaluator"),
+ SecAcc3 = ?SEV_START("sec-acceptor-3", SecAcceptorSeq, SecAccInitState),
+
+ i("start 'tester' evaluator"),
+ TesterInitState = #{prim_acc => PrimAcc#ev.pid,
+ sec_acc1 => SecAcc1#ev.pid,
+ sec_acc2 => SecAcc2#ev.pid,
+ sec_acc3 => SecAcc3#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([PrimAcc, SecAcc1, SecAcc2, SecAcc3, Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 IPv4.
+%%
+%% 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.
+%%
+
+sc_rc_recv_response_tcp4(suite) ->
+ [];
+sc_rc_recv_response_tcp4(doc) ->
+ [];
+sc_rc_recv_response_tcp4(_Config) when is_list(_Config) ->
+ 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)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 IPv6.
+
+sc_rc_recv_response_tcp6(suite) ->
+ [];
+sc_rc_recv_response_tcp6(doc) ->
+ [];
+sc_rc_recv_response_tcp6(_Config) when is_list(_Config) ->
+ tc_try(sc_rc_recv_response_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?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)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+sc_rc_receive_response_tcp(InitState) ->
+ %% Each connection are handled by handler processes.
+ %% These are created (on the fly) and handled internally
+ %% by the server!
+ 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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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 => "bind to local address",
+ cmd => fun(#{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(#{tester := Tester, local_sa := LSA, lport := Port}) ->
+ ServerSA = LSA#{port => Port},
+ ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept all three connections)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "accept 1",
+ cmd => fun(#{lsock := LSock, recv := Recv} = State) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ ?SEV_IPRINT("accepted: try start handler"),
+ Handler = sc_rc_tcp_handler_start(1, Recv, Sock),
+ ?SEV_IPRINT("handler started"),
+ {ok, State#{csock1 => Sock,
+ handler1 => Handler}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await handler 1 ready (init)",
+ cmd => fun(#{tester := Tester,
+ handler1 := Handler1} = _State) ->
+ ?SEV_AWAIT_READY(Handler1, handler1, init,
+ [{tester, Tester}])
+ end},
+ #{desc => "accept 2",
+ cmd => fun(#{lsock := LSock, recv := Recv} = State) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ ?SEV_IPRINT("accepted: try start handler"),
+ Handler = sc_rc_tcp_handler_start(2, Recv, Sock),
+ ?SEV_IPRINT("handler started"),
+ {ok, State#{csock2 => Sock,
+ handler2 => Handler}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await handler 2 ready (init)",
+ cmd => fun(#{tester := Tester,
+ handler1 := Handler1,
+ handler2 := Handler2} = _State) ->
+ ?SEV_AWAIT_READY(Handler2, handler2, init,
+ [{tester, Tester},
+ {handler1, Handler1}])
+ end},
+ #{desc => "accept 3",
+ cmd => fun(#{lsock := LSock, recv := Recv} = State) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ ?SEV_IPRINT("accepted: try start handler"),
+ Handler = sc_rc_tcp_handler_start(3, Recv, Sock),
+ ?SEV_IPRINT("handler started"),
+ {ok, State#{csock3 => Sock,
+ handler3 => Handler}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await handler 3 ready (init)",
+ cmd => fun(#{tester := Tester,
+ handler1 := Handler1,
+ handler2 := Handler2,
+ handler3 := Handler3} = _State) ->
+ ?SEV_AWAIT_READY(Handler3, handler3, init,
+ [{tester, Tester},
+ {handler1, Handler1},
+ {handler2, Handler2}])
+ end},
+ #{desc => "announce ready (accept all three connections)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+ #{desc => "await continue (recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "order handler 1 to receive",
+ cmd => fun(#{handler1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "order handler 2 to receive",
+ cmd => fun(#{handler2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "order handler 3 to receive",
+ cmd => fun(#{handler3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await ready from handler 1 (recv)",
+ cmd => fun(#{tester := Tester, handler1 := Pid} = _State) ->
+ case ?SEV_AWAIT_READY(Pid, handler1, recv,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await ready from handler 2 (recv)",
+ cmd => fun(#{tester := Tester, handler2 := Pid} = _State) ->
+ case ?SEV_AWAIT_READY(Pid, handler2, recv,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await ready from handler 3 (recv)",
+ cmd => fun(#{tester := Tester, handler3 := Pid} = _State) ->
+ case ?SEV_AWAIT_READY(Pid, handler3, recv,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv closed from all handlers)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv_closed),
+ 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 => "order handler 1 to terminate",
+ cmd => fun(#{handler1 := Pid} = _State) ->
+ %% Pid ! {terminate, self(), ok},
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await handler 1 termination",
+ cmd => fun(#{handler1 := Pid} = State) ->
+ ?SEV_AWAIT_TERMINATION(Pid),
+ State1 = maps:remove(csock1, State),
+ State2 = maps:remove(handler1, State1),
+ {ok, State2}
+ end},
+ #{desc => "order handler 2 to terminate",
+ cmd => fun(#{handler2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await handler 2 termination",
+ cmd => fun(#{handler2 := Pid} = State) ->
+ ?SEV_AWAIT_TERMINATION(Pid),
+ State1 = maps:remove(csock2, State),
+ State2 = maps:remove(handler2, State1),
+ {ok, State2}
+ end},
+ #{desc => "order handler 3 to terminate",
+ cmd => fun(#{handler3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await handler 3 termination",
+ cmd => fun(#{handler3 := Pid} = State) ->
+ ?SEV_AWAIT_TERMINATION(Pid),
+ State1 = maps:remove(csock3, State),
+ State2 = maps:remove(handler3, State1),
+ {ok, State2}
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:close(LSock) of
+ ok ->
+ {ok, maps:remove(lsock, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ ClientSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ {Tester, {NodeID, ServerSA}} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester,
+ node_id => NodeID,
+ server_sa => ServerSA}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "create node",
+ cmd => fun(#{host := Host, node_id := NodeID} = State) ->
+ case start_node(Host, l2a(f("client_~w", [NodeID]))) of
+ {ok, Node} ->
+ ?SEV_IPRINT("client node ~p started", [Node]),
+ {ok, State#{node => Node}};
+ {error, Reason, _} ->
+ {error, Reason}
+ end
+ end},
+ #{desc => "monitor client node 1",
+ cmd => fun(#{node := Node} = _State) ->
+ true = erlang:monitor_node(Node, true),
+ ok
+ end},
+ #{desc => "start remote client on client node",
+ cmd => fun(#{node := Node} = State) ->
+ Pid = sc_rc_tcp_client_start(Node),
+ ?SEV_IPRINT("client ~p started", [Pid]),
+ {ok, State#{rclient => Pid}}
+ end},
+ #{desc => "monitor remote client",
+ cmd => fun(#{rclient := Pid}) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "order remote client to start",
+ cmd => fun(#{rclient := Client, server_sa := ServerSA}) ->
+ ?SEV_ANNOUNCE_START(Client, ServerSA),
+ ok
+ end},
+ #{desc => "await remote client ready",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, init,
+ [{tester, Tester}])
+ 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,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect,
+ [{rclient, Client}]),
+ ok
+ end},
+ #{desc => "order remote client to continue (connect)",
+ cmd => fun(#{rclient := Client}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, connect),
+ ok
+ end},
+ #{desc => "await client process ready (connect)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, connect,
+ [{tester, Tester}])
+ end},
+ #{desc => "announce ready (connected)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect),
+ ok
+ end},
+ #{desc => "await continue (close)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, close,
+ [{rclient, Client}]),
+ ok
+ end},
+ #{desc => "order remote client to close",
+ cmd => fun(#{rclient := Client}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, close),
+ ok
+ end},
+ #{desc => "await remote client ready (closed)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, close,
+ [{tester, Tester}])
+ end},
+ #{desc => "announce ready (close)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, close),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester,
+ [{rclient, Client}]) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "kill remote client",
+ cmd => fun(#{rclient := Client}) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await remote client termination",
+ cmd => fun(#{rclient := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(rclient, State),
+ {ok, State1}
+ end},
+ #{desc => "stop client node",
+ cmd => fun(#{node := Node} = _State) ->
+ stop_node(Node)
+ end},
+ #{desc => "await client node termination",
+ cmd => fun(#{node := Node} = State) ->
+ receive
+ {nodedown, Node} ->
+ State1 = maps:remove(node_id, State),
+ State2 = maps:remove(node, State1),
+ {ok, State2}
+ 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 client 1",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client 2",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "monitor client 3",
+ cmd => fun(#{client3 := 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(s)
+ #{desc => "order client 1 start",
+ cmd => fun(#{client1 := Pid,
+ server_sa := ServerSA} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, {1, ServerSA}),
+ ok
+ end},
+ #{desc => "await client 1 ready (init)",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client1, init)
+ end},
+ #{desc => "order client 2 start",
+ cmd => fun(#{client2 := Pid,
+ server_sa := ServerSA} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, {2, ServerSA}),
+ ok
+ end},
+ #{desc => "await client 2 ready (init)",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client2, init)
+ end},
+ #{desc => "order client 3 start",
+ cmd => fun(#{client3 := Pid,
+ server_sa := ServerSA} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, {3, ServerSA}),
+ ok
+ end},
+ #{desc => "await client 3 ready (init)",
+ cmd => fun(#{client3 := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, client3, init)
+ end},
+
+ %% The actual test
+ #{desc => "order server continue (accept)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client 1 continue (connect)",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
+ ok
+ end},
+ #{desc => "await client 1 ready (connect)",
+ cmd => fun(#{server := Server,
+ client1 := Client1,
+ client2 := Client2,
+ client3 := Client3} = _State) ->
+ ?SEV_AWAIT_READY(Client1, client1, connect,
+ [{server, Server},
+ {client2, Client2},
+ {client3, Client3}]),
+ ok
+ end},
+ #{desc => "order client 2 continue (connect)",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
+ ok
+ end},
+ #{desc => "await client 2 ready (connect)",
+ cmd => fun(#{server := Server,
+ client1 := Client1,
+ client2 := Client2,
+ client3 := Client3} = _State) ->
+ ?SEV_AWAIT_READY(Client2, client2, connect,
+ [{server, Server},
+ {client1, Client1},
+ {client3, Client3}]),
+ ok
+ end},
+ #{desc => "order client 3 continue (connect)",
+ cmd => fun(#{client3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
+ ok
+ end},
+ #{desc => "await client 3 ready (connect)",
+ cmd => fun(#{server := Server,
+ client1 := Client1,
+ client2 := Client2,
+ client3 := Client3} = _State) ->
+ ?SEV_AWAIT_READY(Client3, client3, connect,
+ [{server, Server},
+ {client1, Client1},
+ {client2, Client2}]),
+ ok
+ end},
+ #{desc => "await server ready (accept from all connections)",
+ cmd => fun(#{server := Server,
+ client1 := Client1,
+ client2 := Client2,
+ client3 := Client3} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, accept,
+ [{client1, Client1},
+ {client2, Client2},
+ {client3, Client3}]),
+ ok
+ end},
+ #{desc => "order server continue (recv for all connections)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client 1 continue (close)",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, close),
+ ok
+ end},
+ #{desc => "await client 1 ready (close)",
+ cmd => fun(#{server := Server,
+ client1 := Client1,
+ client2 := Client2,
+ client3 := Client3} = _State) ->
+ ?SEV_AWAIT_READY(Client1, client1, close,
+ [{server, Server},
+ {client2, Client2},
+ {client3, Client3}]),
+ ok
+ end},
+ #{desc => "order client 2 continue (close)",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, close),
+ ok
+ end},
+ #{desc => "await client 2 ready (close)",
+ cmd => fun(#{server := Server,
+ client1 := Client1,
+ client2 := Client2,
+ client3 := Client3} = _State) ->
+ ?SEV_AWAIT_READY(Client2, client2, close,
+ [{server, Server},
+ {client1, Client1},
+ {client3, Client3}]),
+ ok
+ end},
+ #{desc => "order client 3 continue (close)",
+ cmd => fun(#{client3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, close),
+ ok
+ end},
+ #{desc => "await client 3 ready (close)",
+ cmd => fun(#{server := Server,
+ client1 := Client1,
+ client2 := Client2,
+ client3 := Client3} = _State) ->
+ ?SEV_AWAIT_READY(Client3, client1, close,
+ [{server, Server},
+ {client1, Client1},
+ {client2, Client2}]),
+ ok
+ end},
+ #{desc => "await server ready (close for all connections)",
+ cmd => fun(#{server := Server,
+ client1 := Client1,
+ client2 := Client2,
+ client3 := Client3} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv_closed,
+ [{client1, Client1},
+ {client2, Client2},
+ {client3, Client3}]),
+ ok
+ end},
+
+ %% Terminations
+ #{desc => "order client 1 to terminate",
+ cmd => fun(#{client1 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await client 1 termination",
+ cmd => fun(#{client1 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(client1, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order client 2 to terminate",
+ cmd => fun(#{client2 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await client 2 termination",
+ cmd => fun(#{client2 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(client2, State),
+ {ok, State1};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order client 3 to terminate",
+ cmd => fun(#{client3 := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await client 3 termination",
+ cmd => fun(#{client3 := Pid} = State) ->
+ case ?SEV_AWAIT_TERMINATION(Pid) of
+ ok ->
+ State1 = maps:remove(client3, 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#{host => local_host()},
+ Client1 = ?SEV_START("client-1", ClientSeq, ClientInitState),
+ Client2 = ?SEV_START("client-2", ClientSeq, ClientInitState),
+ Client3 = ?SEV_START("client-3", ClientSeq, ClientInitState),
+
+ i("start 'tester' evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ client1 => Client1#ev.pid,
+ client2 => Client2#ev.pid,
+ client3 => Client3#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Server,
+ Client1, Client2, Client3,
+ Tester]).
+
+
+sc_rc_tcp_client_start(Node) ->
+ Self = self(),
+ Fun = fun() -> sc_rc_tcp_client(Self) end,
+ erlang:spawn(Node, Fun).
+
+
+sc_rc_tcp_client(Parent) ->
+ sc_rc_tcp_client_init(Parent),
+ ServerSA = 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),
+ 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_announce_ready(Parent, close),
+ Reason = sc_rc_tcp_client_await_terminate(Parent),
+ ?SEV_IPRINT("terminate"),
+ exit(Reason).
+
+sc_rc_tcp_client_init(Parent) ->
+ put(sname, "rclient"),
+ ?SEV_IPRINT("init"),
+ _MRef = erlang:monitor(process, Parent),
+ ok.
+
+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) ->
+ i("sc_rc_tcp_client_create -> entry"),
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ case socket:getopt(Sock, otp, fd) of
+ {ok, FD} ->
+ put(sname, f("rclient-~w", [FD])); % Update SName
+ _ ->
+ ok
+ end,
+ Sock;
+ {error, Reason} ->
+ exit({open_failed, Reason})
+ end.
+
+sc_rc_tcp_client_bind(Sock, Domain) ->
+ i("sc_rc_tcp_client_bind -> entry"),
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ case socket:bind(Sock, LSA) of
+ {ok, _} ->
+ ok;
+ {error, Reason} ->
+ exit({bind, Reason})
+ end.
+
+sc_rc_tcp_client_announce_ready(Parent, Slogan) ->
+ ?SEV_IPRINT("ready ~w", [Slogan]),
+ ?SEV_ANNOUNCE_READY(Parent, Slogan).
+
+sc_rc_tcp_client_await_continue(Parent, Slogan) ->
+ ?SEV_IPRINT("await ~w continue", [Slogan]),
+ ?SEV_AWAIT_CONTINUE(Parent, parent, Slogan).
+
+sc_rc_tcp_client_connect(Sock, ServerSA) ->
+ i("sc_rc_tcp_client_connect -> entry"),
+ case socket:connect(Sock, ServerSA) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({connect, Reason})
+ end.
+
+sc_rc_tcp_client_close(Sock) ->
+ i("sc_rc_tcp_client_close -> entry"),
+ case socket:close(Sock) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({close, Reason})
+ end.
+
+sc_rc_tcp_client_await_terminate(Parent) ->
+ i("sc_rc_tcp_client_await_terminate -> entry"),
+ case ?SEV_AWAIT_TERMINATE(Parent, parent) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ Reason
+ end.
+
+
+%% The handlers run on the same node as the server (the local node).
+
+sc_rc_tcp_handler_start(ID, Recv, Sock) ->
+ Self = self(),
+ Fun = fun() -> sc_rc_tcp_handler(ID, Self, Recv, Sock) end,
+ {Pid, _} = erlang:spawn_monitor(Fun),
+ Pid.
+
+sc_rc_tcp_handler(ID, Parent, Recv, Sock) ->
+ sc_rc_tcp_handler_init(ID, socket:getopt(Sock, otp, fd), Parent),
+ sc_rc_tcp_handler_await(Parent, recv),
+ RecvRes = sc_rc_tcp_handler_recv(Recv, Sock),
+ sc_rc_tcp_handler_announce_ready(Parent, recv, RecvRes),
+ Reason = sc_rc_tcp_handler_await(Parent, terminate),
+ exit(Reason).
+
+sc_rc_tcp_handler_init(ID, {ok, FD}, Parent) ->
+ put(sname, f("handler-~w:~w", [ID, FD])),
+ _MRef = erlang:monitor(process, Parent),
+ ?SEV_IPRINT("started"),
+ ?SEV_ANNOUNCE_READY(Parent, init),
+ ok.
+
+sc_rc_tcp_handler_await(Parent, terminate) ->
+ ?SEV_IPRINT("await terminate"),
+ ?SEV_AWAIT_TERMINATE(Parent, tester);
+sc_rc_tcp_handler_await(Parent, Slogan) ->
+ ?SEV_IPRINT("await ~w", [Slogan]),
+ ?SEV_AWAIT_CONTINUE(Parent, parent, Slogan).
+
+sc_rc_tcp_handler_recv(Recv, Sock) ->
+ ?SEV_IPRINT("recv"),
+ try Recv(Sock) of
+ {error, closed} ->
+ ok;
+ {ok, _} ->
+ ?SEV_IPRINT("unexpected success"),
+ {error, unexpected_success};
+ {error, Reason} = ERROR ->
+ ?SEV_IPRINT("receive error: "
+ "~n ~p", [Reason]),
+ ERROR
+ catch
+ C:E:S ->
+ ?SEV_IPRINT("receive failure: "
+ "~n Class: ~p"
+ "~n Error: ~p"
+ "~n Stack: ~p", [C, E, S]),
+ {error, {recv, C, E, S}}
+ end.
+
+sc_rc_tcp_handler_announce_ready(Parent, Slogan, Result) ->
+ ?SEV_IPRINT("announce ready"),
+ ?SEV_ANNOUNCE_READY(Parent, Slogan, Result),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 IPv4.
+
+sc_rc_recvmsg_response_tcp4(suite) ->
+ [];
+sc_rc_recvmsg_response_tcp4(doc) ->
+ [];
+sc_rc_recvmsg_response_tcp4(_Config) when is_list(_Config) ->
+ 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)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 IPv6.
+
+sc_rc_recvmsg_response_tcp6(suite) ->
+ [];
+sc_rc_recvmsg_response_tcp6(doc) ->
+ [];
+sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) ->
+ tc_try(sc_rc_recvmsg_response_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?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)
+ 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 IPv4.
+%%
+%% 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.
+%%
+
+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) ->
+ tc_try(sc_rs_recv_send_shutdown_receive_tcp4,
+ fun() ->
+ ?TT(?SECS(30)),
+ MsgData = ?DATA,
+ Recv = fun(Sock) ->
+ socket:recv(Sock)
+ end,
+ Send = fun(Sock, Data) ->
+ socket:send(Sock, Data)
+ end,
+ InitState = #{domain => inet,
+ 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 IPv6.
+
+sc_rs_recv_send_shutdown_receive_tcp6(suite) ->
+ [];
+sc_rs_recv_send_shutdown_receive_tcp6(doc) ->
+ [];
+sc_rs_recv_send_shutdown_receive_tcp6(_Config) when is_list(_Config) ->
+ tc_try(sc_rs_recv_send_shutdown_receive_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(10)),
+ MsgData = ?DATA,
+ Recv = fun(Sock) ->
+ socket:recv(Sock)
+ end,
+ Send = fun(Sock, Data) ->
+ socket:send(Sock, Data)
+ end,
+ InitState = #{domain => inet6,
+ recv => Recv,
+ send => Send,
+ data => MsgData},
+ ok = sc_rs_send_shutdown_receive_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+sc_rs_send_shutdown_receive_tcp(InitState) ->
+ %% The connection is handled by a handler processes.
+ %% This are created (on the fly) and handled internally
+ %% by the server!
+ 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) ->
+ i("get local address for ~p", [Domain]),
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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 => "bind to local address",
+ cmd => fun(#{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(#{tester := Tester, local_sa := LSA, lport := Port}) ->
+ ServerSA = LSA#{port => Port},
+ ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (accept)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Tester, tester, accept)
+ end},
+ #{desc => "accept",
+ cmd => fun(#{lsock := LSock, recv := Recv} = State) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ ?SEV_IPRINT("accepted: try start handler"),
+ Handler =
+ sc_rs_tcp_handler_start(Recv, Sock),
+ ?SEV_IPRINT("handler started"),
+ {ok, State#{csock => Sock,
+ handler => Handler}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await handler ready (init)",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = _State) ->
+ ?SEV_AWAIT_READY(Handler, handler, init,
+ [{tester, Tester}])
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+
+ #{desc => "await continue (first recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "order handler to receive (first)",
+ cmd => fun(#{handler := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await ready from handler (first recv)",
+ cmd => fun(#{tester := Tester, handler := Pid} = _State) ->
+ case ?SEV_AWAIT_READY(Pid, handler, recv,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ ?SEV_IPRINT("first recv: ~p", [Result]),
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (first recv)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv),
+ ok
+ end},
+ #{desc => "await continue (second recv)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
+ end},
+ #{desc => "order handler to receive (second)",
+ cmd => fun(#{handler := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await ready from handler (second recv)",
+ cmd => fun(#{tester := Tester, handler := Pid} = _State) ->
+ case ?SEV_AWAIT_READY(Pid, handler, recv,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ ?SEV_IPRINT("second recv: ~p", [Result]),
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (second recv)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv),
+ 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 => "order handler to terminate",
+ cmd => fun(#{handler := Pid} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Pid),
+ ok
+ end},
+ #{desc => "await handler termination",
+ cmd => fun(#{handler := Pid} = State) ->
+ ?SEV_AWAIT_TERMINATION(Pid),
+ State1 = maps:remove(csock, State),
+ State2 = maps:remove(handler, State1),
+ {ok, State2}
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{lsock := LSock} = State) ->
+ case socket:close(LSock) of
+ ok ->
+ {ok, maps:remove(lsock, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ 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 => "create node",
+ cmd => fun(#{host := Host} = State) ->
+ case start_node(Host, client) of
+ {ok, Node} ->
+ ?SEV_IPRINT("client node ~p started",
+ [Node]),
+ {ok, State#{node => Node}};
+ {error, Reason, _} ->
+ {error, Reason}
+ end
+ end},
+ #{desc => "monitor client node",
+ cmd => fun(#{node := Node} = _State) ->
+ true = erlang:monitor_node(Node, true),
+ ok
+ end},
+ #{desc => "start remote client on client node",
+ cmd => fun(#{node := Node,
+ send := Send} = State) ->
+ Pid = sc_rs_tcp_client_start(Node, Send),
+ ?SEV_IPRINT("client ~p started", [Pid]),
+ {ok, State#{rclient => Pid}}
+ end},
+ #{desc => "monitor remote client",
+ cmd => fun(#{rclient := Pid}) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "order remote client to start",
+ cmd => fun(#{rclient := Client, server_sa := ServerSA}) ->
+ ?SEV_ANNOUNCE_START(Client, ServerSA),
+ ok
+ end},
+ #{desc => "await remote client ready",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, init,
+ [{tester, Tester}])
+ 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,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect,
+ [{rclient, Client}]),
+ ok
+ end},
+ #{desc => "order remote client to continue (connect)",
+ cmd => fun(#{rclient := Client}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, connect),
+ ok
+ end},
+ #{desc => "await client process ready (connect)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, connect,
+ [{tester, Tester}])
+ end},
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect),
+ ok
+ end},
+
+ #{desc => "await continue (send)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, send,
+ [{rclient, Client}]) of
+ {ok, Data} ->
+ {ok, State#{rclient_data => Data}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to send",
+ cmd => fun(#{rclient := Client,
+ rclient_data := Data}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Data),
+ ok
+ end},
+ #{desc => "await remote client ready (closed)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}])
+ end},
+ #{desc => "announce ready (send)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send),
+ ok
+ end},
+
+
+ #{desc => "await continue (shutdown)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, shutdown,
+ [{rclient, Client}]),
+ ok
+ end},
+ #{desc => "order remote client to shutdown",
+ cmd => fun(#{rclient := Client}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, shutdown),
+ ok
+ end},
+ #{desc => "await remote client ready (shiutdown)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, shutdown,
+ [{tester, Tester}])
+ end},
+ #{desc => "announce ready (shutdown)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, shutdown),
+ ok
+ end},
+
+ #{desc => "await continue (close)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, close,
+ [{rclient, Client}]),
+ ok
+ end},
+ #{desc => "order remote client to close",
+ cmd => fun(#{rclient := Client}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, close),
+ ok
+ end},
+ #{desc => "await remote client ready (closed)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, close,
+ [{tester, Tester}])
+ end},
+ #{desc => "announce ready (close)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, close),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester,
+ [{rclient, Client}]) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "kill remote client",
+ cmd => fun(#{rclient := Client}) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await remote client termination",
+ cmd => fun(#{rclient := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(rclient, State),
+ {ok, State1}
+ end},
+ #{desc => "stop client node",
+ cmd => fun(#{node := Node} = _State) ->
+ stop_node(Node)
+ end},
+ #{desc => "await client node termination",
+ cmd => fun(#{node := Node} = State) ->
+ receive
+ {nodedown, Node} ->
+ State1 = maps:remove(node_id, State),
+ State2 = maps:remove(node, State1),
+ {ok, State2}
+ 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 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(s)
+ #{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 (accept)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client continue (connect)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
+ ok
+ end},
+ #{desc => "await client ready (connect)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, connect,
+ [{server, Server}]),
+ ok
+ end},
+ #{desc => "await server ready (accept)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, accept,
+ [{client, Client}]),
+ ok
+ end},
+
+ #{desc => "order client continue (send)",
+ cmd => fun(#{client := Pid,
+ data := Data} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, send, Data),
+ ok
+ end},
+ #{desc => "await client ready (send)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, send,
+ [{server, Server}]),
+ ok
+ end},
+
+ #{desc => "order client continue (shutdown)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, shutdown),
+ ok
+ end},
+ #{desc => "await client ready (shutdown)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, shutdown,
+ [{server, Server}]),
+ ok
+ end},
+
+ #{desc => "order server continue (first recv)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await server ready (first recv)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv,
+ [{client, Client}]),
+ ok
+ end},
+
+ #{desc => "order server continue (second recv)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ #{desc => "await server ready (second recv)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, recv,
+ [{client, Client}]),
+ ok
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "order client continue (close)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, close),
+ ok
+ end},
+ #{desc => "await client ready (close)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, close,
+ [{server, Server}]),
+ ok
+ 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 = #{domain => maps:get(domain, 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),
+ send => maps:get(send, InitState)},
+ Client = ?SEV_START("client", ClientSeq, ClientInitState),
+
+ i("start 'tester' evaluator"),
+ TesterInitState = #{server => Server#ev.pid,
+ client => Client#ev.pid,
+ data => maps:get(data, InitState)},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
+
+
+sc_rs_tcp_client_start(Node, Send) ->
+ Self = self(),
+ Fun = fun() -> sc_rs_tcp_client(Self, Send) end,
+ erlang:spawn(Node, Fun).
+
+
+sc_rs_tcp_client(Parent, Send) ->
+ sc_rs_tcp_client_init(Parent),
+ ServerSA = 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),
+ sc_rs_tcp_client_announce_ready(Parent, init),
+ sc_rs_tcp_client_await_continue(Parent, connect),
+ sc_rs_tcp_client_connect(Sock, ServerSA),
+ sc_rs_tcp_client_announce_ready(Parent, connect),
+ Data = sc_rs_tcp_client_await_continue(Parent, send),
+ sc_rs_tcp_client_send(Sock, Send, Data),
+ sc_rs_tcp_client_announce_ready(Parent, send),
+ sc_rs_tcp_client_await_continue(Parent, shutdown),
+ 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_announce_ready(Parent, close),
+ Reason = sc_rs_tcp_client_await_terminate(Parent),
+ ?SEV_IPRINT("terminate"),
+ exit(Reason).
+
+sc_rs_tcp_client_init(Parent) ->
+ put(sname, "rclient"),
+ ?SEV_IPRINT("init"),
+ _MRef = erlang:monitor(process, Parent),
+ ok.
+
+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) ->
+ i("sc_rs_tcp_client_create -> entry"),
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ Sock;
+ {error, Reason} ->
+ exit({open_failed, Reason})
+ end.
+
+sc_rs_tcp_client_bind(Sock, Domain) ->
+ i("sc_rs_tcp_client_bind -> entry"),
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ case socket:bind(Sock, LSA) of
+ {ok, _} ->
+ ok;
+ {error, Reason} ->
+ exit({bind, Reason})
+ end.
+
+sc_rs_tcp_client_announce_ready(Parent, Slogan) ->
+ ?SEV_ANNOUNCE_READY(Parent, Slogan).
+
+sc_rs_tcp_client_await_continue(Parent, Slogan) ->
+ i("sc_rs_tcp_client_await_continue -> entry"),
+ case ?SEV_AWAIT_CONTINUE(Parent, parent, Slogan) of
+ ok ->
+ ok;
+ {ok, Extra} ->
+ Extra;
+ {error, Reason} ->
+ exit({await_continue, Slogan, Reason})
+ end.
+
+
+sc_rs_tcp_client_connect(Sock, ServerSA) ->
+ i("sc_rs_tcp_client_connect -> entry"),
+ case socket:connect(Sock, ServerSA) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({connect, Reason})
+ end.
+
+sc_rs_tcp_client_send(Sock, Send, Data) ->
+ i("sc_rs_tcp_client_send -> entry"),
+ case Send(Sock, Data) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({send, Reason})
+ end.
+
+sc_rs_tcp_client_shutdown(Sock) ->
+ i("sc_rs_tcp_client_shutdown -> entry"),
+ case socket:shutdown(Sock, write) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({shutdown, Reason})
+ end.
+
+sc_rs_tcp_client_close(Sock) ->
+ i("sc_rs_tcp_client_close -> entry"),
+ case socket:close(Sock) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({close, Reason})
+ end.
+
+sc_rs_tcp_client_await_terminate(Parent) ->
+ i("sc_rs_tcp_client_await_terminate -> entry"),
+ case ?SEV_AWAIT_TERMINATE(Parent, parent) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ Reason
+ end.
+
+
+%% The handlers run on the same node as the server (the local node).
+
+sc_rs_tcp_handler_start(Recv, Sock) ->
+ Self = self(),
+ Fun = fun() -> sc_rs_tcp_handler(Self, Recv, Sock) end,
+ {Pid, _} = erlang:spawn_monitor(Fun),
+ Pid.
+
+sc_rs_tcp_handler(Parent, Recv, Sock) ->
+ sc_rs_tcp_handler_init(Parent),
+ sc_rs_tcp_handler_await(Parent, recv),
+ ok = sc_rs_tcp_handler_recv(Recv, Sock, true),
+ sc_rs_tcp_handler_announce_ready(Parent, recv, received),
+ sc_rs_tcp_handler_await(Parent, recv),
+ ok = sc_rs_tcp_handler_recv(Recv, Sock, false),
+ sc_rs_tcp_handler_announce_ready(Parent, recv, closed),
+ Reason = sc_rs_tcp_handler_await(Parent, terminate),
+ exit(Reason).
+
+sc_rs_tcp_handler_init(Parent) ->
+ put(sname, "handler"),
+ _MRef = erlang:monitor(process, Parent),
+ ?SEV_IPRINT("started"),
+ ?SEV_ANNOUNCE_READY(Parent, init),
+ ok.
+
+sc_rs_tcp_handler_await(Parent, terminate) ->
+ ?SEV_IPRINT("await terminate"),
+ ?SEV_AWAIT_TERMINATE(Parent, tester);
+sc_rs_tcp_handler_await(Parent, Slogan) ->
+ ?SEV_IPRINT("await ~w", [Slogan]),
+ ?SEV_AWAIT_CONTINUE(Parent, parent, Slogan).
+
+%% This hould actually work - we leave it for now
+sc_rs_tcp_handler_recv(Recv, Sock, First) ->
+ ?SEV_IPRINT("recv"),
+ try Recv(Sock) of
+ {ok, _} when (First =:= true) ->
+ ok;
+ {error, closed} when (First =:= false) ->
+ ok;
+ {ok, _} ->
+ ?SEV_IPRINT("unexpected success"),
+ {error, unexpected_success};
+ {error, Reason} = ERROR ->
+ ?SEV_IPRINT("receive error: "
+ "~n ~p", [Reason]),
+ ERROR
+ catch
+ C:E:S ->
+ ?SEV_IPRINT("receive failure: "
+ "~n Class: ~p"
+ "~n Error: ~p"
+ "~n Stack: ~p", [C, E, S]),
+ {error, {recv, C, E, S}}
+ end.
+
+sc_rs_tcp_handler_announce_ready(Parent, Slogan, Result) ->
+ ?SEV_IPRINT("announce ready"),
+ ?SEV_ANNOUNCE_READY(Parent, Slogan, Result),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 IPv4.
+
+sc_rs_recvmsg_send_shutdown_receive_tcp4(suite) ->
+ [];
+sc_rs_recvmsg_send_shutdown_receive_tcp4(doc) ->
+ [];
+sc_rs_recvmsg_send_shutdown_receive_tcp4(_Config) when is_list(_Config) ->
+ tc_try(sc_rs_recvmsg_send_shutdown_receive_tcp4,
+ fun() ->
+ ?TT(?SECS(30)),
+ MsgData = ?DATA,
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock) of
+ {ok, #{addr := undefined,
+ 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 => inet,
+ type => stream,
+ protocol => 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 IPv6.
+
+sc_rs_recvmsg_send_shutdown_receive_tcp6(suite) ->
+ [];
+sc_rs_recvmsg_send_shutdown_receive_tcp6(doc) ->
+ [];
+sc_rs_recvmsg_send_shutdown_receive_tcp6(_Config) when is_list(_Config) ->
+ tc_try(sc_rs_recvmsg_send_shutdown_receive_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(10)),
+ MsgData = ?DATA,
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock) of
+ {ok, #{addr := undefined,
+ 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 => inet6,
+ type => stream,
+ protocol => tcp,
+ recv => Recv,
+ send => Send,
+ data => MsgData},
+ ok = sc_rs_send_shutdown_receive_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 IPv4.
+
+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) ->
+ tc_try(traffic_send_and_recv_chunks_tcp4,
+ fun() ->
+ ?TT(?SECS(30)),
+ InitState = #{domain => inet},
+ 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 IPv6.
+
+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) ->
+ tc_try(traffic_send_and_recv_chunks_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(30)),
+ InitState = #{domain => inet6},
+ ok = traffic_send_and_recv_chunks_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+traffic_send_and_recv_chunks_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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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 => "bind to local address",
+ cmd => fun(#{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(#{tester := Tester, local_sa := LSA, lport := Port}) ->
+ ServerSA = LSA#{port => Port},
+ ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
+ 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 => "announce ready (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+
+ #{desc => "await continue (recv-many-small)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv_many_small)
+ end},
+ #{desc => "recv chunk 1",
+ cmd => fun(#{csock := Sock} = State) ->
+ case socket:recv(Sock, 100) of
+ {ok, Chunk} ->
+ ?SEV_IPRINT("recv of chunk 1 of ~p bytes",
+ [size(Chunk)]),
+ {ok, State#{chunks => [b2l(Chunk)]}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "recv chunk 2",
+ cmd => fun(#{csock := Sock,
+ chunks := Chunks} = State) ->
+ case socket:recv(Sock, 100) of
+ {ok, Chunk} ->
+ ?SEV_IPRINT("recv of chunk 2 of ~p bytes",
+ [size(Chunk)]),
+ {ok, State#{chunks => [b2l(Chunk)|Chunks]}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "recv chunk 3",
+ cmd => fun(#{csock := Sock,
+ chunks := Chunks} = State) ->
+ case socket:recv(Sock, 100) of
+ {ok, Chunk} ->
+ ?SEV_IPRINT("recv of chunk 3 of ~p bytes",
+ [size(Chunk)]),
+ {ok, State#{chunks => [b2l(Chunk)|Chunks]}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "recv chunk 4",
+ cmd => fun(#{csock := Sock,
+ chunks := Chunks} = State) ->
+ case socket:recv(Sock, 100) of
+ {ok, Chunk} ->
+ ?SEV_IPRINT("recv of chunk 4 of ~p bytes",
+ [size(Chunk)]),
+ {ok, State#{chunks => [b2l(Chunk)|Chunks]}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "recv chunk 5",
+ cmd => fun(#{csock := Sock,
+ chunks := Chunks} = State) ->
+ case socket:recv(Sock, 100) of
+ {ok, Chunk} ->
+ ?SEV_IPRINT("recv of chunk 5 of ~p bytes",
+ [size(Chunk)]),
+ {ok, State#{chunks => [b2l(Chunk)|Chunks]}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "recv chunk 6",
+ cmd => fun(#{csock := Sock,
+ chunks := Chunks} = State) ->
+ case socket:recv(Sock, 100) of
+ {ok, Chunk} ->
+ ?SEV_IPRINT("recv of chunk 6 of ~p bytes",
+ [size(Chunk)]),
+ {ok, State#{chunks => [b2l(Chunk)|Chunks]}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "recv chunk 7",
+ cmd => fun(#{csock := Sock,
+ chunks := Chunks} = State) ->
+ case socket:recv(Sock, 100) of
+ {ok, Chunk} ->
+ ?SEV_IPRINT("recv of chunk 7 of ~p bytes",
+ [size(Chunk)]),
+ {ok, State#{chunks => [b2l(Chunk)|Chunks]}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "recv chunk 8",
+ cmd => fun(#{csock := Sock,
+ chunks := Chunks} = State) ->
+ case socket:recv(Sock, 100) of
+ {ok, Chunk} ->
+ ?SEV_IPRINT("recv of chunk 8 of ~p bytes",
+ [size(Chunk)]),
+ {ok, State#{chunks => [b2l(Chunk)|Chunks]}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "recv chunk 9",
+ cmd => fun(#{csock := Sock,
+ chunks := Chunks} = State) ->
+ case socket:recv(Sock, 100) of
+ {ok, Chunk} ->
+ ?SEV_IPRINT("recv of chunk 9 of ~p bytes",
+ [size(Chunk)]),
+ {ok, State#{chunks => [b2l(Chunk)|Chunks]}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "recv chunk 10",
+ cmd => fun(#{csock := Sock,
+ chunks := Chunks} = State) ->
+ case socket:recv(Sock, 100) of
+ {ok, Chunk} ->
+ ?SEV_IPRINT("recv of chunk 10 of ~p bytes",
+ [size(Chunk)]),
+ {ok, State#{chunks => [b2l(Chunk)|Chunks]}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv-many-small)",
+ cmd => fun(#{tester := Tester,
+ chunks := Chunks} = State) ->
+ Data = lists:flatten(lists:reverse(Chunks)),
+ ?SEV_ANNOUNCE_READY(Tester, recv_many_small, Data),
+ {ok, maps:remove(chunks, State)}
+ end},
+
+ #{desc => "await continue (recv-one-big)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester, recv_one_big) of
+ {ok, Size} ->
+ {ok, State#{size => Size}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "recv (one big)",
+ cmd => fun(#{tester := Tester, csock := Sock, size := Size} = _State) ->
+ case socket:recv(Sock, Size) of
+ {ok, Data} ->
+ ?SEV_ANNOUNCE_READY(Tester,
+ recv_one_big,
+ b2l(Data)),
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{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(#{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",
+ 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 => "create node",
+ cmd => fun(#{host := Host} = State) ->
+ case start_node(Host, client) of
+ {ok, Node} ->
+ ?SEV_IPRINT("(remote) client node ~p started",
+ [Node]),
+ {ok, State#{node => Node}};
+ {error, Reason, _} ->
+ {error, Reason}
+ end
+ end},
+ #{desc => "monitor client node",
+ cmd => fun(#{node := Node} = _State) ->
+ true = erlang:monitor_node(Node, true),
+ ok
+ end},
+ #{desc => "start remote client",
+ cmd => fun(#{node := Node} = State) ->
+ Pid = traffic_snr_tcp_client_start(Node),
+ ?SEV_IPRINT("client ~p started", [Pid]),
+ {ok, State#{rclient => Pid}}
+ end},
+ #{desc => "monitor remote client",
+ cmd => fun(#{rclient := Pid}) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "order remote client to start",
+ cmd => fun(#{rclient := Client, server_sa := ServerSA}) ->
+ ?SEV_ANNOUNCE_START(Client, ServerSA),
+ ok
+ end},
+ #{desc => "await remote client ready",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, init,
+ [{tester, Tester}])
+ 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,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect,
+ [{rclient, Client}]),
+ ok
+ end},
+ #{desc => "order remote client to continue (connect)",
+ cmd => fun(#{rclient := Client}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, connect),
+ ok
+ end},
+ #{desc => "await client process ready (connect)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, rclient, connect,
+ [{tester, Tester}])
+ end},
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect),
+ ok
+ end},
+
+ #{desc => "await continue (send-one-big)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester,
+ send_one_big,
+ [{rclient, Client}]) of
+ {ok, Data} ->
+ {ok, State#{data => Data}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send)",
+ cmd => fun(#{rclient := Client, data := Data}) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Data),
+ ok
+ end},
+ #{desc => "await client process ready (send)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (send-one-big)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_one_big),
+ ok
+ end},
+
+ #{desc => "await continue (send-many-small)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = State) ->
+ case ?SEV_AWAIT_CONTINUE(Tester, tester,
+ send_many_small,
+ [{rclient, Client}]) of
+ {ok, Data} ->
+ {ok, State#{data => Data}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send chunk 1)",
+ cmd => fun(#{rclient := Client,
+ data := Data} = State) ->
+ {Chunk, RestData} = lists:split(100, Data),
+ %% ?SEV_IPRINT("order send of chunk 1: "
+ %% "~n Size: ~p"
+ %% "~n ~p", [length(Chunk), Chunk]),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Chunk),
+ {ok, State#{data => RestData}}
+ end},
+ #{desc => "await client process ready (send chunk 1)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send chunk 2)",
+ cmd => fun(#{rclient := Client,
+ data := Data} = State) ->
+ {Chunk, RestData} = lists:split(100, Data),
+ %% ?SEV_IPRINT("order send of chunk 2: "
+ %% "~n Size: ~p"
+ %% "~n ~p", [length(Chunk), Chunk]),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Chunk),
+ {ok, State#{data => RestData}}
+ end},
+ #{desc => "await client process ready (send chunk 2)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send chunk 3)",
+ cmd => fun(#{rclient := Client,
+ data := Data} = State) ->
+ {Chunk, RestData} = lists:split(100, Data),
+ %% ?SEV_IPRINT("order send of chunk 3: "
+ %% "~n Size: ~p"
+ %% "~n ~p", [length(Chunk), Chunk]),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Chunk),
+ {ok, State#{data => RestData}}
+ end},
+ #{desc => "await client process ready (send chunk 3)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send chunk 4)",
+ cmd => fun(#{rclient := Client,
+ data := Data} = State) ->
+ {Chunk, RestData} = lists:split(100, Data),
+ %% ?SEV_IPRINT("order send of chunk 4: "
+ %% "~n Size: ~p"
+ %% "~n ~p", [length(Chunk), Chunk]),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Chunk),
+ {ok, State#{data => RestData}}
+ end},
+ #{desc => "await client process ready (send chunk 4)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send chunk 5)",
+ cmd => fun(#{rclient := Client,
+ data := Data} = State) ->
+ {Chunk, RestData} = lists:split(100, Data),
+ %% ?SEV_IPRINT("order send of chunk 5: "
+ %% "~n Size: ~p"
+ %% "~n ~p", [length(Chunk), Chunk]),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Chunk),
+ {ok, State#{data => RestData}}
+ end},
+ #{desc => "await client process ready (send chunk 5)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send chunk 6)",
+ cmd => fun(#{rclient := Client,
+ data := Data} = State) ->
+ {Chunk, RestData} = lists:split(100, Data),
+ %% ?SEV_IPRINT("order send of chunk 6: "
+ %% "~n Size: ~p"
+ %% "~n ~p", [length(Chunk), Chunk]),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Chunk),
+ {ok, State#{data => RestData}}
+ end},
+ #{desc => "await client process ready (send chunk 6)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send chunk 7)",
+ cmd => fun(#{rclient := Client,
+ data := Data} = State) ->
+ {Chunk, RestData} = lists:split(100, Data),
+ %% ?SEV_IPRINT("order send of chunk 7: "
+ %% "~n Size: ~p"
+ %% "~n ~p", [length(Chunk), Chunk]),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Chunk),
+ {ok, State#{data => RestData}}
+ end},
+ #{desc => "await client process ready (send chunk 7)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send chunk 8)",
+ cmd => fun(#{rclient := Client,
+ data := Data} = State) ->
+ {Chunk, RestData} = lists:split(100, Data),
+ %% ?SEV_IPRINT("order send of chunk 8: "
+ %% "~n Size: ~p"
+ %% "~n ~p", [length(Chunk), Chunk]),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Chunk),
+ {ok, State#{data => RestData}}
+ end},
+ #{desc => "await client process ready (send chunk 8)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send chunk 9)",
+ cmd => fun(#{rclient := Client,
+ data := Data} = State) ->
+ {Chunk, RestData} = lists:split(100, Data),
+ %% ?SEV_IPRINT("order send of chunk 9: "
+ %% "~n Size: ~p"
+ %% "~n ~p", [length(Chunk), Chunk]),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Chunk),
+ {ok, State#{data => RestData}}
+ end},
+ #{desc => "await client process ready (send chunk 9)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send chunk 10)",
+ cmd => fun(#{rclient := Client,
+ data := Data} = State) ->
+ {Chunk, []} = lists:split(100, Data),
+ %% ?SEV_IPRINT("order send of chunk 10: "
+ %% "~n Size: ~p"
+ %% "~n ~p", [length(Chunk), Chunk]),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, Chunk),
+ {ok, maps:remove(data, State)}
+ end},
+ #{desc => "await client process ready (send chunk 10)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order remote client to continue (send stop)",
+ cmd => fun(#{rclient := Client} = State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, send, stop),
+ {ok, maps:remove(data, State)}
+ end},
+ #{desc => "await client process ready (send stop)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Client, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ Result;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (send-many-small)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, send_many_small),
+ ok
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester,
+ rclient := Client} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester,
+ [{rclient, Client}]) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "kill remote client",
+ cmd => fun(#{rclient := Client}) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await remote client termination",
+ cmd => fun(#{rclient := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(rclient, State),
+ {ok, State1}
+ end},
+ #{desc => "stop client node",
+ cmd => fun(#{node := Node} = _State) ->
+ stop_node(Node)
+ end},
+ #{desc => "await client node termination",
+ cmd => fun(#{node := Node} = State) ->
+ receive
+ {nodedown, Node} ->
+ {ok, maps:remove(node, State)}
+ 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 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 (accept)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client continue (connect)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
+ ok
+ end},
+ #{desc => "await server ready (accept)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, accept,
+ [{client, Client}]),
+ ok
+ end},
+ #{desc => "await client ready (connect)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, connect,
+ [{server, Server}])
+ end},
+
+ #{desc => "generate data",
+ cmd => fun(State) ->
+ D1 = lists:seq(1,250),
+ D2 = lists:duplicate(4, D1),
+ D3 = lists:flatten(D2),
+ {ok, State#{data => D3}}
+ end},
+
+ %% (client) Send one big and (server) recv may small
+ #{desc => "order server continue (recv-many-small)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv_many_small),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client continue (send-one-big)",
+ cmd => fun(#{client := Pid, data := Data} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, send_one_big, Data),
+ ok
+ end},
+ #{desc => "await client ready (send-one-big)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ok = ?SEV_AWAIT_READY(Client, client, send_one_big,
+ [{server, Server}])
+ end},
+ #{desc => "await server ready (recv-many-small)",
+ cmd => fun(#{server := Server,
+ client := Client,
+ data := Data} = _State) ->
+ case ?SEV_AWAIT_READY(Server, server, recv_many_small,
+ [{client, Client}]) of
+ {ok, Data} ->
+ ok;
+ {ok, OtherData} ->
+ {error, {mismatched_data, Data, OtherData}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "order server continue (recv-one-big)",
+ cmd => fun(#{server := Pid, data := Data} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv_one_big, length(Data)),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client continue (send-many-small)",
+ cmd => fun(#{client := Pid, data := Data} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, send_many_small, Data),
+ ok
+ end},
+ #{desc => "await client ready (send-many-small)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ok = ?SEV_AWAIT_READY(Client, client, send_many_small,
+ [{server, Server}])
+ end},
+ #{desc => "await server ready (recv-one-big)",
+ cmd => fun(#{server := Server,
+ client := Client,
+ data := Data} = State) ->
+ case ?SEV_AWAIT_READY(Server, server, recv_one_big,
+ [{client, Client}]) of
+ {ok, Data} ->
+ {ok, maps:remove(data, State)};
+ {ok, OtherData} ->
+ {error, {mismatched_data, Data, OtherData}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ 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#{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_snr_tcp_client_start(Node) ->
+ Self = self(),
+ Fun = fun() -> traffic_snr_tcp_client(Self) end,
+ erlang:spawn(Node, Fun).
+
+traffic_snr_tcp_client(Parent) ->
+ {Sock, ServerSA} = 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),
+ exit(Reason).
+
+
+traffic_snr_tcp_client_send_loop(Parent, Sock) ->
+ case ?SEV_AWAIT_CONTINUE(Parent, parent, send) of
+ {ok, stop} -> % Breakes the loop
+ ?SEV_ANNOUNCE_READY(Parent, send, ok),
+ ok;
+ {ok, Data} ->
+ case socket:send(Sock, Data) of
+ ok ->
+ ?SEV_ANNOUNCE_READY(Parent, send, ok),
+ traffic_snr_tcp_client_send_loop(Parent, Sock);
+ {error, Reason} = ERROR ->
+ ?SEV_ANNOUNCE_READY(Parent, send, ERROR),
+ exit({send, Reason})
+ end;
+ {error, Reason} ->
+ exit({await_continue, Reason})
+ end.
+
+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),
+ Domain = maps:get(family, ServerSA),
+ Sock = traffic_snr_tcp_client_create(Domain),
+ traffic_snr_tcp_client_bind(Sock, Domain),
+ {Sock, ServerSA}.
+
+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) ->
+ i("traffic_snr_tcp_client_create -> entry"),
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ Sock;
+ {error, Reason} ->
+ exit({open_failed, Reason})
+ end.
+
+traffic_snr_tcp_client_bind(Sock, Domain) ->
+ i("traffic_snr_tcp_client_bind -> entry"),
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ case socket:bind(Sock, LSA) of
+ {ok, _} ->
+ ok;
+ {error, Reason} ->
+ exit({bind, Reason})
+ end.
+
+traffic_snr_tcp_client_announce_ready(Parent, Slogan) ->
+ ?SEV_ANNOUNCE_READY(Parent, Slogan).
+
+traffic_snr_tcp_client_await_continue(Parent, Slogan) ->
+ i("traffic_snr_tcp_client_await_continue -> entry"),
+ ?SEV_AWAIT_CONTINUE(Parent, parent, Slogan).
+
+traffic_snr_tcp_client_connect(Sock, ServerSA) ->
+ i("traffic_snr_tcp_client_connect -> entry"),
+ case socket:connect(Sock, ServerSA) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({connect, Reason})
+ end.
+
+traffic_snr_tcp_client_close(Sock) ->
+ i("traffic_snr_tcp_client_close -> entry"),
+ case socket:close(Sock) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({close, Reason})
+ end.
+
+traffic_snr_tcp_client_await_terminate(Parent) ->
+ i("traffic_snr_tcp_client_await_terminate -> entry"),
+ case ?SEV_AWAIT_TERMINATE(Parent, parent) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ Reason
+ 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 IPv4.
+
+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) ->
+ 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,
+ 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 IPv6.
+
+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) ->
+ Msg = l2b(?TPP_SMALL),
+ Num = ?TPP_SMALL_NUM,
+ tc_try(traffic_ping_pong_small_send_and_recv_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(15)),
+ InitState = #{domain => inet6,
+ 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 IPv4.
+
+traffic_ping_pong_medium_send_and_recv_tcp4(suite) ->
+ [];
+traffic_ping_pong_medium_send_and_recv_tcp4(doc) ->
+ [];
+traffic_ping_pong_medium_send_and_recv_tcp4(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_MEDIUM),
+ Num = ?TPP_MEDIUM_NUM,
+ tc_try(traffic_ping_pong_medium_send_and_recv_tcp4,
+ fun() ->
+ ?TT(?SECS(30)),
+ InitState = #{domain => inet,
+ 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 IPv6.
+
+traffic_ping_pong_medium_send_and_recv_tcp6(suite) ->
+ [];
+traffic_ping_pong_medium_send_and_recv_tcp6(doc) ->
+ [];
+traffic_ping_pong_medium_send_and_recv_tcp6(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_MEDIUM),
+ Num = ?TPP_MEDIUM_NUM,
+ tc_try(traffic_ping_pong_medium_send_and_recv_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(30)),
+ InitState = #{domain => inet6,
+ 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 'large' message test case, for IPv4.
+
+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) ->
+ 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,
+ 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 'large' message test case, for IPv6.
+
+traffic_ping_pong_large_send_and_recv_tcp6(suite) ->
+ [];
+traffic_ping_pong_large_send_and_recv_tcp6(doc) ->
+ [];
+traffic_ping_pong_large_send_and_recv_tcp6(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_LARGE),
+ Num = ?TPP_LARGE_NUM,
+ tc_try(traffic_ping_pong_large_send_and_recv_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(45)),
+ InitState = #{domain => inet6,
+ msg => Msg,
+ num => Num},
+ ok = traffic_ping_pong_send_and_recv_tcp(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 IPv4.
+
+traffic_ping_pong_small_sendto_and_recvfrom_udp4(suite) ->
+ [];
+traffic_ping_pong_small_sendto_and_recvfrom_udp4(doc) ->
+ [];
+traffic_ping_pong_small_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_SMALL),
+ Num = ?TPP_SMALL_NUM,
+ tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udp4,
+ fun() ->
+ ?TT(?SECS(45)),
+ InitState = #{domain => inet,
+ 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 IPv6.
+
+traffic_ping_pong_small_sendto_and_recvfrom_udp6(suite) ->
+ [];
+traffic_ping_pong_small_sendto_and_recvfrom_udp6(doc) ->
+ [];
+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() ->
+ ?TT(?SECS(45)),
+ InitState = #{domain => inet,
+ 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 IPv4.
+
+traffic_ping_pong_medium_sendto_and_recvfrom_udp4(suite) ->
+ [];
+traffic_ping_pong_medium_sendto_and_recvfrom_udp4(doc) ->
+ [];
+traffic_ping_pong_medium_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_MEDIUM),
+ Num = ?TPP_MEDIUM_NUM,
+ tc_try(traffic_ping_pong_medium_sendto_and_recvfrom_udp4,
+ fun() ->
+ ?TT(?SECS(45)),
+ InitState = #{domain => inet,
+ 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 IPv6.
+
+traffic_ping_pong_medium_sendto_and_recvfrom_udp6(suite) ->
+ [];
+traffic_ping_pong_medium_sendto_and_recvfrom_udp6(doc) ->
+ [];
+traffic_ping_pong_medium_sendto_and_recvfrom_udp6(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_MEDIUM),
+ Num = ?TPP_MEDIUM_NUM,
+ tc_try(traffic_ping_pong_medium_sendto_and_recvfrom_udp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(45)),
+ InitState = #{domain => inet6,
+ msg => Msg,
+ num => Num},
+ ok = traffic_ping_pong_sendto_and_recvfrom_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), 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 IPv4.
+
+traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4(suite) ->
+ [];
+traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4(doc) ->
+ [];
+traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_SMALL),
+ Num = ?TPP_SMALL_NUM,
+ tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4,
+ fun() ->
+ ?TT(?SECS(20)),
+ InitState = #{domain => inet,
+ 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 IPv6.
+
+traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6(suite) ->
+ [];
+traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6(doc) ->
+ [];
+traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_SMALL),
+ Num = ?TPP_SMALL_NUM,
+ tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(20)),
+ InitState = #{domain => inet6,
+ 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 IPv4.
+
+traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4(suite) ->
+ [];
+traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4(doc) ->
+ [];
+traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_MEDIUM),
+ Num = ?TPP_MEDIUM_NUM,
+ tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4,
+ fun() ->
+ ?TT(?SECS(30)),
+ InitState = #{domain => inet,
+ 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 IPv6.
+
+traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6(suite) ->
+ [];
+traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6(doc) ->
+ [];
+traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_MEDIUM),
+ Num = ?TPP_MEDIUM_NUM,
+ tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(20)),
+ InitState = #{domain => ine6,
+ 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 IPv4.
+
+traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4(suite) ->
+ [];
+traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4(doc) ->
+ [];
+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() ->
+ ?TT(?SECS(30)),
+ InitState = #{domain => inet,
+ 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 IPv6.
+
+traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6(suite) ->
+ [];
+traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6(doc) ->
+ [];
+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() ->
+ not_yet_implemented(),
+ ?TT(?SECS(30)),
+ InitState = #{domain => inet6,
+ 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) 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 IPv4.
+
+traffic_ping_pong_small_sendmsg_and_recvmsg_udp4(suite) ->
+ [];
+traffic_ping_pong_small_sendmsg_and_recvmsg_udp4(doc) ->
+ [];
+traffic_ping_pong_small_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_SMALL),
+ Num = ?TPP_SMALL_NUM,
+ tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udp4,
+ fun() ->
+ ?TT(?SECS(20)),
+ InitState = #{domain => inet,
+ 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 IPv6.
+
+traffic_ping_pong_small_sendmsg_and_recvmsg_udp6(suite) ->
+ [];
+traffic_ping_pong_small_sendmsg_and_recvmsg_udp6(doc) ->
+ [];
+traffic_ping_pong_small_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_SMALL),
+ Num = ?TPP_SMALL_NUM,
+ tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(20)),
+ InitState = #{domain => inet,
+ 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 IPv4.
+
+traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4(suite) ->
+ [];
+traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4(doc) ->
+ [];
+traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_MEDIUM),
+ Num = ?TPP_MEDIUM_NUM,
+ tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4,
+ fun() ->
+ ?TT(?SECS(30)),
+ InitState = #{domain => inet,
+ 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 IPv6.
+
+traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6(suite) ->
+ [];
+traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6(doc) ->
+ [];
+traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) ->
+ Msg = l2b(?TPP_MEDIUM),
+ Num = ?TPP_MEDIUM_NUM,
+ tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6,
+ fun() ->
+ not_yet_implemented(),
+ ?TT(?SECS(20)),
+ InitState = #{domain => ine6,
+ msg => Msg,
+ num => Num},
+ ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Ping-Pong for TCP
+
+traffic_ping_pong_send_and_recv_tcp(InitState) ->
+ Send = fun(Sock, Data) -> socket:send(Sock, Data) end,
+ Recv = fun(Sock, Sz) -> socket:recv(Sock, Sz) end,
+ InitState2 = InitState#{send => Send, % Send function
+ recv => Recv % Receive function
+ },
+ 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)
+ end,
+ Recv = fun(Sock, Sz) ->
+ case socket:recvmsg(Sock, Sz, 0) of
+ {ok, #{addr := undefined,
+ iov := [Data]}} ->
+ {ok, Data};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState2 = InitState#{send => Send, % Send function
+ recv => Recv % Receive function
+ },
+ traffic_ping_pong_send_and_receive_tcp(InitState2).
+
+
+traffic_ping_pong_send_and_receive_tcp(#{msg := Msg} = InitState) ->
+ Fun = fun(Sock) ->
+ {ok, RcvSz} = socket:getopt(Sock, socket, rcvbuf),
+ ?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
+ ok ->
+ ok;
+ {error, enobufs} ->
+ skip({failed_change, rcvbuf});
+ {error, Reason1} ->
+ ?FAIL({rcvbuf, Reason1})
+ end;
+ true ->
+ ok
+ end,
+ {ok, SndSz} = socket:getopt(Sock, socket, sndbuf),
+ ?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
+ ok ->
+ ok;
+ {error, enobufs} ->
+ skip({failed_change, sndbuf});
+ {error, Reason2} ->
+ ?FAIL({sndbuf, Reason2})
+ end;
+ true ->
+ ok
+ end,
+ ok = socket:setopt(Sock, otp, rcvbuf, {12, 1024})
+ end,
+ traffic_ping_pong_send_and_receive_tcp2(InitState#{buf_init => Fun}).
+
+traffic_ping_pong_send_and_receive_tcp2(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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {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 => "bind to local address",
+ cmd => fun(#{lsock := LSock, local_sa := LSA} = State) ->
+ case socket:bind(LSock, LSA) of
+ {ok, Port} ->
+ {ok, State#{lport => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "maybe init buffers",
+ cmd => fun(#{lsock := LSock, buf_init := BufInit} = _State) ->
+ BufInit(LSock)
+ end},
+ #{desc => "make listen socket",
+ cmd => fun(#{lsock := LSock}) ->
+ socket:listen(LSock)
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) ->
+ ServerSA = LSA#{port => Port},
+ ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
+ 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 => "create handler",
+ cmd => fun(State) ->
+ Handler = tpp_tcp_handler_create(),
+ ?SEV_IPRINT("handler created: ~p", [Handler]),
+ {ok, State#{handler => Handler}}
+ end},
+ #{desc => "monitor handler",
+ cmd => fun(#{handler := Handler} = _State) ->
+ _MRef = erlang:monitor(process, Handler),
+ ok
+ end},
+ #{desc => "transfer connection socket ownership to handler",
+ cmd => fun(#{handler := Handler, csock := Sock} = _State) ->
+ socket:setopt(Sock, otp, controlling_process, Handler)
+ end},
+ #{desc => "start handler",
+ cmd => fun(#{handler := Handler,
+ csock := Sock,
+ send := Send,
+ recv := Recv} = _State) ->
+ ?SEV_ANNOUNCE_START(Handler, {Sock, Send, Recv}),
+ ok
+ end},
+ #{desc => "await handler ready (init)",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = State) ->
+ case ?SEV_AWAIT_READY(Handler, handler, init,
+ [{tester, Tester}]) of
+ ok ->
+ {ok, maps:remove(csock, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (accept)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, accept),
+ ok
+ end},
+ #{desc => "await continue (recv)",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv,
+ [{handler, Handler}])
+ end},
+ #{desc => "order handler to recv",
+ cmd => fun(#{handler := Handler} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Handler, recv),
+ ok
+ end},
+ #{desc => "await handler ready (recv)",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = State) ->
+ case ?SEV_AWAIT_READY(Handler, handler, recv,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ %% ?SEV_IPRINT("Result: ~p", [Result]),
+ {ok, State#{result => Result}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv)",
+ cmd => fun(#{tester := Tester,
+ result := Result} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv, Result),
+ {ok, maps:remove(result, State)}
+ 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 => "stop handler",
+ cmd => fun(#{handler := Handler}) ->
+ ?SEV_ANNOUNCE_TERMINATE(Handler),
+ ok
+ end},
+ #{desc => "await handler termination",
+ cmd => fun(#{handler := Handler} = State) ->
+ ?SEV_AWAIT_TERMINATION(Handler),
+ State1 = maps:remove(handler, State),
+ {ok, State1}
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{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",
+ 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 => "create node",
+ cmd => fun(#{host := Host} = State) ->
+ case start_node(Host, client) of
+ {ok, Node} ->
+ ?SEV_IPRINT("(remote) client node ~p started",
+ [Node]),
+ {ok, State#{node => Node}};
+ {error, Reason, _} ->
+ {error, Reason}
+ end
+ end},
+ #{desc => "monitor client node",
+ cmd => fun(#{node := Node} = _State) ->
+ true = erlang:monitor_node(Node, true),
+ ok
+ end},
+ #{desc => "create remote client",
+ cmd => fun(#{node := Node} = State) ->
+ Pid = tpp_tcp_client_create(Node),
+ ?SEV_IPRINT("remote client created: ~p", [Pid]),
+ {ok, State#{rclient => Pid}}
+ end},
+ #{desc => "monitor remote client",
+ cmd => fun(#{rclient := Pid}) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "order remote client to start",
+ cmd => fun(#{rclient := RClient,
+ server_sa := ServerSA,
+ buf_init := BufInit,
+ send := Send,
+ recv := Recv}) ->
+ ?SEV_ANNOUNCE_START(RClient,
+ {ServerSA, BufInit, Send, Recv}),
+ ok
+ end},
+ #{desc => "await remote client ready",
+ cmd => fun(#{tester := Tester,
+ rclient := RClient} = _State) ->
+ ?SEV_AWAIT_READY(RClient, rclient, init,
+ [{tester, Tester}])
+ 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,
+ rclient := RClient} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect,
+ [{rclient, RClient}]),
+ ok
+ end},
+ #{desc => "order remote client to continue (connect)",
+ cmd => fun(#{rclient := RClient}) ->
+ ?SEV_ANNOUNCE_CONTINUE(RClient, connect),
+ ok
+ end},
+ #{desc => "await remote client ready (connect)",
+ cmd => fun(#{tester := Tester,
+ rclient := RClient} = _State) ->
+ ?SEV_AWAIT_READY(RClient, rclient, connect,
+ [{tester, Tester}])
+ end},
+ #{desc => "announce ready (connect)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, connect),
+ ok
+ end},
+ #{desc => "await continue (send)",
+ cmd => fun(#{tester := Tester,
+ rclient := RClient} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester,
+ send,
+ [{rclient, RClient}])
+ end},
+ #{desc => "order remote client to continue (send)",
+ cmd => fun(#{rclient := RClient,
+ msg := Msg,
+ num := Num} = State) ->
+ Data = {Msg, Num},
+ ?SEV_ANNOUNCE_CONTINUE(RClient, send, Data),
+ {ok, maps:remove(data, State)}
+ end},
+ #{desc => "await remote client ready (send)",
+ cmd => fun(#{tester := Tester,
+ rclient := RClient} = State) ->
+ case ?SEV_AWAIT_READY(RClient, rclient, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ %% ?SEV_IPRINT("remote client result: "
+ %% "~n ~p", [Result]),
+ {ok, State#{result => Result}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (send)",
+ cmd => fun(#{tester := Tester, result := Result} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, send, Result),
+ {ok, maps:remove(result, State)}
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester,
+ rclient := RClient} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester,
+ [{rclient, RClient}]) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "stop remote client",
+ cmd => fun(#{rclient := RClient}) ->
+ ?SEV_ANNOUNCE_TERMINATE(RClient),
+ ok
+ end},
+ #{desc => "await remote client termination",
+ cmd => fun(#{rclient := RClient} = State) ->
+ ?SEV_AWAIT_TERMINATION(RClient),
+ State1 = maps:remove(rclient, State),
+ {ok, State1}
+ end},
+ #{desc => "stop client node",
+ cmd => fun(#{node := Node} = _State) ->
+ stop_node(Node)
+ end},
+ #{desc => "await client node termination",
+ cmd => fun(#{node := Node} = State) ->
+ receive
+ {nodedown, Node} ->
+ {ok, maps:remove(node, State)}
+ 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 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 (accept)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client continue (connect)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
+ ok
+ end},
+ #{desc => "await server ready (accept)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, accept,
+ [{client, Client}]),
+ ok
+ end},
+ #{desc => "await client ready (connect)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ ?SEV_AWAIT_READY(Client, client, connect,
+ [{server, Server}])
+ end},
+ #{desc => "order server continue (recv)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
+ ok
+ end},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client continue (send)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, send),
+ ok
+ end},
+ #{desc => "await client ready (send)",
+ cmd => fun(#{server := Server,
+ client := Client} = State) ->
+ case ?SEV_AWAIT_READY(Client, client, send,
+ [{server, Server}]) of
+ {ok, {_, _, _, _} = Result} ->
+ ?SEV_IPRINT("client result: "
+ "~n ~p", [Result]),
+ {ok, State#{client_result => Result}};
+ {ok, BadResult} ->
+ ?SEV_EPRINT("client result: "
+ "~n ~p", [BadResult]),
+ {error, {invalid_client_result, BadResult}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await server ready (recv)",
+ cmd => fun(#{server := Server,
+ client := Client,
+ num := Num} = State) ->
+ case ?SEV_AWAIT_READY(Server, server, recv,
+ [{client, Client}]) of
+ {ok, {Num, _, _, _, _} = Result} ->
+ ?SEV_IPRINT("server result: "
+ "~n ~p", [Result]),
+ Result2 = erlang:delete_element(1, Result),
+ {ok, State#{server_result => Result2}};
+ {ok, BadResult} ->
+ ?SEV_EPRINT("bad server result: "
+ "~n ~p", [BadResult]),
+ {error, {invalid_server_result, BadResult}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "present result",
+ cmd => fun(#{server_result := SRes,
+ client_result := CRes,
+ num := Num} = State) ->
+ {SSent, SReceived, SStart, SStop} = SRes,
+ {CSent, CReceived, CStart, CStop} = CRes,
+ STime = tdiff(SStart, SStop),
+ CTime = tdiff(CStart, CStop),
+ %% Note that the sizes we are counting is only
+ %% the "data" part of the messages. There is also
+ %% fixed header for each message, which of cource
+ %% is small for the large messages, but comparatively
+ %% big for the small messages!
+ ?SEV_IPRINT("Results: ~w messages exchanged"
+ "~n Server: ~w msec"
+ "~n ~.2f msec/message (roundtrip)"
+ "~n ~.2f messages/msec (roundtrip)"
+ "~n ~w bytes/msec sent"
+ "~n ~w bytes/msec received"
+ "~n Client: ~w msec"
+ "~n ~.2f msec/message (roundtrip)"
+ "~n ~.2f messages/msec (roundtrip)"
+ "~n ~w bytes/msec sent"
+ "~n ~w bytes/msec received",
+ [Num,
+ STime,
+ STime / Num,
+ Num / STime,
+ SSent div STime,
+ SReceived div STime,
+ CTime,
+ CTime / Num,
+ Num / CTime,
+ CSent div CTime,
+ CReceived div CTime]),
+ State1 = maps:remove(server_result, State),
+ State2 = maps:remove(client_result, State1),
+ {ok, State2}
+ 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 = #{domain => maps:get(domain, InitState),
+ recv => maps:get(recv, InitState),
+ send => maps:get(send, InitState),
+ buf_init => maps:get(buf_init, InitState)},
+ 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,
+ num => maps:get(num, InitState)},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
+
+
+tpp_tcp_handler_create() ->
+ Self = self(),
+ erlang:spawn(fun() -> tpp_tcp_handler(Self) end).
+
+tpp_tcp_handler(Parent) ->
+ tpp_tcp_handler_init(Parent),
+ {Sock, Send, Recv} = tpp_tcp_handler_await_start(Parent),
+ tpp_tcp_handler_announce_ready(Parent, init),
+ tpp_tcp_handler_await_continue(Parent, recv),
+ Result = tpp_tcp_handler_msg_exchange(Sock, Send, Recv),
+ tpp_tcp_handler_announce_ready(Parent, recv, Result),
+ Reason = tpp_tcp_handler_await_terminate(Parent),
+ ?SEV_IPRINT("terminating"),
+ exit(Reason).
+
+tpp_tcp_handler_init(Parent) ->
+ put(sname, "handler"),
+ ?SEV_IPRINT("init"),
+ _MRef = erlang:monitor(process, Parent),
+ ok.
+
+tpp_tcp_handler_await_start(Parent) ->
+ ?SEV_IPRINT("await start"),
+ ?SEV_AWAIT_START(Parent).
+
+tpp_tcp_handler_announce_ready(Parent, Slogan) ->
+ ?SEV_IPRINT("announce ready (~p)", [Slogan]),
+ ?SEV_ANNOUNCE_READY(Parent, Slogan).
+tpp_tcp_handler_announce_ready(Parent, Slogan, Extra) ->
+ ?SEV_IPRINT("announce ready (~p)", [Slogan]),
+ ?SEV_ANNOUNCE_READY(Parent, Slogan, Extra).
+
+tpp_tcp_handler_await_continue(Parent, Slogan) ->
+ ?SEV_IPRINT("await continue (~p)", [Slogan]),
+ case ?SEV_AWAIT_CONTINUE(Parent, parent, Slogan) of
+ ok ->
+ %% ?SEV_IPRINT("continue (~p): ok", [Slogan]),
+ ok;
+ {error, Reason} ->
+ ?SEV_EPRINT("continue (~p): error"
+ "~n ~p", [Slogan, Reason]),
+ exit({continue, Slogan, Reason})
+ end.
+
+tpp_tcp_handler_await_terminate(Parent) ->
+ ?SEV_IPRINT("await terminate"),
+ case ?SEV_AWAIT_TERMINATE(Parent, parent) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ Reason
+ end.
+
+tpp_tcp_handler_msg_exchange(Sock, Send, Recv) ->
+ tpp_tcp_handler_msg_exchange_loop(Sock, Send, Recv, 0, 0, 0, undefined).
+
+tpp_tcp_handler_msg_exchange_loop(Sock, Send, Recv, N, Sent, Received, Start) ->
+ %% ?SEV_IPRINT("[~w] try receive", [N]),
+ case tpp_tcp_recv_req(Sock, Recv) of
+ {ok, Msg, RecvSz} ->
+ NewStart = if (Start =:= undefined) -> ?LIB:timestamp();
+ true -> Start end,
+ %% ?SEV_IPRINT("[~w] received - now try send", [N]),
+ case tpp_tcp_send_rep(Sock, Send, Msg) of
+ {ok, SendSz} ->
+ tpp_tcp_handler_msg_exchange_loop(Sock, Send, Recv,
+ N+1,
+ Sent+SendSz,
+ Received+RecvSz,
+ NewStart);
+ {error, SReason} ->
+ ?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(),
+ {N, Sent, Received, Start, Stop};
+ {error, RReason} ->
+ ?SEV_EPRINT("recv (~w): ~p", [N, RReason]),
+ exit({recv, RReason, N})
+ end.
+
+%% The (remote) client process
+
+tpp_tcp_client_create(Node) ->
+ Self = self(),
+ Fun = fun() -> tpp_tcp_client(Self) end,
+ erlang:spawn(Node, Fun).
+
+tpp_tcp_client(Parent) ->
+ tpp_tcp_client_init(Parent),
+ {ServerSA, 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),
+ tpp_tcp_client_announce_ready(Parent, init),
+ tpp_tcp_client_await_continue(Parent, connect),
+ tpp_tcp_client_sock_connect(Sock, ServerSA),
+ tpp_tcp_client_announce_ready(Parent, connect),
+ {InitMsg, Num} = tpp_tcp_client_await_continue(Parent, send),
+ 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),
+ ?SEV_IPRINT("terminating"),
+ exit(Reason).
+
+tpp_tcp_client_init(Parent) ->
+ put(sname, "rclient"),
+ ?SEV_IPRINT("init"),
+ _MRef = erlang:monitor(process, Parent),
+ ok.
+
+tpp_tcp_client_await_start(Parent) ->
+ ?SEV_IPRINT("await start"),
+ ?SEV_AWAIT_START(Parent).
+
+tpp_tcp_client_announce_ready(Parent, Slogan) ->
+ ?SEV_IPRINT("announce ready (~p)", [Slogan]),
+ ?SEV_ANNOUNCE_READY(Parent, Slogan).
+tpp_tcp_client_announce_ready(Parent, Slogan, Extra) ->
+ ?SEV_IPRINT("announce ready (~p): ~p", [Slogan, Extra]),
+ ?SEV_ANNOUNCE_READY(Parent, Slogan, Extra).
+
+tpp_tcp_client_await_continue(Parent, Slogan) ->
+ ?SEV_IPRINT("await continue (~p)", [Slogan]),
+ case ?SEV_AWAIT_CONTINUE(Parent, parent, Slogan) of
+ ok ->
+ ?SEV_IPRINT("continue (~p): ok", [Slogan]),
+ ok;
+ {ok, Data} ->
+ ?SEV_IPRINT("continue (~p): ok with data", [Slogan]),
+ Data;
+ {error, Reason} ->
+ ?SEV_EPRINT("continue (~p): error"
+ "~n ~p", [Slogan, Reason]),
+ exit({continue, Slogan, Reason})
+ end.
+
+tpp_tcp_client_await_terminate(Parent) ->
+ ?SEV_IPRINT("await terminate"),
+ case ?SEV_AWAIT_TERMINATE(Parent, parent) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ Reason
+ end.
+
+tpp_tcp_client_msg_exchange(Sock, Send, Recv, InitMsg, Num) ->
+ Start = ?LIB:timestamp(),
+ tpp_tcp_client_msg_exchange_loop(Sock, Send, Recv, InitMsg,
+ Num, 0, 0, 0, Start).
+
+tpp_tcp_client_msg_exchange_loop(Sock, _Send, _Recv, _Msg,
+ Num, Num, Sent, Received,
+ Start) ->
+ Stop = ?LIB:timestamp(),
+ case socket:close(Sock) of
+ ok ->
+ {Sent, Received, Start, Stop};
+ {error, Reason} ->
+ exit({failed_closing, Reason})
+ end;
+tpp_tcp_client_msg_exchange_loop(Sock, Send, Recv, Data,
+ Num, N, Sent, Received, Start) ->
+ %% d("tpp_tcp_client_msg_exchange_loop(~w,~w) try send", [Num,N]),
+ case tpp_tcp_send_req(Sock, Send, Data) of
+ {ok, SendSz} ->
+ %% d("tpp_tcp_client_msg_exchange_loop(~w,~w) sent - "
+ %% "now try recv", [Num,N]),
+ case tpp_tcp_recv_rep(Sock, Recv) of
+ {ok, NewData, RecvSz} ->
+ tpp_tcp_client_msg_exchange_loop(Sock, 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.
+
+tpp_tcp_client_sock_open(Domain, BufInit) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ ok = BufInit(Sock),
+ Sock;
+ {error, Reason} ->
+ exit({open_failed, Reason})
+ end.
+
+tpp_tcp_client_sock_bind(Sock, Domain) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ case socket:bind(Sock, LSA) of
+ {ok, _} ->
+ ok;
+ {error, Reason} ->
+ exit({bind, Reason})
+ end.
+
+tpp_tcp_client_sock_connect(Sock, ServerSA) ->
+ case socket:connect(Sock, ServerSA) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({connect, Reason})
+ end.
+
+tpp_tcp_client_sock_close(Sock) ->
+ case socket:close(Sock) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({close, Reason})
+ end.
+
+-define(TPP_REQUEST, 1).
+-define(TPP_REPLY, 2).
+
+tpp_tcp_recv_req(Sock, Recv) ->
+ tpp_tcp_recv(Sock, Recv, ?TPP_REQUEST).
+
+tpp_tcp_recv_rep(Sock, Recv) ->
+ tpp_tcp_recv(Sock, Recv, ?TPP_REPLY).
+
+tpp_tcp_recv(Sock, Recv, Tag) ->
+ case Recv(Sock, 0) of
+ {ok, <<Tag:32/integer, Sz:32/integer, Data/binary>> = Msg}
+ when (Sz =:= size(Data)) ->
+ %% We got it all
+ {ok, Data, size(Msg)};
+ {ok, <<Tag:32/integer, Sz:32/integer, Data/binary>> = Msg} ->
+ Remains = Sz - size(Data),
+ tpp_tcp_recv(Sock, Recv, Tag, Remains, size(Msg), [Data]);
+ {ok, <<Tag:32/integer, _/binary>>} ->
+ {error, {invalid_msg_tag, Tag}};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+tpp_tcp_recv(Sock, Recv, Tag, Remaining, AccSz, Acc) ->
+ case Recv(Sock, Remaining) of
+ {ok, Data} when (Remaining =:= size(Data)) ->
+ %% We got the rest
+ TotSz = AccSz + size(Data),
+ {ok, erlang:iolist_to_binary(lists:reverse([Data | Acc])), TotSz};
+ {ok, Data} when (Remaining > size(Data)) ->
+ tpp_tcp_recv(Sock, Recv, Tag,
+ Remaining - size(Data), AccSz + size(Data),
+ [Data | Acc]);
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+tpp_tcp_send_req(Sock, Send, Data) ->
+ tpp_tcp_send(Sock, Send, ?TPP_REQUEST, Data).
+
+tpp_tcp_send_rep(Sock, Send, Data) ->
+ tpp_tcp_send(Sock, Send, ?TPP_REPLY, Data).
+
+tpp_tcp_send(Sock, Send, Tag, Data) ->
+ DataSz = size(Data),
+ Msg = <<Tag:32/integer, DataSz:32/integer, Data/binary>>,
+ tpp_tcp_send_msg(Sock, Send, Msg, 0).
+
+tpp_tcp_send_msg(Sock, Send, Msg, AccSz) when is_binary(Msg) ->
+ case Send(Sock, Msg) of
+ ok ->
+ {ok, AccSz+size(Msg)};
+ {ok, Rest} -> % This is an IOVec
+ RestBin = list_to_binary(Rest),
+ tpp_tcp_send_msg(Sock, Send, RestBin, AccSz+(size(Msg)-size(RestBin)));
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+%% size_of_data(Data) when is_binary(Data) ->
+%% size(Data);
+%% size_of_data(Data) when is_list(Data) ->
+%% size_of_iovec(Data, 0).
+
+%% size_of_iovec([], Sz) ->
+%% Sz;
+%% size_of_iovec([B|IOVec], Sz) ->
+%% size_of_iovec(IOVec, Sz+size(B)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Ping-Pong for UDP
+
+traffic_ping_pong_sendto_and_recvfrom_udp(InitState) ->
+ Send = fun(Sock, Data, Dest) ->
+ socket:sendto(Sock, Data, Dest)
+ end,
+ Recv = fun(Sock, Sz) ->
+ socket:recvfrom(Sock, Sz)
+ end,
+ InitState2 = InitState#{send => Send, % Send function
+ recv => Recv % Receive function
+ },
+ traffic_ping_pong_send_and_receive_udp(InitState2).
+
+traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) ->
+ Send = fun(Sock, Data, Dest) when is_binary(Data) ->
+ MsgHdr = #{addr => Dest, iov => [Data]},
+ socket:sendmsg(Sock, MsgHdr);
+ (Sock, Data, Dest) when is_list(Data) -> %% We assume iovec...
+ MsgHdr = #{addr => Dest, iov => Data},
+ socket:sendmsg(Sock, MsgHdr)
+ end,
+ Recv = fun(Sock, Sz) ->
+ case socket:recvmsg(Sock, Sz, 0) of
+ {ok, #{addr := Source,
+ iov := [Data]}} ->
+ {ok, {Source, Data}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState2 = InitState#{send => Send, % Send function
+ recv => Recv % Receive function
+ },
+ traffic_ping_pong_send_and_receive_udp(InitState2).
+
+
+traffic_ping_pong_send_and_receive_udp(#{msg := Msg} = InitState) ->
+ Fun = fun(Sock) ->
+ {ok, RcvSz} = socket:getopt(Sock, socket, rcvbuf),
+ if (RcvSz =< (8+size(Msg))) ->
+ i("adjust socket rcvbuf buffer size"),
+ ok = socket:setopt(Sock, socket, rcvbuf, 1024+size(Msg));
+ true ->
+ ok
+ end,
+ {ok, SndSz} = socket:getopt(Sock, socket, sndbuf),
+ if (SndSz =< (8+size(Msg))) ->
+ i("adjust socket sndbuf buffer size"),
+ ok = socket:setopt(Sock, socket, sndbuf, 1024+size(Msg));
+ true ->
+ ok
+ end,
+ {ok, OtpRcvBuf} = socket:getopt(Sock, otp, rcvbuf),
+ if
+ (OtpRcvBuf =< (8+size(Msg))) ->
+ i("adjust otp rcvbuf buffer size"),
+ ok = socket:setopt(Sock, otp, rcvbuf, 1024+size(Msg));
+ true ->
+ ok
+ end
+ end,
+ traffic_ping_pong_send_and_receive_udp2(InitState#{buf_init => Fun}).
+
+traffic_ping_pong_send_and_receive_udp2(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) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{local_sa => LSA}}
+ end},
+ #{desc => "create listen 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 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 => "maybe init buffers",
+ cmd => fun(#{sock := Sock, buf_init := BufInit} = _State) ->
+ BufInit(Sock)
+ end},
+ #{desc => "create handler",
+ cmd => fun(State) ->
+ Handler = tpp_udp_server_handler_create(),
+ ?SEV_IPRINT("handler created: ~p", [Handler]),
+ {ok, State#{handler => Handler}}
+ end},
+ #{desc => "monitor handler",
+ cmd => fun(#{handler := Handler} = _State) ->
+ _MRef = erlang:monitor(process, Handler),
+ ok
+ end},
+ #{desc => "start handler",
+ cmd => fun(#{handler := Handler,
+ sock := Sock,
+ send := Send,
+ recv := Recv} = _State) ->
+ ?SEV_ANNOUNCE_START(Handler, {Sock, Send, Recv}),
+ ok
+ end},
+ #{desc => "await handler ready (init)",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = State) ->
+ case ?SEV_AWAIT_READY(Handler, handler, init,
+ [{tester, Tester}]) of
+ ok ->
+ {ok, maps:remove(csock, State)};
+ {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,
+ handler := Handler} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, recv,
+ [{handler, Handler}])
+ end},
+ #{desc => "order handler to recv",
+ cmd => fun(#{handler := Handler} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Handler, recv),
+ ok
+ end},
+ #{desc => "await continue (close)",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, close,
+ [{handler, Handler}])
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ %% socket:setopt(Sock, otp, debug, true),
+ case socket:close(Sock) of
+ ok ->
+ {ok, maps:remove(sock, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (close)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_ANNOUNCE_READY(Tester, close),
+ ok
+ end},
+ #{desc => "await handler ready (recv)",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = State) ->
+ case ?SEV_AWAIT_READY(Handler, handler, recv,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ %% ?SEV_IPRINT("Result: ~p", [Result]),
+ {ok, State#{result => Result}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (recv)",
+ cmd => fun(#{tester := Tester,
+ result := Result} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, recv, Result),
+ {ok, maps:remove(result, State)}
+ 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 => "stop handler",
+ cmd => fun(#{handler := Handler}) ->
+ ?SEV_ANNOUNCE_TERMINATE(Handler),
+ ok
+ end},
+ #{desc => "await handler termination",
+ cmd => fun(#{handler := Handler} = State) ->
+ ?SEV_AWAIT_TERMINATION(Handler),
+ State1 = maps:remove(handler, State),
+ {ok, State1}
+ 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 => "create node",
+ cmd => fun(#{host := Host} = State) ->
+ case start_node(Host, client) of
+ {ok, Node} ->
+ ?SEV_IPRINT("(remote) client node ~p started",
+ [Node]),
+ {ok, State#{node => Node}};
+ {error, Reason, _} ->
+ {error, Reason}
+ end
+ end},
+ #{desc => "monitor client node",
+ cmd => fun(#{node := Node} = _State) ->
+ true = erlang:monitor_node(Node, true),
+ ok
+ end},
+ #{desc => "create (remote) handler",
+ cmd => fun(#{node := Node} = State) ->
+ Pid = tpp_udp_client_handler_create(Node),
+ ?SEV_IPRINT("handler created: ~p", [Pid]),
+ {ok, State#{handler => Pid}}
+ end},
+ #{desc => "monitor remote handler",
+ cmd => fun(#{handler := Pid}) ->
+ _MRef = erlang:monitor(process, Pid),
+ ok
+ end},
+ #{desc => "order remote handler to start",
+ cmd => fun(#{handler := Handler,
+ server_sa := ServerSA,
+ buf_init := BufInit,
+ send := Send,
+ recv := Recv}) ->
+ ?SEV_ANNOUNCE_START(Handler,
+ {ServerSA, BufInit, Send, Recv}),
+ ok
+ end},
+ #{desc => "await (remote) handler ready",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = _State) ->
+ ?SEV_AWAIT_READY(Handler, handler, init,
+ [{tester, Tester}])
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (send)",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester,
+ send,
+ [{handler, Handler}])
+ end},
+ #{desc => "order handler to continue (send)",
+ cmd => fun(#{handler := Handler,
+ msg := Msg,
+ num := Num} = State) ->
+ Data = {Msg, Num},
+ ?SEV_ANNOUNCE_CONTINUE(Handler, send, Data),
+ {ok, maps:remove(data, State)}
+ end},
+ #{desc => "await remote handler ready (send)",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = State) ->
+ case ?SEV_AWAIT_READY(Handler, handler, send,
+ [{tester, Tester}]) of
+ {ok, Result} ->
+ %% ?SEV_IPRINT("remote client result: "
+ %% "~n ~p", [Result]),
+ {ok, State#{result => Result}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (send)",
+ cmd => fun(#{tester := Tester, result := Result} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, send, Result),
+ {ok, maps:remove(result, State)}
+ end},
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester,
+ handler := Handler} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester,
+ [{handler, Handler}]) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "stop (remote) handler",
+ cmd => fun(#{handler := Handler}) ->
+ ?SEV_ANNOUNCE_TERMINATE(Handler),
+ ok
+ end},
+ #{desc => "await (remote) handler termination",
+ cmd => fun(#{handler := Handler} = State) ->
+ ?SEV_AWAIT_TERMINATION(Handler),
+ State1 = maps:remove(handler, State),
+ {ok, State1}
+ end},
+ #{desc => "stop client node",
+ cmd => fun(#{node := Node} = _State) ->
+ stop_node(Node)
+ end},
+ #{desc => "await client node termination",
+ cmd => fun(#{node := Node} = State) ->
+ receive
+ {nodedown, Node} ->
+ {ok, maps:remove(node, State)}
+ 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 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},
+ ?SEV_SLEEP(?SECS(1)),
+ #{desc => "order client continue (send)",
+ cmd => fun(#{client := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, send),
+ ok
+ end},
+ #{desc => "await client ready (send)",
+ cmd => fun(#{server := Server,
+ client := Client} = State) ->
+ case ?SEV_AWAIT_READY(Client, client, send,
+ [{server, Server}]) of
+ {ok, {_, _, _, _} = Result} ->
+ ?SEV_IPRINT("client result: "
+ "~n ~p", [Result]),
+ {ok, State#{client_result => Result}};
+ {ok, BadResult} ->
+ ?SEV_EPRINT("client result: "
+ "~n ~p", [BadResult]),
+ {error, {invalid_client_result, BadResult}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "order server continue (close)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Pid, close),
+ ok
+ end},
+ #{desc => "await server ready (close)",
+ cmd => fun(#{server := Pid} = _State) ->
+ ok = ?SEV_AWAIT_READY(Pid, server, close)
+ end},
+ %% Because of the way we control the server, there is no real
+ %% point in collecting statistics from it (the time will include
+ %% our communication with it).
+ #{desc => "await server ready (recv)",
+ cmd => fun(#{server := Server,
+ client := Client} = _State) ->
+ case ?SEV_AWAIT_READY(Server, server, recv,
+ [{client, Client}]) of
+ {ok, _Result} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "present result",
+ cmd => fun(#{client_result := CRes,
+ num := Num} = State) ->
+ {CSent, CReceived, CStart, CStop} = CRes,
+ CTime = tdiff(CStart, CStop),
+ %% Note that the sizes we are counting is only
+ %% the "data" part of the messages. There is also
+ %% fixed header for each message, which of cource
+ %% is small for the large messages, but comparatively
+ %% big for the small messages!
+ ?SEV_IPRINT("Results: ~w messages exchanged"
+ "~n Client: ~w msec"
+ "~n ~.2f msec/message (roundtrip)"
+ "~n ~.2f messages/msec (roundtrip)"
+ "~n ~w bytes/msec sent"
+ "~n ~w bytes/msec received",
+ [Num,
+ CTime,
+ CTime / Num,
+ Num / CTime,
+ CSent div CTime,
+ CReceived div CTime]),
+ State1 = maps:remove(client_result, State),
+ {ok, State1}
+ 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 = #{domain => maps:get(domain, InitState),
+ recv => maps:get(recv, InitState),
+ send => maps:get(send, InitState),
+ buf_init => maps:get(buf_init, InitState)},
+ 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,
+ num => maps:get(num, InitState)},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator"),
+ ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
+
+
+
+%% Server side handler process
+%% We don't actually need a separate process for this socket,
+%% but we do it anyway to simplify the sequence.
+tpp_udp_server_handler_create() ->
+ Self = self(),
+ erlang:spawn(fun() -> tpp_udp_server_handler(Self) end).
+
+tpp_udp_server_handler(Parent) ->
+ tpp_udp_server_handler_init(Parent),
+ {Sock, Send, Recv} = tpp_udp_handler_await_start(Parent),
+ tpp_udp_handler_announce_ready(Parent, init),
+ tpp_udp_handler_await_continue(Parent, recv),
+ Result = tpp_udp_server_handler_msg_exchange(Sock, Send, Recv),
+ tpp_udp_handler_announce_ready(Parent, recv, Result),
+ Reason = tpp_udp_handler_await_terminate(Parent),
+ ?SEV_IPRINT("terminating"),
+ exit(Reason).
+
+tpp_udp_server_handler_init(Parent) ->
+ put(sname, "shandler"),
+ ?SEV_IPRINT("init"),
+ _MRef = erlang:monitor(process, Parent),
+ ok.
+
+tpp_udp_server_handler_msg_exchange(Sock, Send, Recv) ->
+ tpp_udp_server_handler_msg_exchange_loop(Sock, Send, Recv,
+ 0, 0, 0, undefined).
+
+tpp_udp_server_handler_msg_exchange_loop(Sock, Send, Recv,
+ N, Sent, Received, Start) ->
+ %% ?SEV_IPRINT("[~w] try receive", [N]),
+ %% if
+ %% (N =:= (?TPP_SMALL_NUM-2)) ->
+ %% ?SEV_IPRINT("[~w] try receive", [N]),
+ %% socket:setopt(Sock, otp, debug, true);
+ %% true -> ok
+ %% end,
+ try tpp_udp_recv_req(Sock, Recv) of
+ {ok, Msg, RecvSz, From} ->
+ NewStart = if (Start =:= undefined) -> ?LIB:timestamp();
+ true -> Start end,
+ %% ?SEV_IPRINT("[~w] received - now try send", [N]),
+ try tpp_udp_send_rep(Sock, Send, Msg, From) of
+ {ok, SendSz} ->
+ tpp_udp_server_handler_msg_exchange_loop(Sock, Send, Recv,
+ N+1,
+ Sent+SendSz,
+ Received+RecvSz,
+ NewStart);
+ {error, SReason} ->
+ ?SEV_EPRINT("send (~w): ~p", [N, SReason]),
+ exit({send, SReason, N})
+ catch
+ SC:SE:SS ->
+ exit({send, {SC, SE, SS}, N})
+ end;
+ {error, closed} ->
+ ?SEV_IPRINT("closed - we are done: ~w, ~w, ~w",
+ [N, Sent, Received]),
+ Stop = ?LIB:timestamp(),
+ {N, Sent, Received, Start, Stop};
+ {error, RReason} ->
+ ?SEV_EPRINT("recv (~w): ~p", [N, RReason]),
+ exit({recv, RReason, N})
+ catch
+ RC:RE:RS ->
+ exit({recv, {RC, RE, RS}, N})
+ end.
+
+
+%% The (remote) client side handler process
+
+tpp_udp_client_handler_create(Node) ->
+ Self = self(),
+ Fun = fun() -> put(sname, "chandler"), tpp_udp_client_handler(Self) end,
+ erlang:spawn(Node, Fun).
+
+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),
+ ?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),
+ ?SEV_IPRINT("announce ready", []),
+ tpp_udp_handler_announce_ready(Parent, init),
+ {InitMsg, Num} = tpp_udp_handler_await_continue(Parent, send),
+ ?SEV_IPRINT("received continue with"
+ "~n Num: ~p", [Num]),
+ Result = tpp_udp_client_handler_msg_exchange(Sock, ServerSA,
+ Send, Recv, InitMsg, Num),
+ ?SEV_IPRINT("ready"),
+ tpp_udp_handler_announce_ready(Parent, send, Result),
+ ?SEV_IPRINT("await terminate"),
+ Reason = tpp_udp_handler_await_terminate(Parent),
+ ?SEV_IPRINT("terminate with ~p", [Reason]),
+ tpp_udp_sock_close(Sock),
+ ?SEV_IPRINT("terminating"),
+ exit(Reason).
+
+tpp_udp_client_handler_init(Parent) ->
+ put(sname, "chandler"),
+ ?SEV_IPRINT("init"),
+ _MRef = erlang:monitor(process, Parent),
+ ok.
+
+tpp_udp_client_handler_msg_exchange(Sock, ServerSA,
+ Send, Recv, InitMsg, Num) ->
+ Start = ?LIB:timestamp(),
+ tpp_udp_client_handler_msg_exchange_loop(Sock, ServerSA,
+ Send, Recv, InitMsg,
+ Num, 0, 0, 0, Start).
+
+tpp_udp_client_handler_msg_exchange_loop(_Sock, _Dest, _Send, _Recv, _Msg,
+ Num, Num, Sent, Received,
+ Start) ->
+ Stop = ?LIB:timestamp(),
+ {Sent, Received, Start, Stop};
+tpp_udp_client_handler_msg_exchange_loop(Sock, Dest, Send, Recv, Data,
+ Num, N, Sent, Received, Start) ->
+ case tpp_udp_send_req(Sock, Send, Data, Dest) of
+ {ok, SendSz} ->
+ case tpp_udp_recv_rep(Sock, Recv) of
+ {ok, NewData, RecvSz, Dest} ->
+ tpp_udp_client_handler_msg_exchange_loop(Sock, Dest,
+ 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.
+
+
+tpp_udp_recv_req(Sock, Recv) ->
+ tpp_udp_recv(Sock, Recv, ?TPP_REQUEST).
+
+tpp_udp_recv_rep(Sock, Recv) ->
+ tpp_udp_recv(Sock, Recv, ?TPP_REPLY).
+
+tpp_udp_recv(Sock, Recv, Tag) ->
+ %% ok = socket:setopt(Sock, otp, debug, true),
+ try Recv(Sock, 0) of
+ {ok, {Source, <<Tag:32/integer, Sz:32/integer, Data/binary>> = Msg}}
+ when (Sz =:= size(Data)) ->
+ %% ok = socket:setopt(Sock, otp, debug, false),
+ %% We got it all
+ %% ?SEV_IPRINT("tpp_udp_recv -> got all: "
+ %% "~n Source: ~p"
+ %% "~n Tag: ~p"
+ %% "~n Sz: ~p"
+ %% "~n size(Data): ~p",
+ %% [Source, Tag, Sz, size(Data)]),
+ {ok, Data, size(Msg), Source};
+ {ok, {_Source, <<Tag:32/integer, Sz:32/integer, Data/binary>>}} ->
+ %% ok = socket:setopt(Sock, otp, debug, false),
+ {error, {invalid_msg, Sz, size(Data)}};
+ {ok, {_, <<Tag:32/integer, _/binary>>}} ->
+ %% ok = socket:setopt(Sock, otp, debug, false),
+ {error, {invalid_msg_tag, Tag}};
+ {error, _} = ERROR ->
+ %% ok = socket:setopt(Sock, otp, debug, false),
+ ERROR
+ catch
+ C:E:S ->
+ {error, {catched, C, E, S}}
+ end.
+
+tpp_udp_send_req(Sock, Send, Data, Dest) ->
+ tpp_udp_send(Sock, Send, ?TPP_REQUEST, Data, Dest).
+
+tpp_udp_send_rep(Sock, Send, Data, Dest) ->
+ tpp_udp_send(Sock, Send, ?TPP_REPLY, Data, Dest).
+
+tpp_udp_send(Sock, Send, Tag, Data, Dest) ->
+ DataSz = size(Data),
+ Msg = <<Tag:32/integer, DataSz:32/integer, Data/binary>>,
+ tpp_udp_send_msg(Sock, Send, Msg, Dest, 0).
+
+tpp_udp_send_msg(Sock, Send, Msg, Dest, AccSz) when is_binary(Msg) ->
+ case Send(Sock, Msg, Dest) of
+ ok ->
+ {ok, AccSz+size(Msg)};
+ {ok, Rest} -> % This is an IOVec
+ RestBin = list_to_binary(Rest),
+ tpp_udp_send_msg(Sock, Send, RestBin, Dest,
+ AccSz+(size(Msg)-size(RestBin)));
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+tpp_udp_handler_await_start(Parent) ->
+ ?SEV_IPRINT("await start"),
+ ?SEV_AWAIT_START(Parent).
+
+tpp_udp_handler_announce_ready(Parent, Slogan) ->
+ ?SEV_IPRINT("announce ready (~p)", [Slogan]),
+ ?SEV_ANNOUNCE_READY(Parent, Slogan).
+tpp_udp_handler_announce_ready(Parent, Slogan, Extra) ->
+ ?SEV_IPRINT("announce ready (~p)", [Slogan]),
+ ?SEV_ANNOUNCE_READY(Parent, Slogan, Extra).
+
+tpp_udp_handler_await_continue(Parent, Slogan) ->
+ ?SEV_IPRINT("await continue (~p)", [Slogan]),
+ case ?SEV_AWAIT_CONTINUE(Parent, parent, Slogan) of
+ ok ->
+ ?SEV_IPRINT("continue (~p): ok", [Slogan]),
+ ok;
+ {ok, Data} ->
+ ?SEV_IPRINT("continue (~p): ok with data", [Slogan]),
+ Data;
+ {error, Reason} ->
+ ?SEV_EPRINT("continue (~p): error"
+ "~n ~p", [Slogan, Reason]),
+ exit({continue, Slogan, Reason})
+ end.
+
+tpp_udp_handler_await_terminate(Parent) ->
+ ?SEV_IPRINT("await terminate"),
+ case ?SEV_AWAIT_TERMINATE(Parent, parent) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ Reason
+ end.
+
+
+tpp_udp_sock_open(Domain, BufInit) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ ok = BufInit(Sock),
+ Sock;
+ {error, Reason} ->
+ exit({open_failed, Reason})
+ end.
+
+tpp_udp_sock_bind(Sock, Domain) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain,
+ addr => LAddr},
+ case socket:bind(Sock, LSA) of
+ {ok, _} ->
+ ok;
+ {error, Reason} ->
+ exit({bind, Reason})
+ end.
+
+tpp_udp_sock_close(Sock) ->
+ case socket:close(Sock) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit({close, Reason})
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgenf_cgenf_small_tcp4(suite) ->
+ [];
+ttest_sgenf_cgenf_small_tcp4(doc) ->
+ [];
+ttest_sgenf_cgenf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgenf_small_tcp4,
+ inet,
+ gen, false,
+ gen, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgenf_cgenf_small_tcp6(suite) ->
+ [];
+ttest_sgenf_cgenf_small_tcp6(doc) ->
+ [];
+ttest_sgenf_cgenf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgenf_small_tcp6,
+ inet6,
+ gen, false,
+ gen, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgenf_cgenf_medium_tcp4(suite) ->
+ [];
+ttest_sgenf_cgenf_medium_tcp4(doc) ->
+ [];
+ttest_sgenf_cgenf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgenf_medium_tcp4,
+ inet,
+ gen, false,
+ gen, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgenf_cgenf_medium_tcp6(suite) ->
+ [];
+ttest_sgenf_cgenf_medium_tcp6(doc) ->
+ [];
+ttest_sgenf_cgenf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgenf_medium_tcp6,
+ inet6,
+ gen, false,
+ gen, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgenf_cgenf_large_tcp4(suite) ->
+ [];
+ttest_sgenf_cgenf_large_tcp4(doc) ->
+ [];
+ttest_sgenf_cgenf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgenf_large_tcp4,
+ inet,
+ gen, false,
+ gen, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgenf_cgenf_large_tcp6(suite) ->
+ [];
+ttest_sgenf_cgenf_large_tcp6(doc) ->
+ [];
+ttest_sgenf_cgenf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgenf_large_tcp6,
+ inet6,
+ gen, false,
+ gen, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgenf_cgeno_small_tcp4(suite) ->
+ [];
+ttest_sgenf_cgeno_small_tcp4(doc) ->
+ [];
+ttest_sgenf_cgeno_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgeno_small_tcp4,
+ inet,
+ gen, false,
+ gen, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgenf_cgeno_small_tcp6(suite) ->
+ [];
+ttest_sgenf_cgeno_small_tcp6(doc) ->
+ [];
+ttest_sgenf_cgeno_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgeno_small_tcp6,
+ inet6,
+ gen, false,
+ gen, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgenf_cgeno_medium_tcp4(suite) ->
+ [];
+ttest_sgenf_cgeno_medium_tcp4(doc) ->
+ [];
+ttest_sgenf_cgeno_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgeno_medium_tcp4,
+ inet,
+ gen, false,
+ gen, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgenf_cgeno_medium_tcp6(suite) ->
+ [];
+ttest_sgenf_cgeno_medium_tcp6(doc) ->
+ [];
+ttest_sgenf_cgeno_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgeno_medium_tcp6,
+ inet6,
+ gen, false,
+ gen, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgenf_cgeno_large_tcp4(suite) ->
+ [];
+ttest_sgenf_cgeno_large_tcp4(doc) ->
+ [];
+ttest_sgenf_cgeno_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgeno_large_tcp4,
+ inet,
+ gen, false,
+ gen, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgenf_cgeno_large_tcp6(suite) ->
+ [];
+ttest_sgenf_cgeno_large_tcp6(doc) ->
+ [];
+ttest_sgenf_cgeno_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgeno_large_tcp6,
+ inet6,
+ gen, false,
+ gen, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgenf_cgent_small_tcp4(suite) ->
+ [];
+ttest_sgenf_cgent_small_tcp4(doc) ->
+ [];
+ttest_sgenf_cgent_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgent_small_tcp4,
+ inet,
+ gen, false,
+ gen, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgenf_cgent_small_tcp6(suite) ->
+ [];
+ttest_sgenf_cgent_small_tcp6(doc) ->
+ [];
+ttest_sgenf_cgent_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgeno_small_tcp6,
+ inet6,
+ gen, false,
+ gen, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgenf_cgent_medium_tcp4(suite) ->
+ [];
+ttest_sgenf_cgent_medium_tcp4(doc) ->
+ [];
+ttest_sgenf_cgent_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgent_medium_tcp4,
+ inet,
+ gen, false,
+ gen, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgenf_cgent_medium_tcp6(suite) ->
+ [];
+ttest_sgenf_cgent_medium_tcp6(doc) ->
+ [];
+ttest_sgenf_cgent_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgent_medium_tcp6,
+ inet6,
+ gen, false,
+ gen, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgenf_cgent_large_tcp4(suite) ->
+ [];
+ttest_sgenf_cgent_large_tcp4(doc) ->
+ [];
+ttest_sgenf_cgent_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgent_large_tcp4,
+ inet,
+ gen, false,
+ gen, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgenf_cgent_large_tcp6(suite) ->
+ [];
+ttest_sgenf_cgent_large_tcp6(doc) ->
+ [];
+ttest_sgenf_cgent_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_cgent_large_tcp6,
+ inet6,
+ gen, false,
+ gen, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgenf_csockf_small_tcp4(suite) ->
+ [];
+ttest_sgenf_csockf_small_tcp4(doc) ->
+ [];
+ttest_sgenf_csockf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockf_small_tcp4,
+ inet,
+ gen, false,
+ sock, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgenf_csockf_small_tcp6(suite) ->
+ [];
+ttest_sgenf_csockf_small_tcp6(doc) ->
+ [];
+ttest_sgenf_csockf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockf_small_tcp6,
+ inet6,
+ gen, false,
+ sock, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgenf_csockf_medium_tcp4(suite) ->
+ [];
+ttest_sgenf_csockf_medium_tcp4(doc) ->
+ [];
+ttest_sgenf_csockf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockf_medium_tcp4,
+ inet,
+ gen, false,
+ sock, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgenf_csockf_medium_tcp6(suite) ->
+ [];
+ttest_sgenf_csockf_medium_tcp6(doc) ->
+ [];
+ttest_sgenf_csockf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockf_medium_tcp6,
+ inet6,
+ gen, false,
+ sock, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgenf_csockf_large_tcp4(suite) ->
+ [];
+ttest_sgenf_csockf_large_tcp4(doc) ->
+ [];
+ttest_sgenf_csockf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockf_large_tcp4,
+ inet,
+ gen, false,
+ sock, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgenf_csockf_large_tcp6(suite) ->
+ [];
+ttest_sgenf_csockf_large_tcp6(doc) ->
+ [];
+ttest_sgenf_csockf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockf_large_tcp6,
+ inet6,
+ gen, false,
+ sock, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgenf_csocko_small_tcp4(suite) ->
+ [];
+ttest_sgenf_csocko_small_tcp4(doc) ->
+ [];
+ttest_sgenf_csocko_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csocko_small_tcp4,
+ inet,
+ gen, false,
+ sock, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgenf_csocko_small_tcp6(suite) ->
+ [];
+ttest_sgenf_csocko_small_tcp6(doc) ->
+ [];
+ttest_sgenf_csocko_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csocko_small_tcp6,
+ inet6,
+ gen, false,
+ sock, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgenf_csocko_medium_tcp4(suite) ->
+ [];
+ttest_sgenf_csocko_medium_tcp4(doc) ->
+ [];
+ttest_sgenf_csocko_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csocko_medium_tcp4,
+ inet,
+ gen, false,
+ sock, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgenf_csocko_medium_tcp6(suite) ->
+ [];
+ttest_sgenf_csocko_medium_tcp6(doc) ->
+ [];
+ttest_sgenf_csocko_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csocko_medium_tcp6,
+ inet6,
+ gen, false,
+ sock, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgenf_csocko_large_tcp4(suite) ->
+ [];
+ttest_sgenf_csocko_large_tcp4(doc) ->
+ [];
+ttest_sgenf_csocko_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csocko_large_tcp4,
+ inet,
+ gen, false,
+ sock, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgenf_csocko_large_tcp6(suite) ->
+ [];
+ttest_sgenf_csocko_large_tcp6(doc) ->
+ [];
+ttest_sgenf_csocko_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csocko_large_tcp6,
+ inet6,
+ gen, false,
+ sock, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgenf_csockt_small_tcp4(suite) ->
+ [];
+ttest_sgenf_csockt_small_tcp4(doc) ->
+ [];
+ttest_sgenf_csockt_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockt_small_tcp4,
+ inet,
+ gen, false,
+ sock, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgenf_csockt_small_tcp6(suite) ->
+ [];
+ttest_sgenf_csockt_small_tcp6(doc) ->
+ [];
+ttest_sgenf_csockt_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csocko_small_tcp6,
+ inet6,
+ gen, false,
+ sock, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgenf_csockt_medium_tcp4(suite) ->
+ [];
+ttest_sgenf_csockt_medium_tcp4(doc) ->
+ [];
+ttest_sgenf_csockt_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockt_medium_tcp4,
+ inet,
+ gen, false,
+ sock, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgenf_csockt_medium_tcp6(suite) ->
+ [];
+ttest_sgenf_csockt_medium_tcp6(doc) ->
+ [];
+ttest_sgenf_csockt_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockt_medium_tcp6,
+ inet6,
+ gen, false,
+ sock, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgenf_csockt_large_tcp4(suite) ->
+ [];
+ttest_sgenf_csockt_large_tcp4(doc) ->
+ [];
+ttest_sgenf_csockt_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockt_large_tcp4,
+ inet,
+ gen, false,
+ sock, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = false
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgenf_csockt_large_tcp6(suite) ->
+ [];
+ttest_sgenf_csockt_large_tcp6(doc) ->
+ [];
+ttest_sgenf_csockt_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgenf_csockt_large_tcp6,
+ inet6,
+ gen, false,
+ sock, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgeno_cgenf_small_tcp4(suite) ->
+ [];
+ttest_sgeno_cgenf_small_tcp4(doc) ->
+ [];
+ttest_sgeno_cgenf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgenf_small_tcp4,
+ inet,
+ gen, once,
+ gen, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgeno_cgenf_small_tcp6(suite) ->
+ [];
+ttest_sgeno_cgenf_small_tcp6(doc) ->
+ [];
+ttest_sgeno_cgenf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgenf_small_tcp6,
+ inet6,
+ gen, once,
+ gen, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgeno_cgenf_medium_tcp4(suite) ->
+ [];
+ttest_sgeno_cgenf_medium_tcp4(doc) ->
+ [];
+ttest_sgeno_cgenf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgenf_medium_tcp4,
+ inet,
+ gen, once,
+ gen, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgeno_cgenf_medium_tcp6(suite) ->
+ [];
+ttest_sgeno_cgenf_medium_tcp6(doc) ->
+ [];
+ttest_sgeno_cgenf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgenf_medium_tcp6,
+ inet6,
+ gen, once,
+ gen, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgeno_cgenf_large_tcp4(suite) ->
+ [];
+ttest_sgeno_cgenf_large_tcp4(doc) ->
+ [];
+ttest_sgeno_cgenf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgenf_large_tcp4,
+ inet,
+ gen, once,
+ gen, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgeno_cgenf_large_tcp6(suite) ->
+ [];
+ttest_sgeno_cgenf_large_tcp6(doc) ->
+ [];
+ttest_sgeno_cgenf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgenf_large_tcp6,
+ inet6,
+ gen, once,
+ gen, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgeno_cgeno_small_tcp4(suite) ->
+ [];
+ttest_sgeno_cgeno_small_tcp4(doc) ->
+ [];
+ttest_sgeno_cgeno_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgeno_small_tcp4,
+ inet,
+ gen, once,
+ gen, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgeno_cgeno_small_tcp6(suite) ->
+ [];
+ttest_sgeno_cgeno_small_tcp6(doc) ->
+ [];
+ttest_sgeno_cgeno_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgeno_small_tcp6,
+ inet6,
+ gen, once,
+ gen, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgeno_cgeno_medium_tcp4(suite) ->
+ [];
+ttest_sgeno_cgeno_medium_tcp4(doc) ->
+ [];
+ttest_sgeno_cgeno_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgeno_medium_tcp4,
+ inet,
+ gen, once,
+ gen, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgeno_cgeno_medium_tcp6(suite) ->
+ [];
+ttest_sgeno_cgeno_medium_tcp6(doc) ->
+ [];
+ttest_sgeno_cgeno_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgeno_medium_tcp6,
+ inet6,
+ gen, once,
+ gen, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgeno_cgeno_large_tcp4(suite) ->
+ [];
+ttest_sgeno_cgeno_large_tcp4(doc) ->
+ [];
+ttest_sgeno_cgeno_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgeno_large_tcp4,
+ inet,
+ gen, once,
+ gen, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgeno_cgeno_large_tcp6(suite) ->
+ [];
+ttest_sgeno_cgeno_large_tcp6(doc) ->
+ [];
+ttest_sgeno_cgeno_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgeno_large_tcp6,
+ inet6,
+ gen, once,
+ gen, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgeno_cgent_small_tcp4(suite) ->
+ [];
+ttest_sgeno_cgent_small_tcp4(doc) ->
+ [];
+ttest_sgeno_cgent_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgent_small_tcp4,
+ inet,
+ gen, once,
+ gen, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgeno_cgent_small_tcp6(suite) ->
+ [];
+ttest_sgeno_cgent_small_tcp6(doc) ->
+ [];
+ttest_sgeno_cgent_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgeno_small_tcp6,
+ inet6,
+ gen, once,
+ gen, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgeno_cgent_medium_tcp4(suite) ->
+ [];
+ttest_sgeno_cgent_medium_tcp4(doc) ->
+ [];
+ttest_sgeno_cgent_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgent_medium_tcp4,
+ inet,
+ gen, once,
+ gen, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgeno_cgent_medium_tcp6(suite) ->
+ [];
+ttest_sgeno_cgent_medium_tcp6(doc) ->
+ [];
+ttest_sgeno_cgent_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgent_medium_tcp6,
+ inet6,
+ gen, once,
+ gen, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgeno_cgent_large_tcp4(suite) ->
+ [];
+ttest_sgeno_cgent_large_tcp4(doc) ->
+ [];
+ttest_sgeno_cgent_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgent_large_tcp4,
+ inet,
+ gen, once,
+ gen, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgeno_cgent_large_tcp6(suite) ->
+ [];
+ttest_sgeno_cgent_large_tcp6(doc) ->
+ [];
+ttest_sgeno_cgent_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_cgent_large_tcp6,
+ inet6,
+ gen, once,
+ gen, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgeno_csockf_small_tcp4(suite) ->
+ [];
+ttest_sgeno_csockf_small_tcp4(doc) ->
+ [];
+ttest_sgeno_csockf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockf_small_tcp4,
+ inet,
+ gen, once,
+ sock, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgeno_csockf_small_tcp6(suite) ->
+ [];
+ttest_sgeno_csockf_small_tcp6(doc) ->
+ [];
+ttest_sgeno_csockf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockf_small_tcp6,
+ inet6,
+ gen, once,
+ sock, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgeno_csockf_medium_tcp4(suite) ->
+ [];
+ttest_sgeno_csockf_medium_tcp4(doc) ->
+ [];
+ttest_sgeno_csockf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockf_medium_tcp4,
+ inet,
+ gen, once,
+ sock, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgeno_csockf_medium_tcp6(suite) ->
+ [];
+ttest_sgeno_csockf_medium_tcp6(doc) ->
+ [];
+ttest_sgeno_csockf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockf_medium_tcp6,
+ inet6,
+ gen, once,
+ sock, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgeno_csockf_large_tcp4(suite) ->
+ [];
+ttest_sgeno_csockf_large_tcp4(doc) ->
+ [];
+ttest_sgeno_csockf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockf_large_tcp4,
+ inet,
+ gen, once,
+ sock, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgeno_csockf_large_tcp6(suite) ->
+ [];
+ttest_sgeno_csockf_large_tcp6(doc) ->
+ [];
+ttest_sgeno_csockf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockf_large_tcp6,
+ inet6,
+ gen, once,
+ sock, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgeno_csocko_small_tcp4(suite) ->
+ [];
+ttest_sgeno_csocko_small_tcp4(doc) ->
+ [];
+ttest_sgeno_csocko_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csocko_small_tcp4,
+ inet,
+ gen, once,
+ sock, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgeno_csocko_small_tcp6(suite) ->
+ [];
+ttest_sgeno_csocko_small_tcp6(doc) ->
+ [];
+ttest_sgeno_csocko_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csocko_small_tcp6,
+ inet6,
+ gen, once,
+ sock, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgeno_csocko_medium_tcp4(suite) ->
+ [];
+ttest_sgeno_csocko_medium_tcp4(doc) ->
+ [];
+ttest_sgeno_csocko_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csocko_medium_tcp4,
+ inet,
+ gen, once,
+ sock, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgeno_csocko_medium_tcp6(suite) ->
+ [];
+ttest_sgeno_csocko_medium_tcp6(doc) ->
+ [];
+ttest_sgeno_csocko_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csocko_medium_tcp6,
+ inet6,
+ gen, once,
+ sock, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgeno_csocko_large_tcp4(suite) ->
+ [];
+ttest_sgeno_csocko_large_tcp4(doc) ->
+ [];
+ttest_sgeno_csocko_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csocko_large_tcp4,
+ inet,
+ gen, once,
+ sock, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgeno_csocko_large_tcp6(suite) ->
+ [];
+ttest_sgeno_csocko_large_tcp6(doc) ->
+ [];
+ttest_sgeno_csocko_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csocko_large_tcp6,
+ inet6,
+ gen, once,
+ sock, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgeno_csockt_small_tcp4(suite) ->
+ [];
+ttest_sgeno_csockt_small_tcp4(doc) ->
+ [];
+ttest_sgeno_csockt_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockt_small_tcp4,
+ inet,
+ gen, once,
+ sock, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgeno_csockt_small_tcp6(suite) ->
+ [];
+ttest_sgeno_csockt_small_tcp6(doc) ->
+ [];
+ttest_sgeno_csockt_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csocko_small_tcp6,
+ inet6,
+ gen, once,
+ sock, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgeno_csockt_medium_tcp4(suite) ->
+ [];
+ttest_sgeno_csockt_medium_tcp4(doc) ->
+ [];
+ttest_sgeno_csockt_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockt_medium_tcp4,
+ inet,
+ gen, once,
+ sock, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgeno_csockt_medium_tcp6(suite) ->
+ [];
+ttest_sgeno_csockt_medium_tcp6(doc) ->
+ [];
+ttest_sgeno_csockt_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockt_medium_tcp6,
+ inet6,
+ gen, once,
+ sock, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgeno_csockt_large_tcp4(suite) ->
+ [];
+ttest_sgeno_csockt_large_tcp4(doc) ->
+ [];
+ttest_sgeno_csockt_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockt_large_tcp4,
+ inet,
+ gen, once,
+ sock, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = once
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgeno_csockt_large_tcp6(suite) ->
+ [];
+ttest_sgeno_csockt_large_tcp6(doc) ->
+ [];
+ttest_sgeno_csockt_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgeno_csockt_large_tcp6,
+ inet6,
+ gen, once,
+ sock, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgent_cgenf_small_tcp4(suite) ->
+ [];
+ttest_sgent_cgenf_small_tcp4(doc) ->
+ [];
+ttest_sgent_cgenf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgenf_small_tcp4,
+ inet,
+ gen, true,
+ gen, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgent_cgenf_small_tcp6(suite) ->
+ [];
+ttest_sgent_cgenf_small_tcp6(doc) ->
+ [];
+ttest_sgent_cgenf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgenf_small_tcp6,
+ inet6,
+ gen, true,
+ gen, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgent_cgenf_medium_tcp4(suite) ->
+ [];
+ttest_sgent_cgenf_medium_tcp4(doc) ->
+ [];
+ttest_sgent_cgenf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgenf_medium_tcp4,
+ inet,
+ gen, true,
+ gen, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgent_cgenf_medium_tcp6(suite) ->
+ [];
+ttest_sgent_cgenf_medium_tcp6(doc) ->
+ [];
+ttest_sgent_cgenf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgenf_medium_tcp6,
+ inet6,
+ gen, true,
+ gen, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgent_cgenf_large_tcp4(suite) ->
+ [];
+ttest_sgent_cgenf_large_tcp4(doc) ->
+ [];
+ttest_sgent_cgenf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgenf_large_tcp4,
+ inet,
+ gen, true,
+ gen, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgent_cgenf_large_tcp6(suite) ->
+ [];
+ttest_sgent_cgenf_large_tcp6(doc) ->
+ [];
+ttest_sgent_cgenf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgenf_large_tcp6,
+ inet6,
+ gen, true,
+ gen, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgent_cgeno_small_tcp4(suite) ->
+ [];
+ttest_sgent_cgeno_small_tcp4(doc) ->
+ [];
+ttest_sgent_cgeno_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgeno_small_tcp4,
+ inet,
+ gen, true,
+ gen, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgent_cgeno_small_tcp6(suite) ->
+ [];
+ttest_sgent_cgeno_small_tcp6(doc) ->
+ [];
+ttest_sgent_cgeno_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgeno_small_tcp6,
+ inet6,
+ gen, true,
+ gen, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgent_cgeno_medium_tcp4(suite) ->
+ [];
+ttest_sgent_cgeno_medium_tcp4(doc) ->
+ [];
+ttest_sgent_cgeno_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgeno_medium_tcp4,
+ inet,
+ gen, true,
+ gen, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgent_cgeno_medium_tcp6(suite) ->
+ [];
+ttest_sgent_cgeno_medium_tcp6(doc) ->
+ [];
+ttest_sgent_cgeno_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgeno_medium_tcp6,
+ inet6,
+ gen, true,
+ gen, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgent_cgeno_large_tcp4(suite) ->
+ [];
+ttest_sgent_cgeno_large_tcp4(doc) ->
+ [];
+ttest_sgent_cgeno_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgeno_large_tcp4,
+ inet,
+ gen, true,
+ gen, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgent_cgeno_large_tcp6(suite) ->
+ [];
+ttest_sgent_cgeno_large_tcp6(doc) ->
+ [];
+ttest_sgent_cgeno_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgeno_large_tcp6,
+ inet6,
+ gen, true,
+ gen, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgent_cgent_small_tcp4(suite) ->
+ [];
+ttest_sgent_cgent_small_tcp4(doc) ->
+ [];
+ttest_sgent_cgent_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgent_small_tcp4,
+ inet,
+ gen, true,
+ gen, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgent_cgent_small_tcp6(suite) ->
+ [];
+ttest_sgent_cgent_small_tcp6(doc) ->
+ [];
+ttest_sgent_cgent_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgeno_small_tcp6,
+ inet6,
+ gen, true,
+ gen, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgent_cgent_medium_tcp4(suite) ->
+ [];
+ttest_sgent_cgent_medium_tcp4(doc) ->
+ ["Server(gen,true), Client(gen,true), Domain=inet, msg=medium"];
+ttest_sgent_cgent_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgent_medium_tcp4,
+ inet,
+ gen, true,
+ gen, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgent_cgent_medium_tcp6(suite) ->
+ [];
+ttest_sgent_cgent_medium_tcp6(doc) ->
+ ["Server(gen,true), Client(gen,true), Domain=inet6, msg=medium"];
+ttest_sgent_cgent_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgent_medium_tcp6,
+ inet6,
+ gen, true,
+ gen, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgent_cgent_large_tcp4(suite) ->
+ [];
+ttest_sgent_cgent_large_tcp4(doc) ->
+ ["Server(gen,true), Client(gen,true), Domain=inet, msg=large"];
+ttest_sgent_cgent_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgent_large_tcp4,
+ inet,
+ gen, true,
+ gen, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgent_cgent_large_tcp6(suite) ->
+ [];
+ttest_sgent_cgent_large_tcp6(doc) ->
+ ["Server(gen,true), Client(gen,true), Domain=inet6, msg=large"];
+ttest_sgent_cgent_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_cgent_large_tcp6,
+ inet6,
+ gen, true,
+ gen, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgent_csockf_small_tcp4(suite) ->
+ [];
+ttest_sgent_csockf_small_tcp4(doc) ->
+ [];
+ttest_sgent_csockf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockf_small_tcp4,
+ inet,
+ gen, true,
+ sock, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgent_csockf_small_tcp6(suite) ->
+ [];
+ttest_sgent_csockf_small_tcp6(doc) ->
+ [];
+ttest_sgent_csockf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockf_small_tcp6,
+ inet6,
+ gen, true,
+ sock, false,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgent_csockf_medium_tcp4(suite) ->
+ [];
+ttest_sgent_csockf_medium_tcp4(doc) ->
+ [];
+ttest_sgent_csockf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockf_medium_tcp4,
+ inet,
+ gen, true,
+ sock, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgent_csockf_medium_tcp6(suite) ->
+ [];
+ttest_sgent_csockf_medium_tcp6(doc) ->
+ [];
+ttest_sgent_csockf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockf_medium_tcp6,
+ inet6,
+ gen, true,
+ sock, false,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgent_csockf_large_tcp4(suite) ->
+ [];
+ttest_sgent_csockf_large_tcp4(doc) ->
+ [];
+ttest_sgent_csockf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockf_large_tcp4,
+ inet,
+ gen, true,
+ sock, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgent_csockf_large_tcp6(suite) ->
+ [];
+ttest_sgent_csockf_large_tcp6(doc) ->
+ [];
+ttest_sgent_csockf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockf_large_tcp6,
+ inet6,
+ gen, true,
+ sock, false,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgent_csocko_small_tcp4(suite) ->
+ [];
+ttest_sgent_csocko_small_tcp4(doc) ->
+ [];
+ttest_sgent_csocko_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csocko_small_tcp4,
+ inet,
+ gen, true,
+ sock, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgent_csocko_small_tcp6(suite) ->
+ [];
+ttest_sgent_csocko_small_tcp6(doc) ->
+ [];
+ttest_sgent_csocko_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csocko_small_tcp6,
+ inet6,
+ gen, true,
+ sock, once,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgent_csocko_medium_tcp4(suite) ->
+ [];
+ttest_sgent_csocko_medium_tcp4(doc) ->
+ [];
+ttest_sgent_csocko_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csocko_medium_tcp4,
+ inet,
+ gen, true,
+ sock, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgent_csocko_medium_tcp6(suite) ->
+ [];
+ttest_sgent_csocko_medium_tcp6(doc) ->
+ [];
+ttest_sgent_csocko_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csocko_medium_tcp6,
+ inet6,
+ gen, true,
+ sock, once,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgent_csocko_large_tcp4(suite) ->
+ [];
+ttest_sgent_csocko_large_tcp4(doc) ->
+ [];
+ttest_sgent_csocko_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csocko_large_tcp4,
+ inet,
+ gen, true,
+ sock, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgent_csocko_large_tcp6(suite) ->
+ [];
+ttest_sgent_csocko_large_tcp6(doc) ->
+ [];
+ttest_sgent_csocko_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csocko_large_tcp6,
+ inet6,
+ gen, true,
+ sock, once,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_sgent_csockt_small_tcp4(suite) ->
+ [];
+ttest_sgent_csockt_small_tcp4(doc) ->
+ [];
+ttest_sgent_csockt_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockt_small_tcp4,
+ inet,
+ gen, true,
+ sock, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_sgent_csockt_small_tcp6(suite) ->
+ [];
+ttest_sgent_csockt_small_tcp6(doc) ->
+ [];
+ttest_sgent_csockt_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csocko_small_tcp6,
+ inet6,
+ gen, true,
+ sock, true,
+ 1, 200).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_sgent_csockt_medium_tcp4(suite) ->
+ [];
+ttest_sgent_csockt_medium_tcp4(doc) ->
+ [];
+ttest_sgent_csockt_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockt_medium_tcp4,
+ inet,
+ gen, true,
+ sock, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_sgent_csockt_medium_tcp6(suite) ->
+ [];
+ttest_sgent_csockt_medium_tcp6(doc) ->
+ [];
+ttest_sgent_csockt_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockt_medium_tcp6,
+ inet6,
+ gen, true,
+ sock, true,
+ 2, 20).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_sgent_csockt_large_tcp4(suite) ->
+ [];
+ttest_sgent_csockt_large_tcp4(doc) ->
+ [];
+ttest_sgent_csockt_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockt_large_tcp4,
+ inet,
+ gen, true,
+ sock, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case uses the time test (ttest) utility to implement a
+%% ping-pong like test case.
+%% Server: Transport = gen_tcp, Active = true
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_sgent_csockt_large_tcp6(suite) ->
+ [];
+ttest_sgent_csockt_large_tcp6(doc) ->
+ [];
+ttest_sgent_csockt_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_sgent_csockt_large_tcp6,
+ inet6,
+ gen, true,
+ 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 = false
+%% Client: Transport = gen_tcp, Active = false
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_ssockf_cgenf_small_tcp4(suite) ->
+ [];
+ttest_ssockf_cgenf_small_tcp4(doc) ->
+ [];
+ttest_ssockf_cgenf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgenf_small_tcp4,
+ inet,
+ sock, false,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockf_cgenf_small_tcp6(suite) ->
+ [];
+ttest_ssockf_cgenf_small_tcp6(doc) ->
+ [];
+ttest_ssockf_cgenf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgenf_small_tcp6,
+ inet6,
+ sock, false,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_ssockf_cgenf_medium_tcp4(suite) ->
+ [];
+ttest_ssockf_cgenf_medium_tcp4(doc) ->
+ [];
+ttest_ssockf_cgenf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgenf_medium_tcp4,
+ inet,
+ sock, false,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockf_cgenf_medium_tcp6(suite) ->
+ [];
+ttest_ssockf_cgenf_medium_tcp6(doc) ->
+ [];
+ttest_ssockf_cgenf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgenf_medium_tcp6,
+ inet6,
+ sock, false,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_ssockf_cgenf_large_tcp4(suite) ->
+ [];
+ttest_ssockf_cgenf_large_tcp4(doc) ->
+ [];
+ttest_ssockf_cgenf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgenf_large_tcp4,
+ inet,
+ sock, false,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockf_cgenf_large_tcp6(suite) ->
+ [];
+ttest_ssockf_cgenf_large_tcp6(doc) ->
+ [];
+ttest_ssockf_cgenf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgenf_large_tcp6,
+ inet6,
+ sock, false,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_ssockf_cgeno_small_tcp4(suite) ->
+ [];
+ttest_ssockf_cgeno_small_tcp4(doc) ->
+ [];
+ttest_ssockf_cgeno_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgeno_small_tcp4,
+ inet,
+ sock, false,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockf_cgeno_small_tcp6(suite) ->
+ [];
+ttest_ssockf_cgeno_small_tcp6(doc) ->
+ [];
+ttest_ssockf_cgeno_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgeno_small_tcp6,
+ inet6,
+ sock, false,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_ssockf_cgeno_medium_tcp4(suite) ->
+ [];
+ttest_ssockf_cgeno_medium_tcp4(doc) ->
+ [];
+ttest_ssockf_cgeno_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgeno_medium_tcp4,
+ inet,
+ sock, false,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockf_cgeno_medium_tcp6(suite) ->
+ [];
+ttest_ssockf_cgeno_medium_tcp6(doc) ->
+ [];
+ttest_ssockf_cgeno_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgeno_medium_tcp6,
+ inet6,
+ sock, false,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_ssockf_cgeno_large_tcp4(suite) ->
+ [];
+ttest_ssockf_cgeno_large_tcp4(doc) ->
+ [];
+ttest_ssockf_cgeno_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgeno_large_tcp4,
+ inet,
+ sock, false,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockf_cgeno_large_tcp6(suite) ->
+ [];
+ttest_ssockf_cgeno_large_tcp6(doc) ->
+ [];
+ttest_ssockf_cgeno_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgeno_large_tcp6,
+ inet6,
+ sock, false,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_ssockf_cgent_small_tcp4(suite) ->
+ [];
+ttest_ssockf_cgent_small_tcp4(doc) ->
+ [];
+ttest_ssockf_cgent_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgent_small_tcp4,
+ inet,
+ sock, false,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockf_cgent_small_tcp6(suite) ->
+ [];
+ttest_ssockf_cgent_small_tcp6(doc) ->
+ [];
+ttest_ssockf_cgent_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgeno_small_tcp6,
+ inet6,
+ sock, false,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_ssockf_cgent_medium_tcp4(suite) ->
+ [];
+ttest_ssockf_cgent_medium_tcp4(doc) ->
+ [];
+ttest_ssockf_cgent_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgent_medium_tcp4,
+ inet,
+ sock, false,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockf_cgent_medium_tcp6(suite) ->
+ [];
+ttest_ssockf_cgent_medium_tcp6(doc) ->
+ [];
+ttest_ssockf_cgent_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgent_medium_tcp6,
+ inet6,
+ sock, false,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_ssockf_cgent_large_tcp4(suite) ->
+ [];
+ttest_ssockf_cgent_large_tcp4(doc) ->
+ [];
+ttest_ssockf_cgent_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgent_large_tcp4,
+ inet,
+ sock, false,
+ gen, 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 = false
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockf_cgent_large_tcp6(suite) ->
+ [];
+ttest_ssockf_cgent_large_tcp6(doc) ->
+ [];
+ttest_ssockf_cgent_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_cgent_large_tcp6,
+ inet6,
+ sock, false,
+ gen, 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 = false
+%% Client: Transport = socket(tcp), Active = false
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_ssockf_csockf_small_tcp4(suite) ->
+ [];
+ttest_ssockf_csockf_small_tcp4(doc) ->
+ [];
+ttest_ssockf_csockf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockf_small_tcp4,
+ inet,
+ 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: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockf_csockf_small_tcp6(suite) ->
+ [];
+ttest_ssockf_csockf_small_tcp6(doc) ->
+ [];
+ttest_ssockf_csockf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockf_small_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockf_csockf_medium_tcp4(suite) ->
+ [];
+ttest_ssockf_csockf_medium_tcp4(doc) ->
+ [];
+ttest_ssockf_csockf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockf_medium_tcp4,
+ inet,
+ 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: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockf_csockf_medium_tcp6(suite) ->
+ [];
+ttest_ssockf_csockf_medium_tcp6(doc) ->
+ [];
+ttest_ssockf_csockf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockf_medium_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockf_csockf_large_tcp4(suite) ->
+ [];
+ttest_ssockf_csockf_large_tcp4(doc) ->
+ [];
+ttest_ssockf_csockf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockf_large_tcp4,
+ inet,
+ 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 = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockf_csockf_large_tcp6(suite) ->
+ [];
+ttest_ssockf_csockf_large_tcp6(doc) ->
+ [];
+ttest_ssockf_csockf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockf_large_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockf_csocko_small_tcp4(suite) ->
+ [];
+ttest_ssockf_csocko_small_tcp4(doc) ->
+ [];
+ttest_ssockf_csocko_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csocko_small_tcp4,
+ inet,
+ 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: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockf_csocko_small_tcp6(suite) ->
+ [];
+ttest_ssockf_csocko_small_tcp6(doc) ->
+ [];
+ttest_ssockf_csocko_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csocko_small_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockf_csocko_medium_tcp4(suite) ->
+ [];
+ttest_ssockf_csocko_medium_tcp4(doc) ->
+ [];
+ttest_ssockf_csocko_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csocko_medium_tcp4,
+ inet,
+ 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: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockf_csocko_medium_tcp6(suite) ->
+ [];
+ttest_ssockf_csocko_medium_tcp6(doc) ->
+ [];
+ttest_ssockf_csocko_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csocko_medium_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockf_csocko_large_tcp4(suite) ->
+ [];
+ttest_ssockf_csocko_large_tcp4(doc) ->
+ [];
+ttest_ssockf_csocko_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csocko_large_tcp4,
+ inet,
+ 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 = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockf_csocko_large_tcp6(suite) ->
+ [];
+ttest_ssockf_csocko_large_tcp6(doc) ->
+ [];
+ttest_ssockf_csocko_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csocko_large_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockf_csockt_small_tcp4(suite) ->
+ [];
+ttest_ssockf_csockt_small_tcp4(doc) ->
+ [];
+ttest_ssockf_csockt_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockt_small_tcp4,
+ inet,
+ 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: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockf_csockt_small_tcp6(suite) ->
+ [];
+ttest_ssockf_csockt_small_tcp6(doc) ->
+ [];
+ttest_ssockf_csockt_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csocko_small_tcp6,
+ inet6,
+ 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)
+%% Domain: inet
+%%
+
+ttest_ssockf_csockt_medium_tcp4(suite) ->
+ [];
+ttest_ssockf_csockt_medium_tcp4(doc) ->
+ [];
+ttest_ssockf_csockt_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockt_medium_tcp4,
+ inet,
+ 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: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockf_csockt_medium_tcp6(suite) ->
+ [];
+ttest_ssockf_csockt_medium_tcp6(doc) ->
+ [];
+ttest_ssockf_csockt_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockt_medium_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockf_csockt_large_tcp4(suite) ->
+ [];
+ttest_ssockf_csockt_large_tcp4(doc) ->
+ [];
+ttest_ssockf_csockt_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockt_large_tcp4,
+ inet,
+ 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 = false
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockf_csockt_large_tcp6(suite) ->
+ [];
+ttest_ssockf_csockt_large_tcp6(doc) ->
+ [];
+ttest_ssockf_csockt_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockf_csockt_large_tcp6,
+ inet6,
+ 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)
+%% Domain: inet
+%%
+
+ttest_ssocko_cgenf_small_tcp4(suite) ->
+ [];
+ttest_ssocko_cgenf_small_tcp4(doc) ->
+ [];
+ttest_ssocko_cgenf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgenf_small_tcp4,
+ inet,
+ sock, once,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssocko_cgenf_small_tcp6(suite) ->
+ [];
+ttest_ssocko_cgenf_small_tcp6(doc) ->
+ [];
+ttest_ssocko_cgenf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgenf_small_tcp6,
+ inet6,
+ sock, once,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_ssocko_cgenf_medium_tcp4(suite) ->
+ [];
+ttest_ssocko_cgenf_medium_tcp4(doc) ->
+ [];
+ttest_ssocko_cgenf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgenf_medium_tcp4,
+ inet,
+ sock, once,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssocko_cgenf_medium_tcp6(suite) ->
+ [];
+ttest_ssocko_cgenf_medium_tcp6(doc) ->
+ [];
+ttest_ssocko_cgenf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgenf_medium_tcp6,
+ inet6,
+ sock, once,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_ssocko_cgenf_large_tcp4(suite) ->
+ [];
+ttest_ssocko_cgenf_large_tcp4(doc) ->
+ [];
+ttest_ssocko_cgenf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgenf_large_tcp4,
+ inet,
+ sock, once,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssocko_cgenf_large_tcp6(suite) ->
+ [];
+ttest_ssocko_cgenf_large_tcp6(doc) ->
+ [];
+ttest_ssocko_cgenf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgenf_large_tcp6,
+ inet6,
+ sock, once,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_ssocko_cgeno_small_tcp4(suite) ->
+ [];
+ttest_ssocko_cgeno_small_tcp4(doc) ->
+ [];
+ttest_ssocko_cgeno_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgeno_small_tcp4,
+ inet,
+ sock, once,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssocko_cgeno_small_tcp6(suite) ->
+ [];
+ttest_ssocko_cgeno_small_tcp6(doc) ->
+ [];
+ttest_ssocko_cgeno_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgeno_small_tcp6,
+ inet6,
+ sock, once,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_ssocko_cgeno_medium_tcp4(suite) ->
+ [];
+ttest_ssocko_cgeno_medium_tcp4(doc) ->
+ [];
+ttest_ssocko_cgeno_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgeno_medium_tcp4,
+ inet,
+ sock, once,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssocko_cgeno_medium_tcp6(suite) ->
+ [];
+ttest_ssocko_cgeno_medium_tcp6(doc) ->
+ [];
+ttest_ssocko_cgeno_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgeno_medium_tcp6,
+ inet6,
+ sock, once,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_ssocko_cgeno_large_tcp4(suite) ->
+ [];
+ttest_ssocko_cgeno_large_tcp4(doc) ->
+ [];
+ttest_ssocko_cgeno_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgeno_large_tcp4,
+ inet,
+ sock, once,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssocko_cgeno_large_tcp6(suite) ->
+ [];
+ttest_ssocko_cgeno_large_tcp6(doc) ->
+ [];
+ttest_ssocko_cgeno_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgeno_large_tcp6,
+ inet6,
+ sock, once,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_ssocko_cgent_small_tcp4(suite) ->
+ [];
+ttest_ssocko_cgent_small_tcp4(doc) ->
+ [];
+ttest_ssocko_cgent_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgent_small_tcp4,
+ inet,
+ sock, once,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssocko_cgent_small_tcp6(suite) ->
+ [];
+ttest_ssocko_cgent_small_tcp6(doc) ->
+ [];
+ttest_ssocko_cgent_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgent_small_tcp6,
+ inet6,
+ sock, once,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_ssocko_cgent_medium_tcp4(suite) ->
+ [];
+ttest_ssocko_cgent_medium_tcp4(doc) ->
+ [];
+ttest_ssocko_cgent_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgent_medium_tcp4,
+ inet,
+ sock, once,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssocko_cgent_medium_tcp6(suite) ->
+ [];
+ttest_ssocko_cgent_medium_tcp6(doc) ->
+ [];
+ttest_ssocko_cgent_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgent_medium_tcp6,
+ inet6,
+ sock, once,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_ssocko_cgent_large_tcp4(suite) ->
+ [];
+ttest_ssocko_cgent_large_tcp4(doc) ->
+ [];
+ttest_ssocko_cgent_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgent_large_tcp4,
+ inet,
+ sock, once,
+ gen, 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 = false
+%% Client: Transport = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssocko_cgent_large_tcp6(suite) ->
+ [];
+ttest_ssocko_cgent_large_tcp6(doc) ->
+ [];
+ttest_ssocko_cgent_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_cgent_large_tcp6,
+ inet6,
+ sock, once,
+ gen, 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 = socket(tcp), Active = false
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_ssocko_csockf_small_tcp4(suite) ->
+ [];
+ttest_ssocko_csockf_small_tcp4(doc) ->
+ [];
+ttest_ssocko_csockf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockf_small_tcp4,
+ inet,
+ 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: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssocko_csockf_small_tcp6(suite) ->
+ [];
+ttest_ssocko_csockf_small_tcp6(doc) ->
+ [];
+ttest_ssocko_csockf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockf_small_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssocko_csockf_medium_tcp4(suite) ->
+ [];
+ttest_ssocko_csockf_medium_tcp4(doc) ->
+ [];
+ttest_ssocko_csockf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockf_medium_tcp4,
+ inet,
+ 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: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssocko_csockf_medium_tcp6(suite) ->
+ [];
+ttest_ssocko_csockf_medium_tcp6(doc) ->
+ [];
+ttest_ssocko_csockf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockf_medium_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssocko_csockf_large_tcp4(suite) ->
+ [];
+ttest_ssocko_csockf_large_tcp4(doc) ->
+ [];
+ttest_ssocko_csockf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockf_large_tcp4,
+ inet,
+ 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 = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssocko_csockf_large_tcp6(suite) ->
+ [];
+ttest_ssocko_csockf_large_tcp6(doc) ->
+ [];
+ttest_ssocko_csockf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockf_large_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssocko_csocko_small_tcp4(suite) ->
+ [];
+ttest_ssocko_csocko_small_tcp4(doc) ->
+ [];
+ttest_ssocko_csocko_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csocko_small_tcp4,
+ inet,
+ 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: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssocko_csocko_small_tcp6(suite) ->
+ [];
+ttest_ssocko_csocko_small_tcp6(doc) ->
+ [];
+ttest_ssocko_csocko_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csocko_small_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssocko_csocko_medium_tcp4(suite) ->
+ [];
+ttest_ssocko_csocko_medium_tcp4(doc) ->
+ [];
+ttest_ssocko_csocko_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csocko_medium_tcp4,
+ inet,
+ 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: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssocko_csocko_medium_tcp6(suite) ->
+ [];
+ttest_ssocko_csocko_medium_tcp6(doc) ->
+ [];
+ttest_ssocko_csocko_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csocko_medium_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssocko_csocko_large_tcp4(suite) ->
+ [];
+ttest_ssocko_csocko_large_tcp4(doc) ->
+ [];
+ttest_ssocko_csocko_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csocko_large_tcp4,
+ inet,
+ 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 = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssocko_csocko_large_tcp6(suite) ->
+ [];
+ttest_ssocko_csocko_large_tcp6(doc) ->
+ [];
+ttest_ssocko_csocko_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csocko_large_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssocko_csockt_small_tcp4(suite) ->
+ [];
+ttest_ssocko_csockt_small_tcp4(doc) ->
+ [];
+ttest_ssocko_csockt_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockt_small_tcp4,
+ inet,
+ 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: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssocko_csockt_small_tcp6(suite) ->
+ [];
+ttest_ssocko_csockt_small_tcp6(doc) ->
+ [];
+ttest_ssocko_csockt_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csocko_small_tcp6,
+ inet6,
+ 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)
+%% Domain: inet
+%%
+
+ttest_ssocko_csockt_medium_tcp4(suite) ->
+ [];
+ttest_ssocko_csockt_medium_tcp4(doc) ->
+ [];
+ttest_ssocko_csockt_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockt_medium_tcp4,
+ inet,
+ 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: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssocko_csockt_medium_tcp6(suite) ->
+ [];
+ttest_ssocko_csockt_medium_tcp6(doc) ->
+ [];
+ttest_ssocko_csockt_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockt_medium_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssocko_csockt_large_tcp4(suite) ->
+ [];
+ttest_ssocko_csockt_large_tcp4(doc) ->
+ [];
+ttest_ssocko_csockt_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockt_large_tcp4,
+ inet,
+ 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 = once
+%% Client: Transport = socket(tcp), Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssocko_csockt_large_tcp6(suite) ->
+ [];
+ttest_ssocko_csockt_large_tcp6(doc) ->
+ [];
+ttest_ssocko_csockt_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssocko_csockt_large_tcp6,
+ inet6,
+ 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)
+%% Domain: inet
+%%
+
+ttest_ssockt_cgenf_small_tcp4(suite) ->
+ [];
+ttest_ssockt_cgenf_small_tcp4(doc) ->
+ [];
+ttest_ssockt_cgenf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgenf_small_tcp4,
+ inet,
+ sock, true,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockt_cgenf_small_tcp6(suite) ->
+ [];
+ttest_ssockt_cgenf_small_tcp6(doc) ->
+ [];
+ttest_ssockt_cgenf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgenf_small_tcp6,
+ inet6,
+ sock, true,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_ssockt_cgenf_medium_tcp4(suite) ->
+ [];
+ttest_ssockt_cgenf_medium_tcp4(doc) ->
+ [];
+ttest_ssockt_cgenf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgenf_medium_tcp4,
+ inet,
+ sock, true,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockt_cgenf_medium_tcp6(suite) ->
+ [];
+ttest_ssockt_cgenf_medium_tcp6(doc) ->
+ [];
+ttest_ssockt_cgenf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgenf_medium_tcp6,
+ inet6,
+ sock, true,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_ssockt_cgenf_large_tcp4(suite) ->
+ [];
+ttest_ssockt_cgenf_large_tcp4(doc) ->
+ [];
+ttest_ssockt_cgenf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgenf_large_tcp4,
+ inet,
+ sock, true,
+ gen, 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 = gen_tcp, Active = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockt_cgenf_large_tcp6(suite) ->
+ [];
+ttest_ssockt_cgenf_large_tcp6(doc) ->
+ [];
+ttest_ssockt_cgenf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgenf_large_tcp6,
+ inet6,
+ sock, true,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_ssockt_cgeno_small_tcp4(suite) ->
+ [];
+ttest_ssockt_cgeno_small_tcp4(doc) ->
+ [];
+ttest_ssockt_cgeno_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgeno_small_tcp4,
+ inet,
+ sock, true,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockt_cgeno_small_tcp6(suite) ->
+ [];
+ttest_ssockt_cgeno_small_tcp6(doc) ->
+ [];
+ttest_ssockt_cgeno_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgeno_small_tcp6,
+ inet6,
+ sock, true,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_ssockt_cgeno_medium_tcp4(suite) ->
+ [];
+ttest_ssockt_cgeno_medium_tcp4(doc) ->
+ [];
+ttest_ssockt_cgeno_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgeno_medium_tcp4,
+ inet,
+ sock, true,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockt_cgeno_medium_tcp6(suite) ->
+ [];
+ttest_ssockt_cgeno_medium_tcp6(doc) ->
+ [];
+ttest_ssockt_cgeno_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgeno_medium_tcp6,
+ inet6,
+ sock, true,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_ssockt_cgeno_large_tcp4(suite) ->
+ [];
+ttest_ssockt_cgeno_large_tcp4(doc) ->
+ [];
+ttest_ssockt_cgeno_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgeno_large_tcp4,
+ inet,
+ sock, true,
+ gen, 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 = gen_tcp, Active = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockt_cgeno_large_tcp6(suite) ->
+ [];
+ttest_ssockt_cgeno_large_tcp6(doc) ->
+ [];
+ttest_ssockt_cgeno_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgeno_large_tcp6,
+ inet6,
+ sock, true,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_ssockt_cgent_small_tcp4(suite) ->
+ [];
+ttest_ssockt_cgent_small_tcp4(doc) ->
+ [];
+ttest_ssockt_cgent_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgent_small_tcp4,
+ inet,
+ sock, true,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockt_cgent_small_tcp6(suite) ->
+ [];
+ttest_ssockt_cgent_small_tcp6(doc) ->
+ [];
+ttest_ssockt_cgent_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgent_small_tcp6,
+ inet6,
+ sock, true,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet
+%%
+
+ttest_ssockt_cgent_medium_tcp4(suite) ->
+ [];
+ttest_ssockt_cgent_medium_tcp4(doc) ->
+ [];
+ttest_ssockt_cgent_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgent_medium_tcp4,
+ inet,
+ sock, true,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockt_cgent_medium_tcp6(suite) ->
+ [];
+ttest_ssockt_cgent_medium_tcp6(doc) ->
+ [];
+ttest_ssockt_cgent_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgent_medium_tcp6,
+ inet6,
+ sock, true,
+ gen, 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 = gen_tcp, Active = true
+%% Message Size: large (=3)
+%% Domain: inet
+%%
+
+ttest_ssockt_cgent_large_tcp4(suite) ->
+ [];
+ttest_ssockt_cgent_large_tcp4(doc) ->
+ [];
+ttest_ssockt_cgent_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgent_large_tcp4,
+ inet,
+ sock, true,
+ gen, 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 = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockt_cgent_large_tcp6(suite) ->
+ [];
+ttest_ssockt_cgent_large_tcp6(doc) ->
+ [];
+ttest_ssockt_cgent_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_cgent_large_tcp6,
+ inet6,
+ sock, true,
+ gen, 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 = socket(tcp), Active = false
+%% Message Size: small (=1)
+%% Domain: inet
+%%
+
+ttest_ssockt_csockf_small_tcp4(suite) ->
+ [];
+ttest_ssockt_csockf_small_tcp4(doc) ->
+ [];
+ttest_ssockt_csockf_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockf_small_tcp4,
+ inet,
+ 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: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockt_csockf_small_tcp6(suite) ->
+ [];
+ttest_ssockt_csockf_small_tcp6(doc) ->
+ [];
+ttest_ssockt_csockf_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockf_small_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockt_csockf_medium_tcp4(suite) ->
+ [];
+ttest_ssockt_csockf_medium_tcp4(doc) ->
+ [];
+ttest_ssockt_csockf_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockf_medium_tcp4,
+ inet,
+ 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: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockt_csockf_medium_tcp6(suite) ->
+ [];
+ttest_ssockt_csockf_medium_tcp6(doc) ->
+ [];
+ttest_ssockt_csockf_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockf_medium_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockt_csockf_large_tcp4(suite) ->
+ [];
+ttest_ssockt_csockf_large_tcp4(doc) ->
+ [];
+ttest_ssockt_csockf_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockf_large_tcp4,
+ inet,
+ 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 = false
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockt_csockf_large_tcp6(suite) ->
+ [];
+ttest_ssockt_csockf_large_tcp6(doc) ->
+ [];
+ttest_ssockt_csockf_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockf_large_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockt_csocko_small_tcp4(suite) ->
+ [];
+ttest_ssockt_csocko_small_tcp4(doc) ->
+ [];
+ttest_ssockt_csocko_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csocko_small_tcp4,
+ inet,
+ 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: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockt_csocko_small_tcp6(suite) ->
+ [];
+ttest_ssockt_csocko_small_tcp6(doc) ->
+ [];
+ttest_ssockt_csocko_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csocko_small_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockt_csocko_medium_tcp4(suite) ->
+ [];
+ttest_ssockt_csocko_medium_tcp4(doc) ->
+ [];
+ttest_ssockt_csocko_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csocko_medium_tcp4,
+ inet,
+ 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: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockt_csocko_medium_tcp6(suite) ->
+ [];
+ttest_ssockt_csocko_medium_tcp6(doc) ->
+ [];
+ttest_ssockt_csocko_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csocko_medium_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockt_csocko_large_tcp4(suite) ->
+ [];
+ttest_ssockt_csocko_large_tcp4(doc) ->
+ [];
+ttest_ssockt_csocko_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csocko_large_tcp4,
+ inet,
+ 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 = once
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockt_csocko_large_tcp6(suite) ->
+ [];
+ttest_ssockt_csocko_large_tcp6(doc) ->
+ [];
+ttest_ssockt_csocko_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csocko_large_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockt_csockt_small_tcp4(suite) ->
+ [];
+ttest_ssockt_csockt_small_tcp4(doc) ->
+ [];
+ttest_ssockt_csockt_small_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockt_small_tcp4,
+ inet,
+ 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: small (=1)
+%% Domain: inet6
+%%
+
+ttest_ssockt_csockt_small_tcp6(suite) ->
+ [];
+ttest_ssockt_csockt_small_tcp6(doc) ->
+ [];
+ttest_ssockt_csockt_small_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csocko_small_tcp6,
+ inet6,
+ 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)
+%% Domain: inet
+%%
+
+ttest_ssockt_csockt_medium_tcp4(suite) ->
+ [];
+ttest_ssockt_csockt_medium_tcp4(doc) ->
+ [];
+ttest_ssockt_csockt_medium_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockt_medium_tcp4,
+ inet,
+ 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: medium (=2)
+%% Domain: inet6
+%%
+
+ttest_ssockt_csockt_medium_tcp6(suite) ->
+ [];
+ttest_ssockt_csockt_medium_tcp6(doc) ->
+ [];
+ttest_ssockt_csockt_medium_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockt_medium_tcp6,
+ inet6,
+ 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
+%%
+
+ttest_ssockt_csockt_large_tcp4(suite) ->
+ [];
+ttest_ssockt_csockt_large_tcp4(doc) ->
+ [];
+ttest_ssockt_csockt_large_tcp4(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockt_large_tcp4,
+ inet,
+ sock, true,
+ 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 = socket(tcp), Active = true
+%% Message Size: large (=3)
+%% Domain: inet6
+%%
+
+ttest_ssockt_csockt_large_tcp6(suite) ->
+ [];
+ttest_ssockt_csockt_large_tcp6(doc) ->
+ [];
+ttest_ssockt_csockt_large_tcp6(_Config) when is_list(_Config) ->
+ ttest_tcp(ttest_ssockt_csockt_large_tcp6,
+ inet6,
+ sock, true,
+ sock, true,
+ 3, 2).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ttest_tcp(TC,
+ Domain,
+ ServerMod, ServerActive,
+ ClientMod, ClientActive,
+ MsgID, MaxOutstanding) ->
+ Runtime = ?TTEST_RUNTIME,
+ tc_try(TC,
+ fun() ->
+ if (Domain =/= inet) -> not_yet_implemented(); true -> ok end,
+ %% This may be overkill, depending on the runtime,
+ %% but better safe then sorry...
+ ?TT(Runtime + ?SECS(60)),
+ InitState = #{domain => Domain,
+ msg_id => MsgID,
+ max_outstanding => MaxOutstanding,
+ runtime => Runtime,
+ server_mod => ServerMod,
+ server_active => ServerActive,
+ client_mod => ClientMod,
+ client_active => ClientActive},
+ ok = ttest_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ttest_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 => "create node",
+ cmd => fun(#{host := Host} = State) ->
+ case start_node(Host, server) of
+ {ok, Node} ->
+ {ok, State#{node => Node}};
+ {error, Reason, _} ->
+ {error, Reason}
+ end
+ end},
+ #{desc => "monitor server node",
+ cmd => fun(#{node := Node} = _State) ->
+ true = erlang:monitor_node(Node, true),
+ ok
+ end},
+ #{desc => "start ttest (remote) server",
+ cmd => fun(#{mod := Mod,
+ active := Active,
+ node := Node} = State) ->
+ case ttest_tcp_server_start(Node, Mod, Active) of
+ {ok, {{Pid, _MRef}, {Addr, Port}}} ->
+ {ok, State#{rserver => Pid,
+ addr => Addr,
+ port => Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester,
+ addr := Addr,
+ port := Port}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init, {Addr, Port}),
+ ok
+ end},
+
+
+ %% *** Termination ***
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester,
+ rserver := RServer} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester,
+ [{rserver, RServer}]) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ %% The remote server is in a accept, with a timeout of 5 seconds,
+ %% so may have to wait a bit...
+ #{desc => "order (remote) ttest server terminate",
+ cmd => fun(#{node := _Node,
+ rserver := RServer}) ->
+ ttest_tcp_server_stop(RServer),
+ ok
+ end},
+ #{desc => "await ttest (remote) server termination",
+ cmd => fun(#{rserver := RServer} = State) ->
+ ?SEV_AWAIT_TERMINATION(RServer),
+ State1 = maps:remove(rserver, State),
+ {ok, State1}
+ end},
+ #{desc => "stop (server) node",
+ cmd => fun(#{node := Node} = _State) ->
+ stop_node(Node)
+ end},
+ #{desc => "await (server) node termination",
+ cmd => fun(#{node := Node} = State) ->
+ receive
+ {nodedown, Node} ->
+ {ok, maps:remove(node, State)}
+ end
+ end},
+
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ ClientSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ {Tester, {ServerAddr, ServerPort}} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester,
+ server_addr => ServerAddr,
+ server_port => ServerPort}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+
+ %% *** Init part ***
+ #{desc => "create node",
+ cmd => fun(#{host := Host} = State) ->
+ case start_node(Host, client) of
+ {ok, Node} ->
+ {ok, State#{node => Node}};
+ {error, Reason, _} ->
+ {error, Reason}
+ end
+ end},
+ #{desc => "monitor client node",
+ cmd => fun(#{node := Node} = _State) ->
+ true = erlang:monitor_node(Node, true),
+ ok
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+
+ %% The actual test
+ #{desc => "await continue (ttest)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, ttest),
+ ok
+ end},
+ #{desc => "start ttest (remote) client",
+ cmd => fun(#{node := Node,
+ mod := Mod,
+ active := Active,
+ msg_id := MsgID,
+ max_outstanding := MaxOutstanding,
+ runtime := RunTime,
+ server_addr := Addr,
+ server_port := Port} = State) ->
+ Self = self(),
+ Notify =
+ fun(Result) ->
+ ?SEV_ANNOUNCE_READY(Self, ttest, Result)
+ end,
+ case ttest_tcp_client_start(Node, Notify,
+ Mod, Active,
+ Addr, Port,
+ MsgID, MaxOutstanding,
+ RunTime) of
+ {ok, {Pid, _MRef}} ->
+ {ok, State#{rclient => Pid}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{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} ->
+ {ok, State#{result => Result}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "await ttest (remote) client termination",
+ cmd => fun(#{rclient := RClient} = State) ->
+ ?SEV_AWAIT_TERMINATION(RClient),
+ State1 = maps:remove(rclient, State),
+ {ok, State1}
+ end},
+ #{desc => "announce ready (ttest)",
+ cmd => fun(#{tester := Tester,
+ result := Result} = State) ->
+ ?SEV_ANNOUNCE_READY(Tester, ttest, Result),
+ {ok, maps:remove(result, State)}
+ 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 => "stop (client) node",
+ cmd => fun(#{node := Node} = _State) ->
+ stop_node(Node)
+ end},
+ #{desc => "await (client) node termination",
+ cmd => fun(#{node := Node} = State) ->
+ receive
+ {nodedown, Node} ->
+ {ok, maps:remove(node, State)}
+ 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 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, {Addr, Port}} = ?SEV_AWAIT_READY(Pid, server, init),
+ {ok, State#{server_addr => Addr,
+ server_port => Port}}
+ end},
+
+
+ %% Start the client
+ #{desc => "order client start",
+ cmd => fun(#{client := Pid,
+ server_addr := Addr,
+ server_port := Port} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, {Addr, Port}),
+ ok
+ end},
+ #{desc => "await client ready (init)",
+ cmd => fun(#{client := Client} = _State) ->
+ ok = ?SEV_AWAIT_READY(Client, client, init)
+ end},
+
+ %% The actual test
+ #{desc => "order client continue (ttest)",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Client, ttest),
+ ok
+ end},
+ #{desc => "await client ready (ttest)",
+ cmd => fun(#{server := Server,
+ client := Client} = State) ->
+ case ?SEV_AWAIT_READY(Client, client, ttest,
+ [{server, Server}]) of
+ {ok, Result} ->
+ {ok, State#{result => Result}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+
+ %% *** Terminate server ***
+ #{desc => "order client terminate",
+ cmd => fun(#{client := Client} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Client),
+ ok
+ end},
+ #{desc => "await client down",
+ cmd => fun(#{client := Client} = State) ->
+ ?SEV_AWAIT_TERMINATION(Client),
+ State1 = maps:remove(client, State),
+ {ok, State1}
+ end},
+ #{desc => "order server terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server down",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ ok
+ end},
+
+
+ %% Present the results
+ #{desc => "present the results",
+ cmd => fun(#{result := Result} = State) ->
+ case Result of
+ #{status := ok,
+ runtime := RunTime,
+ cnt := Cnt,
+ bcnt := BCnt} ->
+ ?SEV_IPRINT(
+ "TTest results: "
+ "~n Run Time: ~s"
+ "~n Byte Count: ~s"
+ "~n Number of message exchanges: ~s"
+ "~n~n",
+ [
+ ?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",
+ [Cnt, Cnt div RunTime])
+ end
+ ]),
+ {ok, maps:remove(result, State)};
+
+ #{status := Failure,
+ runtime := RunTime,
+ sid := SID,
+ rid := RID,
+ scnt := SCnt,
+ rcnt := RCnt,
+ bcnt := BCnt,
+ num := Num} ->
+ ?SEV_EPRINT("Time Test failed: "
+ "~n ~p"
+ "~n"
+ "~nwhen"
+ "~n"
+ "~n Run Time: ~s"
+ "~n Send ID: ~p"
+ "~n Recv ID: ~p"
+ "~n Send Count: ~p"
+ "~n Recv Count: ~p"
+ "~n Byte Count: ~p"
+ "~n Num Iterations: ~p",
+ [Failure,
+ ?TTEST_LIB:format_time(RunTime),
+ SID, RID, SCnt, RCnt, BCnt, Num]),
+ {error, Failure}
+ end
+ end},
+
+ %% This is just so that the printout above shall have time to come
+ %% out before then end of the test case.
+ ?SEV_SLEEP(?SECS(1)),
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ i("start server evaluator"),
+ ServerInitState = #{host => local_host(),
+ domain => maps:get(domain, InitState),
+ 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),
+ mod => maps:get(client_mod, InitState),
+ active => maps:get(client_active, InitState),
+ msg_id => maps:get(msg_id, InitState),
+ max_outstanding => maps:get(max_outstanding, InitState),
+ runtime => maps:get(runtime, 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(s)"),
+ ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
+
+
+
+ttest_tcp_server_start(Node, gen, Active) ->
+ Transport = socket_test_ttest_tcp_gen,
+ socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active);
+ttest_tcp_server_start(Node, sock, Active) ->
+ TransportMod = socket_test_ttest_tcp_socket,
+ Transport = {TransportMod, #{method => plain}},
+ socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active).
+
+ttest_tcp_server_stop(Pid) ->
+ socket_test_ttest_tcp_server:stop(Pid).
+
+ttest_tcp_client_start(Node,
+ Notify,
+ gen,
+ Active, Addr, Port, MsgID, MaxOutstanding, RunTime) ->
+ Transport = socket_test_ttest_tcp_gen,
+ socket_test_ttest_tcp_client:start_monitor(Node,
+ Notify,
+ Transport,
+ Active,
+ Addr, Port,
+ MsgID, MaxOutstanding, RunTime);
+ttest_tcp_client_start(Node,
+ Notify,
+ sock,
+ Active, Addr, Port, MsgID, MaxOutstanding, RunTime) ->
+ TransportMod = socket_test_ttest_tcp_socket,
+ Transport = {TransportMod, #{method => plain}},
+ socket_test_ttest_tcp_client:start_monitor(Node,
+ Notify,
+ Transport,
+ Active,
+ Addr, Port,
+ MsgID, MaxOutstanding, RunTime).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_node(Host, NodeName) ->
+ UniqueNodeName = f("~w_~w", [NodeName, erlang:system_time(millisecond)]),
+ case do_start_node(Host, UniqueNodeName) of
+ {ok, _} = OK ->
+ global:sync(),
+ %% i("Node ~p started: "
+ %% "~n Nodes: ~p"
+ %% "~n Logger: ~p"
+ %% "~n Global Names: ~p",
+ %% [NodeName, nodes(),
+ %% global:whereis_name(socket_test_logger),
+ %% global:registered_names()]),
+ OK;
+ {error, Reason, _} ->
+ {error, Reason}
+ end.
+
+do_start_node(Host, NodeName) when is_list(NodeName) ->
+ do_start_node(Host, list_to_atom(NodeName));
+do_start_node(Host, NodeName) when is_atom(NodeName) ->
+ Dir = filename:dirname(code:which(?MODULE)),
+ Flags = "-pa " ++ Dir,
+ Opts = [{monitor_master, true}, {erl_flags, Flags}],
+ ct_slave:start(Host, NodeName, Opts).
+
+
+stop_node(Node) ->
+ case ct_slave:stop(Node) of
+ {ok, _} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+sock_open(Domain, Type, Proto) ->
+ try socket:open(Domain, Type, Proto) of
+ {ok, Socket} ->
+ Socket;
+ {error, Reason} ->
+ ?FAIL({open, Reason})
+ catch
+ C:E:S ->
+ ?FAIL({open, C, E, S})
+ 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 ->
+ ok;
+ {error, Reason} ->
+ ?FAIL({connect, Reason})
+ catch
+ C:E:S ->
+ ?FAIL({connect, C, E, S})
+ end.
+
+sock_sockname(Sock) ->
+ try socket:sockname(Sock) of
+ {ok, SockAddr} ->
+ SockAddr;
+ {error, Reason} ->
+ ?FAIL({sockname, Reason})
+ catch
+ C:E:S ->
+ ?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 ->
+ ok;
+ {error, Reason} ->
+ i("sock_close -> error: ~p", [Reason]),
+ ?FAIL({close, Reason})
+ catch
+ C:E:S ->
+ i("sock_close -> failed: ~p, ~p, ~p", [C, E, S]),
+ ?FAIL({close, C, E, S})
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+local_host() ->
+ try net_adm:localhost() of
+ Host when is_list(Host) ->
+ %% Convert to shortname if long
+ case string:tokens(Host, [$.]) of
+ [H|_] ->
+ list_to_atom(H)
+ end
+ catch
+ C:E:S ->
+ erlang:raise(C, E, S)
+ end.
+
+
+%% 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);
+ {error, Reason} ->
+ ?FAIL({inet, getifaddrs, 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).
+
+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).
+
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+not_yet_implemented() ->
+ skip("not yet implemented").
+
+skip(Reason) ->
+ throw({skip, Reason}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+t() ->
+ os:timestamp().
+
+
+tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) ->
+ T1 = A1*1000000000+B1*1000+(C1 div 1000),
+ T2 = A2*1000000000+B2*1000+(C2 div 1000),
+ T2 - T1.
+
+
+formated_timestamp() ->
+ format_timestamp(os:timestamp()).
+
+format_timestamp({_N1, _N2, _N3} = TS) ->
+ {_Date, Time} = calendar:now_to_local_time(TS),
+ %% {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ %% FormatTS =
+ %% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w.~w",
+ %% [YYYY, MM, DD, Hour, Min, Sec, N3]),
+ FormatTS = io_lib:format("~.2.0w:~.2.0w:~.2.0w", [Hour, Min, Sec]),
+ lists:flatten(FormatTS).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+set_tc_name(N) when is_atom(N) ->
+ set_tc_name(atom_to_list(N));
+set_tc_name(N) when is_list(N) ->
+ put(tc_name, N).
+
+%% get_tc_name() ->
+%% get(tc_name).
+
+tc_begin(TC) ->
+ set_tc_name(TC),
+ tc_print("begin ***",
+ "~n----------------------------------------------------~n", "").
+
+tc_end(Result) when is_list(Result) ->
+ tc_print("done: ~s", [Result],
+ "", "----------------------------------------------------~n~n"),
+ ok.
+
+
+tc_try(Case, Fun) when is_atom(Case) andalso is_function(Fun, 0) ->
+ tc_begin(Case),
+ try
+ begin
+ Fun(),
+ ?SLEEP(?SECS(1)),
+ tc_end("ok")
+ end
+ catch
+ throw:{skip, _} = SKIP ->
+ tc_end("skipping"),
+ SKIP;
+ Class:Error:Stack ->
+ tc_end("failed"),
+ erlang:raise(Class, Error, Stack)
+ end.
+
+
+tc_print(F, Before, After) ->
+ tc_print(F, [], Before, After).
+
+tc_print(F, A, Before, After) ->
+ Name = tc_which_name(),
+ FStr = f("*** [~s][~s][~p] " ++ F ++ "~n",
+ [formated_timestamp(),Name,self()|A]),
+ io:format(user, Before ++ FStr ++ After, []).
+
+tc_which_name() ->
+ case get(tc_name) of
+ undefined ->
+ case get(sname) of
+ undefined ->
+ "";
+ SName when is_list(SName) ->
+ SName
+ end;
+ Name when is_list(Name) ->
+ Name
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+l2a(S) when is_list(S) ->
+ list_to_atom(S).
+
+l2b(L) when is_list(L) ->
+ list_to_binary(L).
+
+b2l(B) when is_binary(B) ->
+ binary_to_list(B).
+
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+%% p(F) ->
+%% p(F, []).
+
+%% p(F, A) ->
+%% p(F, A, "", "").
+
+%% p(F, A, Before, After) when is_list(Before) andalso is_list(After) ->
+%% TcName =
+%% case get(tc_name) of
+%% undefined ->
+%% case get(sname) of
+%% undefined ->
+%% "";
+%% SName when is_list(SName) ->
+%% SName
+%% end;
+%% Name when is_list(Name) ->
+%% Name
+%% end,
+%% FStr = f("*** [~s][~s][~p] " ++ F ++ "~n",
+%% [formated_timestamp(),TcName,self()|A]),
+%% i(Before ++ FStr ++ After, []).
+
+
+%% d(F, A) ->
+%% d(get(dbg_fd), F, A).
+
+%% d(undefined, F, A) ->
+%% [NodeNameStr|_] = string:split(atom_to_list(node()), [$@]),
+%% DbgFileName = f("~s-dbg.txt", [NodeNameStr]),
+%% case file:open(DbgFileName, [write]) of
+%% {ok, FD} ->
+%% put(dbg_fd, FD),
+%% d(FD, F, A);
+%% {error, Reason} ->
+%% exit({failed_open_dbg_file, Reason})
+%% end;
+%% d(FD, F, A) ->
+%% io:format(FD, "~s~n", [f("[~s] " ++ F, [formated_timestamp()|A])]).
+
+i(F) ->
+ i(F, []).
+
+i(F, A) ->
+ FStr = f("[~s] " ++ F, [formated_timestamp()|A]),
+ io:format(user, FStr ++ "~n", []),
+ io:format(FStr, []).
+
diff --git a/erts/emulator/test/socket_client.erl b/erts/emulator/test/socket_client.erl
new file mode 100644
index 0000000000..1c07e799b8
--- /dev/null
+++ b/erts/emulator/test/socket_client.erl
@@ -0,0 +1,538 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_client).
+
+-export([
+ start/1, start/2, start/5, start/6,
+ start_tcp/1, start_tcp/2, start_tcp/3,
+ start_tcp4/1, start_tcp4/2, start_tcp6/1, start_tcp6/2,
+ start_udp/1, start_udp/2, start_udp/3,
+ start_udp4/1, start_udp4/2, start_udp6/1, start_udp6/2
+ ]).
+
+-define(LIB, socket_lib).
+
+-record(client, {socket, verbose = true, msg = true, type, dest, msg_id = 1}).
+
+start(Port) ->
+ start(Port, 1).
+
+start(Port, Num) ->
+ start_tcp(Port, Num).
+
+start_tcp(Port) ->
+ start_tcp(Port, 1).
+
+start_tcp(Port, Num) ->
+ start_tcp4(Port, Num).
+
+start_tcp4(Port) ->
+ start_tcp4(Port, 1).
+
+start_tcp4(Port, Num) ->
+ start(inet, stream, tcp, Port, Num).
+
+start_tcp6(Port) ->
+ start_tcp6(Port, 1).
+
+start_tcp6(Port, Num) ->
+ start(inet6, stream, tcp, Port, Num).
+
+start_tcp(Addr, Port, Num) when (size(Addr) =:= 4) andalso
+ is_integer(Num) andalso
+ (Num > 0) ->
+ start(inet, stream, tcp, Addr, Port, Num);
+start_tcp(Addr, Port, Num) when (size(Addr) =:= 8) andalso
+ is_integer(Num) andalso
+ (Num > 0) ->
+ start(inet6, stream, tcp, Addr, Port, Num).
+
+
+start_udp(Port) ->
+ start_udp(Port, 1).
+
+start_udp(Port, Num) ->
+ start_udp4(Port, Num).
+
+start_udp4(Port) ->
+ start_udp4(Port, 1).
+
+start_udp4(Port, Num) ->
+ start(inet, dgram, udp, Port, Num).
+
+start_udp6(Port) ->
+ start_udp6(Port, 1).
+
+start_udp6(Port, Num) ->
+ start(inet6, dgram, udp, Port, Num).
+
+start_udp(Addr, Port, Num) when (size(Addr) =:= 4) ->
+ start(inet, dgram, udp, Addr, Port, Num);
+start_udp(Addr, Port, Num) when (size(Addr) =:= 8) ->
+ start(inet6, dgram, udp, Addr, Port, Num).
+
+
+start(Domain, Type, Proto, Port, Num)
+ when is_integer(Port) andalso is_integer(Num) ->
+ start(Domain, Type, Proto, which_addr(Domain), Port, Num);
+
+start(Domain, Type, Proto, Addr, Port) ->
+ start(Domain, Type, Proto, Addr, Port, 1).
+
+start(Domain, Type, Proto, Addr, Port, 1 = Num) ->
+ start(Domain, Type, Proto, Addr, Port, Num, true);
+start(Domain, Type, Proto, Addr, Port, Num)
+ when is_integer(Num) andalso (Num > 1) ->
+ start(Domain, Type, Proto, Addr, Port, Num, false).
+
+start(Domain, Type, Proto, Addr, Port, Num, Verbose) ->
+ put(sname, "starter"),
+ Clients = start_clients(Num, Domain, Type, Proto, Addr, Port, Verbose),
+ await_clients(Clients).
+
+start_clients(Num, Domain, Type, Proto, Addr, Port, Verbose) ->
+ start_clients(Num, 1, Domain, Type, Proto, Addr, Port, Verbose, []).
+
+start_clients(Num, ID, Domain, Type, Proto, Addr, Port, Verbose, Acc)
+ when (Num > 0) ->
+ StartClient = fun() ->
+ start_client(ID, Domain, Type, Proto, Addr, Port, Verbose)
+ end,
+ {Pid, _} = spawn_monitor(StartClient),
+ ?LIB:sleep(500),
+ i("start client ~w", [ID]),
+ start_clients(Num-1, ID+1, Domain, Type, Proto, Addr, Port, Verbose, [Pid|Acc]);
+start_clients(_, _, _, _, _, _, _, _, Acc) ->
+ i("all client(s) started"),
+ lists:reverse(Acc).
+
+await_clients([]) ->
+ i("all clients done");
+await_clients(Clients) ->
+ receive
+ {'DOWN', _MRef, process, Pid, _Reason} ->
+ case lists:delete(Pid, Clients) of
+ Clients2 when (Clients2 =/= Clients) ->
+ i("client ~p done", [Pid]),
+ await_clients(Clients2);
+ _ ->
+ await_clients(Clients)
+ end
+ end.
+
+
+start_client(ID, Domain, Type, Proto, Addr, Port, Verbose) ->
+ put(sname, ?LIB:f("client[~w]", [ID])),
+ SA = #{family => Domain,
+ addr => Addr,
+ port => Port},
+ %% The way we use tos only works because we
+ %% send so few messages (a new value for every
+ %% message).
+ tos_init(),
+ do_start(Domain, Type, Proto, SA, Verbose).
+
+do_start(Domain, stream = Type, Proto, SA, Verbose) ->
+ try do_init(Domain, Type, Proto) of
+ Sock ->
+ connect(Sock, SA),
+ maybe_print_start_info(Verbose, Sock, Type),
+ %% Give the server some time...
+ ?LIB:sleep(5000),
+ %% ok = socket:close(Sock),
+ send_loop(#client{socket = Sock,
+ type = Type,
+ verbose = Verbose})
+ catch
+ throw:E ->
+ e("Failed initiate: "
+ "~n Error: ~p", [E])
+ end;
+do_start(Domain, dgram = Type, Proto, SA, Verbose) ->
+ try do_init(Domain, Type, Proto) of
+ Sock ->
+ maybe_print_start_info(Verbose, Sock, Type),
+ %% Give the server some time...
+ ?LIB:sleep(5000),
+ %% ok = socket:close(Sock),
+ send_loop(#client{socket = Sock,
+ type = Type,
+ dest = SA,
+ verbose = Verbose})
+ catch
+ throw:E ->
+ e("Failed initiate: "
+ "~n Error: ~p", [E])
+ end.
+
+maybe_print_start_info(true = _Verbose, Sock, stream = _Type) ->
+ {ok, Name} = socket:sockname(Sock),
+ {ok, Peer} = socket:peername(Sock),
+ {ok, Domain} = socket:getopt(Sock, socket, domain),
+ {ok, Type} = socket:getopt(Sock, socket, type),
+ {ok, Proto} = socket:getopt(Sock, socket, protocol),
+ {ok, OOBI} = socket:getopt(Sock, socket, oobinline),
+ {ok, SndBuf} = socket:getopt(Sock, socket, sndbuf),
+ {ok, RcvBuf} = socket:getopt(Sock, socket, rcvbuf),
+ {ok, Linger} = socket:getopt(Sock, socket, linger),
+ {ok, MTU} = socket:getopt(Sock, ip, mtu),
+ {ok, MTUDisc} = socket:getopt(Sock, ip, mtu_discover),
+ {ok, MALL} = socket:getopt(Sock, ip, multicast_all),
+ {ok, MIF} = socket:getopt(Sock, ip, multicast_if),
+ {ok, MLoop} = socket:getopt(Sock, ip, multicast_loop),
+ {ok, MTTL} = socket:getopt(Sock, ip, multicast_ttl),
+ {ok, RecvTOS} = socket:getopt(Sock, ip, recvtos),
+ i("connected: "
+ "~n From: ~p"
+ "~n To: ~p"
+ "~nwhen"
+ "~n (socket) Domain: ~p"
+ "~n (socket) Type: ~p"
+ "~n (socket) Protocol: ~p"
+ "~n (socket) OOBInline: ~p"
+ "~n (socket) SndBuf: ~p"
+ "~n (socket) RcvBuf: ~p"
+ "~n (socket) Linger: ~p"
+ "~n (ip) MTU: ~p"
+ "~n (ip) MTU Discovery: ~p"
+ "~n (ip) Multicast ALL: ~p"
+ "~n (ip) Multicast IF: ~p"
+ "~n (ip) Multicast Loop: ~p"
+ "~n (ip) Multicast TTL: ~p"
+ "~n (ip) RecvTOS: ~p"
+ "~n => wait some",
+ [Name, Peer,
+ Domain, Type, Proto,
+ OOBI, SndBuf, RcvBuf, Linger,
+ MTU, MTUDisc, MALL, MIF, MLoop, MTTL,
+ RecvTOS]);
+maybe_print_start_info(true = _Verbose, Sock, dgram = _Type) ->
+ {ok, Domain} = socket:getopt(Sock, socket, domain),
+ {ok, Type} = socket:getopt(Sock, socket, type),
+ {ok, Proto} = socket:getopt(Sock, socket, protocol),
+ {ok, OOBI} = socket:getopt(Sock, socket, oobinline),
+ {ok, SndBuf} = socket:getopt(Sock, socket, sndbuf),
+ {ok, RcvBuf} = socket:getopt(Sock, socket, rcvbuf),
+ {ok, Linger} = socket:getopt(Sock, socket, linger),
+ {ok, MALL} = socket:getopt(Sock, ip, multicast_all),
+ {ok, MIF} = socket:getopt(Sock, ip, multicast_if),
+ {ok, MLoop} = socket:getopt(Sock, ip, multicast_loop),
+ {ok, MTTL} = socket:getopt(Sock, ip, multicast_ttl),
+ {ok, RecvTOS} = socket:getopt(Sock, ip, recvtos),
+ {ok, RecvTTL} = socket:getopt(Sock, ip, recvttl),
+ i("initiated when: "
+ "~n (socket) Domain: ~p"
+ "~n (socket) Type: ~p"
+ "~n (socket) Protocol: ~p"
+ "~n (socket) OOBInline: ~p"
+ "~n (socket) SndBuf: ~p"
+ "~n (socket) RcvBuf: ~p"
+ "~n (socket) Linger: ~p"
+ "~n (ip) Multicast ALL: ~p"
+ "~n (ip) Multicast IF: ~p"
+ "~n (ip) Multicast Loop: ~p"
+ "~n (ip) Multicast TTL: ~p"
+ "~n (ip) RecvTOS: ~p"
+ "~n (ip) RecvTTL: ~p"
+ "~n => wait some",
+ [Domain, Type, Proto,
+ OOBI, SndBuf, RcvBuf, Linger,
+ MALL, MIF, MLoop, MTTL,
+ RecvTOS, RecvTTL]);
+maybe_print_start_info(_Verbose, _Sock, _Type) ->
+ ok.
+
+
+do_init(Domain, stream = Type, Proto) ->
+ i("try (socket) open"),
+ Sock = case socket:open(Domain, Type, Proto) of
+ {ok, S} ->
+ S;
+ {error, OReason} ->
+ throw({open, OReason})
+ end,
+ i("try (socket) bind"),
+ case socket:bind(Sock, any) of
+ {ok, _P} ->
+ ok = socket:setopt(Sock, socket, timestamp, true),
+ ok = socket:setopt(Sock, ip, tos, mincost),
+ ok = socket:setopt(Sock, ip, recvtos, true),
+ Sock;
+ {error, BReason} ->
+ throw({bind, BReason})
+ end;
+do_init(Domain, dgram = Type, Proto) ->
+ i("try (socket) open"),
+ Sock = case socket:open(Domain, Type, Proto) of
+ {ok, S} ->
+ S;
+ {error, OReason} ->
+ throw({open, OReason})
+ end,
+ case socket:bind(Sock, any) of
+ {ok, _} ->
+ ok = socket:setopt(Sock, socket, timestamp, true),
+ ok = socket:setopt(Sock, ip, tos, mincost),
+ ok = socket:setopt(Sock, ip, recvtos, true),
+ ok = socket:setopt(Sock, ip, recvttl, true),
+ Sock;
+ {error, BReason} ->
+ throw({bind, BReason})
+ end.
+
+
+which_addr(Domain) ->
+ Iflist = case inet:getifaddrs() of
+ {ok, IFL} ->
+ IFL;
+ {error, Reason} ->
+ throw({inet,getifaddrs,Reason})
+ end,
+ which_addr(Domain, Iflist).
+
+
+connect(Sock, SA) ->
+ i("try (socket) connect to:"
+ "~n ~p", [SA]),
+ case socket:connect(Sock, SA) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ e("connect failure: "
+ "~n ~p", [Reason]),
+ exit({connect, Reason})
+ end.
+
+
+send_loop(#client{msg_id = N} = C) when (N =< 10) ->
+ i("try send request ~w", [N]),
+ Req = ?LIB:enc_req_msg(N, "hejsan"),
+ case send(C, Req) of
+ ok ->
+ i("request ~w sent - now try read answer", [N]),
+ case recv(C) of
+ {ok, {Source, Msg}} ->
+ if
+ (C#client.verbose =:= true) ->
+ i("received ~w bytes of data~s",
+ [size(Msg), case Source of
+ undefined -> "";
+ _ -> ?LIB:f(" from:~n ~p", [Source])
+ end]);
+ true ->
+ i("received ~w bytes", [size(Msg)])
+ end,
+ case ?LIB:dec_msg(Msg) of
+ {reply, N, Reply} ->
+ if
+ (C#client.verbose =:= true) ->
+ i("received reply ~w: ~p", [N, Reply]);
+ true ->
+ i("received reply ~w", [N])
+ end,
+ ?LIB:sleep(500), % Just to spread it out a bit
+ send_loop(C#client{msg_id = N+1})
+ end;
+ {error, RReason} ->
+ e("Failed recv response for request ~w: "
+ "~n ~p", [N, RReason]),
+ exit({failed_recv, RReason})
+ end;
+ {error, SReason} ->
+ e("Failed send request ~w: "
+ "~n ~p", [N, SReason]),
+ exit({failed_send, SReason})
+ end;
+send_loop(Client) ->
+ sock_close(Client).
+
+sock_close(#client{socket = Sock, verbose = true}) ->
+ i("we are done - close the socket when: "
+ "~n ~p", [socket:info()]),
+ ok = socket:close(Sock),
+ i("we are done - socket closed when: "
+ "~n ~p", [socket:info()]);
+sock_close(#client{socket = Sock}) ->
+ i("we are done"),
+ ok = socket:close(Sock).
+
+
+
+send(#client{socket = Sock, type = stream}, Msg) ->
+ socket:send(Sock, Msg);
+send(#client{socket = Sock, type = dgram, dest = Dest}, Msg) ->
+ %% i("try send to: "
+ %% "~n ~p", [Dest]),
+ %% ok = socket:setopt(Sock, otp, debug, true),
+ TOS = tos_next(),
+ ok = socket:setopt(Sock, ip, tos, TOS),
+ case socket:sendto(Sock, Msg, Dest) of
+ ok = OK ->
+ OK;
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+recv(#client{socket = Sock, type = stream, msg = false}) ->
+ case socket:recv(Sock) of
+ {ok, Msg} ->
+ {ok, {undefined, Msg}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+recv(#client{socket = Sock, verbose = Verbose, type = stream, msg = true}) ->
+ case socket:recvmsg(Sock) of
+ %% An iov of length 1 is an simplification...
+ {ok, #{addr := undefined = Source,
+ iov := [Msg],
+ ctrl := CMsgHdrs,
+ flags := Flags}} ->
+ if
+ (Verbose =:= true) ->
+ i("received message: "
+ "~n CMsgHdr: ~p"
+ "~n Flags: ~p", [CMsgHdrs, Flags]);
+ true ->
+ ok
+ end,
+ {ok, {Source, Msg}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+recv(#client{socket = Sock, type = dgram, msg = false}) ->
+ socket:recvfrom(Sock);
+recv(#client{socket = Sock, verbose = Verbose, type = dgram, msg = true}) ->
+ case socket:recvmsg(Sock) of
+ {ok, #{addr := Source,
+ iov := [Msg],
+ ctrl := CMsgHdrs,
+ flags := Flags}} ->
+ if
+ (Verbose =:= true) ->
+ i("received message: "
+ "~n CMsgHdr: ~p"
+ "~n Flags: ~p", [CMsgHdrs, Flags]);
+ true ->
+ ok
+ end,
+ {ok, {Source, Msg}};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+
+which_addr(_Domain, []) ->
+ throw(no_address);
+which_addr(Domain, [{Name, IFO}|_IFL]) when (Name =/= "lo") ->
+ which_addr2(Domain, IFO);
+which_addr(Domain, [_|IFL]) ->
+ which_addr(Domain, IFL).
+
+which_addr2(inet = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 4) ->
+ Addr;
+which_addr2(inet6 = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 8) ->
+ Addr;
+which_addr2(Domain, [_|IFO]) ->
+ which_addr2(Domain, IFO).
+
+
+%% ---
+
+%% enc_req_msg(N, Data) ->
+%% enc_msg(?REQ, N, Data).
+
+%% enc_rep_msg(N, Data) ->
+%% enc_msg(?REP, N, Data).
+
+%% enc_msg(Type, N, Data) when is_list(Data) ->
+%% enc_msg(Type, N, list_to_binary(Data));
+%% enc_msg(Type, N, Data)
+%% when is_integer(Type) andalso is_integer(N) andalso is_binary(Data) ->
+%% <<Type:32/integer, N:32/integer, Data/binary>>.
+
+%% dec_msg(<<?REQ:32/integer, N:32/integer, Data/binary>>) ->
+%% {request, N, Data};
+%% dec_msg(<<?REP:32/integer, N:32/integer, Data/binary>>) ->
+%% {reply, N, Data}.
+
+
+%% ---
+
+%% sleep(T) ->
+%% receive after T -> ok end.
+
+
+%% ---
+
+%% formated_timestamp() ->
+%% format_timestamp(os:timestamp()).
+
+%% format_timestamp(Now) ->
+%% N2T = fun(N) -> calendar:now_to_local_time(N) end,
+%% format_timestamp(Now, N2T, true).
+
+%% format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
+%% FormatExtra = ".~.2.0w",
+%% ArgsExtra = [N3 div 10000],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra);
+%% format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
+%% FormatExtra = "",
+%% ArgsExtra = [],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra).
+
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
+%% {Date, Time} = N2T(N),
+%% {YYYY,MM,DD} = Date,
+%% {Hour,Min,Sec} = Time,
+%% FormatDate =
+%% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
+%% [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
+%% lists:flatten(FormatDate).
+
+
+%% ---
+
+tos_init() ->
+ put(tos, 1).
+
+tos_next() ->
+ case get(tos) of
+ TOS when (TOS < 100) ->
+ put(tos, TOS + 1),
+ TOS;
+ _ ->
+ put(tos, 1),
+ 1
+ end.
+
+
+%% ---
+
+e(F, A) ->
+ ?LIB:e(F, A).
+
+i(F) ->
+ ?LIB:i(F).
+
+i(F, A) ->
+ ?LIB:i(F, A).
+
diff --git a/erts/emulator/test/socket_lib.erl b/erts/emulator/test/socket_lib.erl
new file mode 100644
index 0000000000..9d6524d467
--- /dev/null
+++ b/erts/emulator/test/socket_lib.erl
@@ -0,0 +1,133 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_lib).
+
+-export([
+ sleep/1,
+ req/0, rep/0,
+ enc_req_msg/2, enc_rep_msg/2,
+ enc_msg/3, dec_msg/1,
+ request/3, reply/4,
+ f/2,
+ i/1, i/2,
+ e/2
+ ]).
+
+
+-define(REQ, 0).
+-define(REP, 1).
+
+
+%% ---
+
+sleep(T) ->
+ receive after T -> ok end.
+
+
+%% ---
+
+req() -> ?REQ.
+rep() -> ?REP.
+
+enc_req_msg(N, Data) ->
+ enc_msg(?REQ, N, Data).
+
+enc_rep_msg(N, Data) ->
+ enc_msg(?REP, N, Data).
+
+enc_msg(Type, N, Data) when is_list(Data) ->
+ enc_msg(Type, N, list_to_binary(Data));
+enc_msg(Type, N, Data)
+ when is_integer(Type) andalso is_integer(N) andalso is_binary(Data) ->
+ <<Type:32/integer, N:32/integer, Data/binary>>.
+
+dec_msg(<<?REQ:32/integer, N:32/integer, Data/binary>>) ->
+ {request, N, Data};
+dec_msg(<<?REP:32/integer, N:32/integer, Data/binary>>) ->
+ {reply, N, Data}.
+
+
+%% ---
+
+request(Tag, Pid, Request) ->
+ Ref = make_ref(),
+ Pid ! {Tag, self(), Ref, Request},
+ receive
+ {Tag, Pid, Ref, Reply} ->
+ Reply
+ end.
+
+reply(Tag, Pid, Ref, Reply) ->
+ Pid ! {Tag, self(), Ref, Reply}.
+
+
+%% ---
+
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+
+%% ---
+
+e(F, A) ->
+ p("<ERROR> " ++ F, A).
+
+i(F) ->
+ i(F, []).
+i(F, A) ->
+ p("*** " ++ F, A).
+
+p(F, A) ->
+ p(get(sname), F, A).
+
+p(SName, F, A) ->
+ io:format("[~s,~p][~s] " ++ F ++ "~n",
+ [SName,self(),formated_timestamp()|A]).
+
+
+%% ---
+
+formated_timestamp() ->
+ format_timestamp(os:timestamp()).
+
+format_timestamp(Now) ->
+ N2T = fun(N) -> calendar:now_to_local_time(N) end,
+ format_timestamp(Now, N2T, true).
+
+format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
+ FormatExtra = ".~.2.0w",
+ ArgsExtra = [N3 div 10000],
+ format_timestamp(N, N2T, FormatExtra, ArgsExtra);
+format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
+ FormatExtra = "",
+ ArgsExtra = [],
+ format_timestamp(N, N2T, FormatExtra, ArgsExtra).
+
+format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
+ {Date, Time} = N2T(N),
+ {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ FormatDate =
+ io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
+ [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
+ lists:flatten(FormatDate).
+
+
diff --git a/erts/emulator/test/socket_server.erl b/erts/emulator/test/socket_server.erl
new file mode 100644
index 0000000000..45adffc5e6
--- /dev/null
+++ b/erts/emulator/test/socket_server.erl
@@ -0,0 +1,954 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_server).
+
+-export([
+ start/0, start/5,
+ start_tcp/0, start_tcp/1, start_tcp/3,
+ start_tcp4/0, start_tcp4/1, start_tcp4/2,
+ start_tcp6/0, start_tcp6/1, start_tcp6/2,
+ start_udp/0, start_udp/1, start_udp/3,
+ start_udp4/0, start_udp4/1, start_udp4/2,
+ start_udp6/0, start_udp6/1, start_udp6/2,
+ start_sctp/0, start_sctp/1
+ ]).
+
+-define(LIB, socket_lib).
+
+-record(manager, {socket, msg, peek, acceptors, handler_id, handlers}).
+-record(acceptor, {id, socket, manager,
+ atimeout = 5000}).
+-record(handler, {socket, peek, msg, type, manager,
+ stimeout = 5000, rtimeout = 5000}).
+
+-define(NUM_ACCEPTORS, 5).
+
+start() ->
+ start_tcp().
+
+start_tcp() ->
+ start_tcp4().
+
+start_tcp(Peek) ->
+ start_tcp4(Peek).
+
+start_tcp4() ->
+ start_tcp4(false).
+
+start_tcp4(Peek) ->
+ start_tcp4(false, Peek).
+
+start_tcp4(UseMsg, Peek) ->
+ start_tcp(inet, UseMsg, Peek).
+
+start_tcp6() ->
+ start_tcp6(false).
+
+start_tcp6(Peek) ->
+ start_tcp6(false, Peek).
+
+start_tcp6(UseMsg, Peek) ->
+ start_tcp(inet6, UseMsg, Peek).
+
+start_tcp(Domain, UseMsg, Peek) when is_boolean(UseMsg) andalso is_boolean(Peek) ->
+ start(Domain, stream, tcp, UseMsg, Peek).
+
+start_udp() ->
+ start_udp4().
+
+start_udp(Peek) ->
+ start_udp4(Peek).
+
+start_udp4() ->
+ start_udp4(false).
+
+start_udp4(Peek) ->
+ start_udp4(false, Peek).
+
+start_udp4(UseMsg, Peek) ->
+ start_udp(inet, UseMsg, Peek).
+
+start_udp6() ->
+ start_udp6(false, false).
+
+start_udp6(Peek) ->
+ start_udp6(false, Peek).
+
+start_udp6(UseMsg, Peek) ->
+ start_udp(inet6, UseMsg, Peek).
+
+start_udp(Domain, UseMsg, Peek) when is_boolean(UseMsg) andalso is_boolean(Peek) ->
+ start(Domain, dgram, udp, UseMsg, Peek).
+
+
+start_sctp() ->
+ start_sctp(inet).
+
+start_sctp(Domain) when ((Domain =:= inet) orelse (Domain =:= inet6)) ->
+ start(Domain, seqpacket, sctp, true, false).
+
+start(Domain, Type, Proto, UseMsg, Peek) ->
+ put(sname, "starter"),
+ i("try start manager"),
+ {Pid, MRef} = manager_start(Domain, Type, Proto, UseMsg, Peek),
+ i("manager (~p) started", [Pid]),
+ loop(Pid, MRef).
+
+loop(Pid, MRef) ->
+ receive
+ {'DOWN', MRef, process, Pid, Reason} ->
+ i("manager process exited: "
+ "~n ~p", [Reason]),
+ ok
+ end.
+
+
+%% =========================================================================
+
+manager_start(Domain, Type, Proto, UseMsg, Peek) ->
+ spawn_monitor(fun() -> manager_init(Domain, Type, Proto, UseMsg, Peek) end).
+
+manager_start_handler(Pid, Sock) ->
+ manager_request(Pid, {start_handler, Sock}).
+
+manager_stop(Pid, Reason) ->
+ manager_request(Pid, {stop, Reason}).
+
+manager_request(Pid, Request) ->
+ ?LIB:request(manager, Pid, Request).
+
+manager_reply(Pid, Ref, Reply) ->
+ ?LIB:reply(manager, Pid, Ref, Reply).
+
+
+manager_init(Domain, Type, Proto, UseMsg, Peek) ->
+ put(sname, "manager"),
+ do_manager_init(Domain, Type, Proto, UseMsg, Peek).
+
+do_manager_init(Domain, stream = Type, Proto, UseMsg, Peek) ->
+ i("try start acceptor(s)"),
+ {Sock, Acceptors} = manager_stream_init(Domain, Type, Proto),
+ manager_loop(#manager{socket = Sock,
+ msg = UseMsg,
+ peek = Peek,
+ acceptors = Acceptors,
+ handler_id = 1,
+ handlers = []});
+do_manager_init(Domain, dgram = Type, Proto, UseMsg, Peek) ->
+ i("try open socket"),
+ case socket:open(Domain, Type, Proto) of
+ {ok, Sock} ->
+ F = fun(X) -> case socket:getopt(Sock, socket, X) of
+ {ok, V} -> f("~p", [V]);
+ {error, R} -> f("error: ~p", [R])
+ end
+ end,
+ i("socket opened (~s,~s,~s): "
+ "~n broadcast: ~s"
+ "~n dontroute: ~s"
+ "~n keepalive: ~s"
+ "~n reuseaddr: ~s"
+ "~n linger: ~s"
+ "~n debug: ~s"
+ "~n prio: ~s"
+ "~n rcvbuf: ~s"
+ "~n rcvtimeo: ~s"
+ "~n sndbuf: ~s"
+ "~n sndtimeo: ~s"
+ "~n => try find (local) address",
+ [F(domain), F(type), F(protocol),
+ F(broadcast), F(dontroute), F(keepalive), F(reuseaddr), F(linger),
+ F(debug), F(priority),
+ F(rcvbuf), F(rcvtimeo), F(sndbuf), F(sndtimeo)]),
+ Addr = which_addr(Domain),
+ SA = #{family => Domain,
+ addr => Addr},
+ i("try bind to: "
+ "~n ~p", [Addr]),
+ case socket:bind(Sock, SA) of
+ {ok, _P} ->
+ ok;
+ {error, BReason} ->
+ throw({bind, BReason})
+ end,
+ i("bound to: "
+ "~n ~s"
+ "~n => try start handler",
+ [case socket:sockname(Sock) of
+ {ok, Name} -> f("~p", [Name]);
+ {error, R} -> f("error: ~p", [R])
+ end]),
+ case handler_start(1, Sock, UseMsg, Peek) of
+ {ok, {Pid, MRef}} ->
+ i("handler (~p) started", [Pid]),
+ handler_continue(Pid),
+ manager_loop(#manager{peek = Peek,
+ msg = UseMsg,
+ handler_id = 2, % Just in case
+ handlers = [{1, Pid, MRef}]});
+ {error, SReason} ->
+ e("Failed starting handler: "
+ "~n ~p", [SReason]),
+ exit({failed_start_handler, SReason})
+ end;
+ {error, OReason} ->
+ e("Failed open socket: "
+ "~n ~p", [OReason]),
+ exit({failed_open_socket, OReason})
+ end;
+do_manager_init(Domain, seqpacket = Type, sctp = Proto, _UseMsg, _Peek) ->
+ %% This is as far as I have got with SCTP at the moment...
+ case socket:open(Domain, Type, Proto) of
+ {ok, Sock} ->
+ i("(sctp) socket opened: "
+ "~n ~p", [Sock]),
+ EXP = fun(_Desc, Expect, Expect) ->
+ Expect;
+ (Desc, Expect, Actual) ->
+ e("Unexpected result ~w: "
+ "~n Expect: ~p"
+ "~n Actual: ~p", [Desc, Expect, Actual]),
+ exit({Desc, Expect, Actual})
+ end,
+ GO = fun(O) -> case socket:getopt(Sock, sctp, O) of
+ {ok, V} -> f("~p", [V]);
+ {error, R} -> f("error: ~p", [R])
+ end
+ end,
+ %% ok = socket:setopt(Sock, otp, debug, true),
+
+ i("Miscellaneous options: "
+ "~n associnfo: ~s"
+ "~n autoclose: ~s"
+ "~n disable-fragments: ~s"
+ "~n initmsg: ~s"
+ "~n maxseg: ~s"
+ "~n nodelay: ~s"
+ "~n rtoinfo: ~s",
+ [GO(associnfo),
+ GO(autoclose),
+ GO(disable_fragments),
+ GO(initmsg),
+ GO(maxseg),
+ GO(nodelay),
+ GO(rtoinfo)]),
+
+ Events = #{data_in => true,
+ association => true,
+ address => true,
+ send_failure => true,
+ peer_error => true,
+ shutdown => true,
+ partial_delivery => true,
+ adaptation_layer => true,
+ authentication => true,
+ sender_dry => true},
+ EXP(set_sctp_events, ok, socket:setopt(Sock, sctp, events, Events)),
+ EXP(close_socket, ok, socket:close(Sock));
+ {error, Reason} ->
+ exit({failed_open, Reason})
+ end;
+do_manager_init(Domain, raw = Type, Proto, UseMsg, Peek) when is_integer(Proto) ->
+ do_manager_init(Domain, Type, {raw, Proto}, UseMsg, Peek);
+do_manager_init(Domain, raw = Type, Proto, _UseMsg, _Peek) ->
+ case socket:open(Domain, Type, Proto) of
+ {ok, Sock} ->
+ i("(sctp) socket opened: "
+ "~n ~p", [Sock]),
+ socket:close(Sock);
+ {error, Reason} ->
+ exit({failed_open, Reason})
+ end.
+
+
+
+manager_stream_init(Domain, Type, Proto) ->
+ i("try (socket) open"),
+ Sock = case socket:open(Domain, Type, Proto) of
+ {ok, S} ->
+ S;
+ {error, OReason} ->
+ throw({open, OReason})
+ end,
+ F = fun(X) -> case socket:getopt(Sock, socket, X) of
+ {ok, V} -> f("~p", [V]);
+ {error, R} -> f("error: ~p", [R])
+ end
+ end,
+ i("(socket) open (~s,~s,~s): "
+ "~n debug: ~s"
+ "~n prio: ~s"
+ "~n => try find (local) address",
+ [F(domain), F(type), F(protocol), F(debug), F(priority)]),
+ Addr = which_addr(Domain),
+ SA = #{family => Domain,
+ addr => Addr},
+ i("found: "
+ "~n ~p"
+ "~n => try (socket) bind", [Addr]),
+ %% ok = socket:setopt(Sock, otp, debug, true),
+ %% ok = socket:setopt(Sock, socket, debug, 1), %% must have rights!!
+ Port = case socket:bind(Sock, SA) of
+ {ok, P} ->
+ %% ok = socket:setopt(Sock, socket, debug, 0), %% must have rights!!
+ %% ok = socket:setopt(Sock, otp, debug, false),
+ P;
+ {error, BReason} ->
+ throw({bind, BReason})
+ end,
+ i("bound to: "
+ "~n ~p"
+ "~n => try (socket) listen (acceptconn: ~s)",
+ [Port, F(acceptconn)]),
+ case socket:listen(Sock) of
+ ok ->
+ i("listening (acceptconn: ~s)",
+ [F(acceptconn)]),
+ manager_stream_init(Sock, 1, ?NUM_ACCEPTORS, []);
+ {error, LReason} ->
+ throw({listen, LReason})
+ end.
+
+which_addr(Domain) ->
+ Iflist = case inet:getifaddrs() of
+ {ok, IFL} ->
+ IFL;
+ {error, Reason} ->
+ throw({inet,getifaddrs,Reason})
+ end,
+ which_addr(Domain, Iflist).
+
+which_addr(_Domain, []) ->
+ throw(no_address);
+which_addr(Domain, [{Name, IFO}|_IFL]) when (Name =/= "lo") ->
+ which_addr2(Domain, IFO);
+which_addr(Domain, [_|IFL]) ->
+ which_addr(Domain, IFL).
+
+which_addr2(_, []) ->
+ throw(no_address);
+which_addr2(inet = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 4) ->
+ Addr;
+which_addr2(inet6 = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 8) ->
+ Addr;
+which_addr2(Domain, [_|IFO]) ->
+ which_addr2(Domain, IFO).
+
+
+manager_stream_init(Sock, ID, NumAcceptors, Acc)
+ when (NumAcceptors > 0) ->
+ i("try start acceptor"),
+ case acceptor_start(Sock, ID) of
+ {ok, {Pid, MRef}} ->
+ i("acceptor ~w (~p) started", [ID, Pid]),
+ ?LIB:sleep(2000),
+ manager_stream_init(Sock, ID+1, NumAcceptors-1,
+ [{ID, Pid, MRef}|Acc]);
+ {error, Reason} ->
+ exit({failed_starting_acceptor, Reason})
+ end;
+manager_stream_init(Sock, _ID, 0, Acc) ->
+ %% Req = {kill_acceptor, length(Acc)}, % Last in the queue
+ %% Req = {kill_acceptor, 3}, % In the "middle" of the queue
+ %% Req = {kill_acceptor, 2}, % The first in the queue
+ %% Req = {kill_acceptor, 1}, % Current acceptor
+ %% Msg = {manager, self(), make_ref(), Req},
+ %% erlang:send_after(timer:seconds(10), self(), Msg),
+ {Sock, lists:reverse(Acc)}.
+
+
+manager_loop(M) ->
+ receive
+ {'DOWN', MRef, process, Pid, Reason} ->
+ M2 = manager_handle_down(M, MRef, Pid, Reason),
+ manager_loop(M2);
+
+ {manager, Pid, Ref, Request} ->
+ M2 = manager_handle_request(M, Pid, Ref, Request),
+ manager_loop(M2)
+ end.
+
+
+manager_handle_down(#manager{acceptors = Acceptors,
+ handlers = Handlers} = M, MRef, Pid, Reason) ->
+ case lists:keysearch(Pid, 2, Acceptors) of
+ {value, {ID, Pid, MRef}} when (Reason =:= normal) ->
+ i("acceptor ~w exited (normally)", [ID]),
+ case lists:keydelete(Pid, 2, Acceptors) of
+ [] ->
+ %% We are done
+ i("the last acceptor - we are done"),
+ exit(normal);
+ Acceptors2 ->
+ M#manager{acceptors = Acceptors2}
+ end;
+ {value, {ID, Pid, MRef}} ->
+ e("acceptor ~w crashed: "
+ "~n ~p", [ID, Reason]),
+ exit({acceptor_died, Reason});
+
+ false -> %% handler!
+ if
+ (Reason =/= normal) ->
+ e("handler ~p died: "
+ "~n ~p", [Pid, Reason]);
+ true ->
+ i("handler ~p terminated", [Pid])
+ end,
+ Handlers2 = lists:keydelete(Pid, 2, Handlers),
+ M#manager{handlers = Handlers2}
+ end.
+
+
+manager_handle_request(#manager{peek = Peek,
+ msg = UseMsg,
+ handler_id = HID,
+ handlers = Handlers} = M, Pid, Ref,
+ {start_handler, Sock}) ->
+ i("try start handler (~w)", [HID]),
+ case handler_start(HID, Sock, UseMsg, Peek) of
+ {ok, {HPid, HMRef}} ->
+ i("handler ~w started", [HID]),
+ manager_reply(Pid, Ref, {ok, HPid}),
+ M#manager{handler_id = HID+1,
+ handlers = [{HID, HPid, HMRef}|Handlers]};
+ {error, Reason} = ERROR ->
+ e("Failed starting new handler: "
+ "~n Sock: ~p"
+ "~n Reason: ~p", [Sock, Reason]),
+ manager_reply(Pid, Ref, ERROR),
+ M
+ end;
+manager_handle_request(#manager{socket = Sock,
+ acceptors = [{AID, APid, AMRef}]} = M, _Pid, _Ref,
+ {kill_acceptor, AID}) ->
+ i("try kill (only remeining) acceptor ~w", [AID]),
+ socket:setopt(Sock, otp, debug, true),
+ manager_stop_acceptor(APid, AMRef, AID, kill),
+ M#manager{acceptors = []};
+manager_handle_request(#manager{socket = Sock,
+ acceptors = Acceptors} = M, _Pid, _Ref,
+ {kill_acceptor, AID}) ->
+ i("try kill acceptor ~w", [AID]),
+ case lists:keysearch(AID, 1, Acceptors) of
+ {value, {AID, APid, AMRef}} ->
+ socket:setopt(Sock, otp, debug, true),
+ manager_stop_acceptor(APid, AMRef, AID, kill),
+ Acceptors2 = lists:keydelete(AID, 1, Acceptors),
+ M#manager{acceptors = Acceptors2};
+ false ->
+ e("no such acceptor"),
+ M
+ end;
+manager_handle_request(#manager{acceptors = Acceptors,
+ handlers = Handlers}, Pid, Ref,
+ {stop, Reason}) ->
+ i("stop"),
+ manager_reply(Pid, Ref, ok),
+ manager_stop_handlers(Handlers, Reason),
+ manager_stop_acceptors(Acceptors, Reason),
+ i("stopped", []),
+ exit(Reason).
+
+manager_stop_acceptors(Acceptors, Reason) ->
+ lists:foreach(fun({ID,P,M}) ->
+ manager_stop_acceptor(P, M, ID, Reason)
+ end, Acceptors).
+
+manager_stop_acceptor(Pid, MRef, ID, Reason) ->
+ i("try stop acceptor ~w (~p): ~p", [ID, Pid, Reason]),
+ erlang:demonitor(MRef, [flush]),
+ acceptor_stop(Pid, Reason),
+ ok.
+
+manager_stop_handlers(Handlers, Reason) ->
+ lists:foreach(fun({ID,P,M}) ->
+ manager_stop_handler(P, M, ID, Reason)
+ end, Handlers).
+
+manager_stop_handler(Pid, MRef, ID, Reason) ->
+ i("try stop handler ~w (~p): ~p", [ID, Pid, Reason]),
+ erlang:demonitor(MRef, [flush]),
+ handler_stop(Pid, Reason),
+ ok.
+
+
+
+%% =========================================================================
+
+acceptor_start(Sock, ID) ->
+ Self = self(),
+ A = {Pid, _} = spawn_monitor(fun() ->
+ acceptor_init(Self, Sock, ID)
+ end),
+ receive
+ {acceptor, Pid, ok} ->
+ {ok, A};
+ {acceptor, Pid, {error, _} = Error} ->
+ exit(Pid, kill), % Just in case
+ Error;
+ {'DOWN', _MRef, process, Pid, Reason} ->
+ {error, {crashed, Reason}}
+ end.
+
+acceptor_stop(Pid, _Reason) ->
+ %% acceptor_request(Pid, {stop, Reason}).
+ exit(Pid, kill).
+
+%% acceptor_request(Pid, Request) ->
+%% request(acceptor, Pid, Request).
+
+%% acceptor_reply(Pid, Ref, Reply) ->
+%% reply(acceptor, Pid, Ref, Reply).
+
+
+acceptor_init(Manager, Sock, ID) ->
+ put(sname, f("acceptor[~w]", [ID])),
+ Manager ! {acceptor, self(), ok},
+ %% ok = socket:setopt(Sock, otp, debug, true),
+ acceptor_loop(#acceptor{id = ID,
+ manager = Manager,
+ socket = Sock}).
+
+acceptor_loop(#acceptor{socket = LSock, atimeout = Timeout} = A) ->
+ i("try accept"),
+ case socket:accept(LSock, Timeout) of
+ {ok, Sock} ->
+ i("accepted: "
+ "~n ~p"
+ "~nwhen"
+ "~n ~p", [Sock, socket:info()]),
+ case acceptor_handle_accept_success(A, Sock) of
+ ok ->
+ acceptor_loop(A);
+ {error, Reason} ->
+ e("Failed starting handler: "
+ "~n ~p", [Reason]),
+ socket:close(Sock),
+ exit({failed_starting_handler, Reason})
+ end;
+ {error, timeout} ->
+ i("timeout"),
+ acceptor_loop(A);
+ {error, Reason} ->
+ e("accept failure: "
+ "~n ~p", [Reason]),
+ exit({accept, Reason})
+ end.
+
+acceptor_handle_accept_success(#acceptor{manager = Manager}, Sock) ->
+ i("try start handler for peer"
+ "~n ~p", [case socket:peername(Sock) of
+ {ok, Peer} -> Peer;
+ {error, _} = E -> E
+ end]),
+ case manager_start_handler(Manager, Sock) of
+ {ok, Pid} ->
+ i("handler (~p) started - now change 'ownership'", [Pid]),
+ case socket:setopt(Sock, otp, controlling_process, Pid) of
+ ok ->
+ %% Normally we should have a msgs collection here
+ %% (of messages we receive before the control was
+ %% handled over to Handler), but since we don't
+ %% have active implemented yet...
+ i("new handler (~p) now controlling process", [Pid]),
+ handler_continue(Pid),
+ ok;
+ {error, _} = ERROR ->
+ exit(Pid, kill),
+ ERROR
+ end;
+ {error, Reason2} ->
+ e("failed starting handler: "
+ "~n (new) Socket: ~p"
+ "~n Reason: ~p", [Sock, Reason2]),
+ exit({failed_starting_handler, Reason2})
+ end.
+
+
+
+%% =========================================================================
+
+handler_start(ID, Sock, UseMsg, Peek) ->
+ Self = self(),
+ H = {Pid, _} = spawn_monitor(fun() ->
+ handler_init(Self, ID, UseMsg, Peek, Sock)
+ end),
+ receive
+ {handler, Pid, ok} ->
+ {ok, H};
+ {handler, Pid, {error, _} = ERROR} ->
+ exit(Pid, kill), % Just in case
+ ERROR
+ end.
+
+handler_stop(Pid, _Reason) ->
+ %% handler_request(Pid, {stop, Reason}).
+ exit(Pid, kill).
+
+handler_continue(Pid) ->
+ handler_request(Pid, continue).
+
+handler_request(Pid, Request) ->
+ ?LIB:request(handler, Pid, Request).
+
+handler_reply(Pid, Ref, Reply) ->
+ ?LIB:reply(handler, Pid, Ref, Reply).
+
+
+handler_init(Manager, ID, Msg, Peek, Sock) ->
+ put(sname, f("handler:~w", [ID])),
+ i("starting"),
+ Manager ! {handler, self(), ok},
+ receive
+ {handler, Pid, Ref, continue} ->
+ i("got continue"),
+ handler_reply(Pid, Ref, ok),
+ G = fun(L, O) -> case socket:getopt(Sock, L, O) of
+ {ok, Val} ->
+ f("~p", [Val]);
+ {error, R} when is_atom(R) ->
+ f("error: ~w", [R]);
+ {error, {T, R}} when is_atom(T) ->
+ f("error: ~w, ~p", [T, R]);
+ {error, R} ->
+ f("error: ~p", [R])
+ end
+ end,
+ GSO = fun(O) -> G(socket, O) end,
+ GIP4 = fun(O) -> G(ip, O) end,
+ GIP6 = fun(O) -> G(ipv6, O) end,
+ {ok, Domain} = socket:getopt(Sock, socket, domain),
+ {ok, Type} = socket:getopt(Sock, socket, type),
+ {ok, Proto} = socket:getopt(Sock, socket, protocol),
+ B2D = GSO(bindtodevice),
+ RA = GSO(reuseaddr),
+ RP = GSO(reuseport),
+ OOBI = GSO(oobinline),
+ RcvBuf = GSO(rcvbuf),
+ RcvLW = GSO(rcvlowat),
+ RcvTO = GSO(rcvtimeo),
+ SndBuf = GSO(sndbuf),
+ SndLW = GSO(sndlowat),
+ SndTO = GSO(sndtimeo),
+ Linger = GSO(linger),
+ Timestamp = GSO(timestamp),
+ FreeBind = GIP4(freebind),
+ MTU = GIP4(mtu),
+ MTUDisc = GIP4(mtu_discover),
+ MALL = GIP4(multicast_all),
+ MIF4 = GIP4(multicast_if),
+ MLoop4 = GIP4(multicast_loop),
+ MTTL = GIP4(multicast_ttl),
+ NF = GIP4(nodefrag), % raw only
+ PktInfo = GIP4(pktinfo), % dgram only
+ RecvErr4 = GIP4(recverr),
+ RecvIF = GIP4(recvif), % Only dgram and raw (and FreeBSD)
+ RecvOPTS = GIP4(recvopts), % Not stream
+ RecvOrigDstAddr = GIP4(recvorigdstaddr),
+ RecvTOS = GIP4(recvtos),
+ RecvTTL = GIP4(recvttl), % not stream
+ RetOpts = GIP4(retopts), % not stream
+ SendSrcAddr = GIP4(sendsrcaddr),
+ TOS = GIP4(tos),
+ Transparent = GIP4(transparent),
+ TTL = GIP4(ttl),
+ MHops = GIP6(multicast_hops),
+ MIF6 = GIP6(multicast_if), % Only dgram and raw
+ MLoop6 = GIP6(multicast_loop),
+ RecvErr6 = GIP6(recverr),
+ RecvPktInfo = GIP6(recvpktinfo),
+ RtHdr = GIP6(rthdr),
+ AuthHdr = GIP6(authhdr),
+ HopLimit = GIP6(hoplimit),
+ HopOpts = GIP6(hopopts),
+ DstOpts = GIP6(dstopts),
+ FlowInfo = GIP6(flowinfo),
+ UHops = GIP6(unicast_hops),
+ i("got continue when: "
+ "~n (socket) Domain: ~p"
+ "~n (socket) Type: ~p"
+ "~n (socket) Protocol: ~p"
+ "~n (socket) Reuse Address: ~s"
+ "~n (socket) Reuse Port: ~s"
+ "~n (socket) Bind To Device: ~s"
+ "~n (socket) OOBInline: ~s"
+ "~n (socket) RcvBuf: ~s"
+ "~n (socket) RcvLW: ~s"
+ "~n (socket) RcvTO: ~s"
+ "~n (socket) SndBuf: ~s"
+ "~n (socket) SndLW: ~s"
+ "~n (socket) SndTO: ~s"
+ "~n (socket) Linger: ~s"
+ "~n (socket) Timestamp: ~s"
+ "~n (ip) FreeBind: ~s"
+ "~n (ip) MTU: ~s"
+ "~n (ip) MTU Discovery: ~s"
+ "~n (ip) Multicast ALL: ~s"
+ "~n (ip) Multicast IF: ~s"
+ "~n (ip) Multicast Loop: ~s"
+ "~n (ip) Multicast TTL: ~s"
+ "~n (ip) Node Frag: ~s"
+ "~n (ip) Pkt Info: ~s"
+ "~n (ip) Recv Err: ~s"
+ "~n (ip) Recv IF: ~s"
+ "~n (ip) Recv OPTS: ~s"
+ "~n (ip) Recv Orig Dst Addr: ~s"
+ "~n (ip) Recv TOS: ~s"
+ "~n (ip) Recv TTL: ~s"
+ "~n (ip) Ret Opts: ~s"
+ "~n (ip) Send Src Addr: ~s"
+ "~n (ip) TOS: ~s"
+ "~n (ip) Transparent: ~s"
+ "~n (ip) TTL: ~s"
+ "~n (ipv6) Multicast Hops: ~s"
+ "~n (ipv6) Multicast IF: ~s"
+ "~n (ipv6) Multicast Loop: ~s"
+ "~n (ipv6) Recv Err: ~s"
+ "~n (ipv6) Recv Pkt Info: ~s"
+ "~n (ipv6) RT Hdr: ~s"
+ "~n (ipv6) Auth Hdr: ~s"
+ "~n (ipv6) Hop Limit: ~s"
+ "~n (ipv6) Hop Opts: ~s"
+ "~n (ipv6) Dst Opts: ~s"
+ "~n (ipv6) Flow Info: ~s"
+ "~n (ipv6) Unicast Hops: ~s",
+ [Domain, Type, Proto,
+ RA, RP, B2D, OOBI,
+ RcvBuf, RcvLW, RcvTO, SndBuf, SndLW, SndTO,
+ Linger, Timestamp,
+ FreeBind, MTU, MTUDisc, MALL, MIF4, MLoop4, MTTL,
+ NF, PktInfo,RecvErr4,
+ RecvIF, RecvOPTS, RecvOrigDstAddr, RecvTOS, RecvTTL, RetOpts,
+ SendSrcAddr, TOS, Transparent, TTL,
+ MHops, MIF6, MLoop6, RecvErr6, RecvPktInfo,
+ RtHdr, AuthHdr, HopLimit, HopOpts, DstOpts, FlowInfo,
+ UHops]),
+
+ %% ok = socket:setopt(Sock, otp, debug, true),
+ %% case socket:getopt(Sock, 0, {13, int}) of
+ %% {ok, Val} ->
+ %% i("PktOpts ok: ~p", [Val]);
+ %% {error, Reason} ->
+ %% e("PktOpts err: ~p", [Reason])
+ %% end,
+ %% ok = socket:setopt(Sock, otp, debug, false),
+ SSO = fun(O, V) -> soso(Sock, O, V) end,
+ SIP4 =
+ fun(O, V) ->
+ if
+ (Type =:= dgram) ->
+ ok = soip(Sock, O, V);
+ true ->
+ ok
+ end
+ end,
+ SSO(timestamp, true),
+ SIP4(pktinfo, true),
+ ok = soip(Sock, recvtos, true),
+ SIP4(recvttl, true),
+ ok = soip(Sock, recvorigdstaddr, true),
+
+ handler_loop(#handler{msg = Msg,
+ peek = Peek,
+ manager = Manager,
+ type = Type,
+ socket = Sock})
+ end.
+
+so(Sock, Lvl, Opt, Val) ->
+ ok = socket:setopt(Sock, Lvl, Opt, Val).
+
+soso(Sock, Opt, Val) ->
+ so(Sock, socket, Opt, Val).
+
+soip(Sock, Opt, Val) ->
+ so(Sock, ip, Opt, Val).
+
+%% soipv6(Sock, Opt, Val) ->
+%% so(Sock, ipv6, Opt, Val).
+
+handler_loop(H) ->
+ i("try read message"),
+ case recv(H) of
+ {ok, {Source, Msg}} ->
+ i("received ~w bytes of data~s",
+ [size(Msg), case Source of
+ undefined -> "";
+ _ -> f(" from:~n ~p", [Source])
+ end]),
+ case ?LIB:dec_msg(Msg) of
+ {request, N, Req} ->
+ i("received request ~w: "
+ "~n ~p", [N, Req]),
+ Reply = ?LIB:enc_rep_msg(N, "hoppsan"),
+ case send(H, Reply, Source) of
+ ok ->
+ i("successfully sent reply ~w", [N]),
+ handler_loop(H);
+ {error, SReason} ->
+ e("failed sending reply ~w:"
+ "~n ~p", [N, SReason]),
+ exit({failed_sending_reply, SReason})
+ end
+ end;
+
+ {error, closed} ->
+ i("closed when"
+ "~n ~p", [socket:info()]),
+ exit(normal);
+
+ {error, RReason} ->
+ e("failed reading request: "
+ "~n ~p", [RReason]),
+ exit({failed_reading_request, RReason})
+ end.
+
+
+recv(#handler{peek = true, socket = Sock, type = stream}) ->
+ peek_recv(Sock);
+recv(#handler{socket = Sock, msg = true, type = stream}) ->
+ case socket:recvmsg(Sock) of
+ {ok, #{addr := undefined = Source,
+ iov := [Data],
+ ctrl := CMsgHdrs,
+ flags := Flags}} ->
+ i("received message: "
+ "~n CMsgHdrs: ~p"
+ "~n Flags: ~p", [CMsgHdrs, Flags]),
+ {ok, {Source, Data}};
+ {ok, X} ->
+ e("received *unexpected* message: "
+ "~n ~p", [X]),
+ {error, {unexpected, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+recv(#handler{socket = Sock, msg = true, type = dgram}) ->
+ case socket:recvmsg(Sock) of
+ {ok, #{addr := Source,
+ iov := [Data],
+ ctrl := CMsgHdrs,
+ flags := Flags}} ->
+ i("received message: "
+ "~n CMsgHdrs: ~p"
+ "~n Flags: ~p", [CMsgHdrs, Flags]),
+ {ok, {Source, Data}};
+ {ok, X} ->
+ {error, {unexpected, X}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+recv(#handler{peek = false, socket = Sock, type = stream}) ->
+ do_recv(Sock);
+recv(#handler{peek = Peek, socket = Sock, type = dgram})
+ when (Peek =:= true) ->
+ %% ok = socket:setopt(Sock, otp, debug, true),
+ RES = peek_recvfrom(Sock, 5),
+ %% ok = socket:setopt(Sock, otp, debug, false),
+ RES;
+recv(#handler{peek = Peek, socket = Sock, type = dgram})
+ when (Peek =:= false) ->
+ %% ok = socket:setopt(Sock, otp, debug, true),
+ socket:recvfrom(Sock).
+
+do_recv(Sock) ->
+ case socket:recv(Sock) of
+ {ok, Msg} ->
+ {ok, {undefined, Msg}};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+peek_recv(Sock) ->
+ i("try peek on the message type (expect request)"),
+ Type = ?LIB:req(),
+ case socket:recv(Sock, 4, [peek]) of
+ {ok, <<Type:32>>} ->
+ i("was request - do proper recv"),
+ do_recv(Sock);
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+peek_recvfrom(Sock, BufSz) ->
+ i("try peek recvfrom with buffer size ~w", [BufSz]),
+ case socket:recvfrom(Sock, BufSz, [peek]) of
+ {ok, {_Source, Msg}} when (BufSz =:= size(Msg)) ->
+ %% i("we filled the buffer: "
+ %% "~n ~p", [Msg]),
+ %% It *may not* fit => try again with double size
+ peek_recvfrom(Sock, BufSz*2);
+ {ok, _} ->
+ %% It fits => read for real
+ i("we did *not* fill the buffer - do the 'real' read"),
+ socket:recvfrom(Sock);
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+send(#handler{socket = Sock, msg = true, type = stream, stimeout = Timeout},
+ Msg, _) ->
+ CMsgHdr = #{level => ip, type => tos, data => reliability},
+ CMsgHdrs = [CMsgHdr],
+ MsgHdr = #{iov => [Msg], ctrl => CMsgHdrs},
+ %% socket:setopt(Sock, otp, debug, true),
+ Res = socket:sendmsg(Sock, MsgHdr, Timeout),
+ %% socket:setopt(Sock, otp, debug, false),
+ Res;
+send(#handler{socket = Sock, type = stream, stimeout = Timeout}, Msg, _) ->
+ socket:send(Sock, Msg, Timeout);
+send(#handler{socket = Sock, msg = true, type = dgram, stimeout = Timeout},
+ Msg, Dest) ->
+ CMsgHdr = #{level => ip, type => tos, data => reliability},
+ CMsgHdrs = [CMsgHdr],
+ MsgHdr = #{addr => Dest,
+ ctrl => CMsgHdrs,
+ iov => [Msg]},
+ %% ok = socket:setopt(Sock, otp, debug, true),
+ Res = socket:sendmsg(Sock, MsgHdr, Timeout),
+ %% ok = socket:setopt(Sock, otp, debug, false),
+ Res;
+send(#handler{socket = Sock, type = dgram, stimeout = Timeout}, Msg, Dest) ->
+ socket:sendto(Sock, Msg, Dest, Timeout).
+
+%% filler() ->
+%% list_to_binary(lists:duplicate(2048, " FILLER ")).
+
+
+
+%% =========================================================================
+
+f(F, A) ->
+ ?LIB:f(F, A).
+
+e(F) ->
+ e(F, []).
+e(F, A) ->
+ ?LIB:e(F, A).
+
+i(F) ->
+ ?LIB:i(F).
+
+i(F, A) ->
+ ?LIB:i(F, A).
+
diff --git a/erts/emulator/test/socket_test_evaluator.erl b/erts/emulator/test/socket_test_evaluator.erl
new file mode 100644
index 0000000000..fe6a6ff70a
--- /dev/null
+++ b/erts/emulator/test/socket_test_evaluator.erl
@@ -0,0 +1,524 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_evaluator).
+
+%% Evaluator control functions
+-export([
+ start/3,
+ await_finish/1
+ ]).
+
+%% Functions used by evaluators to interact with eachother
+-export([
+ %% Announce functions
+ %% (Send an announcement from one evaluator to another)
+ announce_start/1, announce_start/2,
+ announce_continue/2, announce_continue/3,
+ announce_ready/2, announce_ready/3,
+ announce_terminate/1,
+
+ %% Await functions
+ %% (Wait for an announcement from another evaluator)
+ await_start/0, await_start/1,
+ await_continue/3, await_continue/4,
+ await_ready/3, await_ready/4,
+ await_terminate/2, await_terminate/3,
+ await_termination/1, await_termination/2
+ ]).
+
+%% Utility functions
+-export([
+ iprint/2, % Info printouts
+ eprint/2 % Error printouts
+ ]).
+
+-export_type([
+ ev/0,
+ initial_evaluator_state/0,
+ evaluator_state/0,
+ command_fun/0,
+ command/0
+ ]).
+
+
+-include("socket_test_evaluator.hrl").
+
+-type ev() :: #ev{}.
+-type initial_evaluator_state() :: map().
+-type evaluator_state() :: term().
+-type command_fun() ::
+ fun((State :: evaluator_state()) -> ok) |
+ fun((State :: evaluator_state()) -> {ok, evaluator_state()}) |
+ fun((State :: evaluator_state()) -> {error, term()}).
+
+-type command() :: #{desc := string(),
+ cmd := command_fun()}.
+
+
+%% ============================================================================
+
+-define(LIB, socket_test_lib).
+-define(LOGGER, socket_test_logger).
+
+-define(EXTRA_NOTHING, '$nothing').
+-define(ANNOUNCEMENT_START, '$start').
+-define(ANNOUNCEMENT_READY, '$ready').
+-define(ANNOUNCEMENT_CONTINUE, '$continue').
+-define(ANNOUNCEMENT_TERMINATE, '$terminate').
+
+-define(START_NAME_NONE, '$no-name').
+-define(START_SLOGAN, ?ANNOUNCEMENT_START).
+-define(TERMINATE_SLOGAN, ?ANNOUNCEMENT_TERMINATE).
+
+
+%% ============================================================================
+
+-spec start(Name, Seq, Init) -> ev() when
+ Name :: string(),
+ Seq :: [command()],
+ Init :: initial_evaluator_state().
+
+start(Name, Seq, InitState)
+ when is_list(Name) andalso is_list(Seq) andalso (Seq =/= []) ->
+ %% Make sure 'parent' is not already used
+ case maps:find(parent, InitState) of
+ {ok, _} ->
+ erlang:error({already_used, parent});
+ error ->
+ InitState2 = InitState#{parent => self()},
+ {Pid, MRef} = erlang:spawn_monitor(
+ fun() -> init(Name, Seq, InitState2) end),
+ #ev{name = Name, pid = Pid, mref = MRef}
+ end.
+
+init(Name, Seq, Init) ->
+ put(sname, Name),
+ loop(1, Seq, Init).
+
+loop(_ID, [], FinalState) ->
+ exit(FinalState);
+loop(ID, [#{desc := Desc,
+ cmd := Cmd}|Cmds], State) when is_function(Cmd, 1) ->
+ iprint("evaluate command ~2w: ~s", [ID, Desc]),
+ try Cmd(State) of
+ ok ->
+ loop(ID + 1, Cmds, State);
+ {ok, NewState} ->
+ loop(ID + 1, Cmds, NewState);
+ {skip, Reason} ->
+ exit({skip, Reason});
+ {error, Reason} ->
+ eprint("command ~w failed: "
+ "~n Reason: ~p", [ID, Reason]),
+ exit({command_failed, ID, Reason, State})
+ catch
+ throw:{skip, R} = E:_ ->
+ eprint("command ~w skip: "
+ "~n Skip Reason: ~p", [ID, R]),
+ exit(E);
+ C:E:S ->
+ eprint("command ~w crashed: "
+ "~n Class: ~p"
+ "~n Error: ~p"
+ "~n Call Stack: ~p", [ID, C, E, S]),
+ exit({command_crashed, ID, {C,E,S}, State})
+ end.
+
+
+%% ============================================================================
+
+-spec await_finish(Evs) -> term() when
+ Evs :: [ev()].
+
+await_finish(Evs) ->
+ await_finish(Evs, []).
+
+await_finish([], []) ->
+ ok;
+await_finish([], Fails) ->
+ ?SEV_EPRINT("Fails: "
+ "~n ~p", [Fails]),
+ Fails;
+await_finish(Evs, Fails) ->
+ receive
+ %% Successfull termination of evaluator
+ {'DOWN', _MRef, process, Pid, normal} ->
+ case lists:keysearch(Pid, #ev.pid, Evs) of
+ {value, #ev{name = Name}} ->
+ iprint("evaluator '~s' (~p) success", [Name, Pid]),
+ NewEvs = lists:keydelete(Pid, #ev.pid, Evs),
+ await_finish(NewEvs, Fails);
+ false ->
+ iprint("unknown process ~p died (normal)", [Pid]),
+ await_finish(Evs, Fails)
+ end;
+
+ %% The evaluator can skip the teat case:
+ {'DOWN', _MRef, process, Pid, {skip, Reason}} ->
+ case lists:keysearch(Pid, #ev.pid, Evs) of
+ {value, #ev{name = Name}} ->
+ iprint("evaluator '~s' (~p) issued SKIP: "
+ "~n ~p", [Name, Pid, Reason]);
+ false ->
+ iprint("unknown process ~p issued SKIP: "
+ "~n ~p", [Pid, Reason])
+ end,
+ ?LIB:skip(Reason);
+
+ %% Evaluator failed
+ {'DOWN', _MRef, process, Pid, Reason} ->
+ case lists:keysearch(Pid, #ev.pid, Evs) of
+ {value, #ev{name = Name}} ->
+ iprint("evaluator '~s' (~p) failed", [Name, Pid]),
+ NewEvs = lists:keydelete(Pid, #ev.pid, Evs),
+ await_finish(NewEvs, [{Pid, Reason}|Fails]);
+ false ->
+ iprint("unknown process ~p died: "
+ "~n ~p", [Pid, Reason]),
+ await_finish(Evs, Fails)
+ end
+ end.
+
+
+%% ============================================================================
+
+-spec announce_start(To) -> ok when
+ To :: pid().
+
+announce_start(To) ->
+ announce(To, ?ANNOUNCEMENT_START, ?START_SLOGAN).
+
+-spec announce_start(To, Extra) -> ok when
+ To :: pid(),
+ Extra :: term().
+
+announce_start(To, Extra) ->
+ announce(To, ?ANNOUNCEMENT_START, ?START_SLOGAN, Extra).
+
+
+%% ============================================================================
+
+-spec announce_continue(To, Slogan) -> ok when
+ To :: pid(),
+ Slogan :: atom().
+
+announce_continue(To, Slogan) ->
+ announce_continue(To, Slogan, ?EXTRA_NOTHING).
+
+-spec announce_continue(To, Slogan, Extra) -> ok when
+ To :: pid(),
+ Slogan :: atom(),
+ Extra :: term().
+
+announce_continue(To, Slogan, Extra) ->
+ announce(To, ?ANNOUNCEMENT_CONTINUE, Slogan, Extra).
+
+
+%% ============================================================================
+
+-spec announce_ready(To, Slogan) -> ok when
+ To :: pid(),
+ Slogan :: atom().
+
+announce_ready(To, Slogan) ->
+ announce_ready(To, Slogan, ?EXTRA_NOTHING).
+
+-spec announce_ready(To, Slogan, Extra) -> ok when
+ To :: pid(),
+ Slogan :: atom(),
+ Extra :: term().
+
+announce_ready(To, Slogan, Extra) ->
+ announce(To, ?ANNOUNCEMENT_READY, Slogan, Extra).
+
+
+%% ============================================================================
+
+-spec announce_terminate(To) -> ok when
+ To :: pid().
+
+announce_terminate(To) ->
+ announce(To, ?ANNOUNCEMENT_TERMINATE, ?TERMINATE_SLOGAN).
+
+
+%% ============================================================================
+
+-spec announce(To, Announcement, Slogan) -> ok when
+ To :: pid(),
+ Announcement :: atom(),
+ Slogan :: atom().
+
+announce(To, Announcement, Slogan) ->
+ announce(To, Announcement, Slogan, ?EXTRA_NOTHING).
+
+-spec announce(To, Announcement, Slogan, Extra) -> ok when
+ To :: pid(),
+ Announcement :: atom(),
+ Slogan :: atom(),
+ Extra :: term().
+
+announce(To, Announcement, Slogan, Extra)
+ when is_pid(To) andalso
+ is_atom(Announcement) andalso
+ is_atom(Slogan) ->
+ %% iprint("announce -> entry with: "
+ %% "~n To: ~p"
+ %% "~n Announcement: ~p"
+ %% "~n Slogan: ~p"
+ %% "~n Extra: ~p",
+ %% [To, Announcement, Slogan, Extra]),
+ To ! {Announcement, self(), Slogan, Extra},
+ ok.
+
+
+
+%% ============================================================================
+
+-spec await_start() -> Pid | {Pid, Extra} when
+ Pid :: pid(),
+ Extra :: term().
+
+await_start() ->
+ await_start(any).
+
+-spec await_start(Pid) -> Pid | {Pid, Extra} when
+ Pid :: pid(),
+ Extra :: term().
+
+await_start(P) when is_pid(P) orelse (P =:= any) ->
+ case await(P, ?START_NAME_NONE, ?ANNOUNCEMENT_START, ?START_SLOGAN, []) of
+ {ok, Any} when is_pid(P) ->
+ Any;
+ {ok, Pid} when is_pid(Pid) andalso (P =:= any) ->
+ Pid;
+ {ok, {Pid, _} = OK} when is_pid(Pid) andalso (P =:= any) ->
+ OK
+ end.
+
+
+%% ============================================================================
+
+-spec await_continue(From, Name, Slogan) -> ok | {ok, Extra} | {error, Reason} when
+ From :: pid(),
+ Name :: atom(),
+ Slogan :: atom(),
+ Extra :: term(),
+ Reason :: term().
+
+await_continue(From, Name, Slogan) ->
+ await_continue(From, Name, Slogan, []).
+
+-spec await_continue(From, Name, Slogan, OtherPids) ->
+ ok | {ok, Extra} | {error, Reason} when
+ From :: pid(),
+ Name :: atom(),
+ Slogan :: atom(),
+ OtherPids :: [{pid(), atom()}],
+ Extra :: term(),
+ Reason :: term().
+
+await_continue(From, Name, Slogan, OtherPids)
+ when is_pid(From) andalso
+ is_atom(Name) andalso
+ is_atom(Slogan) andalso
+ is_list(OtherPids) ->
+ await(From, Name, ?ANNOUNCEMENT_CONTINUE, Slogan, OtherPids).
+
+
+
+%% ============================================================================
+
+-spec await_ready(From, Name, Slogan) -> ok | {ok, Extra} | {error, Reason} when
+ From :: pid(),
+ Name :: atom(),
+ Slogan :: atom(),
+ Extra :: term(),
+ Reason :: term().
+
+await_ready(From, Name, Slogan) ->
+ await_ready(From, Name, Slogan, []).
+
+-spec await_ready(From, Name, Slogan, OtherPids) ->
+ ok | {ok, Extra} | {error, Reason} when
+ From :: pid(),
+ Name :: atom(),
+ Slogan :: atom(),
+ OtherPids :: [{pid(), atom()}],
+ Extra :: term(),
+ Reason :: term().
+
+await_ready(From, Name, Slogan, OtherPids)
+ when is_pid(From) andalso
+ is_atom(Name) andalso
+ is_atom(Slogan) andalso
+ is_list(OtherPids) ->
+ await(From, Name, ?ANNOUNCEMENT_READY, Slogan, OtherPids).
+
+
+
+%% ============================================================================
+
+-spec await_terminate(Pid, Name) -> ok | {error, Reason} when
+ Pid :: pid(),
+ Name :: atom(),
+ Reason :: term().
+
+await_terminate(Pid, Name) when is_pid(Pid) andalso is_atom(Name) ->
+ await_terminate(Pid, Name, []).
+
+-spec await_terminate(Pid, Name, OtherPids) -> ok | {error, Reason} when
+ Pid :: pid(),
+ Name :: atom(),
+ OtherPids :: [{pid(), atom()}],
+ Reason :: term().
+
+await_terminate(Pid, Name, OtherPids) ->
+ await(Pid, Name, ?ANNOUNCEMENT_TERMINATE, ?TERMINATE_SLOGAN, OtherPids).
+
+
+%% ============================================================================
+
+-spec await_termination(Pid) -> ok | {error, Reason} when
+ Pid :: pid(),
+ Reason :: term().
+
+await_termination(Pid) when is_pid(Pid) ->
+ await_termination(Pid, any).
+
+-spec await_termination(Pid, ExpReason) -> ok | {error, Reason} when
+ Pid :: pid(),
+ ExpReason :: term(),
+ Reason :: term().
+
+await_termination(Pid, ExpReason) ->
+ receive
+ {'DOWN', _, process, Pid, _} when (ExpReason =:= any) ->
+ ok;
+ {'DOWN', _, process, Pid, Reason} when (ExpReason =:= Reason) ->
+ ok;
+ {'DOWN', _, process, Pid, Reason} ->
+ {error, {unexpected_exit, ExpReason, Reason}}
+ end.
+
+
+%% ============================================================================
+
+%% We expect a message (announcement) from Pid, but we also watch for DOWN from
+%% both Pid and OtherPids, in which case the test has failed!
+
+-spec await(ExpPid, Name, Announcement, Slogan, OtherPids) ->
+ ok | {ok, Extra} | {error, Reason} when
+ ExpPid :: any | pid(),
+ Name :: atom(),
+ Announcement :: atom(),
+ Slogan :: atom(),
+ OtherPids :: [{pid(), atom()}],
+ Extra :: term(),
+ Reason :: term().
+
+await(ExpPid, Name, Announcement, Slogan, OtherPids)
+ when (is_pid(ExpPid) orelse (ExpPid =:= any)) andalso
+ is_atom(Name) andalso
+ is_atom(Announcement) andalso
+ is_atom(Slogan) andalso
+ is_list(OtherPids) ->
+ receive
+ {Announcement, Pid, Slogan, ?EXTRA_NOTHING} when (ExpPid =:= any) ->
+ {ok, Pid};
+ {Announcement, Pid, Slogan, Extra} when (ExpPid =:= any) ->
+ {ok, {Pid, Extra}};
+ {Announcement, Pid, Slogan, ?EXTRA_NOTHING} when (Pid =:= ExpPid) ->
+ ok;
+ {Announcement, Pid, Slogan, Extra} when (Pid =:= ExpPid) ->
+ {ok, Extra};
+ {'DOWN', _, process, Pid, {skip, SkipReason}} when (Pid =:= ExpPid) ->
+ iprint("Unexpected SKIP from ~w (~p): "
+ "~n ~p", [Name, Pid, SkipReason]),
+ ?LIB:skip({Name, SkipReason});
+ {'DOWN', _, process, Pid, Reason} when (Pid =:= ExpPid) ->
+ eprint("Unexpected DOWN from ~w (~p): "
+ "~n ~p", [Name, Pid, Reason]),
+ {error, {unexpected_exit, Name}};
+ {'DOWN', _, process, OtherPid, Reason} ->
+ case check_down(OtherPid, Reason, OtherPids) of
+ ok ->
+ iprint("DOWN from unknown process ~p: "
+ "~n ~p", [OtherPid, Reason]),
+ await(ExpPid, Name, Announcement, Slogan, OtherPids);
+ {error, _} = ERROR ->
+ ERROR
+ end
+ after infinity -> % For easy debugging, just change to some valid time (5000)
+ iprint("await -> timeout for msg from ~p (~w): "
+ "~n Announcement: ~p"
+ "~n Slogan: ~p"
+ "~nwhen"
+ "~n Messages: ~p",
+ [ExpPid, Name, Announcement, Slogan, pi(messages)]),
+ await(ExpPid, Name, Announcement, Slogan, OtherPids)
+ end.
+
+pi(Item) ->
+ pi(self(), Item).
+
+pi(Pid, Item) ->
+ {Item, Info} = process_info(Pid, Item),
+ Info.
+
+check_down(Pid, DownReason, Pids) ->
+ case lists:keymember(Pid, 1, Pids) of
+ {value, {_, Name}} ->
+ eprint("Unexpected DOWN from ~w (~p): "
+ "~n ~p", [Name, Pid, DownReason]),
+ {error, {unexpected_exit, Name}};
+ false ->
+ ok
+ end.
+
+
+%% ============================================================================
+
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+
+iprint(F, A) ->
+ print("", F, A).
+
+eprint(F, A) ->
+ print("<ERROR> ", F, A).
+
+print(Prefix, F, A) ->
+ %% The two prints is to get the output both in the shell (for when
+ %% "personal" testing is going on) and in the logs.
+ IDStr =
+ case get(sname) of
+ undefined ->
+ %% This means its not an evaluator,
+ %% or a named process. Instead its
+ %% most likely the test case itself,
+ %% so skip the name and the pid.
+ "";
+ SName ->
+ f("[~s][~p]", [SName, self()])
+ end,
+ ?LOGGER:format("[~s]~s ~s" ++ F,
+ [?LIB:formated_timestamp(), IDStr, Prefix | A]).
diff --git a/erts/emulator/test/socket_test_evaluator.hrl b/erts/emulator/test/socket_test_evaluator.hrl
new file mode 100644
index 0000000000..5be49dc022
--- /dev/null
+++ b/erts/emulator/test/socket_test_evaluator.hrl
@@ -0,0 +1,68 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-ifndef(socket_test_evaluator).
+-define(socket_test_evaluator, true).
+
+-record(ev, {name :: string(),
+ pid :: pid(),
+ mref :: reference()}).
+
+-define(SEV, socket_test_evaluator).
+
+-define(SEV_START(N, S, IS), ?SEV:start(N, S, IS)).
+-define(SEV_AWAIT_FINISH(Evs), ?SEV:await_finish(Evs)).
+
+-define(SEV_ANNOUNCE_START(To), ?SEV:announce_start(To)).
+-define(SEV_ANNOUNCE_START(To, Ex), ?SEV:announce_start(To, Ex)).
+-define(SEV_ANNOUNCE_CONTINUE(To, S), ?SEV:announce_continue(To, S)).
+-define(SEV_ANNOUNCE_CONTINUE(To, S, Ex), ?SEV:announce_continue(To, S, Ex)).
+-define(SEV_ANNOUNCE_READY(To, S), ?SEV:announce_ready(To, S)).
+-define(SEV_ANNOUNCE_READY(To, S, Ex), ?SEV:announce_ready(To, S, Ex)).
+-define(SEV_ANNOUNCE_TERMINATE(To), ?SEV:announce_terminate(To)).
+
+-define(SEV_AWAIT_START(), ?SEV:await_start()).
+-define(SEV_AWAIT_START(P), ?SEV:await_start(P)).
+-define(SEV_AWAIT_CONTINUE(F, N, S), ?SEV:await_continue(F, N, S)).
+-define(SEV_AWAIT_CONTINUE(F, N, S, Ps), ?SEV:await_continue(F, N, S, Ps)).
+-define(SEV_AWAIT_READY(F, N, S), ?SEV:await_ready(F, N, S)).
+-define(SEV_AWAIT_READY(F, N, S, Ps), ?SEV:await_ready(F, N, S, Ps)).
+-define(SEV_AWAIT_TERMINATE(F, N), ?SEV:await_terminate(F, N)).
+-define(SEV_AWAIT_TERMINATE(F, N, Ps), ?SEV:await_terminate(F, N, Ps)).
+-define(SEV_AWAIT_TERMINATION(P), ?SEV:await_termination(P)).
+-define(SEV_AWAIT_TERMINATION(P, R), ?SEV:await_termination(P, R)).
+
+-define(SEV_IPRINT(F, A), ?SEV:iprint(F, A)).
+-define(SEV_IPRINT(F), ?SEV_IPRINT(F, [])).
+-define(SEV_EPRINT(F, A), ?SEV:eprint(F, A)).
+-define(SEV_EPRINT(F), ?SEV_EPRINT(F, [])).
+
+-define(SEV_SLEEP(T), #{desc => "sleep",
+ cmd => fun(_) ->
+ ?SLEEP(T),
+ ok
+ end}).
+-define(SEV_FINISH_NORMAL, #{desc => "finish",
+ cmd => fun(_) ->
+ {ok, normal}
+ end}).
+
+-endif. % -ifdef(socket_test_evaluator).
+
diff --git a/erts/emulator/test/socket_test_lib.erl b/erts/emulator/test/socket_test_lib.erl
new file mode 100644
index 0000000000..4e65c4f3c0
--- /dev/null
+++ b/erts/emulator/test/socket_test_lib.erl
@@ -0,0 +1,98 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_lib).
+
+-export([
+ pi/1, pi/2, pi/3,
+
+ %% Time stuff
+ timestamp/0,
+ tdiff/2,
+ formated_timestamp/0,
+ format_timestamp/1,
+
+ %% String and format
+ f/2,
+
+ %% Skipping
+ not_yet_implemented/0,
+ skip/1
+ ]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+pi(Item) when is_atom(Item) ->
+ pi(self(), Item).
+
+pi(Pid, Item) when is_pid(Pid) andalso is_atom(Item) ->
+ {Item, Info} = process_info(Pid, Item),
+ Info;
+pi(Node, Pid) when is_pid(Pid) ->
+ rpc:call(Node, erlang, process_info, [Pid]).
+
+pi(Node, Pid, Item) when is_pid(Pid) andalso is_atom(Item) ->
+ rpc:call(Node, erlang, process_info, [Pid, Item]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+timestamp() ->
+ os:timestamp().
+
+
+tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) ->
+ T1 = A1*1000000000+B1*1000+(C1 div 1000),
+ T2 = A2*1000000000+B2*1000+(C2 div 1000),
+ T2 - T1.
+
+
+formated_timestamp() ->
+ format_timestamp(os:timestamp()).
+
+format_timestamp({_N1, _N2, _N3} = TS) ->
+ {_Date, Time} = calendar:now_to_local_time(TS),
+ %% {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ %% FormatTS =
+ %% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w.~w",
+ %% [YYYY, MM, DD, Hour, Min, Sec, N3]),
+ FormatTS = io_lib:format("~.2.0w:~.2.0w:~.2.0w", [Hour, Min, Sec]),
+ lists:flatten(FormatTS).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+not_yet_implemented() ->
+ skip("not yet implemented").
+
+skip(Reason) ->
+ throw({skip, Reason}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/erts/emulator/test/socket_test_logger.erl b/erts/emulator/test/socket_test_logger.erl
new file mode 100644
index 0000000000..e42251158f
--- /dev/null
+++ b/erts/emulator/test/socket_test_logger.erl
@@ -0,0 +1,111 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_logger).
+
+-export([
+ start/0,
+ stop/0,
+ format/2
+ ]).
+
+
+-define(LIB, socket_test_lib).
+-define(LOGGER, ?MODULE).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start() ->
+ case global:whereis_name(?LOGGER) of
+ Pid when is_pid(Pid) ->
+ ok;
+ undefined ->
+ Self = self(),
+ Pid = spawn_link(fun() -> init(Self) end),
+ yes = global:register_name(?LOGGER, Pid),
+ ok
+ end.
+
+
+stop() ->
+ case global:whereis_name(?LOGGER) of
+ undefined ->
+ ok;
+ Pid when is_pid(Pid) ->
+ global:unregister_name(?LOGGER),
+ Pid ! {?LOGGER, '$logger', stop},
+ ok
+ end.
+
+
+format(F, []) ->
+ do_format(F);
+format(F, A) ->
+ do_format(?LIB:f(F, A)).
+
+do_format(Msg) ->
+ case global:whereis_name(?LOGGER) of
+ undefined ->
+ ok;
+ Pid when is_pid(Pid) ->
+ Pid ! {?MODULE, '$logger', {msg, Msg}},
+ ok
+ end.
+
+init(Parent) ->
+ put(sname, "logger"),
+ print("[~s][logger] starting~n", [?LIB:formated_timestamp()]),
+ loop(#{parent => Parent}).
+
+loop(#{parent := Parent} = State) ->
+ receive
+ {'EXIT', Parent, _} ->
+ print("[~s][logger] parent exit~n", [?LIB:formated_timestamp()]),
+ exit(normal);
+
+ {?MODULE, '$logger', stop} ->
+ print("[~s][logger] stopping~n", [?LIB:formated_timestamp()]),
+ exit(normal);
+
+ {?MODULE, '$logger', {msg, Msg}} ->
+ print(Msg),
+ loop(State)
+ end.
+
+
+print(F, A) ->
+ print(?LIB:f(F, A)).
+
+print(Str) ->
+ try
+ begin
+ io:format(user, Str ++ "~n", []),
+ io:format(Str, [])
+ end
+ catch
+ _:_:_ ->
+ io:format(user,
+ "~nFailed Format message:"
+ "~n~p~n", [Str]),
+ io:format("~nFailed Format message:"
+ "~n~p~n", [Str])
+ end.
+
diff --git a/erts/emulator/test/socket_test_ttest.hrl b/erts/emulator/test/socket_test_ttest.hrl
new file mode 100644
index 0000000000..1a004a9a7a
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest.hrl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-ifndef(socket_test_ttest).
+-define(socket_test_ttest, true).
+
+-define(TTEST_TAG, 42).
+-define(TTEST_TYPE_REQUEST, 101).
+-define(TTEST_TYPE_REPLY, 102).
+
+-define(SECS(I), timer:seconds(I)).
+
+-define(SLEEP(T), receive after T -> ok end).
+
+-endif. % -ifdef(socket_test_ttest).
diff --git a/erts/emulator/test/socket_test_ttest_client.hrl b/erts/emulator/test/socket_test_ttest_client.hrl
new file mode 100644
index 0000000000..84e736cc34
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest_client.hrl
@@ -0,0 +1,141 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-ifndef(socket_test_ttest_client).
+-define(socket_test_ttest_client, true).
+
+-define(MSG_ID_DEFAULT, 2).
+-define(RUNTIME_DEFAULT, ?SECS(10)).
+-define(MAX_ID, 16#FFFFFFFF).
+
+-define(MSG_DATA1, <<"This is test data 0123456789 0123456789 0123456789">>).
+-define(MSG_DATA2, <<"This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789">>).
+-define(MSG_DATA3, <<"This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789"
+ "This is test data 0123456789 0123456789 0123456789">>).
+
+
+-endif. % -ifdef(socket_test_ttest_client).
diff --git a/erts/emulator/test/socket_test_ttest_lib.erl b/erts/emulator/test/socket_test_ttest_lib.erl
new file mode 100644
index 0000000000..7fc13df46a
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest_lib.erl
@@ -0,0 +1,127 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_ttest_lib).
+
+-compile({no_auto_import, [error/2]}).
+
+-export([
+ t/0, tdiff/2,
+ formated_timestamp/0, format_timestamp/1,
+ format_time/1,
+
+ formated_process_stats/1, formated_process_stats/2,
+
+ format/2,
+ error/1, error/2,
+ info/1, info/2
+ ]).
+
+%% ==========================================================================
+
+t() ->
+ os:timestamp().
+
+tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) ->
+ T1 = A1*1000000000+B1*1000+(C1 div 1000),
+ T2 = A2*1000000000+B2*1000+(C2 div 1000),
+ T2 - T1.
+
+formated_timestamp() ->
+ format_timestamp(os:timestamp()).
+
+format_timestamp({_N1, _N2, N3} = TS) ->
+ {_Date, Time} = calendar:now_to_local_time(TS),
+ {Hour,Min,Sec} = Time,
+ FormatTS = io_lib:format("~.2.0w:~.2.0w:~.2.0w.4~w",
+ [Hour, Min, Sec, round(N3/1000)]),
+ lists:flatten(FormatTS).
+
+%% Time is always in number os ms (milli seconds)
+%% At some point, we should convert this to a more readable format...
+format_time(T) when (T < 1000) ->
+ format("~w ms", [T]);
+format_time(T) ->
+ format("~w sec (~w ms)", [T div 1000, T]).
+
+
+formated_process_stats(Pid) ->
+ formated_process_stats("", Pid).
+
+formated_process_stats(Prefix, Pid) when is_list(Prefix) andalso is_pid(Pid) ->
+ try
+ begin
+ TotHeapSz = pi(Pid, total_heap_size),
+ HeapSz = pi(Pid, heap_size),
+ StackSz = pi(Pid, stack_size),
+ Reds = pi(Pid, reductions),
+ GCInfo = pi(Pid, garbage_collection),
+ MinBinVHeapSz = proplists:get_value(min_bin_vheap_size, GCInfo),
+ MinHeapSz = proplists:get_value(min_heap_size, GCInfo),
+ MinGCS = proplists:get_value(minor_gcs, GCInfo),
+ format("~n ~sTotal Heap Size: ~p"
+ "~n ~sHeap Size: ~p"
+ "~n ~sStack Size: ~p"
+ "~n ~sReductions: ~p"
+ "~n ~s[GC] Min Bin VHeap Size: ~p"
+ "~n ~s[GC] Min Heap Size: ~p"
+ "~n ~s[GC] Minor GCS: ~p",
+ [Prefix, TotHeapSz,
+ Prefix, HeapSz,
+ Prefix, StackSz,
+ Prefix, Reds,
+ Prefix, MinBinVHeapSz,
+ Prefix, MinHeapSz,
+ Prefix, MinGCS])
+ end
+ catch
+ _:_:_ ->
+ ""
+ end.
+
+
+pi(Pid, Item) ->
+ {Item, Info} = process_info(Pid, Item),
+ Info.
+
+
+
+%% ==========================================================================
+
+format(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+error(F) ->
+ error(F, []).
+
+error(F, A) ->
+ print(get(sname), "<ERROR> " ++ F, A).
+
+info(F) ->
+ info(F, []).
+
+info(F, A) ->
+ print(get(sname), "<INFO> " ++ F, A).
+
+print(undefined, F, A) ->
+ print("- ", F, A);
+print(Prefix, F, A) ->
+ io:format("[~s, ~s] " ++ F ++ "~n", [formated_timestamp(), Prefix |A]).
+
diff --git a/erts/emulator/test/socket_test_ttest_tcp_client.erl b/erts/emulator/test/socket_test_ttest_tcp_client.erl
new file mode 100644
index 0000000000..5efa3fe491
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest_tcp_client.erl
@@ -0,0 +1,678 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% ==========================================================================
+%%
+%% This is the "simple" client using gen_tcp. The client is supposed to be
+%% as simple as possible in order to incur as little overhead as possible.
+%%
+%% There are three ways to run the client: active, passive or active-once.
+%%
+%% The client is the entity that controls the test, timing and counting.
+%%
+%% ==========================================================================
+%%
+%% Before the actual test starts, the client performs a "warmup".
+%% The warmup has two functions. First, to ensure that everything is "loaded"
+%% and, second, to calculate an approximate roundtrip time, in order to
+%% "know" how many iterations we should make (to run for the expected time).
+%% This is not intended to be exact, but just to ensure that all tests take
+%% approx the same time to run.
+%%
+%% ==========================================================================
+
+-module(socket_test_ttest_tcp_client).
+
+-export([
+ %% These are for the test suite
+ start_monitor/6, start_monitor/7, start_monitor/9,
+
+ %% These are for starting in a shell when run "manually"
+ start/4, start/5, start/7, start/8,
+ stop/1
+ ]).
+
+%% Internal exports
+-export([
+ do_start/10
+ ]).
+
+-include_lib("kernel/include/inet.hrl").
+-include("socket_test_ttest.hrl").
+-include("socket_test_ttest_client.hrl").
+
+-define(RECV_TIMEOUT, 10000).
+-define(MAX_OUTSTANDING_DEFAULT_1, 100).
+-define(MAX_OUTSTANDING_DEFAULT_2, 10).
+-define(MAX_OUTSTANDING_DEFAULT_3, 3).
+
+-define(LIB, socket_test_ttest_lib).
+-define(I(F), ?LIB:info(F)).
+-define(I(F,A), ?LIB:info(F, A)).
+-define(E(F,A), ?LIB:error(F, A)).
+-define(F(F,A), ?LIB:format(F, A)).
+-define(FORMAT_TIME(T), ?LIB:format_time(T)).
+-define(T(), ?LIB:t()).
+-define(TDIFF(T1,T2), ?LIB:tdiff(T1, T2)).
+
+-type active() :: once | boolean().
+-type msg_id() :: 1..3.
+-type max_outstanding() :: pos_integer().
+-type runtime() :: pos_integer().
+
+
+%% ==========================================================================
+
+start_monitor(Node, Notify, Transport, Active, Addr, Port) ->
+ start_monitor(Node, Notify, Transport, Active, Addr, Port, ?MSG_ID_DEFAULT).
+
+start_monitor(Node, Notify, Transport, Active, Addr, Port, 1 = MsgID) ->
+ start_monitor(Node, Notify, Transport, Active, Addr, Port, 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,
+ ?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,
+ ?MAX_OUTSTANDING_DEFAULT_3, ?RUNTIME_DEFAULT).
+
+start_monitor(Node, Notify, Transport, Active, Addr, Port,
+ MsgID, MaxOutstanding, RunTime)
+ when (Node =/= node()) ->
+ Args = [false,
+ self(), Notify,
+ Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime],
+ case rpc:call(Node, ?MODULE, do_start, Args) of
+ {badrpc, _} = Reason ->
+ {error, Reason};
+ {ok, Pid} when is_pid(Pid) ->
+ MRef = erlang:monitor(process, Pid),
+ {ok, {Pid, MRef}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+start_monitor(_, Notify, Transport, Active, Addr, Port,
+ MsgID, MaxOutstanding, RunTime) ->
+ case do_start(false,
+ self(), Notify,
+ Transport, Active, Addr, Port,
+ MsgID, MaxOutstanding, RunTime) of
+ {ok, Pid} ->
+ MRef = erlang:monitor(process, Pid),
+ {ok, {Pid, MRef}};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+start(Transport, Active, Addr, Port) ->
+ start(Transport, Active, Addr, Port, ?MSG_ID_DEFAULT).
+
+start(Transport, Active, Addr, Port, 1 = MsgID) ->
+ start(false,
+ Transport, Active, Addr, Port, MsgID,
+ ?MAX_OUTSTANDING_DEFAULT_1, ?RUNTIME_DEFAULT);
+start(Transport, Active, Addr, Port, 2 = MsgID) ->
+ start(false,
+ Transport, Active, Addr, Port, MsgID,
+ ?MAX_OUTSTANDING_DEFAULT_2, ?RUNTIME_DEFAULT);
+start(Transport, Active, Addr, Port, 3 = MsgID) ->
+ start(false,
+ Transport, Active, Addr, Port, MsgID,
+ ?MAX_OUTSTANDING_DEFAULT_3, ?RUNTIME_DEFAULT).
+
+start(Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) ->
+ start(false,
+ Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime).
+
+start(Quiet, Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) ->
+ Notify = fun(R) -> present_results(R) end,
+ do_start(Quiet,
+ self(), Notify,
+ Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime).
+
+
+-spec do_start(Quiet,
+ Parent,
+ Notify,
+ Transport,
+ Active,
+ Addr,
+ Port,
+ MsgID,
+ MaxOutstanding,
+ RunTime) -> {ok, Pid} | {error, Reason} when
+ Quiet :: pid(),
+ Parent :: pid(),
+ Notify :: function(),
+ Transport :: atom() | tuple(),
+ Active :: active(),
+ Addr :: inet:ip_address(),
+ Port :: inet:port_number(),
+ MsgID :: msg_id(),
+ MaxOutstanding :: max_outstanding(),
+ RunTime :: runtime(),
+ Pid :: pid(),
+ Reason :: term().
+
+do_start(Quiet,
+ Parent, Notify,
+ Transport, Active, Addr, Port, 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_integer(MsgID) andalso (MsgID >= 1) andalso (MsgID =< 3)) andalso
+ (is_integer(MaxOutstanding) andalso (MaxOutstanding > 0)) andalso
+ (is_integer(RunTime) andalso (RunTime > 0)) ->
+ Starter = self(),
+ Init = fun() -> put(sname, "client"),
+ init(Quiet,
+ Starter,
+ Parent,
+ Notify,
+ Transport, Active, Addr, Port,
+ MsgID, MaxOutstanding, RunTime)
+ end,
+ {Pid, MRef} = spawn_monitor(Init),
+ receive
+ {'DOWN', MRef, process, Pid, Reason} ->
+ {error, Reason};
+ {?MODULE, Pid, ok} ->
+ erlang:demonitor(MRef),
+ {ok, Pid};
+ {?MODULE, Pid, {error, _} = ERROR} ->
+ erlang:demonitor(MRef, [flush]),
+ ERROR
+ end.
+
+
+%% We should not normally stop this (it terminates when its done).
+stop(Pid) when is_pid(Pid) ->
+ req(Pid, stop).
+
+
+%% ==========================================================================
+
+init(Quiet,
+ Starter,
+ Parent, Notify,
+ Transport, Active, Addr, Port,
+ MsgID, MaxOutstanding, RunTime) ->
+ if
+ not Quiet ->
+ ?I("init with"
+ "~n Transport: ~p"
+ "~n Active: ~p"
+ "~n Addr: ~s"
+ "~n Port: ~p"
+ "~n Msg ID: ~p (=> 16 + ~w bytes)"
+ "~n Max Outstanding: ~p"
+ "~n (Suggested) Run Time: ~p ms",
+ [Transport, Active, inet:ntoa(Addr), Port,
+ MsgID, size(which_msg_data(MsgID)), MaxOutstanding, RunTime]);
+ true ->
+ ok
+ end,
+ {Mod, Connect} = process_transport(Transport),
+ case Connect(Addr, Port) of
+ {ok, Sock} ->
+ if not Quiet -> ?I("connected");
+ true -> ok
+ end,
+ Starter ! {?MODULE, self(), ok},
+ initial_activation(Mod, Sock, Active),
+ Results = loop(#{quiet => Quiet,
+ slogan => run,
+ runtime => RunTime,
+ start => ?T(),
+ parent => Parent,
+ mod => Mod,
+ sock => Sock,
+ active => Active,
+ msg_data => which_msg_data(MsgID),
+ outstanding => 0,
+ max_outstanding => MaxOutstanding,
+ sid => 1,
+ rid => 1,
+ scnt => 0,
+ rcnt => 0,
+ bcnt => 0,
+ num => undefined,
+ acc => <<>>}),
+ Notify(Results),
+ (catch Mod:close(Sock)),
+ exit(normal);
+ {error, Reason} ->
+ ?E("connect failed: ~p", [Reason]),
+ exit({connect, Reason})
+ 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}.
+
+
+which_msg_data(1) -> ?MSG_DATA1;
+which_msg_data(2) -> ?MSG_DATA2;
+which_msg_data(3) -> ?MSG_DATA3.
+
+
+present_results(#{status := ok,
+ runtime := RunTime,
+ bcnt := ByteCnt,
+ cnt := NumIterations}) ->
+ ?I("Results: "
+ "~n Run Time: ~s"
+ "~n ByteCnt: ~s"
+ "~n NumIterations: ~s",
+ [?FORMAT_TIME(RunTime),
+ if ((ByteCnt =:= 0) orelse (RunTime =:= 0)) ->
+ ?F("~w, ~w", [ByteCnt, RunTime]);
+ true ->
+ ?F("~p => ~p byte / ms", [ByteCnt, ByteCnt div RunTime])
+ end,
+ if (RunTime =:= 0) ->
+ "-";
+ true ->
+ ?F("~p => ~p iterations / ms",
+ [NumIterations, NumIterations div RunTime])
+ end]),
+ ok;
+present_results(#{status := Failure,
+ runtime := RunTime,
+ sid := SID,
+ rid := RID,
+ scnt := SCnt,
+ rcnt := RCnt,
+ bcnt := BCnt,
+ num := Num}) ->
+ ?I("Time Test failed: "
+ "~n ~p"
+ "~n"
+ "~nwhen"
+ "~n"
+ "~n Run Time: ~s"
+ "~n Send ID: ~p"
+ "~n Recv ID: ~p"
+ "~n Send Count: ~p"
+ "~n Recv Count: ~p"
+ "~n Byte Count: ~p"
+ "~n Num Iterations: ~p",
+ [Failure,
+ ?FORMAT_TIME(RunTime),
+ SID, RID, SCnt, RCnt, BCnt, Num]).
+
+
+
+loop(#{runtime := RunTime} = State) ->
+ erlang:start_timer(RunTime, self(), stop),
+ try do_loop(State)
+ catch
+ throw:Results ->
+ Results
+ end.
+
+do_loop(State) ->
+ do_loop( handle_message( msg_exchange(State) ) ).
+
+msg_exchange(#{rcnt := Num, num := Num} = State) ->
+ finish(ok, State);
+msg_exchange(#{scnt := Num, num := Num} = State) ->
+ %% We are done sending more requests - now we will just await
+ %% the replies for the (still) outstanding replies.
+ msg_exchange( recv_reply(State) );
+msg_exchange(#{outstanding := Outstanding,
+ max_outstanding := MaxOutstanding} = State)
+ when (Outstanding < MaxOutstanding) ->
+ msg_exchange( send_request(State) );
+msg_exchange(State) ->
+ send_request( recv_reply(State) ).
+
+
+finish(ok,
+ #{start := Start, bcnt := BCnt, num := Num}) ->
+ Stop = ?T(),
+ throw(#{status => ok,
+ runtime => ?TDIFF(Start, Stop),
+ bcnt => BCnt,
+ cnt => Num});
+finish(Reason,
+ #{start := Start,
+ sid := SID, rid := RID,
+ scnt := SCnt, rcnt := RCnt, bcnt := BCnt,
+ num := Num}) ->
+ Stop = ?T(),
+ throw(#{status => Reason,
+ runtime => ?TDIFF(Start, Stop),
+ sid => SID,
+ rid => RID,
+ scnt => SCnt,
+ rcnt => RCnt,
+ bcnt => BCnt,
+ num => Num}).
+
+send_request(#{mod := Mod,
+ sock := Sock,
+ sid := ID,
+ scnt := Cnt,
+ outstanding := Outstanding,
+ max_outstanding := MaxOutstanding,
+ msg_data := Data} = State)
+ when (MaxOutstanding > Outstanding) ->
+ SZ = size(Data),
+ Req = <<?TTEST_TAG:32,
+ ?TTEST_TYPE_REQUEST:32,
+ ID:32,
+ SZ:32,
+ Data/binary>>,
+ case Mod:send(Sock, Req) of
+ ok ->
+ State#{sid => next_id(ID),
+ scnt => Cnt + 1,
+ outstanding => Outstanding + 1};
+ {error, Reason} ->
+ ?E("Failed sending request: ~p", [Reason]),
+ exit({send, Reason})
+ end;
+send_request(State) ->
+ State.
+
+
+
+recv_reply(#{mod := Mod,
+ sock := Sock,
+ rid := ID,
+ active := false,
+ bcnt := BCnt,
+ rcnt := Cnt,
+ outstanding := Outstanding} = State) ->
+ case recv_reply_message1(Mod, Sock, ID) of
+ {ok, MsgSz} ->
+ State#{rid => next_id(ID),
+ bcnt => BCnt + MsgSz,
+ rcnt => Cnt + 1,
+ outstanding => Outstanding - 1};
+
+ {error, timeout} ->
+ ?I("receive timeout"),
+ State;
+
+ {error, Reason} ->
+ finish(Reason, State)
+ end;
+recv_reply(#{mod := Mod,
+ sock := Sock,
+ rid := ID,
+ active := Active,
+ bcnt := BCnt,
+ scnt := SCnt,
+ rcnt := RCnt,
+ outstanding := Outstanding,
+ acc := Acc} = State) ->
+ case recv_reply_message2(Mod, Sock, ID, Acc) of
+ {ok, {MsgSz, NewAcc}} when is_integer(MsgSz) andalso is_binary(NewAcc) ->
+ maybe_activate(Mod, Sock, Active),
+ State#{rid => next_id(ID),
+ bcnt => BCnt + MsgSz,
+ rcnt => RCnt + 1,
+ outstanding => Outstanding - 1,
+ acc => NewAcc};
+
+ ok ->
+ State;
+
+ {error, stop} ->
+ ?I("receive [~w] -> stop", [Active]),
+ %% This will have the effect that no more requests are sent...
+ State#{num => SCnt, stop_started => ?T()};
+
+ {error, timeout} ->
+ ?I("receive[~w] -> timeout", [Active]),
+ State;
+
+ {error, Reason} ->
+ finish(Reason, State)
+ end.
+
+
+%% This function reads exactly one (reply) message. No more no less.
+recv_reply_message1(Mod, Sock, ID) ->
+ case Mod:recv(Sock, 4*4, ?RECV_TIMEOUT) of
+ {ok, <<?TTEST_TAG:32,
+ ?TTEST_TYPE_REPLY:32,
+ ID:32,
+ SZ:32>> = Hdr} ->
+ %% Receive the ping-pong reply boby
+ case Mod:recv(Sock, SZ, ?RECV_TIMEOUT) of
+ {ok, Data} when (size(Data) =:= SZ) ->
+ {ok, size(Hdr) + size(Data)};
+ {error, Reason2} ->
+ ?E("Failed reading body: "
+ "~n ~p: ~p", [Reason2]),
+ {error, {recv_body, Reason2}}
+ end;
+
+ {ok, <<BadTag:32,
+ BadType:32,
+ BadID:32,
+ BadSZ:32>>} ->
+ {error, {invalid_hdr,
+ {?TTEST_TAG, BadTag},
+ {?TTEST_TYPE_REPLY, BadType},
+ {ID, BadID},
+ BadSZ}};
+ {ok, _InvHdr} ->
+ {error, invalid_hdr};
+
+ {error, Reason1} ->
+ ?E("Feiled reading header: "
+ "~n ~p", [Reason1]),
+ {error, {recv_hdr, Reason1}}
+ end.
+
+
+%% This function first attempts to process the data we have already
+%% accumulated. If that is not enough for a (complete) reply, it
+%% will attempt to receive more.
+recv_reply_message2(Mod, Sock, ID, Acc) ->
+ case process_acc_data(ID, Acc) of
+ ok ->
+ %% No or insufficient data, so get more
+ recv_reply_message3(Mod, Sock, ID, Acc);
+
+ {ok, _} = OK -> % We already had a reply accumulated - no need to read more
+ OK;
+
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+%% This function receives a "chunk" of data, then it tries to extract
+%% one (reply) message from the accumulated and new data (combined).
+recv_reply_message3(_Mod, Sock, ID, Acc) ->
+ receive
+ {timeout, _TRef, stop} ->
+ {error, stop};
+
+ {TagClosed, Sock} when (TagClosed =:= tcp_closed) orelse
+ (TagClosed =:= socket_closed) ->
+ {error, closed};
+
+ {TagErr, Sock, Reason} when (TagErr =:= tcp_error) orelse
+ (TagErr =:= socket_error) ->
+ {error, Reason};
+
+ {Tag, Sock, Msg} when (Tag =:= tcp) orelse
+ (Tag =:= socket) ->
+ process_acc_data(ID, <<Acc/binary, Msg/binary>>)
+
+ after ?RECV_TIMEOUT ->
+ ?I("timeout when"
+ "~n ID: ~p"
+ "~n size(Acc): ~p",
+ [ID, size(Acc)]),
+ %% {error, timeout}
+ recv_reply_message3(_Mod, Sock, ID, Acc)
+ end.
+
+
+process_acc_data(ID, <<?TTEST_TAG:32,
+ ?TTEST_TYPE_REPLY:32,
+ ID:32,
+ SZ:32,
+ Data/binary>>) when (SZ =< size(Data)) ->
+ <<_Body:SZ/binary, Rest/binary>> = Data,
+ {ok, {4*4+SZ, Rest}};
+process_acc_data(ID, <<BadTag:32,
+ BadType:32,
+ BadID:32,
+ BadSZ:32,
+ _Data/binary>>)
+ when ((BadTag =/= ?TTEST_TAG) orelse
+ (BadType =/= ?TTEST_TYPE_REPLY) orelse
+ (BadID =/= ID)) ->
+ {error, {invalid_hdr,
+ {?TTEST_TAG, BadTag},
+ {?TTEST_TYPE_REPLY, BadType},
+ {ID, BadID},
+ BadSZ}};
+%% Not enough for an entire (reply) message
+process_acc_data(_ID, _Data) ->
+ ok.
+
+
+handle_message(#{quiet := Quiet,
+ parent := Parent, sock := Sock, scnt := SCnt} = State) ->
+ receive
+ {timeout, _TRef, stop} ->
+ if not Quiet -> ?I("STOP");
+ true -> ok
+ end,
+ %% This will have the effect that no more requests are sent...
+ State#{num => SCnt, stop_started => ?T()};
+
+ {?MODULE, Ref, Parent, stop} ->
+ %% This *aborts* the test
+ reply(Parent, Ref, ok),
+ exit(normal);
+
+ %% Only when active
+ {TagClosed, Sock, Reason} when (TagClosed =:= tcp_closed) orelse
+ (TagClosed =:= socket_closed) ->
+ %% We should never get this (unless the server crashed)
+ exit({closed, Reason});
+
+ %% Only when active
+ {TagErr, Sock, Reason} when (TagErr =:= tcp_error) orelse
+ (TagErr =:= socket_error) ->
+ exit({error, Reason})
+
+ after 0 ->
+ State
+ end.
+
+
+initial_activation(_Mod, _Sock, false = _Active) ->
+ ok;
+initial_activation(Mod, Sock, Active) ->
+ Mod:active(Sock, Active).
+
+
+maybe_activate(Mod, Sock, once = Active) ->
+ Mod:active(Sock, Active);
+maybe_activate(_, _, _) ->
+ ok.
+
+
+%% ==========================================================================
+
+req(Pid, Req) ->
+ Ref = make_ref(),
+ Pid ! {?MODULE, Ref, Pid, Req},
+ receive
+ {'EXIT', Pid, Reason} ->
+ {error, {exit, Reason}};
+ {?MODULE, Ref, Reply} ->
+ Reply
+ end.
+
+reply(Pid, Ref, Reply) ->
+ Pid ! {?MODULE, Ref, Reply}.
+
+
+%% ==========================================================================
+
+next_id(ID) when (ID < ?MAX_ID) ->
+ ID + 1;
+next_id(_) ->
+ 1.
+
+
+%% ==========================================================================
+
+%% t() ->
+%% os:timestamp().
+
+%% tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) ->
+%% T1 = A1*1000000000+B1*1000+(C1 div 1000),
+%% T2 = A2*1000000000+B2*1000+(C2 div 1000),
+%% T2 - T1.
+
+%% formated_timestamp() ->
+%% format_timestamp(os:timestamp()).
+
+%% format_timestamp({_N1, _N2, N3} = TS) ->
+%% {_Date, Time} = calendar:now_to_local_time(TS),
+%% {Hour,Min,Sec} = Time,
+%% FormatTS = io_lib:format("~.2.0w:~.2.0w:~.2.0w.4~w",
+%% [Hour, Min, Sec, round(N3/1000)]),
+%% lists:flatten(FormatTS).
+
+%% %% Time is always in number os ms (milli seconds)
+%% format_time(T) ->
+%% f("~p", [T]).
+
+
+%% ==========================================================================
+
+%% f(F, A) ->
+%% lists:flatten(io_lib:format(F, A)).
+
+%% %% e(F) ->
+%% %% i("<ERROR> " ++ F).
+
+%% e(F, A) ->
+%% p(get(sname), "<ERROR> " ++ F, A).
+
+%% i(F) ->
+%% i(F, []).
+
+%% i(F, A) ->
+%% p(get(sname), "<INFO> " ++ F, A).
+
+%% p(undefined, F, A) ->
+%% p("- ", F, A);
+%% p(Prefix, F, A) ->
+%% io:format("[~s, ~s] " ++ F ++ "~n", [formated_timestamp(), Prefix |A]).
diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl
new file mode 100644
index 0000000000..0ec2e908d7
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl
@@ -0,0 +1,49 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_ttest_tcp_client_gen).
+
+-export([
+ start/3, start/4, start/6, start/7,
+ 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(Active, Addr, Port, MsgID) ->
+ socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, Active, Addr, Port, MsgID).
+
+start(Active, Addr, Port, MsgID, MaxOutstanding, RunTime) ->
+ socket_test_ttest_tcp_client:start(false,
+ ?TRANSPORT_MOD,
+ Active, Addr, Port,
+ MsgID, MaxOutstanding, RunTime).
+
+start(Quiet, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) ->
+ socket_test_ttest_tcp_client:start(Quiet,
+ ?TRANSPORT_MOD,
+ Active, Addr, Port,
+ MsgID, MaxOutstanding, RunTime).
+
+stop(Pid) ->
+ socket_test_ttest_tcp_client: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
new file mode 100644
index 0000000000..acf2556793
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl
@@ -0,0 +1,51 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_ttest_tcp_client_socket).
+
+-export([
+ start/4, start/5, start/7, start/8,
+ stop/1
+ ]).
+
+-define(TRANSPORT_MOD, socket_test_ttest_tcp_socket).
+-define(MOD(M), {?TRANSPORT_MOD, #{method => Method}}).
+
+start(Method, Active, Addr, Port) ->
+ socket_test_ttest_tcp_client:start_monitor(?MOD(Method), Active, Addr, Port).
+
+start(Method, Active, Addr, Port, MsgID) ->
+ socket_test_ttest_tcp_client:start(?MOD(Method),
+ Active, Addr, Port, MsgID).
+
+start(Method, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) ->
+ socket_test_ttest_tcp_client:start(false,
+ ?MOD(Method),
+ Active, Addr, Port,
+ MsgID, MaxOutstanding, RunTime).
+
+start(Quiet, Method, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) ->
+ socket_test_ttest_tcp_client:start(Quiet,
+ ?MOD(Method),
+ Active, Addr, Port,
+ MsgID, MaxOutstanding, RunTime).
+
+stop(Pid) ->
+ socket_test_ttest_client:stop(Pid).
diff --git a/erts/emulator/test/socket_test_ttest_tcp_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_gen.erl
new file mode 100644
index 0000000000..604408c489
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest_tcp_gen.erl
@@ -0,0 +1,138 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_ttest_tcp_gen).
+
+-export([
+ accept/1, accept/2,
+ active/2,
+ close/1,
+ connect/2,
+ controlling_process/2,
+ listen/0, listen/1,
+ peername/1,
+ port/1,
+ recv/2, recv/3,
+ send/2,
+ shutdown/2,
+ sockname/1
+ ]).
+
+
+%% ==========================================================================
+
+%% 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}]).
+
+
+%% ==========================================================================
+
+accept(Sock) ->
+ case gen_tcp:accept(Sock) of
+ {ok, NewSock} ->
+ {ok, NewSock};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+accept(Sock, Timeout) ->
+ case gen_tcp:accept(Sock, Timeout) of
+ {ok, NewSock} ->
+ {ok, NewSock};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+active(Sock, NewActive)
+ when (is_boolean(NewActive) orelse (NewActive =:= once)) ->
+ inet:setopts(Sock, [{active, NewActive}]).
+
+
+close(Sock) ->
+ gen_tcp:close(Sock).
+
+
+connect(Addr, Port) ->
+ Opts = [binary, {packet, raw}, {active, false}, {buffer, 32*1024}],
+ case gen_tcp:connect(Addr, Port, Opts) of
+ {ok, Sock} ->
+ {ok, Sock};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+controlling_process(Sock, NewPid) ->
+ gen_tcp:controlling_process(Sock, NewPid).
+
+
+%% Create a listen socket
+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};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+peername(Sock) ->
+ inet:peername(Sock).
+
+
+port(Sock) ->
+ inet:port(Sock).
+
+
+recv(Sock, Length) ->
+ gen_tcp:recv(Sock, Length).
+recv(Sock, Length, Timeout) ->
+ gen_tcp:recv(Sock, Length, Timeout).
+
+
+send(Sock, Data) ->
+ gen_tcp:send(Sock, Data).
+
+
+shutdown(Sock, How) ->
+ gen_tcp:shutdown(Sock, How).
+
+
+sockname(Sock) ->
+ inet:sockname(Sock).
+
+
+%% ==========================================================================
+
+
+
diff --git a/erts/emulator/test/socket_test_ttest_tcp_server.erl b/erts/emulator/test/socket_test_ttest_tcp_server.erl
new file mode 100644
index 0000000000..e8d626e3d8
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest_tcp_server.erl
@@ -0,0 +1,642 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% ==========================================================================
+%%
+%% This is the "simple" server using gen_tcp. The server is supposed to be
+%% as simple as possible in order to incur as little overhead as possible.
+%%
+%% There are three ways to run the server: active, passive or active-once.
+%%
+%% The server does only two things; accept connnections and then reply
+%% to requests (actually the handler(s) does that). No timing or counting.
+%% That is all done by the clients.
+%%
+%% ==========================================================================
+
+-module(socket_test_ttest_tcp_server).
+
+-export([
+ %% This are for the test suite
+ start_monitor/3,
+
+ %% This are for starting in a shell when run "manually"
+ start/2,
+
+ stop/1
+ ]).
+
+%% Internal exports
+-export([
+ do_start/3
+ ]).
+
+-include_lib("kernel/include/inet.hrl").
+-include("socket_test_ttest.hrl").
+
+-define(ACC_TIMEOUT, 5000).
+-define(RECV_TIMEOUT, 5000).
+
+-define(LIB, socket_test_ttest_lib).
+-define(I(F), ?LIB:info(F)).
+-define(I(F,A), ?LIB:info(F, A)).
+-define(E(F,A), ?LIB:error(F, A)).
+-define(F(F,A), ?LIB:format(F, A)).
+-define(FORMAT_TIME(T), ?LIB:format_time(T)).
+-define(T(), ?LIB:t()).
+-define(TDIFF(T1,T2), ?LIB:tdiff(T1, T2)).
+
+
+%% ==========================================================================
+
+start_monitor(Node, Transport, Active) when (Node =/= node()) ->
+ case rpc:call(Node, ?MODULE, do_start, [self(), Transport, Active]) of
+ {badrpc, _} = Reason ->
+ {error, Reason};
+ {ok, {Pid, AddrPort}} ->
+ MRef = erlang:monitor(process, Pid),
+ {ok, {{Pid, MRef}, AddrPort}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+start_monitor(_, Transport, Active) ->
+ case do_start(self(), Transport, Active) of
+ {ok, {Pid, AddrPort}} ->
+ MRef = erlang:monitor(process, Pid),
+ {ok, {{Pid, MRef}, AddrPort}};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+
+start(Transport, Active) ->
+ do_start(self(), Transport, Active).
+
+
+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)
+ end,
+ {Pid, MRef} = spawn_monitor(ServerInit),
+ receive
+ {'DOWN', MRef, process, Pid, Reason} ->
+ {error, Reason};
+ {?MODULE, Pid, {ok, AddrPort}} ->
+ erlang:demonitor(MRef),
+ {ok, {Pid, AddrPort}};
+ {?MODULE, Pid, {error, _} = ERROR} ->
+ erlang:demonitor(MRef, [flush]),
+ ERROR
+ end.
+
+
+stop(Pid) when is_pid(Pid) ->
+ req(Pid, stop).
+
+
+%% ==========================================================================
+
+server_init(Starter, Parent, Transport, Active) ->
+ ?I("init with"
+ "~n Transport: ~p"
+ "~n Active: ~p", [Transport, Active]),
+ {Mod, Listen, StatsInterval} = process_transport(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}}},
+ server_loop(#{parent => Parent,
+ mod => Mod,
+ active => Active,
+ lsock => LSock,
+ handlers => [],
+ stats_interval => StatsInterval,
+ %% Accumulation
+ runtime => 0,
+ mcnt => 0,
+ bcnt => 0,
+ hcnt => 0
+ });
+ {error, PReason} ->
+ (catch Mod:close(LSock)),
+ exit({port, PReason})
+ end;
+ {error, LReason} ->
+ exit({listen, LReason})
+ end.
+
+process_transport(Mod, _) when is_atom(Mod) ->
+ {Mod, fun(Port) -> Mod:listen(Port) end, infinity};
+process_transport({Mod, #{stats_interval := T} = Opts}, Active)
+ when (Active =/= false) ->
+ {Mod, fun(Port) -> Mod:listen(Port, Opts#{stats_to => self()}) end, T};
+process_transport({Mod, #{stats_interval := T} = Opts}, _Active) ->
+ {Mod, fun(Port) -> Mod:listen(Port, Opts) end, T};
+process_transport({Mod, Opts}, _Active) ->
+ {Mod, fun(Port) -> Mod:listen(Port, Opts) end, infinity}.
+
+
+
+server_loop(State) ->
+ server_loop( server_handle_message( server_accept(State) ) ).
+
+server_accept(#{mod := Mod,
+ active := Active,
+ lsock := LSock,
+ handlers := Handlers} = State) ->
+ case Mod:accept(LSock, ?ACC_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} ->
+ State;
+ {error, AReason} ->
+ (catch Mod:close(LSock)),
+ exit({accept, AReason})
+ 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.
+
+maybe_start_stats_timer(#{active := Active, stats_interval := Time}, Handler)
+ when (Active =/= false) andalso (is_integer(Time) andalso (Time > 0)) ->
+ start_stats_timer(Time, "handler", Handler);
+maybe_start_stats_timer(_, _) ->
+ ok.
+
+start_stats_timer(Time, ProcStr, Pid) ->
+ erlang:start_timer(Time, self(), {stats, Time, ProcStr, Pid}).
+
+server_handle_message(#{parent := Parent, handlers := H} = State) ->
+ receive
+ {timeout, _TRef, {stats, Interval, ProcStr, Pid}} ->
+ case server_handle_stats(ProcStr, Pid) of
+ ok ->
+ start_stats_timer(Interval, ProcStr, Pid);
+ skip ->
+ ok
+ end,
+ State;
+
+ {?MODULE, Ref, Parent, stop} ->
+ reply(Parent, Ref, ok),
+ lists:foreach(fun(P) -> handler_stop(P) end, H),
+ exit(normal);
+
+ {'DOWN', _MRef, process, Pid, Reason} ->
+ server_handle_down(Pid, Reason, State)
+
+ after 0 ->
+ State
+ end.
+
+server_handle_stats(ProcStr, Pid) ->
+ case ?LIB:formated_process_stats(Pid) of
+ "" ->
+ skip;
+ FormatedStats ->
+ ?I("Statistics for ~s ~p:~s", [ProcStr, Pid, FormatedStats]),
+ ok
+ end.
+
+
+server_handle_down(Pid, Reason, #{handlers := Handlers} = State) ->
+ case lists:delete(Pid, Handlers) of
+ Handlers ->
+ ?I("unknown process ~p died", [Pid]),
+ State;
+ Handlers2 ->
+ server_handle_handler_down(Pid, Reason, State#{handlers => Handlers2})
+ end.
+
+
+server_handle_handler_down(Pid,
+ {done, RunTime, MCnt, BCnt},
+ #{runtime := AccRunTime,
+ mcnt := AccMCnt,
+ bcnt := AccBCnt,
+ hcnt := AccHCnt} = State) ->
+ AccRunTime2 = AccRunTime + RunTime,
+ AccMCnt2 = AccMCnt + MCnt,
+ AccBCnt2 = AccBCnt + BCnt,
+ AccHCnt2 = AccHCnt + 1,
+ ?I("handler ~p (~w) done => accumulated results: "
+ "~n Run Time: ~s ms"
+ "~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]),
+ State#{runtime => AccRunTime2,
+ mcnt => AccMCnt2,
+ bcnt => AccBCnt2,
+ hcnt => AccHCnt2};
+server_handle_handler_down(Pid, Reason, State) ->
+ ?I("handler ~p terminated: "
+ "~n ~p", [Pid, Reason]),
+ State.
+
+
+
+%% ==========================================================================
+
+handler_start() ->
+ Self = self(),
+ HandlerInit = fun() -> put(sname, "handler"), handler_init(Self) end,
+ spawn_monitor(HandlerInit).
+
+handler_continue(Pid, Mod, Sock, Active) ->
+ req(Pid, {continue, Mod, Sock, Active}).
+
+handler_stop(Pid) ->
+ req(Pid, stop).
+
+handler_init(Parent) ->
+ ?I("starting"),
+ receive
+ {?MODULE, Ref, Parent, {continue, Mod, Sock, Active}} ->
+ ?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 => <<>>})
+
+ after 5000 ->
+ ?I("timeout when message queue: "
+ "~n ~p"
+ "~nwhen"
+ "~n Parent: ~p", [process_info(self(), messages), Parent]),
+ handler_init(Parent)
+ end.
+
+handler_loop(State) ->
+ handler_loop( handler_handle_message( handler_recv_message(State) ) ).
+
+%% When passive, we read *one* request and then attempt to reply to it.
+handler_recv_message(#{mod := Mod,
+ sock := Sock,
+ active := false,
+ mcnt := MCnt,
+ bcnt := BCnt,
+ last_reply := LID} = State) ->
+ case handler_recv_message2(Mod, Sock) of
+ {ok, {MsgSz, ID, Body}} ->
+ handler_send_reply(Mod, Sock, ID, Body),
+ State#{mcnt => MCnt + 1,
+ bcnt => BCnt + MsgSz,
+ last_reply => ID};
+ {error, closed} ->
+ handler_done(State);
+ {error, timeout} ->
+ ?I("timeout when: "
+ "~n MCnt: ~p"
+ "~n BCnt: ~p"
+ "~n LID: ~p", [MCnt, BCnt, LID]),
+ State
+ end;
+
+
+%% When "active" (once or true), we receive one data "message", which may
+%% contain any number of requests or only part of a request. Then we
+%% process this data together with whatever we had "accumulated" from
+%% prevous messages. Each request will be extracted and replied to. If
+%% there is some data left, not enough for a complete request, we store
+%% this in 'acc' (accumulate it).
+handler_recv_message(#{mod := Mod,
+ sock := Sock,
+ active := Active,
+ mcnt := MCnt,
+ bcnt := BCnt,
+ last_reply := LID,
+ acc := Acc} = State) ->
+ case handler_recv_message3(Mod, Sock, Acc, LID) of
+ {ok, {MCnt2, BCnt2, LID2}, NewAcc} ->
+ handler_maybe_activate(Mod, Sock, Active),
+ State#{mcnt => MCnt + MCnt2,
+ bcnt => BCnt + BCnt2,
+ last_reply => LID2,
+ acc => NewAcc};
+
+ {error, closed} ->
+ if
+ (size(Acc) =:= 0) ->
+ handler_done(State);
+ true ->
+ ?E("client done with partial message: "
+ "~n Last Reply Sent: ~w"
+ "~n Message Count: ~w"
+ "~n Byte Count: ~w"
+ "~n Partial Message: ~w bytes",
+ [LID, MCnt, BCnt, size(Acc)]),
+ exit({closed_with_partial_message, LID})
+ end;
+
+ {error, timeout} ->
+ ?I("timeout when: "
+ "~n MCnt: ~p"
+ "~n BCnt: ~p"
+ "~n LID: ~p"
+ "~n size(Acc): ~p", [MCnt, BCnt, LID, size(Acc)]),
+ State
+ end.
+
+handler_process_data(Acc, Mod, Sock, LID) ->
+ handler_process_data(Acc, Mod, Sock, 0, 0, LID).
+
+%% Extract each complete request, one at a time.
+handler_process_data(<<?TTEST_TAG:32,
+ ?TTEST_TYPE_REQUEST:32,
+ ID:32,
+ SZ:32,
+ Rest/binary>>,
+ Mod, Sock,
+ MCnt, BCnt, _LID) when (size(Rest) >= SZ) ->
+ <<Body:SZ/binary, Rest2/binary>> = Rest,
+ case handler_send_reply(Mod, Sock, ID, Body) of
+ ok ->
+ handler_process_data(Rest2, Mod, Sock, MCnt+1, BCnt+16+SZ, ID);
+ {error, _} = ERROR ->
+ ERROR
+ end;
+handler_process_data(Data, _Mod, _Sock, MCnt, BCnt, LID) ->
+ {ok, {MCnt, BCnt, LID}, Data}.
+
+
+handler_recv_message2(Mod, Sock) ->
+ case Mod:recv(Sock, 4*4, ?RECV_TIMEOUT) of
+ {ok, <<?TTEST_TAG:32,
+ ?TTEST_TYPE_REQUEST:32,
+ ID:32,
+ SZ:32>> = Hdr} ->
+ case Mod:recv(Sock, SZ, ?RECV_TIMEOUT) of
+ {ok, Body} when (SZ =:= size(Body)) ->
+ {ok, {size(Hdr) + size(Body), ID, Body}};
+ {error, BReason} ->
+ ?E("failed reading body (~w) of message ~w:"
+ "~n ~p", [SZ, ID, BReason]),
+ exit({recv, body, ID, SZ, BReason})
+ end;
+ {error, timeout} = ERROR ->
+ ERROR;
+ {error, closed} = ERROR ->
+ ERROR;
+ {error, HReason} ->
+ ?E("Failed reading header of message:"
+ "~n ~p", [HReason]),
+ exit({recv, header, HReason})
+ end.
+
+
+handler_recv_message3(Mod, Sock, Acc, LID) ->
+ receive
+ {TagClosed, Sock} when (TagClosed =:= tcp_closed) orelse
+ (TagClosed =:= socket_closed) ->
+ {error, closed};
+
+ {TagErr, Sock, Reason} when (TagErr =:= tcp_error) orelse
+ (TagErr =:= socket_error) ->
+ {error, Reason};
+
+ {Tag, Sock, Msg} when (Tag =:= tcp) orelse
+ (Tag =:= socket) ->
+ handler_process_data(<<Acc/binary, Msg/binary>>, Mod, Sock, LID)
+
+ after ?RECV_TIMEOUT ->
+ {error, timeout}
+ end.
+
+
+
+handler_send_reply(Mod, Sock, ID, Data) ->
+ SZ = size(Data),
+ Msg = <<?TTEST_TAG:32,
+ ?TTEST_TYPE_REPLY:32,
+ ID:32,
+ SZ:32,
+ Data/binary>>,
+ case Mod:send(Sock, Msg) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ (catch Mod:close(Sock)),
+ exit({send, Reason})
+ end.
+
+
+handler_done(State) ->
+ handler_done(State, ?T()).
+
+handler_done(#{start := Start,
+ mod := Mod,
+ sock := Sock,
+ mcnt := MCnt,
+ bcnt := BCnt}, Stop) ->
+ (catch Mod:close(Sock)),
+ exit({done, ?TDIFF(Start, Stop), MCnt, BCnt}).
+
+
+handler_handle_message(#{parent := Parent} = State) ->
+ receive
+ {'EXIT', Parent, Reason} ->
+ exit({parent_exit, Reason})
+ after 0 ->
+ State
+ end.
+
+
+handler_initial_activation(_Mod, _Sock, false = _Active) ->
+ ok;
+handler_initial_activation(Mod, Sock, Active) ->
+ Mod:active(Sock, Active).
+
+
+handler_maybe_activate(Mod, Sock, once = Active) ->
+ Mod:active(Sock, Active);
+handler_maybe_activate(_, _, _) ->
+ ok.
+
+
+
+%% ==========================================================================
+
+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).
+
+
+%% ==========================================================================
+
+req(Pid, Req) ->
+ Ref = make_ref(),
+ Pid ! {?MODULE, Ref, self(), Req},
+ receive
+ {'EXIT', Pid, Reason} ->
+ {error, {exit, Reason}};
+ {?MODULE, Ref, Reply} ->
+ Reply
+ end.
+
+reply(Pid, Ref, Reply) ->
+ Pid ! {?MODULE, Ref, Reply}.
+
+
+%% ==========================================================================
+
+%% t() ->
+%% os:timestamp().
+
+%% tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) ->
+%% T1 = A1*1000000000+B1*1000+(C1 div 1000),
+%% T2 = A2*1000000000+B2*1000+(C2 div 1000),
+%% T2 - T1.
+
+%% formated_timestamp() ->
+%% format_timestamp(os:timestamp()).
+
+%% format_timestamp({_N1, _N2, N3} = TS) ->
+%% {_Date, Time} = calendar:now_to_local_time(TS),
+%% {Hour,Min,Sec} = Time,
+%% FormatTS = io_lib:format("~.2.0w:~.2.0w:~.2.0w.4~w",
+%% [Hour, Min, Sec, round(N3/1000)]),
+%% lists:flatten(FormatTS).
+
+%% %% Time is always in number os ms (milli seconds)
+%% format_time(T) ->
+%% f("~p", [T]).
+
+
+%% ==========================================================================
+
+%% f(F, A) ->
+%% lists:flatten(io_lib:format(F, A)).
+
+%% e(F, A) ->
+%% p(get(sname), "<ERROR> " ++ F, A).
+
+%% i(F) ->
+%% i(F, []).
+
+%% i(F, A) ->
+%% p(get(sname), "<INFO> " ++ F, A).
+
+%% p(undefined, F, A) ->
+%% p("- ", F, A);
+%% p(Prefix, F, A) ->
+%% io:format("[~s, ~s] " ++ F ++ "~n", [formated_timestamp(), Prefix |A]).
+
diff --git a/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl
new file mode 100644
index 0000000000..b1b31f5158
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl
@@ -0,0 +1,41 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_ttest_tcp_server_gen).
+
+-export([
+ start/1,
+ stop/1
+ ]).
+
+-define(TRANSPORT_MOD, socket_test_ttest_tcp_gen).
+
+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.
+
+
+stop(Pid) ->
+ socket_test_ttest_tcp_server: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
new file mode 100644
index 0000000000..b7ea1e8e93
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl
@@ -0,0 +1,43 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_ttest_tcp_server_socket).
+
+-export([
+ start/2,
+ stop/1
+ ]).
+
+-define(TRANSPORT_MOD, socket_test_ttest_tcp_socket).
+%% -define(MOD(M), {?TRANSPORT_MOD, #{method => M,
+%% stats_interval => 10000}}).
+-define(MOD(M), {?TRANSPORT_MOD, #{method => M}}).
+
+start(Method, Active) ->
+ socket_test_ttest_tcp_server:start(?MOD(Method), Active).
+ %% {ok, {Pid, AddrPort}} ->
+ %% MRef = erlang:monitor(process, Pid),
+ %% {ok, {Pid, MRef, AddrPort}};
+ %% {error, _} = ERROR ->
+ %% ERROR
+ %% end.
+
+stop(Pid) ->
+ socket_test_ttest_tcp_server:stop(Pid).
diff --git a/erts/emulator/test/socket_test_ttest_tcp_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_socket.erl
new file mode 100644
index 0000000000..0ae2412e4c
--- /dev/null
+++ b/erts/emulator/test/socket_test_ttest_tcp_socket.erl
@@ -0,0 +1,414 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_ttest_tcp_socket).
+
+-export([
+ accept/1, accept/2,
+ active/2,
+ close/1,
+ connect/2, connect/3,
+ controlling_process/2,
+ listen/0, listen/1, listen/2,
+ port/1,
+ peername/1,
+ recv/2, recv/3,
+ send/2,
+ shutdown/2,
+ sockname/1
+ ]).
+
+
+-define(READER_RECV_TIMEOUT, 1000).
+
+-define(DATA_MSG(Sock, Method, Data),
+ {socket,
+ #{sock => Sock, reader => self(), method => Method},
+ Data}).
+
+-define(CLOSED_MSG(Sock, Method),
+ {socket_closed,
+ #{sock => Sock, reader => self(), method => Method}}).
+
+-define(ERROR_MSG(Sock, Method, Reason),
+ {socket_error,
+ #{sock => Sock, reader => self(), method => Method},
+ Reason}).
+
+
+%% ==========================================================================
+
+%% This does not really work. Its just a placeholder for the time beeing...
+
+%% getopt(Sock, Opt) when is_atom(Opt) ->
+%% socket:getopt(Sock, socket, Opt).
+
+%% setopt(Sock, Opt, Value) when is_atom(Opt) ->
+%% socket:setopts(Sock, socket, Opt, Value).
+
+
+%% ==========================================================================
+
+accept(#{sock := LSock, opts := #{method := Method} = Opts}) ->
+ case socket:accept(LSock) of
+ {ok, Sock} ->
+ Self = self(),
+ Reader = spawn(fun() -> reader_init(Self, Sock, 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) ->
+ case socket:accept(LSock, Timeout) of
+ {ok, Sock} ->
+ Self = self(),
+ Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end),
+ maybe_start_stats_timer(Opts, Reader),
+ {ok, #{sock => Sock, reader => Reader, method => Method}};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+active(#{reader := Pid}, NewActive)
+ when (is_boolean(NewActive) orelse (NewActive =:= once)) ->
+ Pid ! {?MODULE, active, NewActive},
+ ok.
+
+
+close(#{sock := Sock, reader := Pid}) ->
+ Pid ! {?MODULE, stop},
+ socket:close(Sock).
+
+%% Create a socket and connect it to a peer
+connect(Addr, Port) ->
+ connect(Addr, Port, #{method => plain}).
+
+connect(Addr, Port, #{method := Method} = Opts) ->
+ try
+ begin
+ Sock =
+ case socket:open(inet, stream, tcp) of
+ {ok, S} ->
+ S;
+ {error, OReason} ->
+ throw({error, {open, OReason}})
+ end,
+ case socket:bind(Sock, any) of
+ {ok, _} ->
+ ok;
+ {error, BReason} ->
+ (catch socket:close(Sock)),
+ throw({error, {bind, BReason}})
+ end,
+ SA = #{family => inet,
+ addr => Addr,
+ port => Port},
+ case socket:connect(Sock, SA) of
+ ok ->
+ ok;
+ {error, CReason} ->
+ (catch socket:close(Sock)),
+ throw({error, {connect, CReason}})
+ end,
+ Self = self(),
+ Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end),
+ maybe_start_stats_timer(Opts, Reader),
+ {ok, #{sock => Sock, reader => Reader, method => Method}}
+ end
+ catch
+ throw:ERROR:_ ->
+ ERROR
+ end.
+
+
+maybe_start_stats_timer(#{stats_to := Pid,
+ stats_interval := T},
+ Reader) when is_pid(Pid) ->
+ erlang:start_timer(T, Pid, {stats, T, "reader", Reader});
+maybe_start_stats_timer(_O, _) ->
+ ok.
+
+controlling_process(#{sock := Sock, reader := Pid}, NewPid) ->
+ case socket:setopt(Sock, otp, controlling_process, NewPid) of
+ ok ->
+ Pid ! {?MODULE, self(), controlling_process, NewPid},
+ receive
+ {?MODULE, Pid, controlling_process} ->
+ ok
+ end;
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+%% Create a listen socket
+listen() ->
+ listen(0, #{method => plain}).
+
+listen(Port) ->
+ listen(Port, #{method => plain}).
+listen(Port, #{method := Method} = Opts)
+ when (is_integer(Port) andalso (Port >= 0)) andalso
+ ((Method =:= plain) orelse (Method =:= msg)) ->
+ try
+ begin
+ Sock = case socket:open(inet, stream, tcp) 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)),
+ throw({error, {bind, BReason}})
+ end,
+ case socket:listen(Sock) of
+ ok ->
+ ok;
+ {error, LReason} ->
+ (catch socket:close(Sock)),
+ throw({error, {listen, LReason}})
+ end,
+ {ok, #{sock => Sock, opts => Opts}}
+ end
+ catch
+ throw:{error, Reason}:_ ->
+ {error, Reason}
+ end.
+
+
+port(#{sock := Sock}) ->
+ case socket:sockname(Sock) of
+ {ok, #{port := Port}} ->
+ {ok, Port};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+peername(#{sock := Sock}) ->
+ case socket:peername(Sock) of
+ {ok, #{addr := Addr, port := Port}} ->
+ {ok, {Addr, Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+recv(#{sock := Sock, method := plain}, Length) ->
+ socket:recv(Sock, Length);
+recv(#{sock := Sock, method := msg}, Length) ->
+ case socket:recvmsg(Sock, Length, 0, [], infinity) of
+ {ok, #{iov := [Bin]}} ->
+ {ok, Bin};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+recv(#{sock := Sock, method := plain}, Length, Timeout) ->
+ socket:recv(Sock, Length, Timeout);
+recv(#{sock := Sock, method := msg}, Length, Timeout) ->
+ case socket:recvmsg(Sock, Length, 0, [], Timeout) of
+ {ok, #{iov := [Bin]}} ->
+ {ok, Bin};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+send(#{sock := Sock, method := plain}, Bin) ->
+ socket:send(Sock, Bin);
+send(#{sock := Sock, method := msg}, Bin) ->
+ socket:sendmsg(Sock, #{iov => [Bin]}).
+
+
+shutdown(#{sock := Sock}, How) ->
+ socket:shutdown(Sock, How).
+
+
+sockname(#{sock := Sock}) ->
+ case socket:sockname(Sock) of
+ {ok, #{addr := Addr, port := Port}} ->
+ {ok, {Addr, Port}};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+%% ==========================================================================
+
+reader_init(ControllingProcess, Sock, Active, Method)
+ when is_pid(ControllingProcess) andalso
+ (is_boolean(Active) orelse (Active =:= once)) andalso
+ ((Method =:= plain) orelse (Method =:= msg)) ->
+ MRef = erlang:monitor(process, ControllingProcess),
+ reader_loop(#{ctrl_proc => ControllingProcess,
+ ctrl_proc_mref => MRef,
+ active => Active,
+ sock => Sock,
+ method => Method}).
+
+
+%% Never read
+reader_loop(#{active := false,
+ ctrl_proc := Pid} = State) ->
+ receive
+ {?MODULE, stop} ->
+ exit(normal);
+
+ {?MODULE, Pid, controlling_process, NewPid} ->
+ MRef = maps:get(ctrl_proc_mref, State),
+ erlang:demonitor(MRef, [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 when (Reason =:= normal) ->
+ exit(normal);
+ MRef ->
+ exit({controlling_process, Reason});
+ _ ->
+ reader_loop(State)
+ end
+ end;
+
+%% Read *once* and then change to false
+reader_loop(#{active := once,
+ sock := Sock,
+ method := Method,
+ ctrl_proc := Pid} = State) ->
+ case do_recv(Method, Sock) of
+ {ok, Data} ->
+ Pid ! ?DATA_MSG(Sock, Method, Data),
+ reader_loop(State#{active => false});
+ {error, timeout} ->
+ receive
+ {?MODULE, stop} ->
+ exit(normal);
+
+ {?MODULE, Pid, controlling_process, NewPid} ->
+ MRef = maps:get(ctrl_proc_mref, State),
+ erlang:demonitor(MRef, [flush]),
+ MRef = erlang:monitor(process, NewPid),
+ Pid ! {?MODULE, self(), controlling_process},
+ reader_loop(State#{ctrl_proc => NewPid,
+ ctrl_proc_mref => MRef});
+
+ {?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_loop(State)
+ end
+ after 0 ->
+ reader_loop(State)
+ end;
+
+ {error, closed} ->
+ Pid ! ?CLOSED_MSG(Sock, Method),
+ exit(normal);
+
+ {error, Reason} ->
+ Pid ! ?ERROR_MSG(Sock, Method, Reason),
+ exit(Reason)
+ end;
+
+%% Read and forward data continuously
+reader_loop(#{active := true,
+ sock := Sock,
+ method := Method,
+ ctrl_proc := Pid} = State) ->
+ case do_recv(Method, Sock) of
+ {ok, Data} ->
+ Pid ! ?DATA_MSG(Sock, Method, Data),
+ reader_loop(State);
+ {error, timeout} ->
+ receive
+ {?MODULE, stop} ->
+ exit(normal);
+
+ {?MODULE, Pid, controlling_process, NewPid} ->
+ MRef = maps:get(ctrl_proc_mref, State),
+ erlang:demonitor(MRef, [flush]),
+ MRef = erlang:monitor(process, NewPid),
+ Pid ! {?MODULE, self(), controlling_process},
+ reader_loop(State#{ctrl_proc => NewPid,
+ ctrl_proc_mref => MRef});
+
+ {?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_loop(State)
+ end
+ after 0 ->
+ reader_loop(State)
+ end;
+
+ {error, closed} ->
+ Pid ! ?CLOSED_MSG(Sock, Method),
+ exit(normal);
+
+ {error, Reason} ->
+ Pid ! ?ERROR_MSG(Sock, Method, Reason),
+ exit(Reason)
+ 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
+ {ok, #{iov := [Bin]}} ->
+ {ok, Bin};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+
+%% ==========================================================================
+
diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl
index 4e663fed7f..55b1162cfb 100644
--- a/erts/emulator/test/system_info_SUITE.erl
+++ b/erts/emulator/test/system_info_SUITE.erl
@@ -458,11 +458,16 @@ cmp_memory(MWs, Str) ->
%% Total, processes, processes_used, and system will seldom
%% give us exactly the same result since the two readings
%% aren't taken atomically.
+ %%
+ %% Torerance is scaled according to the number of schedulers
+ %% to match spawn_mem_workers.
+
+ Tolerance = 1.05 + 0.01 * erlang:system_info(schedulers_online),
- cmp_memory(total, EM, EDM, 1.05),
- cmp_memory(processes, EM, EDM, 1.05),
- cmp_memory(processes_used, EM, EDM, 1.05),
- cmp_memory(system, EM, EDM, 1.05),
+ cmp_memory(total, EM, EDM, Tolerance),
+ cmp_memory(processes, EM, EDM, Tolerance),
+ cmp_memory(processes_used, EM, EDM, Tolerance),
+ cmp_memory(system, EM, EDM, Tolerance),
ok.
diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl
index fc11a04a31..15fe13c8c0 100644
--- a/erts/emulator/test/timer_bif_SUITE.erl
+++ b/erts/emulator/test/timer_bif_SUITE.erl
@@ -361,7 +361,7 @@ evil_timers(Config) when is_list(Config) ->
%%
%% 1. A timer started with erlang:start_timer(Time, Receiver, Msg),
%% where Msg is a composite term, expires, and the receivers main
- %% lock *can not* be acquired immediately (typically when the
+ %% lock *cannot* be acquired immediately (typically when the
%% receiver *is* running).
%%
%% The wrap tuple ({timeout, TRef, Msg}) will in this case
@@ -372,7 +372,7 @@ evil_timers(Config) when is_list(Config) ->
RecvTimeOutMsgs0 = evil_recv_timeouts(200),
%% 2. A timer started with erlang:start_timer(Time, Receiver, Msg),
%% where Msg is an immediate term, expires, and the receivers main
- %% lock *can not* be acquired immediately (typically when the
+ %% lock *cannot* be acquired immediately (typically when the
%% receiver *is* running).
%%
%% The wrap tuple will in this case be allocated in a new