diff options
Diffstat (limited to 'erts/emulator/test')
57 files changed, 26425 insertions, 682 deletions
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 6a064ec8d4..019af2162f 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -28,6 +28,24 @@ EBIN = . # Target Specs # ---------------------------------------------------- +SOCKET_MODULES = \ + 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 +102,7 @@ MODULES= \ monitor_SUITE \ multi_load_SUITE \ nested_SUITE \ + $(NET_MODULES) \ nif_SUITE \ node_container_SUITE \ nofrag_SUITE \ @@ -105,7 +124,9 @@ MODULES= \ send_term_SUITE \ sensitive_SUITE \ signal_SUITE \ + small_SUITE \ smoke_test_SUITE \ + $(SOCKET_MODULES) \ statistics_SUITE \ system_info_SUITE \ system_profile_SUITE \ @@ -130,6 +151,7 @@ MODULES= \ ignore_cores \ dgawd_handler \ random_iolist \ + erts_test_utils \ crypto_reference NO_OPT= bs_bincomp \ @@ -152,8 +174,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 @@ -179,14 +207,10 @@ ERL_COMPILE_FLAGS += # ---------------------------------------------------- make_emakefile: $(NO_OPT_ERL_FILES) $(NATIVE_ERL_FILES) - # This special rule can be removed when communication with R7B nodes - # is no longer supported. - $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) +compressed -o$(EBIN) \ - '*_SUITE_make' > $(EMAKEFILE) $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) +compressed -o$(EBIN) \ $(MODULES) >> $(EMAKEFILE) - $(ERL_TOP)/make/make_emakefile +no_copt +no_postopt $(ERL_COMPILE_FLAGS) \ - -o$(EBIN) $(NO_OPT_MODULES) >> $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile +no_copt +no_postopt +no_ssa_opt +no_bsm_opt \ + $(ERL_COMPILE_FLAGS) -o$(EBIN) $(NO_OPT_MODULES) >> $(EMAKEFILE) $(ERL_TOP)/make/make_emakefile +native $(ERL_COMPILE_FLAGS) \ -o$(EBIN) $(NATIVE_MODULES) >> $(EMAKEFILE) @@ -200,6 +224,10 @@ clean: docs: +targets: $(TARGET_FILES) +socket_targets: $(SOCKET_TARGETS) +net_targets: $(NET_TARGETS) + # ---------------------------------------------------- # Special targets # ---------------------------------------------------- @@ -220,7 +248,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..43975d1800 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -37,7 +37,9 @@ 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, + fixed_apply_badarg/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -52,7 +54,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,fixed_apply_badarg]. %% Uses erlang:display to test that erts_printf does not do deep recursion display(Config) when is_list(Config) -> @@ -1181,7 +1184,70 @@ 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. + +%% apply/3 with a fixed number of arguments didn't include all arguments on +%% badarg exceptions. +fixed_apply_badarg(Config) when is_list(Config) -> + Bad = id({}), + + {'EXIT',{badarg, [{erlang,apply,[{},baz,[a,b]],[]} | _]}} = + (catch Bad:baz(a,b)), + {'EXIT',{badarg, [{erlang,apply,[baz,{},[c,d]],[]} | _]}} = + (catch baz:Bad(c,d)), + + {'EXIT',{badarg, [{erlang,apply,[{},baz,[e,f]],[]} | _]}} = + (catch apply(Bad,baz,[e,f])), + {'EXIT',{badarg, [{erlang,apply,[baz,{},[g,h]],[]} | _]}} = + (catch apply(baz,Bad,[g,h])), + + ok. + %% helpers id(I) -> I. diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 23c675733c..4fb339926e 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, @@ -62,7 +71,8 @@ term2bin_tuple_fallbacks/1, robustness/1,otp_8117/1, otp_8180/1, trapping/1, large/1, - error_after_yield/1, cmp_old_impl/1]). + error_after_yield/1, cmp_old_impl/1, + t2b_system_limit/1]). %% Internal exports. -export([sleeper/0,trapping_loop/4]). @@ -70,15 +80,19 @@ suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,4}}]. -all() -> +all() -> [copy_terms, conversions, deep_lists, deep_bitstr_lists, 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, - bad_term_to_binary, more_bad_terms, otp_5484, otp_5933, + bad_term_to_binary, t2b_system_limit, more_bad_terms, + otp_5484, otp_5933, ordering, unaligned_order, gc_test, bit_sized_binary_sizes, otp_6817, otp_8117, deep, term2bin_tuple_fallbacks, @@ -86,13 +100,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. @@ -427,6 +464,54 @@ bad_term_to_binary(Config) when is_list(Config) -> ok. +t2b_system_limit(Config) when is_list(Config) -> + case erlang:system_info(wordsize) of + 8 -> + case proplists:get_value(system_total_memory, + memsup:get_system_memory_data()) of + Memory when is_integer(Memory), + Memory > 6*1024*1024*1024 -> + test_t2b_system_limit(), + garbage_collect(), + ok; + _ -> + {skipped, "Not enough memory on this machine"} + end; + 4 -> + {skipped, "Only interesting on 64-bit builds"} + end. + +test_t2b_system_limit() -> + io:format("Creating HugeBin~n", []), + Bits = ((1 bsl 32)+1)*8, + HugeBin = <<0:Bits>>, + + io:format("Testing term_to_binary(HugeBin)~n", []), + {'EXIT',{system_limit,[{erlang,term_to_binary, + [HugeBin], + _} |_]}} = (catch term_to_binary(HugeBin)), + + io:format("Testing term_to_binary(HugeBin, [compressed])~n", []), + {'EXIT',{system_limit,[{erlang,term_to_binary, + [HugeBin, [compressed]], + _} |_]}} = (catch term_to_binary(HugeBin, [compressed])), + + %% Check that it works also after we have trapped... + io:format("Creating HugeListBin~n", []), + HugeListBin = [lists:duplicate(2000000,2000000), HugeBin], + + io:format("Testing term_to_binary(HugeListBin)~n", []), + {'EXIT',{system_limit,[{erlang,term_to_binary, + [HugeListBin], + _} |_]}} = (catch term_to_binary(HugeListBin)), + + io:format("Testing term_to_binary(HugeListBin, [compressed])~n", []), + {'EXIT',{system_limit,[{erlang,term_to_binary, + [HugeListBin, [compressed]], + _} |_]}} = (catch term_to_binary(HugeListBin, [compressed])), + + ok. + %% Tests binary_to_term/1 and term_to_binary/1. terms(Config) when is_list(Config) -> @@ -615,6 +700,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) -> @@ -1531,13 +1753,16 @@ error_after_yield_bad_ext_term() -> BadAtomExt]). %% Invalid atom at the end cmp_old_impl(Config) when is_list(Config) -> - %% Compare results from new yielding implementations with - %% old non yielding implementations + %% This test was originally a comparison with the non yielding + %% implementation in R16B. Since OTP 22 we can't talk distribution with such + %% old nodes (< 19). The test case it kept but compares with previous major + %% version for semantic regression test. Cookie = atom_to_list(erlang:get_cookie()), - Rel = "r16b_latest", + Rel = (integer_to_list(list_to_integer(erlang:system_info(otp_release)) - 1) + ++ "_latest"), case test_server:is_release_available(Rel) of false -> - {skipped, "No "++Rel++" available"}; + {skipped, "No OTP "++Rel++" available"}; true -> {ok, Node} = test_server:start_node(list_to_atom(atom_to_list(?MODULE)++"_"++Rel), peer, diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index ce50bcdd86..8fab4f5bc4 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -16,8 +16,6 @@ %% limitations under the License. %% %% %CopyrightEnd% -%% -%% Purpose : Common utilities used by several optimization passes. %% -module(bs_construct_SUITE). @@ -26,10 +24,10 @@ 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, - huge_float_field/1, huge_binary/1, system_limit/1, badarg/1, + mem_leak/1, coerce_to_float/1, bjorn/1, append_empty_is_same/1, + huge_float_field/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]). + otp_7422/1, zero_width/1, bad_append/1, bs_append_overflow/1]). -include_lib("common_test/include/ct.hrl"). @@ -39,10 +37,10 @@ suite() -> all() -> [test1, test2, test3, test4, test5, testf, not_used, - in_guard, mem_leak, coerce_to_float, bjorn, - huge_float_field, huge_binary, system_limit, badarg, + in_guard, mem_leak, coerce_to_float, bjorn, append_empty_is_same, + huge_float_field, system_limit, badarg, copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width, - bad_append, bs_add_overflow]. + bad_append, bs_append_overflow]. init_per_suite(Config) -> Config. @@ -520,6 +518,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>>), @@ -533,56 +541,6 @@ huge_float_field(Config) when is_list(Config) -> huge_float_check({'EXIT',{system_limit,_}}) -> ok; huge_float_check({'EXIT',{badarg,_}}) -> ok. -huge_binary(Config) when is_list(Config) -> - ct:timetrap({seconds, 60}), - 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>), - garbage_collect(), - FreeMem = free_mem(), - io:format("Free memory (Mb): ~p\n", [FreeMem]), - {Shift,Return} = case free_mem() of - undefined -> - %% This test has to be inlined inside the case to - %% use a literal Shift - garbage_collect(), - id(<<0:((1 bsl 32)-1)>>), - {32,ok}; - Mb when Mb > 600 -> - garbage_collect(), - id(<<0:((1 bsl 32)-1)>>), - {32,ok}; - Mb when Mb > 300 -> - garbage_collect(), - id(<<0:((1 bsl 31)-1)>>), - {31,"Limit huge binaries to 256 Mb"}; - Mb when Mb > 200 -> - garbage_collect(), - id(<<0:((1 bsl 30)-1)>>), - {30,"Limit huge binary to 128 Mb"}; - _ -> - garbage_collect(), - id(<<0:((1 bsl 29)-1)>>), - {29,"Limit huge binary to 64 Mb"} - end, - garbage_collect(), - id(<<0:((1 bsl Shift)-1)>>), - garbage_collect(), - id(<<0:(id((1 bsl Shift)-1))>>), - garbage_collect(), - case Return of - ok -> ok; - Comment -> {comment, Comment} - end. - -%% Return the amount of free memory in Mb. -free_mem() -> - {ok,Apps} = application:ensure_all_started(os_mon), - Mem = memsup:get_system_memory_data(), - [ok = application:stop(App)||App <- Apps], - case proplists:get_value(free_memory,Mem) of - undefined -> undefined; - Val -> Val div (1024*1024) - end. - system_limit(Config) when is_list(Config) -> WordSize = erlang:system_info(wordsize), BitsPerWord = WordSize * 8, @@ -894,33 +852,37 @@ append_unit_8(Bin) -> append_unit_16(Bin) -> <<Bin/binary-unit:16,0:1>>. -%% Produce a large result of bs_add that, if cast to signed int, would overflow -%% into a negative number that fits a smallnum. -bs_add_overflow(_Config) -> +%% Test that the bs_append instruction will correctly check for +%% overflow by producing a binary whose total size would exceed the +%% maximum allowed size for a binary on a 32-bit computer. + +bs_append_overflow(_Config) -> Memsize = memsize(), io:format("Memsize = ~w Bytes~n", [Memsize]), case erlang:system_info(wordsize) of 8 -> + %% Not possible to test on a 64-bit computer. {skip, "64-bit architecture"}; _ when Memsize < (2 bsl 30) -> - {skip, "Less then 2 GB of memory"}; + {skip, "Less than 2 GB of memory"}; 4 -> - {'EXIT', {system_limit, _}} = (catch bs_add_overflow_signed()), - {'EXIT', {system_limit, _}} = (catch bs_add_overflow_unsigned()), + {'EXIT', {system_limit, _}} = (catch bs_append_overflow_signed()), + erlang:garbage_collect(), + {'EXIT', {system_limit, _}} = (catch bs_append_overflow_unsigned()), + erlang:garbage_collect(), ok end. -bs_add_overflow_signed() -> - %% Produce a large result of bs_add that, if cast to signed int, would +bs_append_overflow_signed() -> + %% Produce a large binary that, if cast to signed int, would %% overflow into a negative number that fits a smallnum. Large = <<0:((1 bsl 30)-1)>>, <<Large/bits, Large/bits, Large/bits, Large/bits, Large/bits, Large/bits, Large/bits, Large/bits, Large/bits>>. -bs_add_overflow_unsigned() -> - %% Produce a large result of bs_add that goes beyond the limit of an - %% unsigned word. This used to succeed but produced an incorrect result +bs_append_overflow_unsigned() -> + %% The following would succeed but would produce an incorrect result %% where B =:= C! A = <<0:((1 bsl 32)-8)>>, B = <<2, 3>>, diff --git a/erts/emulator/test/bs_match_int_SUITE.erl b/erts/emulator/test/bs_match_int_SUITE.erl index e913dc98b0..454e55d017 100644 --- a/erts/emulator/test/bs_match_int_SUITE.erl +++ b/erts/emulator/test/bs_match_int_SUITE.erl @@ -234,34 +234,49 @@ mml_choose(<<_A:8>>) -> single_byte_binary; mml_choose(<<_A:8,_T/binary>>) -> multi_byte_binary. match_huge_int(Config) when is_list(Config) -> - Sz = 1 bsl 27, - Bin = <<0:Sz,13:8>>, - skip_huge_int_1(Sz, Bin), - 0 = match_huge_int_1(Sz, Bin), - - %% Test overflowing the size of an integer field. - nomatch = overflow_huge_int_skip_32(Bin), - case erlang:system_info(wordsize) of - 4 -> - nomatch = overflow_huge_int_32(Bin); - 8 -> - %% An attempt will be made to allocate heap space for - %% the bignum (which will probably fail); only if the - %% allocation succeeds will the matching fail because - %% the binary is too small. - ok - end, - nomatch = overflow_huge_int_skip_64(Bin), - nomatch = overflow_huge_int_64(Bin), - - %% Test overflowing the size of an integer field using variables as sizes. - Sizes = case erlang:system_info(wordsize) of - 4 -> lists:seq(25, 32); - 8 -> [] - end ++ lists:seq(50, 64), - ok = overflow_huge_int_unit128(Bin, Sizes), - - ok. + case ?MODULE of + bs_match_int_no_opt_SUITE -> + %% This test case is written with the assumption that + %% bs_skip2 instructions are used when the value of the + %% extracted segment will not be used. In OTP 21 and earlier, that + %% assumption was always true, because the bs_skip optimization + %% was included in v3_codegen and could not be disabled. + %% In OTP 22, the bs_skip optimization is done by beam_ssa_opt + %% and is disabled. + %% + %% On memory-constrained computers, using bs_get_integer2 + %% instructions may cause the runtime system to terminate + %% because of insufficient memory. + {skip, "unoptimized code would use too much memory"}; + bs_match_int_SUITE -> + Sz = 1 bsl 27, + Bin = <<0:Sz,13:8>>, + skip_huge_int_1(Sz, Bin), + 0 = match_huge_int_1(Sz, Bin), + + %% Test overflowing the size of an integer field. + nomatch = overflow_huge_int_skip_32(Bin), + case erlang:system_info(wordsize) of + 4 -> + nomatch = overflow_huge_int_32(Bin); + 8 -> + %% An attempt will be made to allocate heap space for + %% the bignum (which will probably fail); only if the + %% allocation succeeds will the matching fail because + %% the binary is too small. + ok + end, + nomatch = overflow_huge_int_skip_64(Bin), + nomatch = overflow_huge_int_64(Bin), + + %% Test overflowing the size of an integer field using + %% variables as sizes. + Sizes = case erlang:system_info(wordsize) of + 4 -> lists:seq(25, 32); + 8 -> [] + end ++ lists:seq(50, 64), + ok = overflow_huge_int_unit128(Bin, Sizes) + end. overflow_huge_int_unit128(Bin, [Sz0|Sizes]) -> Sz = id(1 bsl Sz0), diff --git a/erts/emulator/test/bs_match_misc_SUITE.erl b/erts/emulator/test/bs_match_misc_SUITE.erl index 17759d78f3..cae4eb54d2 100644 --- a/erts/emulator/test/bs_match_misc_SUITE.erl +++ b/erts/emulator/test/bs_match_misc_SUITE.erl @@ -24,7 +24,7 @@ kenneth/1,encode_binary/1,native/1,happi/1, size_var/1,wiger/1,x0_context/1,huge_float_field/1, writable_binary_matched/1,otp_7198/1,unordered_bindings/1, - float_middle_endian/1]). + float_middle_endian/1,unsafe_get_binary_reuse/1]). -include_lib("common_test/include/ct.hrl"). @@ -36,7 +36,8 @@ all() -> [bound_var, bound_tail, t_float, little_float, sean, kenneth, encode_binary, native, happi, size_var, wiger, x0_context, huge_float_field, writable_binary_matched, - otp_7198, unordered_bindings, float_middle_endian]. + otp_7198, unordered_bindings, float_middle_endian, + unsafe_get_binary_reuse]. %% Test matching of bound variables. @@ -556,5 +557,21 @@ unordered_bindings(CompressedLength, HashSize, PadLength, T) -> Padding:PadLength/binary,PadLength>> = T, {Content,Mac,Padding}. +%% ERL-901: A load-time optimization assumed that match contexts had no further +%% uses when a bs_get_binary2 overwrote the match context's register, and +%% figured it would be safe to reuse the match context's memory for the +%% resulting binary. +%% +%% This is no longer safe as of OTP 22, as a match context may be reused after +%% being passed to another function. +unsafe_get_binary_reuse(Config) when is_list(Config) -> + <<_First, Rest/binary>> = <<"hello">>, + ubgr_1(Rest), + <<Second,_/bits>> = Rest, + $e = Second, + ok. + +ubgr_1(<<_CP/utf8, Rest/binary>>) -> id(Rest); +ubgr_1(_) -> false. id(I) -> I. 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..7885d35d9d 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -19,7 +19,6 @@ %% -module(distribution_SUITE). --compile(r16). -define(VERSION_MAGIC, 131). @@ -39,6 +38,8 @@ -define(Line,). -export([all/0, suite/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, ping/1, bulk_send_small/1, group_leader/1, optimistic_dflags/1, @@ -53,7 +54,6 @@ dist_parallel_send/1, atom_roundtrip/1, unicode_atom_roundtrip/1, - atom_roundtrip_r16b/1, contended_atom_cache_entry/1, contended_unicode_atom_cache_entry/1, bad_dist_structure/1, @@ -62,7 +62,13 @@ 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, + system_limit/1]). %% Internal exports. -export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0, @@ -70,7 +76,7 @@ optimistic_dflags_echo/0, optimistic_dflags_sender/1, roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1, dist_parallel_sender/3, dist_parallel_receiver/0, - dist_evil_parallel_receiver/0]). + dist_evil_parallel_receiver/0, make_busy/2]). %% epmd_module exports -export([start_link/0, register_node/2, register_node/3, port_please/2, address_please/3]). @@ -88,10 +94,10 @@ all() -> ref_port_roundtrip, nil_roundtrip, stop_dist, {group, trap_bif}, {group, dist_auto_connect}, 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}, - start_epmd_false, epmd_module]. + {group, message_latency}, + {group, bad_dist}, {group, bad_dist_ext}, + start_epmd_false, epmd_module, system_limit]. groups() -> [{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]}, @@ -100,10 +106,40 @@ 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]} + ]. + +init_per_suite(Config) -> + {ok, Apps} = application:ensure_all_started(os_mon), + [{started_apps, Apps} | Config]. + +end_per_suite(Config) -> + Apps = proplists:get_value(started_apps, Config), + [application:stop(App) || App <- lists:reverse(Apps)], + Config. + +init_per_group(message_latency, Config) -> + Free = free_memory(), + if Free < 2048 -> + {skip, "Not enough memory"}; + true -> + Config + end; +init_per_group(_, Config) -> + Config. + +end_per_group(_, Config) -> + Config. %% Tests pinging a node in different ways. ping(Config) when is_list(Config) -> @@ -568,10 +604,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. @@ -720,6 +766,7 @@ link_to_dead_new_node(Config) when is_list(Config) -> {'EXIT', Pid, noproc} -> ok; Other -> + stop_node(Node), ct:fail({unexpected_message, Other}) after 5000 -> ct:fail(nothing_received) @@ -931,7 +978,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 @@ -1107,23 +1156,6 @@ atom_roundtrip(Config) when is_list(Config) -> stop_node(Node), ok. -atom_roundtrip_r16b(Config) when is_list(Config) -> - case test_server:is_release_available("r16b") of - true -> - ct:timetrap({minutes, 6}), - AtomData = unicode_atom_data(), - verify_atom_data(AtomData), - case start_node(Config, [], "r16b") of - {ok, Node} -> - do_atom_roundtrip(Node, AtomData), - stop_node(Node); - {error, timeout} -> - {skip,"Unable to start OTP R16B release"} - end; - false -> - {skip,"No OTP R16B available"} - end. - unicode_atom_roundtrip(Config) when is_list(Config) -> AtomData = unicode_atom_data(), verify_atom_data(AtomData), @@ -1364,6 +1396,286 @@ 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, _, _} -> + 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), + + BuildType = erlang:system_info(build_type), + WordSize = erlang:system_info(wordsize), + + if + BuildType =/= opt; WordSize =:= 4 -> + %% Test 3.2 MB and 32 MB and test the latency difference of sent messages + Payloads = [{I, <<0:(I * 32 * 1024 * 8)>>} || I <- [1,10]]; + true -> + %% 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]] + end, + + IndexTimes = [{I, measure_latency(DataFun, Dropper, Echo, P)} + || {I, P} <- Payloads], + + Times = [ Time || {_I, Time} <- IndexTimes], + + ct:pal("~p",[IndexTimes]), + + stop_node(N), + + case {lists:max(Times), lists:min(Times)} of + {Max, Min} when Max * 0.25 > Min, BuildType =:= opt -> + 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} -> + ok + end || _ <- lists:seq(1,10)], + + {TS, Times} = + timer:tc(fun() -> + [begin + T0 = erlang:monotonic_time(), + Echo ! {self(), hello}, + receive hello -> ok end, + (erlang:monotonic_time() - T0) / 1000000 + end || _ <- lists:seq(1,100)] + end), + Avg = lists:sum(Times) / length(Times), + StdDev = math:sqrt(lists:sum([math:pow(V - Avg,2) || V <- Times]) / length(Times)), + ct:pal("Times: Avg: ~p Max: ~p Min: ~p Var: ~p", + [Avg, lists:max(Times), lists:min(Times), StdDev]), + [begin + Sender ! die, + receive + {'DOWN', Ref, process, _, _} -> + ok + end + end || {Sender, Ref} <- Senders], + TS. + +flush() -> + receive + _ -> + flush() + after 0 -> + ok + end. + +system_limit(Config) when is_list(Config) -> + case erlang:system_info(wordsize) of + 8 -> + case proplists:get_value(system_total_memory, + memsup:get_system_memory_data()) of + Memory when is_integer(Memory), + Memory > 6*1024*1024*1024 -> + test_system_limit(Config), + garbage_collect(), + ok; + _ -> + {skipped, "Not enough memory on this machine"} + end; + 4 -> + {skipped, "Only interesting on 64-bit builds"} + end. + +test_system_limit(Config) when is_list(Config) -> + Bits = ((1 bsl 32)+1)*8, + HugeBin = <<0:Bits>>, + HugeListBin = [lists:duplicate(2000000,2000000), HugeBin], + {ok, N1} = start_node(Config), + monitor_node(N1, true), + receive + {nodedown, N1} -> + ct:fail({unexpected_nodedown, N1}) + after 0 -> + ok + end, + P1 = spawn(N1, + fun () -> + receive after infinity -> ok end + end), + + io:format("~n** distributed send **~n~n", []), + try + P1 ! HugeBin, + exit(oops1) + catch + error:system_limit -> ok + end, + try + P1 ! HugeListBin, + exit(oops2) + catch + error:system_limit -> ok + end, + + io:format("~n** distributed exit **~n~n", []), + try + exit(P1, HugeBin), + exit(oops3) + catch + error:system_limit -> ok + end, + try + exit(P1, HugeListBin), + exit(oops4) + catch + error:system_limit -> ok + end, + + io:format("~n** distributed registered send **~n~n", []), + try + {missing_proc, N1} ! HugeBin, + exit(oops5) + catch + error:system_limit -> ok + end, + try + {missing_proc, N1} ! HugeListBin, + exit(oops6) + catch + error:system_limit -> ok + end, + receive + {nodedown, N1} -> + ct:fail({unexpected_nodedown, N1}) + after 0 -> + ok + end, + + %% + %% system_limit in exit reasons brings the + %% connection down... + %% + + io:format("~n** distributed link exit **~n~n", []), + spawn(fun () -> + link(P1), + exit(HugeBin) + end), + receive {nodedown, N1} -> ok end, + + {ok, N2} = start_node(Config), + monitor_node(N2, true), + P2 = spawn(N2, + fun () -> + receive after infinity -> ok end + end), + spawn(fun () -> + link(P2), + exit(HugeListBin) + end), + receive {nodedown, N2} -> ok end, + + io:format("~n** distributed monitor down **~n~n", []), + {ok, N3} = start_node(Config), + monitor_node(N3, true), + Go1 = make_ref(), + LP1 = spawn(fun () -> + receive Go1 -> ok end, + exit(HugeBin) + end), + _ = spawn(N3, + fun () -> + _ = erlang:monitor(process, LP1), + LP1 ! Go1, + receive after infinity -> ok end + end), + receive {nodedown, N3} -> ok end, + + {ok, N4} = start_node(Config), + monitor_node(N4, true), + Go2 = make_ref(), + LP2 = spawn(fun () -> + receive Go2 -> ok end, + exit(HugeListBin) + end), + _ = spawn(N4, + fun () -> + _ = erlang:monitor(process, LP2), + LP2 ! Go2, + receive after infinity -> ok end + end), + receive {nodedown, N4} -> ok end, + ok. + -define(COOKIE, ''). -define(DOP_LINK, 1). -define(DOP_SEND, 2). @@ -1382,6 +1694,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 +1836,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,Exit2Victim},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 +2583,30 @@ 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(), + Pid = spawn(Node, + fun() -> + R = erts_test_utils:check_node_dist(fun(E) -> E end), + P ! {Ref, R} + end), + MonRef = monitor(process, Pid), + receive + {Ref, ok} -> + demonitor(MonRef,[flush]), + ok; + {Ref, Error} -> + ct:log("~p",[Error]), + ct:fail(failed_nc_refc_check); + {'DOWN', MonRef, _, _, _} = Down -> + ct:log("~p",[Down]), + ct:fail(crashed_nc_refc_check) + end. + freeze_node(Node, MS) -> Own = 300, DoingIt = make_ref(), @@ -2485,6 +2966,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, @@ -2505,3 +2997,23 @@ uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 -> Uint band 16#ff; uint8(Uint) -> exit({badarg, uint8, [Uint]}). + +free_memory() -> + %% Free memory in MB. + try + SMD = memsup:get_system_memory_data(), + {value, {free_memory, Free}} = lists:keysearch(free_memory, 1, SMD), + TotFree = (Free + + case lists:keysearch(cached_memory, 1, SMD) of + {value, {cached_memory, Cached}} -> Cached; + false -> 0 + end + + case lists:keysearch(buffered_memory, 1, SMD) of + {value, {buffered_memory, Buffed}} -> Buffed; + false -> 0 + end), + TotFree div (1024*1024) + catch + error : undef -> + ct:fail({"os_mon not built"}) + end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 1d2ae4fb51..f6d7c55017 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -120,29 +120,6 @@ -define(heap_binary_size, 64). -init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - CIOD = rpc(Config, - fun() -> - case catch erts_debug:get_internal_state(available_internal_state) of - true -> ok; - _ -> erts_debug:set_internal_state(available_internal_state, true) - end, - erts_debug:get_internal_state(check_io_debug) - end), - erlang:display({init_per_testcase, Case}), - 0 = element(1, CIOD), - [{testcase, Case}|Config]. - -end_per_testcase(Case, Config) -> - erlang:display({end_per_testcase, Case}), - CIOD = rpc(Config, - fun() -> - get_stable_check_io_info(), - erts_debug:get_internal_state(check_io_debug) - end), - 0 = element(1, CIOD), - ok. - suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 1}}]. @@ -219,6 +196,48 @@ end_per_group(_GroupName, Config) -> end, Config. +init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> + CIOD = rpc(Config, + fun() -> + case catch erts_debug:get_internal_state(available_internal_state) of + true -> ok; + _ -> erts_debug:set_internal_state(available_internal_state, true) + end, + erts_debug:get_internal_state(check_io_debug) + end), + erlang:display({init_per_testcase, Case}), + 0 = element(1, CIOD), + [{testcase, Case}|Config]. + +end_per_testcase(Case, Config) -> + erlang:display({end_per_testcase, Case}), + try rpc(Config, fun() -> + get_stable_check_io_info(), + erts_debug:get_internal_state(check_io_debug) + end) of + CIOD -> + 0 = element(1, CIOD) + catch _E:_R:_ST -> + %% Logs some info about the system + ct_os_cmd("epmd -names"), + ct_os_cmd("ps aux"), + %% Restart the node + case proplists:get_value(node, Config) of + undefined -> + ok; + Node -> + timer:sleep(1000), %% Give the node time to die + [NodeName, _] = string:lexemes(atom_to_list(Node),"@"), + {ok, Node} = start_node_final( + list_to_atom(NodeName), + proplists:get_value(node_args, Config)) + end + end, + ok. + +ct_os_cmd(Cmd) -> + ct:log("~s: ~s",[Cmd,os:cmd(Cmd)]). + %% Test sending bad types to port with an outputv-capable driver. outputv_errors(Config) when is_list(Config) -> Path = proplists:get_value(data_dir, Config), @@ -998,7 +1017,9 @@ chkio_test({erts_poll_info, Before}, During = get_check_io_total(erlang:system_info(check_io)), erlang:display(During), - 0 = element(1, erts_debug:get_internal_state(check_io_debug)), + [0 = element(1, erts_debug:get_internal_state(check_io_debug)) || + %% The pollset is not stable when running the fallback testcase + Test /= ?CHKIO_USE_FALLBACK_POLLSET], io:format("During test: ~p~n", [During]), chk_chkio_port(Port), case erlang:port_control(Port, ?CHKIO_STOP, "") of @@ -2642,7 +2663,6 @@ start_node(Config) when is_list(Config) -> start_node(Name) -> start_node(Name, ""). start_node(NodeName, Args) -> - Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" ++ atom_to_list(NodeName) @@ -2650,7 +2670,17 @@ start_node(NodeName, Args) -> ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - test_server:start_node(Name, slave, [{args, Args ++ " -pa "++Pa}]). + start_node_final(Name, Args). +start_node_final(Name, Args) -> + {ok, Pwd} = file:get_cwd(), + FinalArgs = [Args, " -pa ", filename:dirname(code:which(?MODULE))], + {ok, Node} = test_server:start_node(Name, slave, [{args, FinalArgs}]), + LogPath = Pwd ++ "/error_log." ++ atom_to_list(Name), + ct:pal("Logging to: ~s", [LogPath]), + rpc:call(Node, logger, add_handler, [file_handler, logger_std_h, + #{formatter => {logger_formatter,#{ single_line => false }}, + config => #{file => LogPath }}]), + {ok, Node}. stop_node(Node) -> test_server:stop_node(Node). @@ -2665,24 +2695,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/dump_SUITE.erl b/erts/emulator/test/dump_SUITE.erl index d0237b78cc..9f8ac42fa9 100644 --- a/erts/emulator/test/dump_SUITE.erl +++ b/erts/emulator/test/dump_SUITE.erl @@ -24,7 +24,7 @@ -export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]). --export([signal_abort/1]). +-export([signal_abort/1, exiting_dump/1, free_dump/1]). -export([load/0]). @@ -35,7 +35,7 @@ suite() -> {timetrap, {minutes, 2}}]. all() -> - [signal_abort]. + [signal_abort, exiting_dump, free_dump]. init_per_testcase(signal_abort, Config) -> SO = erlang:system_info(schedulers_online), @@ -48,7 +48,10 @@ init_per_testcase(signal_abort, Config) -> {skip, "the platform does not support scheduler dump"}; Dump -> Config - end. + end; +init_per_testcase(_, Config) -> + Config. + end_per_testcase(_, Config) -> Config. @@ -79,8 +82,6 @@ signal_abort(Config) -> {ok, Bin} = get_dump_when_done(Dump), - ct:log("~s",[Bin]), - {match, Matches} = re:run(Bin,"Current Process: <",[global]), ct:log("Found ~p",[Matches]), @@ -91,6 +92,102 @@ signal_abort(Config) -> ok. +load() -> + lists:seq(1,10000), + load(). + + +%% Test that crash dumping when a process is in the state EXITING works +exiting_dump(Config) when is_list(Config) -> + Dump = filename:join(proplists:get_value(priv_dir, Config),"signal_abort.dump"), + + {ok, Node} = start_node(Config), + + Self = self(), + + Pid = spawn_link(Node, + fun() -> + [begin + T = ets:new(hej,[]), + [ets:insert(T,{I,I}) || I <- lists:seq(1,1000)] + end || _ <- lists:seq(1,1000)], + Self ! ready, + receive ok -> ok end + end), + + true = rpc:call(Node, os, putenv, ["ERL_CRASH_DUMP",Dump]), + + receive ready -> unlink(Pid), Pid ! ok end, + + rpc:call(Node, erlang, halt, ["dump"]), + + {ok, Bin} = get_dump_when_done(Dump), + + {match, Matches} = re:run(Bin,"^State: Exiting", [global, multiline]), + + ct:log("Found ~p",[Matches]), + + true = length(Matches) == 1, + + file:delete(Dump), + + ok. + +%% Test that crash dumping when a process is in the state FREE works +free_dump(Config) when is_list(Config) -> + Dump = filename:join(proplists:get_value(priv_dir, Config),"signal_abort.dump"), + + {ok, NodeA} = start_node(Config), + {ok, NodeB} = start_node(Config), + + + Self = self(), + + PidA = spawn_link( + NodeA, + fun() -> + Self ! ready, + receive + ok -> + spawn(fun() -> + erlang:system_monitor(self(), [busy_dist_port]), + timer:sleep(5), + receive + M -> + io:format("~p",[M]), + erlang:halt("dump") + end + end), + exit(lists:duplicate(1000000,100)) + end + end), + + spawn_link(NodeB, + fun() -> + [erlang:monitor(process, PidA) || _ <- lists:seq(1,10000)], + Self ! done, + receive _ -> ok end + end), + + receive done -> ok end, + true = rpc:call(NodeA, os, putenv, ["ERL_CRASH_DUMP",Dump]), + ct:pal("~p",[rpc:call(NodeA, distribution_SUITE, make_busy, [NodeB, 1000])]), + + receive ready -> unlink(PidA), PidA ! ok end, + + {ok, Bin} = get_dump_when_done(Dump), + + {match, Matches} = re:run(Bin,"^State: Non Existing", [global, multiline]), + + ct:log("Found ~p",[Matches]), + + true = length(Matches) == 1, + + file:delete(Dump), + + ok. + + get_dump_when_done(Dump) -> case file:read_file_info(Dump) of {ok, #file_info{ size = Sz }} -> @@ -104,15 +201,13 @@ get_dump_when_done(Dump, Sz) -> timer:sleep(1000), case file:read_file_info(Dump) of {ok, #file_info{ size = Sz }} -> - file:read_file(Dump); + {ok, Bin} = file:read_file(Dump), + ct:log("~s",[Bin]), + {ok, Bin}; {ok, #file_info{ size = NewSz }} -> get_dump_when_done(Dump, NewSz) end. -load() -> - lists:seq(1,10000), - load(). - start_node(Config) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index 7dcf302742..045b351e02 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -45,7 +45,12 @@ iter_max_files(Config) when is_list(Config) -> iter_max_files_1(Config) -> DataDir = proplists:get_value(data_dir,Config), TestFile = filename:join(DataDir, "existing_file"), - N = 10, + case erlang:system_info(debug_compiled) of + true -> + N = 5; + false -> + N = 10 + end, %% Run on a different node in order to make the test more stable. Dir = filename:dirname(code:which(?MODULE)), {ok,Node} = test_server:start_node(test_iter_max_files,slave, @@ -100,34 +105,27 @@ open_files(Name) -> %% a /proc directory), let's read some zero sized files 500 times each, while %% ensuring that response isn't empty << >> proc_zero_sized_files(Config) when is_list(Config) -> - {Type, Flavor} = os:type(), - %% Some files which exist on Linux but might be missing on other systems - Inputs = ["/proc/cpuinfo", - "/proc/meminfo", - "/proc/partitions", - "/proc/swaps", - "/proc/version", - "/proc/uptime", - %% curproc is present on freebsd - "/proc/curproc/cmdline"], - case filelib:is_dir("/proc") of - false -> {skip, "/proc not found"}; % skip the test if no /proc - _ when Type =:= unix andalso Flavor =:= sunos -> - %% SunOS has a /proc, but no zero sized special files - {skip, "sunos does not have any zero sized special files"}; - true -> - %% Take away files which do not exist in proc - Inputs1 = lists:filter(fun filelib:is_file/1, Inputs), - - %% Fail if none of mentioned files exist in /proc, did we just get - %% a normal /proc directory without any special files? - ?assertNotEqual([], Inputs1), - + TestFiles0 = [%% Some files which exist on Linux but might be missing on + %% other systems + "/proc/cpuinfo", + "/proc/meminfo", + "/proc/partitions", + "/proc/swaps", + "/proc/version", + "/proc/uptime", + %% curproc is present on FreeBSD + "/proc/curproc/cmdline"], + + TestFiles = [F || F <- TestFiles0, filelib:is_file(F)], + + case TestFiles of + [_|_] -> %% For 6 inputs and 500 attempts each this do run anywhere %% between 500 and 3000 function calls. - lists:foreach( - fun(Filename) -> do_proc_zero_sized(Filename, 500) end, - Inputs1) + [do_proc_zero_sized(F, 500) || F <- TestFiles], + ok; + [] -> + {skip, "Failed to find any known zero-sized files"} end. %% @doc Test one file N times to also trigger possible leaking fds and memory diff --git a/erts/emulator/test/emulator_bench.spec b/erts/emulator/test/emulator_bench.spec index f709d913b7..03638bfa23 100644 --- a/erts/emulator/test/emulator_bench.spec +++ b/erts/emulator/test/emulator_bench.spec @@ -1 +1,3 @@ {groups,"../emulator_test",estone_SUITE,[estone_bench]}. +{groups,"../emulator_test",binary_SUITE,[iolist_size_benchmarks]}. +{groups,"../emulator_test",erts_debug_SUITE,[interpreter_size_bench]}. diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index 6aa7a445b5..6798e3bf25 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -20,10 +20,14 @@ -module(erts_debug_SUITE). -include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). --export([all/0, suite/0, - test_size/1,flat_size_big/1,df/1,term_type/1, - instructions/1, stack_check/1]). +-export([all/0, suite/0, groups/0, + test_size/1,flat_size_big/1,df/1,term_type/1, + instructions/1, stack_check/1, alloc_blocks_size/1, + interpreter_size_bench/1]). + +-export([do_alloc_blocks_size/0]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -31,7 +35,16 @@ suite() -> all() -> [test_size, flat_size_big, df, instructions, term_type, - stack_check]. + stack_check, alloc_blocks_size]. + +groups() -> + [{interpreter_size_bench, [], [interpreter_size_bench]}]. + +interpreter_size_bench(_Config) -> + Size = erts_debug:interpreter_size(), + ct_event:notify(#event{name=benchmark_data, + data=[{value,Size}]}), + {comment,integer_to_list(Size)++" bytes"}. test_size(Config) when is_list(Config) -> ConsCell1 = id([a|b]), @@ -210,5 +223,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..9c9eaa70ed --- /dev/null +++ b/erts/emulator/test/erts_test_utils.erl @@ -0,0 +1,272 @@ +%% +%% %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). +-compile(r20). + +%% +%% 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_misc/socket_client.erl b/erts/emulator/test/esock_misc/socket_client.erl new file mode 100644 index 0000000000..1c07e799b8 --- /dev/null +++ b/erts/emulator/test/esock_misc/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/esock_misc/socket_lib.erl b/erts/emulator/test/esock_misc/socket_lib.erl new file mode 100644 index 0000000000..9d6524d467 --- /dev/null +++ b/erts/emulator/test/esock_misc/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/esock_misc/socket_server.erl b/erts/emulator/test/esock_misc/socket_server.erl new file mode 100644 index 0000000000..45adffc5e6 --- /dev/null +++ b/erts/emulator/test/esock_misc/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/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..154bce3c35 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -23,6 +23,7 @@ -export([all/0, suite/0, badmatch/1, pending_errors/1, nil_arith/1, top_of_stacktrace/1, stacktrace/1, nested_stacktrace/1, raise/1, gunilla/1, per/1, + change_exception_class/1, exception_with_heap_frag/1, backtrace_depth/1, line_numbers/1]). @@ -36,6 +37,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}}]. @@ -43,6 +49,7 @@ suite() -> all() -> [badmatch, pending_errors, nil_arith, top_of_stacktrace, stacktrace, nested_stacktrace, raise, gunilla, per, + change_exception_class, exception_with_heap_frag, backtrace_depth, line_numbers]. -define(try_match(E), @@ -507,6 +514,38 @@ t1(_,X,_) -> t2(_,X,_) -> (X bsl 1) + 1. +change_exception_class(_Config) -> + try + change_exception_class_1(fun() -> throw(arne) end) + catch + error:arne -> + ok; + Class:arne -> + ct:fail({wrong_exception_class,Class}) + end. + +change_exception_class_1(F) -> + try + change_exception_class_2(F) + after + %% The exception would be caught and rethrown using + %% an i_raise instruction. Before the correction + %% of the raw_raise instruction, the change of class + %% would not stick. + io:put_chars("Exception automatically rethrown here\n") + end. + +change_exception_class_2(F) -> + try + F() + catch + throw:Reason:Stack -> + %% Translated to a raw_raise instruction. + %% The change of exception class would not stick + %% if the i_raise instruction was later executed. + erlang:raise(error, Reason, Stack) + end. + %% %% Make sure that even if a BIF builds an heap fragment, then causes an exception, %% the stacktrace term will still be OK (specifically, that it does not contain diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 73fe9b0d8f..2cbde621ce 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -513,6 +513,8 @@ refc(Config) when is_list(Config) -> Other -> ct:fail({unexpected,Other}) end, process_flag(trap_exit, false), + %% Wait to make sure that the process has terminated completely. + receive after 1 -> ok end, {refc,3} = erlang:fun_info(F1, refc), %% Garbage collect. Only the F2 fun will be left. @@ -576,7 +578,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, @@ -592,13 +594,14 @@ refc_dist(Config) when is_list(Config) -> 3 = fun_refc(F2), true = erlang:garbage_collect(), 2 = fun_refc(F), - refc_dist_send(Node, F). + refc_dist_send(Node, F), + test_server:stop_node(Node). 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 +629,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), @@ -682,6 +685,7 @@ t_arity(Config) when is_list(Config) -> 43 = spawn_call(Node, fun(X, Y) -> A+X+Y end), 1 = spawn_call(Node, fun(X, Y) -> X+Y end), 45 = spawn_call(Node, fun(X, Y, Z) -> A+X+Y+Z end), + test_server:stop_node(Node), ok. t_is_function2(Config) when is_list(Config) -> @@ -710,6 +714,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 +820,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 +837,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/lcnt_SUITE.erl b/erts/emulator/test/lcnt_SUITE.erl index 87b97037d6..2dbaec9942 100644 --- a/erts/emulator/test/lcnt_SUITE.erl +++ b/erts/emulator/test/lcnt_SUITE.erl @@ -187,5 +187,8 @@ remove_untoggleable_locks([]) -> []; remove_untoggleable_locks([{resource_monitors, _, _, _} | T]) -> remove_untoggleable_locks(T); +remove_untoggleable_locks([{'socket[gcnt]', _, _, _} | T]) -> + %% Global lock used by socket NIF + remove_untoggleable_locks(T); remove_untoggleable_locks([H | T]) -> [H | remove_untoggleable_locks(T)]. diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index d0a6763fe5..9ea59e1084 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -17,7 +17,7 @@ %% %CopyrightEnd% %% -module(map_SUITE). --export([all/0, suite/0]). +-export([all/0, suite/0, init_per_suite/1, end_per_suite/1]). -export([t_build_and_match_literals/1, t_build_and_match_literals_large/1, t_update_literals/1, t_update_literals_large/1, @@ -84,7 +84,10 @@ %% instruction-level tests t_has_map_fields/1, y_regs/1, - badmap_17/1]). + badmap_17/1, + + %%Bugs + t_large_unequal_bins_same_hash_bug/1]). -include_lib("stdlib/include/ms_transform.hrl"). @@ -149,7 +152,26 @@ all() -> [t_build_and_match_literals, t_build_and_match_literals_large, %% instruction-level tests t_has_map_fields, y_regs, - badmap_17]. + badmap_17, + + %% Bugs + t_large_unequal_bins_same_hash_bug]. + +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. %% tests @@ -3374,3 +3396,67 @@ fannerl() -> 104,2,97,9,97,16,70,63,184,100,97,32,0,0,0,104,2,97,10,97,16,70,63,169,174, 254,64,0,0,0,104,2,97,11,97,16,70,191,119,121,234,0,0,0,0,104,2,97,12,97, 16,70,63,149,12,170,128,0,0,0,104,2,97,13,97,16,70,191,144,193,191,0,0,0,0>>. + +%% This test case checks that the bug with ticket number OTP-15707 is +%% fixed. The bug could cause a crash or memory usage to grow until +%% the machine ran out of memory. +t_large_unequal_bins_same_hash_bug(Config) when is_list(Config) -> + run_when_enough_resources( + fun() -> + K1 = get_4GB_bin(1), + K2 = get_4GB_bin(2), + Map = make_map(500), + Map2 = maps:put(K1, 42, Map), + %% The map needed to contain at least 32 key-value pairs + %% at this point to get the crash or out of memory + %% problem on the next line + Map3 = maps:put(K2, 43, Map2), + %% The following line should avoid that the compiler + %% optimizes away the above + io:format("~p ~p~n", [erlang:phash2(Map3), maps:size(Map3)]) + end). + +make_map(0) -> + #{}; +make_map(Size) -> + maps:put(Size, Size, make_map(Size-1)). + +get_4GB_bin(Value) -> + List = lists:duplicate(65536, Value), + Bin = erlang:iolist_to_binary(List), + IOList4GB = duplicate_iolist(Bin, 16), + Bin4GB = erlang:iolist_to_binary(IOList4GB), + 4294967296 = size(Bin4GB), + Bin4GB. + +duplicate_iolist(IOList, 0) -> + IOList; +duplicate_iolist(IOList, NrOfTimes) -> + duplicate_iolist([IOList, IOList], NrOfTimes - 1). + +run_when_enough_resources(Fun) -> + case {total_memory(), erlang:system_info(wordsize)} of + {Mem, 8} when is_integer(Mem) andalso Mem >= 31 -> + Fun(); + {Mem, WordSize} -> + {skipped, + io_lib:format("Not enough resources (System Memory >= ~p, Word Size = ~p)", + [Mem, WordSize])} + end. + +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. diff --git a/erts/emulator/test/net_SUITE.erl b/erts/emulator/test/net_SUITE.erl new file mode 100644 index 0000000000..6111fc76a5 --- /dev/null +++ b/erts/emulator/test/net_SUITE.erl @@ -0,0 +1,486 @@ +%% +%% %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 lists:member(socket, erlang:loaded()) of + true -> + case os:type() of + {win32, _} -> + not_yet_implemented(); + _ -> + %% ?LOGGER:start(), + Config + end; + false -> + {skip, "esock disabled"} + 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..b824daea67 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -28,6 +28,7 @@ -include_lib("stdlib/include/assert.hrl"). -export([all/0, suite/0, groups/0, + init_per_suite/1, end_per_suite/1, init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2, basic/1, reload_error/1, upgrade/1, heap_frag/1, @@ -64,7 +65,9 @@ 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, + nif_term_type/1 ]). -export([many_args_100/100]). @@ -103,7 +106,17 @@ all() -> nif_internal_hash_salted, nif_phash2, nif_whereis, nif_whereis_parallel, nif_whereis_threaded, - nif_ioq]. + nif_ioq, + pid, + nif_term_type]. + +init_per_suite(Config) -> + erts_debug:set_internal_state(available_internal_state, true), + Config. + +end_per_suite(_Config) -> + catch erts_debug:set_internal_state(available_internal_state, false), + ok. groups() -> [{G, [], api_repeaters()} || G <- api_groups()] @@ -114,7 +127,6 @@ groups() -> monitor_process_d, demonitor_process]}]. - api_groups() -> [api_latest, api_2_4, api_2_0]. api_repeaters() -> [upgrade, resource_takeover, t_on_load]. @@ -485,91 +497,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 +617,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 +649,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 +657,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 +675,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 +689,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 +834,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 +852,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. @@ -1179,7 +1231,7 @@ maps(Config) when is_list(Config) -> repeat_while(fun({35,_}) -> false; ({K,Map}) -> Map = maps_from_list_nif(maps:to_list(Map)), - Map = maps:filter(fun(K,V) -> V =:= K*100 end, Map), + Map = maps:filter(fun(K2,V) -> V =:= K2*100 end, Map), {K+1, maps:put(K,K*100,Map)} end, {1,#{}}), @@ -1250,24 +1302,29 @@ resource_hugo_do(Type) -> release_resource(HugoPtr), erlang:garbage_collect(), {HugoPtr,HugoBin} = get_resource(Type,Hugo), - Pid = spawn_link(fun() -> - receive {Pid, Type, Resource, Ptr, Bin} -> - Pid ! {self(), got_it}, - receive {Pid, check_it} -> - {Ptr,Bin} = get_resource(Type,Resource), - Pid ! {self(), ok} - end - end - end), + {Pid,_} = + spawn_monitor(fun() -> + receive {Pid, Type, Resource, Ptr, Bin} -> + Pid ! {self(), got_it}, + receive {Pid, check_it} -> + {Ptr,Bin} = get_resource(Type,Resource) + end + end, + gc_and_exit(ok) + end), Pid ! {self(), Type, Hugo, HugoPtr, HugoBin}, {Pid, got_it} = receive_any(), erlang:garbage_collect(), % just to make our ProcBin move in memory Pid ! {self(), check_it}, - {Pid, ok} = receive_any(), + {'DOWN', _, process, Pid, ok} = receive_any(), [] = last_resource_dtor_call(), {HugoPtr,HugoBin} = get_resource(Type,Hugo), {HugoPtr, HugoBin, 1}. +gc_and_exit(Reason) -> + erlang:garbage_collect(), + exit(Reason). + resource_otto(Type) -> {OttoPtr, OttoBin} = resource_otto_do(Type), erlang:garbage_collect(), @@ -1344,14 +1401,14 @@ resource_binary_do() -> ResInfo = {Ptr,_} = get_resource(binary_resource_type,ResBin1), Papa = self(), - Forwarder = spawn_link(fun() -> forwarder(Papa) end), + {Forwarder,_} = spawn_monitor(fun() -> forwarder(Papa) end), io:format("sending to forwarder pid=~p\n",[Forwarder]), Forwarder ! ResBin1, ResBin2 = receive_any(), ResBin2 = ResBin1, ResInfo = get_resource(binary_resource_type,ResBin2), Forwarder ! terminate, - {Forwarder, 1} = receive_any(), + {'DOWN', _, process, Forwarder, 1} = receive_any(), erlang:garbage_collect(), ResInfo = get_resource(binary_resource_type,ResBin1), ResInfo = get_resource(binary_resource_type,ResBin2), @@ -1711,6 +1768,7 @@ read_resource(Type, {Holder,Id}) -> forget_resource({Holder,Id}) -> Holder ! {self(), forget, Id}, {Holder, forget_ok, Id} = receive_any(), + erts_debug:set_internal_state(wait, aux_work), ok. @@ -1871,11 +1929,11 @@ send2_do1(SendBlobF) -> send2_do2(SendBlobF, self()), Papa = self(), - Forwarder = spawn_link(fun() -> forwarder(Papa) end), + {Forwarder,_} = spawn_monitor(fun() -> forwarder(Papa) end), io:format("sending to forwarder pid=~p\n",[Forwarder]), send2_do2(SendBlobF, Forwarder), Forwarder ! terminate, - {Forwarder, 4} = receive_any(), + {'DOWN', _, process, Forwarder, 4} = receive_any(), ok. send2_do2(SendBlobF, To) -> @@ -1931,7 +1989,7 @@ forwarder(To) -> forwarder(To, N) -> case receive_any() of terminate -> - To ! {self(), N}; + gc_and_exit(N); Msg -> To ! Msg, forwarder(To, N+1) @@ -3056,22 +3114,31 @@ nif_whereis_threaded(Config) when is_list(Config) -> RegName = nif_whereis_test_threaded, undefined = erlang:whereis(RegName), - Ref = make_ref(), - {Pid, Mon} = spawn_monitor(?MODULE, nif_whereis_proxy, [Ref]), - true = register(RegName, Pid), + Self = self(), + true = register(RegName, Self), - {ok, ProcThr} = whereis_thd_lookup(pid, RegName), - {ok, Pid} = whereis_thd_result(ProcThr), + {ok, ProcThr} = whereis_thd_lookup(pid, RegName, "dtor to proc"), + {ok, Self} = whereis_thd_result(ProcThr), - Pid ! {Ref, quit}, - ok = receive {'DOWN', Mon, process, Pid, normal} -> ok end, + nif_whereis_threaded_2(RegName). + +nif_whereis_threaded_2(RegName) -> + erlang:garbage_collect(), + "dtor to proc" = receive_any(1000), + true = unregister(RegName), Port = open_port({spawn, echo_drv}, [eof]), true = register(RegName, Port), - {ok, PortThr} = whereis_thd_lookup(port, RegName), + {ok, PortThr} = whereis_thd_lookup(port, RegName, "dtor to port"), {ok, Port} = whereis_thd_result(PortThr), + nif_whereis_threaded_3(Port). + +nif_whereis_threaded_3(Port) -> + erlang:garbage_collect(), + {Port, {data, "dtor to port"}} = receive_any(1000), + port_close(Port), ok. @@ -3327,6 +3394,69 @@ 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. + +nif_term_type(Config) -> + ensure_lib_loaded(Config), + + atom = term_type_nif(atom), + + bitstring = term_type_nif(<<1:1>>), + bitstring = term_type_nif(<<1:8>>), + + float = term_type_nif(0.0), + + 'fun' = term_type_nif(fun external:function/1), + 'fun' = term_type_nif(fun(A) -> A end), + 'fun' = term_type_nif(fun id/1), + + integer = term_type_nif(1 bsl 1024), %Bignum. + integer = term_type_nif(1), + + list = term_type_nif([list]), + list = term_type_nif([]), + + LargeMap = maps:from_list([{N, N} || N <- lists:seq(1, 256)]), + map = term_type_nif(LargeMap), + map = term_type_nif(#{ small => map }), + + pid = term_type_nif(self()), + + Port = open_port({spawn,echo_drv},[eof]), + port = term_type_nif(Port), + port_close(Port), + + reference = term_type_nif(make_ref()), + + tuple = term_type_nif({}), + + ok. + +last_resource_dtor_call() -> + erts_debug:set_internal_state(wait, aux_work), + last_resource_dtor_call_nif(). + id(I) -> I. %% The NIFs: @@ -3354,7 +3484,7 @@ make_resource(_) -> ?nif_stub. get_resource(_,_) -> ?nif_stub. release_resource(_) -> ?nif_stub. release_resource_from_thread(_) -> ?nif_stub. -last_resource_dtor_call() -> ?nif_stub. +last_resource_dtor_call_nif() -> ?nif_stub. make_new_resource(_,_) -> ?nif_stub. check_is(_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub. check_is_exception() -> ?nif_stub. @@ -3392,7 +3522,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 +3534,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. @@ -3413,7 +3544,7 @@ ioq_nif(_,_,_,_) -> ?nif_stub. %% whereis whereis_send(_Type,_Name,_Msg) -> ?nif_stub. whereis_term(_Type,_Name) -> ?nif_stub. -whereis_thd_lookup(_Type,_Name) -> ?nif_stub. +whereis_thd_lookup(_Type,_Name, _Msg) -> ?nif_stub. whereis_thd_result(_Thd) -> ?nif_stub. %% maps @@ -3434,5 +3565,13 @@ 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. + +term_type_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..ff47cfe500 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -27,6 +27,7 @@ #ifndef __WIN32__ #include <unistd.h> #include <fcntl.h> +#include <sys/uio.h> #endif #include "nif_mod.h" @@ -707,28 +708,23 @@ static ERL_NIF_TERM tuple_2_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar static ERL_NIF_TERM is_identical(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - if (argc != 2) { - return enif_make_badarg(env); - } + assert(argc == 2); return enif_make_atom(env, (enif_is_identical(argv[0],argv[1]) ? "true" : "false")); } static ERL_NIF_TERM compare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - if (argc != 2) { - return enif_make_badarg(env); - } + assert(argc == 2); return enif_make_int(env, enif_compare(argv[0],argv[1])); } static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - if (argc != 3) { - return enif_make_badarg(env); - } - ErlNifHash type; + ErlNifUInt64 salt; + + assert(argc == 3); if (enif_is_identical(argv[0], enif_make_atom(env, "internal"))) { type = ERL_NIF_INTERNAL_HASH; } @@ -739,7 +735,6 @@ static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] return enif_make_badarg(env); } - ErlNifUInt64 salt; if (! enif_get_uint64(env, argv[2], &salt)) { return enif_make_badarg(env); } @@ -866,7 +861,7 @@ static ERL_NIF_TERM iolist_2_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar return enif_make_binary(env,&obin); } -static ERL_NIF_TERM last_resource_dtor_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM last_resource_dtor_call_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM ret; if (resource_dtor_last != NULL) { @@ -1010,6 +1005,7 @@ static ERL_NIF_TERM release_resource(ErlNifEnv* env, int argc, const ERL_NIF_TER static void* threaded_release_resource(void* resource) { enif_release_resource(resource); + return NULL; } static ERL_NIF_TERM release_resource_from_thread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) @@ -1201,7 +1197,6 @@ static void fill(void* dst, unsigned bytes, int seed) enum { /* results */ WHEREIS_SUCCESS, - WHEREIS_ERROR_TYPE, WHEREIS_ERROR_LOOKUP, WHEREIS_ERROR_SEND, /* types */ @@ -1221,6 +1216,8 @@ typedef struct { whereis_term_data_t res; ErlNifTid tid; int type; + int rc; + ERL_NIF_TERM dtor_msg; } whereis_thread_resource_t; static whereis_thread_resource_t* whereis_thread_resource_create(void) @@ -1233,21 +1230,35 @@ static whereis_thread_resource_t* whereis_thread_resource_create(void) return rp; } +static int whereis_lookup_internal(ErlNifEnv*, int type, ERL_NIF_TERM name, + whereis_term_data_t* out); +static int whereis_send_internal(ErlNifEnv*, int type, whereis_term_data_t* to, + ERL_NIF_TERM msg); + + static void whereis_thread_resource_dtor(ErlNifEnv* env, void* obj) { whereis_thread_resource_t* rp = (whereis_thread_resource_t*) obj; + whereis_term_data_t to; + + if (whereis_lookup_internal(env, rp->type, rp->name, &to) + == WHEREIS_SUCCESS) { + whereis_send_internal(env, rp->type, &to, rp->dtor_msg); + } enif_free_env(rp->env); } -static int whereis_type(ERL_NIF_TERM type) +static int whereis_type(ERL_NIF_TERM type_term, int* type_p) { - if (enif_is_identical(type, atom_pid)) - return WHEREIS_LOOKUP_PID; - - if (enif_is_identical(type, atom_port)) - return WHEREIS_LOOKUP_PORT; - - return WHEREIS_ERROR_TYPE; + if (enif_is_identical(type_term, atom_pid)) { + *type_p = WHEREIS_LOOKUP_PID; + return 1; + } + if (enif_is_identical(type_term, atom_port)) { + *type_p = WHEREIS_LOOKUP_PORT; + return 1; + } + return 0; } static int whereis_lookup_internal( @@ -1261,7 +1272,7 @@ static int whereis_lookup_internal( return enif_whereis_port(env, name, & out->port) ? WHEREIS_SUCCESS : WHEREIS_ERROR_LOOKUP; - return WHEREIS_ERROR_TYPE; + abort(); } static int whereis_send_internal( @@ -1275,23 +1286,20 @@ static int whereis_send_internal( return enif_port_command(env, & to->port, NULL, msg) ? WHEREIS_SUCCESS : WHEREIS_ERROR_SEND; - return WHEREIS_ERROR_TYPE; + abort(); } -static int whereis_resolved_term( - ErlNifEnv* env, int type, whereis_term_data_t* res, ERL_NIF_TERM* out) +static ERL_NIF_TERM whereis_resolved_term( + ErlNifEnv* env, int type, whereis_term_data_t* res) { switch (type) { case WHEREIS_LOOKUP_PID: - *out = enif_make_pid(env, & res->pid); - break; + return enif_make_pid(env, &res->pid); case WHEREIS_LOOKUP_PORT: - *out = enif_make_port(env, & res->port); - break; + return enif_make_port(env, &res->port); default: - return WHEREIS_ERROR_TYPE; + abort(); } - return WHEREIS_SUCCESS; } static ERL_NIF_TERM whereis_result_term(ErlNifEnv* env, int result) @@ -1307,9 +1315,6 @@ static ERL_NIF_TERM whereis_result_term(ErlNifEnv* env, int result) case WHEREIS_ERROR_SEND: err = atom_send; break; - case WHEREIS_ERROR_TYPE: - err = atom_badarg; - break; default: err = enif_make_int(env, -result); break; @@ -1320,14 +1325,10 @@ static ERL_NIF_TERM whereis_result_term(ErlNifEnv* env, int result) static void* whereis_lookup_thread(void* arg) { whereis_thread_resource_t* rp = (whereis_thread_resource_t*) arg; - int rc; - /* enif_whereis_xxx should work with allocated or null env */ - rc = whereis_lookup_internal( - ((rp->type == WHEREIS_LOOKUP_PID) ? NULL : rp->env), - rp->type, rp->name, & rp->res); + rp->rc = whereis_lookup_internal(NULL, rp->type, rp->name, &rp->res); - return (((char*) NULL) + rc); + return NULL; } /* whereis_term(Type, Name) -> pid() | port() | false */ @@ -1335,20 +1336,16 @@ static ERL_NIF_TERM whereis_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { whereis_term_data_t res; - ERL_NIF_TERM ret; int type, rc; - if (argc != 2) /* allow non-atom name for testing */ - return enif_make_badarg(env); - - if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE) + assert(argc == 2); + if (!whereis_type(argv[0], &type)) return enif_make_badarg(env); rc = whereis_lookup_internal(env, type, argv[1], & res); - if (rc == WHEREIS_SUCCESS) { - rc = whereis_resolved_term(env, type, & res, & ret); - } - return (rc == WHEREIS_SUCCESS) ? ret : atom_false; + return (rc == WHEREIS_SUCCESS ? + whereis_resolved_term(env, type, &res) : + atom_false); } /* whereis_send(Type, Name, Message) -> ok | {error, Reason} */ @@ -1358,10 +1355,11 @@ whereis_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) whereis_term_data_t to; int type, rc; - if (argc != 3 || !enif_is_atom(env, argv[1])) + assert(argc == 3); + if (!enif_is_atom(env, argv[1])) return enif_make_badarg(env); - if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE) + if (!whereis_type(argv[0], &type)) return enif_make_badarg(env); rc = whereis_lookup_internal(env, type, argv[1], & to); @@ -1371,33 +1369,35 @@ whereis_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return whereis_result_term(env, rc); } -/* whereis_thd_lookup(Type, Name) -> {ok, Resource} | {error, SysErrno} */ +/* whereis_thd_lookup(Type, Name, DtorMsg) -> {ok, Resource} | {error, SysErrno} */ static ERL_NIF_TERM whereis_thd_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { whereis_thread_resource_t* rp; int type, rc; + ERL_NIF_TERM ret; - if (argc != 2 || !enif_is_atom(env, argv[1])) + assert(argc == 3); + if (!enif_is_atom(env, argv[1])) return enif_make_badarg(env); - if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE) + if (!whereis_type(argv[0], &type)) return enif_make_badarg(env); rp = whereis_thread_resource_create(); rp->type = type; rp->name = enif_make_copy(rp->env, argv[1]); + rp->dtor_msg = enif_make_copy(rp->env, argv[2]); rc = enif_thread_create( "nif_SUITE:whereis_thd", & rp->tid, whereis_lookup_thread, rp, NULL); - if (rc == 0) { - return enif_make_tuple2(env, atom_ok, enif_make_resource(env, rp)); - } - else { - enif_release_resource(rp); - return enif_make_tuple2(env, atom_error, enif_make_int(env, rc)); - } + if (rc == 0) + ret = enif_make_tuple2(env, atom_ok, enif_make_resource(env, rp)); + else + ret = enif_make_tuple2(env, atom_error, enif_make_int(env, rc)); + enif_release_resource(rp); + return ret; } /* whereis_thd_result(Resource) -> {ok, pid() | port()} | {error, ErrNum} */ @@ -1406,24 +1406,22 @@ whereis_thd_result(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { whereis_thread_resource_t* rp; ERL_NIF_TERM ret; - char* thdret; /* so we can keep compilers happy converting to int */ - int rc; + int join_rc; - if (argc != 1 - || !enif_get_resource(env, argv[0], whereis_resource_type, (void**) & rp)) + assert(argc == 1); + if (!enif_get_resource(env, argv[0], whereis_resource_type, (void**) & rp)) return enif_make_badarg(env); - if ((rc = enif_thread_join(rp->tid, (void**) & thdret)) != 0) - return enif_make_tuple2(env, atom_error, enif_make_int(env, rc)); + if ((join_rc = enif_thread_join(rp->tid, NULL)) != 0) + return enif_make_tuple2(env, atom_error, enif_make_int(env, join_rc)); - rc = (int)(thdret - ((char*) NULL)); - if (rc == WHEREIS_SUCCESS) { - rc = whereis_resolved_term(env, rp->type, & rp->res, & ret); + if (rp->rc == WHEREIS_SUCCESS) { + ret = enif_make_tuple2(env, atom_ok, + whereis_resolved_term(env, rp->type, &rp->res)); } - ret = (rc == WHEREIS_SUCCESS) - ? enif_make_tuple2(env, atom_ok, ret) : whereis_result_term(env, rc); + else + ret = whereis_result_term(env, rp->rc); - enif_release_resource(rp); return ret; } @@ -1882,12 +1880,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(); @@ -1996,8 +2005,7 @@ static ERL_NIF_TERM nif_sched1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM result; - if (argc != 2) - return enif_make_atom(env, "false"); + assert(argc == 2); result = enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv); assert(!enif_is_exception(env, result)); return result; @@ -2144,7 +2152,8 @@ static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_T ERL_NIF_TERM result, cell; unsigned count; - if (argc != 1 || !enif_get_list_length(env, argv[0], &count)) { + assert(argc == 1); + if (!enif_get_list_length(env, argv[0], &count)) { return enif_make_badarg(env); } @@ -2191,7 +2200,8 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER ErlNifMapIterator iter_b; int cnt, next_ret, prev_ret; - if (argc != 1 && !enif_is_map(env, map)) + assert(argc == 1); + if (!enif_is_map(env, map)) return enif_make_int(env, __LINE__); if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_FIRST)) @@ -2252,9 +2262,7 @@ static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM { ErlNifTimeUnit time_unit; - if (argc != 1) - return atom_false; - + assert(argc == 1); if (enif_compare(argv[0], atom_second) == 0) time_unit = ERL_NIF_SEC; else if (enif_compare(argv[0], atom_millisecond) == 0) @@ -2273,9 +2281,7 @@ static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg { ErlNifTimeUnit time_unit; - if (argc != 1) - return atom_false; - + assert(argc == 1); if (enif_compare(argv[0], atom_second) == 0) time_unit = ERL_NIF_SEC; else if (enif_compare(argv[0], atom_millisecond) == 0) @@ -2295,9 +2301,7 @@ static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TE ErlNifTime val; ErlNifTimeUnit from, to; - if (argc != 3) - return atom_false; - + assert(argc == 3); if (!enif_get_int64(env, argv[0], &i64)) return enif_make_badarg(env); @@ -2486,7 +2490,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 +2506,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 +2851,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 ************/ @@ -3281,7 +3312,7 @@ static int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail, Erl } /* Attempt to write the data */ - n = writev(fd, sysiovec, iovcnt); + n = writev(fd, (struct iovec*)sysiovec, iovcnt); saved_errno = errno; if (enif_ioq_size(q) == 0) { @@ -3355,10 +3386,11 @@ static ERL_NIF_TERM ioq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if (enif_is_identical(argv[0], enif_make_atom(env, "example"))) { #ifndef __WIN32__ - int fd[2], res = 0, cnt = 0, queue_cnt; + int fd[2], res = 0, cnt = 0; ERL_NIF_TERM tail; char buff[255]; - pipe(fd); + res = pipe(fd); + assert(res == 0); fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_NONBLOCK); fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL) | O_NONBLOCK); @@ -3486,6 +3518,95 @@ 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 ERL_NIF_TERM term_type_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + switch (enif_term_type(env, argv[0])) { + case ERL_NIF_TERM_TYPE_ATOM: + return enif_make_atom(env, "atom"); + case ERL_NIF_TERM_TYPE_BITSTRING: + return enif_make_atom(env, "bitstring"); + case ERL_NIF_TERM_TYPE_FLOAT: + return enif_make_atom(env, "float"); + case ERL_NIF_TERM_TYPE_FUN: + return enif_make_atom(env, "fun"); + case ERL_NIF_TERM_TYPE_INTEGER: + return enif_make_atom(env, "integer"); + case ERL_NIF_TERM_TYPE_LIST: + return enif_make_atom(env, "list"); + case ERL_NIF_TERM_TYPE_MAP: + return enif_make_atom(env, "map"); + case ERL_NIF_TERM_TYPE_PID: + return enif_make_atom(env, "pid"); + case ERL_NIF_TERM_TYPE_PORT: + return enif_make_atom(env, "port"); + case ERL_NIF_TERM_TYPE_REFERENCE: + return enif_make_atom(env, "reference"); + case ERL_NIF_TERM_TYPE_TUPLE: + return enif_make_atom(env, "tuple"); + default: + return enif_make_badarg(env); + } +} + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -3512,7 +3633,7 @@ static ErlNifFunc nif_funcs[] = {"get_resource", 2, get_resource}, {"release_resource", 1, release_resource}, {"release_resource_from_thread", 1, release_resource_from_thread}, - {"last_resource_dtor_call", 0, last_resource_dtor_call}, + {"last_resource_dtor_call_nif", 0, last_resource_dtor_call_nif}, {"make_new_resource", 2, make_new_resource}, {"check_is", 11, check_is}, {"check_is_exception", 0, check_is_exception}, @@ -3565,7 +3686,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,15 +3700,22 @@ 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}, - {"whereis_thd_lookup", 2, whereis_thd_lookup}, + {"whereis_thd_lookup", 3, whereis_thd_lookup}, {"whereis_thd_result", 1, whereis_thd_result}, {"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}, + {"term_type_nif", 1, term_type_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/persistent_term_SUITE.erl b/erts/emulator/test/persistent_term_SUITE.erl index 93eb026ced..c9874e5679 100644 --- a/erts/emulator/test/persistent_term_SUITE.erl +++ b/erts/emulator/test/persistent_term_SUITE.erl @@ -25,7 +25,9 @@ basic/1,purging/1,sharing/1,get_trapping/1, info/1,info_trapping/1,killed_while_trapping/1, off_heap_values/1,keys/1,collisions/1, - init_restart/1]). + init_restart/1, put_erase_trapping/1, + killed_while_trapping_put/1, + killed_while_trapping_erase/1]). %% -export([test_init_restart_cmd/1]). @@ -37,7 +39,8 @@ suite() -> all() -> [basic,purging,sharing,get_trapping,info,info_trapping, killed_while_trapping,off_heap_values,keys,collisions, - init_restart]. + init_restart, put_erase_trapping, killed_while_trapping_put, + killed_while_trapping_erase]. init_per_suite(Config) -> %% Put a term in the dict so that we know that the testcases handle @@ -627,3 +630,69 @@ chk_not_stuck(Term) -> pget({_, Initial}) -> persistent_term:get() -- Initial. + + +killed_while_trapping_put(_Config) -> + erts_debug:set_internal_state(available_internal_state, true), + repeat( + fun() -> + NrOfPutsInChild = 10000, + do_puts(2500, my_value), + Pid = + spawn(fun() -> + do_puts(NrOfPutsInChild, my_value2) + end), + timer:sleep(1), + erlang:exit(Pid, kill), + do_erases(NrOfPutsInChild) + end, + 10), + erts_debug:set_internal_state(available_internal_state, false). + +killed_while_trapping_erase(_Config) -> + erts_debug:set_internal_state(available_internal_state, true), + repeat( + fun() -> + NrOfErases = 2500, + do_puts(NrOfErases, my_value), + Pid = + spawn(fun() -> + do_erases(NrOfErases) + end), + timer:sleep(1), + erlang:exit(Pid, kill), + do_erases(NrOfErases) + end, + 10), + erts_debug:set_internal_state(available_internal_state, false). + +put_erase_trapping(_Config) -> + NrOfItems = 5000, + erts_debug:set_internal_state(available_internal_state, true), + do_puts(NrOfItems, first), + do_puts(NrOfItems, second), + do_erases(NrOfItems), + erts_debug:set_internal_state(available_internal_state, false). + +do_puts(0, _) -> ok; +do_puts(NrOfPuts, ValuePrefix) -> + Key = {?MODULE, NrOfPuts}, + Value = {ValuePrefix, NrOfPuts}, + erts_debug:set_internal_state(reds_left, rand:uniform(250)), + persistent_term:put(Key, Value), + Value = persistent_term:get(Key), + do_puts(NrOfPuts - 1, ValuePrefix). + +do_erases(0) -> ok; +do_erases(NrOfErases) -> + Key = {?MODULE,NrOfErases}, + erts_debug:set_internal_state(reds_left, rand:uniform(500)), + persistent_term:erase(Key), + not_found = persistent_term:get(Key, not_found), + do_erases(NrOfErases - 1). + +repeat(_Fun, 0) -> + ok; +repeat(Fun, N) -> + Fun(), + repeat(Fun, N-1). diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 0cb0d6c1e2..b530ced566 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -135,6 +135,15 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(Func, Config) + when Func =:= processes_default_tab; + Func =:= processes_this_tab -> + case erlang:system_info(debug_compiled) of + true -> + {skip, "Don't run in debug"}; + false -> + [{testcase, Func} | Config] + end; init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> [{testcase, Func}|Config]. @@ -2289,8 +2298,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. @@ -2298,10 +2307,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/small_SUITE.erl b/erts/emulator/test/small_SUITE.erl new file mode 100644 index 0000000000..00a02e5560 --- /dev/null +++ b/erts/emulator/test/small_SUITE.erl @@ -0,0 +1,115 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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(small_SUITE). + +-export([all/0, suite/0]). +-export([edge_cases/1]). + +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. + +all() -> + [edge_cases]. + +edge_cases(Config) when is_list(Config) -> + {MinSmall, MaxSmall} = Limits = determine_small_limits(0), + ct:pal("Limits = ~p", [Limits]), + + true = (MaxSmall + 1) =:= MaxSmall + id(1), + true = (MinSmall - 1) =:= MinSmall - id(1), + true = (MaxSmall + 1) > id(MaxSmall), + true = (MinSmall - 1) < id(MinSmall), + -1 = MinSmall + id(MaxSmall), + -1 = MaxSmall + id(MinSmall), + + false = is_small(MinSmall * -1), + false = is_small(MinSmall - id(1)), + false = is_small(MinSmall - 1), + false = is_small(MaxSmall + id(1)), + + Lower = lists:seq(MinSmall, MinSmall + 128), + Upper = lists:seq(MaxSmall, MaxSmall - 128, -1), + Pow2 = seq_pow2(MinSmall, MaxSmall), + NearZero = lists:seq(-128, 128), + + ok = test_combinations([Lower, Upper, Pow2, NearZero], MinSmall, MaxSmall), + + ok. + +test_combinations([As | Rest]=TestVectors, MinS, MaxS) -> + [begin + _ = [arith_test(A, B, MinS, MaxS) || B <- Bs] + end || A <- As, Bs <- TestVectors], + test_combinations(Rest, MinS, MaxS); +test_combinations([], _MinS, _MaxS) -> + ok. + +%% Builds a sequence of all powers of 2 between MinSmall and MaxSmall +seq_pow2(MinSmall, MaxSmall) -> + sp2_1(MinSmall, MinSmall, MaxSmall). + +sp2_1(N, _MinS, MaxS) when N >= MaxS -> + []; +sp2_1(-1, MinS, MaxS) -> + [-1 | sp2_1(1, MinS, MaxS)]; +sp2_1(N, MinS, MaxS) when N < 0 -> + [N | sp2_1(N bsr 1, MinS, MaxS)]; +sp2_1(N, MinS, MaxS) when N > 0 -> + [N | sp2_1(N bsl 1, MinS, MaxS)]. + +arith_test(A, B, MinS, MaxS) -> + verify_kind(A + B, MinS, MaxS), + verify_kind(B + A, MinS, MaxS), + verify_kind(A - B, MinS, MaxS), + verify_kind(B - A, MinS, MaxS), + verify_kind(A * B, MinS, MaxS), + verify_kind(B * A, MinS, MaxS), + + true = A + B =:= apply(erlang, id('+'), [A, B]), + true = A - B =:= apply(erlang, id('-'), [A, B]), + true = A * B =:= apply(erlang, id('*'), [A, B]), + + true = A + B =:= B + id(A), + true = A - B =:= A + id(-B), + true = B - A =:= B + id(-A), + true = A * B =:= B * id(A), + + true = B =:= 0 orelse ((A * B) div id(B) =:= A), + true = A =:= 0 orelse ((B * A) div id(A) =:= B), + + ok. + +%% Verifies that N is a small when it should be +verify_kind(N, MinS, MaxS) -> + true = is_small(N) =:= (N >= MinS andalso N =< MaxS). + +is_small(N) when is_integer(N) -> + 0 =:= erts_debug:flat_size(N). + +determine_small_limits(N) -> + case is_small(-1 bsl N) of + true -> determine_small_limits(N + 1); + false -> {-1 bsl (N - 1), (1 bsl (N - 1)) - 1} + end. + +id(I) -> I. 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..e3545ccbf9 --- /dev/null +++ b/erts/emulator/test/socket_SUITE.erl @@ -0,0 +1,18170 @@ +%% +%% %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% +%% + +%% There are some environment variables that can be used to "manipulate" +%% the test suite: +%% +%% Variable that controls which 'groups' are to run (with default values) +%% +%% ESOCK_TEST_API: include +%% ESOCK_TEST_SOCK_CLOSE: include +%% ESOCK_TEST_TRAFFIC: include +%% ESOCK_TEST_TTEST: exclude +%% +%% Variable that controls "verbosity" of the test case(s): +%% +%% ESOCK_TEST_QUIET: true (default) | false +%% +%% Defines the runtime of the ttest cases +%% (This is the time during which "measurement" is performed. +%% the actual time it takes for the test case to complete +%% will be longer; setup, completion, ...) +%% +%% ESOCK_TEST_TTEST_RUNTIME: 10 seconds +%% Format of values: <integer>[<unit>] +%% Where unit is: ms | s | m +%% ms - milli seconds +%% s - seconds (default) +%% m - minutes +%% + +%% 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_group/2, end_per_group/2, + 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 lists:member(socket, erlang:loaded()) of + true -> + case os:type() of + {win32, _} -> + (catch not_yet_implemented()); + _ -> + case quiet_mode(Config) of + default -> + ?LOGGER:start(), + Config; + Quiet -> + ?LOGGER:start(Quiet), + [{esock_test_quiet, Quiet}|Config] + end + end; + false -> + {skip, "esock disabled"} + end. + +end_per_suite(_) -> + (catch ?LOGGER:stop()), + ok. + + +init_per_group(ttest = _GroupName, Config) -> + io:format("init_per_group(~w) -> entry with" + "~n Config: ~p" + "~n", [_GroupName, Config]), + case lists:keysearch(esock_test_ttest_runtime, 1, Config) of + {value, _} -> + Config; + false -> + [{esock_test_ttest_runtime, which_ttest_runtime_env()}|Config] + end; +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(ttest = _GroupName, Config) -> + io:format("init_per_group(~w) -> entry with" + "~n Config: ~p" + "~n", [_GroupName, Config]), + lists:keydelete(esock_test_ttest_runtime, 1, Config); +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(_TC, Config) -> + io:format("init_per_testcase(~w) -> entry with" + "~n Config: ~p" + "~n", [_TC, Config]), + case quiet_mode(Config) of + default -> + ?LOGGER:start(); + Quiet -> + ?LOGGER:start(Quiet) + end, + Config. + +end_per_testcase(_TC, Config) -> + ?LOGGER:stop(), + Config. + + +quiet_mode(Config) -> + case lists:keysearch(esock_test_quiet, 1, Config) of + {value, {esock_test_quiet, Quiet}} -> + Quiet; + false -> + case os:getenv("ESOCK_TEST_QUIET") of + "true" -> true; + "false" -> false; + _ -> default + end + end. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% +%% 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) -> + %% We need tests for this, + %% but this is not the place it. + %% CMsgHdr = #{level => ip, + %% type => tos, + %% data => reliability}, + %% CMsgHdrs = [CMsgHdr], + MsgHdr = #{addr => Dest, + %% ctrl => CMsgHdrs, + iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + %% We have some issues on old darwing... + socket:setopt(Sock, otp, debug, true), + case socket:recvmsg(Sock) of + {ok, #{addr := Source, + iov := [Data]}} -> + socket:setopt(Sock, otp, debug, false), + {ok, {Source, Data}}; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => 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}) -> + Send(Sock, ?BASIC_REQ, Dst) + end}, + #{desc => "recv req (from src)", + cmd => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) -> + case Recv(Sock) of + {ok, {Src, ?BASIC_REQ}} -> + ok; + {ok, UnexpData} -> + {error, {unexpected_data, UnexpData}}; + {error, _} = ERROR -> + %% At the moment there is no way to get + %% status or state for the socket... + ERROR + end + end}, + #{desc => "send rep (to src)", + cmd => fun(#{sock_dst := Sock, sa_src := Src, send := Send}) -> + Send(Sock, ?BASIC_REP, Src) + end}, + #{desc => "recv rep (from dst)", + cmd => fun(#{sock_src := Sock, sa_dst := Dst, recv := Recv}) -> + case Recv(Sock) of + {ok, {Dst, ?BASIC_REP}} -> + ok; + {ok, UnexpData} -> + {error, {unexpected_data, UnexpData}}; + {error, _} = ERROR -> + %% At the moment there is no way to get + %% status or state for the socket... + ERROR + end + end}, + #{desc => "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) -> + Cond = fun() -> api_to_connect_cond() end, + tc_try(api_to_connect_tcp4, + Cond, + fun() -> + ?TT(?SECS(10)), + InitState = #{domain => inet, + backlog => 1, + timeout => 5000, + connect_limit => 3}, + ok = api_to_connect_tcp(InitState) + end). + +api_to_connect_cond() -> + api_to_connect_cond(os:type(), os:version()). + +%% I don't know exactly at which version this starts to work. +%% I know it does not work for 4.4.*, but is does for 4.15. +%% So, just to simplify, we require atleast 4.15 +api_to_connect_cond({unix, linux}, {Maj, Min, _Rev}) -> + if + ((Maj >= 4) andalso (Min >= 15)) -> + ok; + true -> + skip("TC does not work") + end; +%% Only test on one machine, which has version 6.3, and there it does +%% not work, so disable for all. +api_to_connect_cond({unix, openbsd}, _) -> + skip("TC does not work"); +api_to_connect_cond({unix, freebsd}, {Maj, Min, _Rev}) -> + if + ((Maj >= 10) andalso (Min >= 4)) -> + ok; + true -> + skip("TC may not work") + end; +api_to_connect_cond({unix, sunos}, {Maj, Min, _Rev}) -> + if + ((Maj >= 5) andalso (Min >= 10)) -> + ok; + true -> + skip("TC may not work") + end; +api_to_connect_cond(_, _) -> + skip("TC may not work"). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% 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() -> has_support_ipv6(), api_to_connect_cond() end, + fun() -> + ?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} -> + {skip, 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 = Stop - Start, + 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() -> has_support_ipv6() end, + fun() -> + ?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 = Stop - Start, + 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() -> has_support_ipv6() end, + fun() -> + 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 = Stop - Start, + 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 = Stop - Start, + 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 => 2000}, + 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() -> has_support_ipv6() end, + fun() -> + 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 => 2000}, + 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 = Stop - Start, + 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 => 2000}, + 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() -> has_support_ipv6() end, + fun() -> + ?TT(?SECS(10)), + Recv = fun(Sock, To) -> socket:recvfrom(Sock, 0, To) end, + InitState = #{domain => inet6, + recv => Recv, + timeout => 2000}, + 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 = Stop - Start, + if + (TDiff >= To) -> + ok; + true -> + {error, {unexpected_timeout, TDiff, To}} + end + end}, + + %% *** Termination *** + #{desc => "close socket", + cmd => fun(#{sock := Sock} = _State) -> + %% socket:setopt(Sock, otp, debug, true), + 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 => 2000}, + 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() -> has_support_ipv6() end, + fun() -> + ?TT(?SECS(10)), + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => inet6, + recv => Recv, + timeout => 2000}, + 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 => 2000}, + 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() -> has_support_ipv6() end, + fun() -> + ?TT(?SECS(10)), + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => inet6, + recv => Recv, + timeout => 2000}, + 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() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?TT(?SECS(10)), + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => inet6, + type => stream, + protocol => tcp, + recv => Recv}, + ok = sc_lc_receive_response_tcp(InitState) + 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) -> + %% ok = socket:setopt(Sock, otp, debug, true), + 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 failure: " + "~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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?TT(?SECS(10)), + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => inet6, + type => stream, + protocol => tcp, + recv => Recv}, + ok = sc_lc_receive_response_tcp(InitState) + 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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?TT(?SECS(10)), + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => inet6, + type => stream, + protocol => tcp, + recv => Recv}, + ok = sc_rc_receive_response_tcp(InitState) + 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} -> + {skip, 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() -> has_support_ipv6() end, + fun() -> + ?TT(?SECS(10)), + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => inet6, + type => stream, + protocol => tcp, + recv => Recv}, + ok = sc_rc_receive_response_tcp(InitState) + 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() -> has_support_ipv6() end, + fun() -> + ?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} -> + {skip, 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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?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) -> + %% socket:setopt(Sock, otp, debug, true), + 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} -> + {skip, 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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?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() -> has_support_ipv6() end, + fun() -> + ?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(60)), + 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() -> has_support_ipv6() end, + 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 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() -> has_support_ipv6() end, + fun() -> + ?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} -> + {skip, 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 ~w", [Num,N,size(Data)]), + 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 ~p", [N, Num, RReason, mq()]), + exit({recv, RReason, N}) + end; + {error, SReason} -> + ?SEV_EPRINT("send (~w of ~w): ~p" + "~n ~p", [N, Num, SReason, mq()]), + 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, _R} = 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, _R} = 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)). + +mq() -> + mq(self()). + +mq(Pid) when is_pid(Pid) -> + Tag = messages, + {Tag, Msgs} = process_info(Pid, Tag), + Msgs. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% 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, + sock := _Sock} = _State) -> + %% socket:setopt(Sock, otp, debug, true), + ?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} -> + {skip, 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgenf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgenf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgenf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgenf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgenf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgenf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgeno_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgeno_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgeno_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgeno_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgeno_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgeno_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgent_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgeno_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgent_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgent_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgent_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_cgent_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csocko_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csocko_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csocko_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csocko_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csocko_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockt_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockt_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockt_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockt_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgenf_csockt_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgenf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgenf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgenf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgenf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgenf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgenf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgeno_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgeno_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgeno_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgeno_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgeno_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgeno_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgent_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgeno_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgent_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgent_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgent_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_cgent_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csocko_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csocko_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csocko_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csocko_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csocko_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockt_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockt_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockt_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockt_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgeno_csockt_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgenf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgenf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgenf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgenf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgenf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgenf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgeno_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgeno_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgeno_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgeno_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgeno_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgeno_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgent_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgeno_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgent_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgent_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgent_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_cgent_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csocko_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csocko_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csocko_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csocko_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csocko_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockt_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockt_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockt_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockt_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_sgent_csockt_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgenf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgenf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgenf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgenf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgenf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgenf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgeno_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgeno_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgeno_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgeno_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgeno_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgeno_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgent_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgeno_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgent_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgent_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgent_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_cgent_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgenf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgenf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgenf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgenf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgenf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgenf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgeno_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgeno_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgeno_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgeno_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgeno_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgeno_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgent_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgent_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgent_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgent_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgent_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_cgent_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgenf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgenf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgenf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgenf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgenf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgenf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgeno_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgeno_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgeno_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgeno_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgeno_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgeno_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgent_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgent_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgent_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgent_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgent_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_cgent_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_large_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_small_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_small_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_medium_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_medium_tcp6, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_large_tcp4, + Runtime, + 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) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_large_tcp6, + Runtime, + inet6, + sock, true, + sock, true, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +which_ttest_runtime(Config) when is_list(Config) -> + case lists:keysearch(esock_test_ttest_runtime, 1, Config) of + {value, {esock_test_ttest_runtime, Runtime}} -> + Runtime; + false -> + which_ttest_runtime_env() + end. + +which_ttest_runtime_env() -> + which_ttest_runtime_env(os:getenv("ESOCK_TEST_TTEST_RUNTIME")). + +which_ttest_runtime_env(TStr) when is_list(TStr) -> + which_ttest_runtime_env2(lists:reverse(TStr)); +which_ttest_runtime_env(false) -> + ?TTEST_RUNTIME. + + +%% The format is: <int>[unit] +%% where the optional unit can be: +%% ms: milliseconds +%% s: seconds (default) +%% m: minutes +which_ttest_runtime_env2([$m, $s | MS]) when (length(MS) > 0) -> + convert_time(MS, fun(X) -> X end); +which_ttest_runtime_env2([$m | M]) when (length(M) > 0) -> + convert_time(M, fun(X) -> ?MINS(X) end); +which_ttest_runtime_env2([$s | S]) when (length(S) > 0) -> + convert_time(S, fun(X) -> ?SECS(X) end); +which_ttest_runtime_env2(S) -> + convert_time(S, fun(X) -> ?SECS(X) end). + +convert_time(TStrRev, Convert) -> + try list_to_integer(lists:reverse(TStrRev)) of + I -> Convert(I) + catch + _:_ -> + ?TTEST_RUNTIME + end. + +%% ttest_tcp(TC, +%% Domain, +%% ServerMod, ServerActive, +%% ClientMod, ClientActive, +%% MsgID, MaxOutstanding) -> +%% ttest_tcp(TC, +%% ?TTEST_RUNTIME, +%% Domain, +%% ServerMod, ServerActive, +%% ClientMod, ClientActive, +%% MsgID, MaxOutstanding). +ttest_tcp(TC, + Runtime, + Domain, + ServerMod, ServerActive, + ClientMod, ClientActive, + MsgID, MaxOutstanding) -> + tc_try(TC, + fun() -> + if + (Domain =/= inet) -> has_support_ipv6(); + true -> ok + end + end, + fun() -> + %% 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} -> + {skip, 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} -> + {skip, 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). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This mechanism has only one purpose: So that we are able to kill +%% the node-starter process if it takes to long. The node-starter +%% runs on the local node. +%% This crapola is hopefully temporary, but we have seen that on +%% some platforms the ct_slave:start simply hangs. +-define(NODE_START_TIMEOUT, 10000). +start_node(Host, NodeName) -> + start_node(Host, NodeName, ?NODE_START_TIMEOUT). + +start_node(Host, NodeName, Timeout) -> + {NodeStarter, _} = + spawn_monitor(fun() -> exit(start_unique_node(Host, NodeName)) end), + receive + {'DOWN', _, process, NodeStarter, Result} -> + %% i("Node Starter (~p) reported: ~p", [NodeStarter, Result]), + Result + after Timeout -> + exit(NodeStarter, kill), + {error, {failed_starting_node, NodeName, timeout}} + end. + +start_unique_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). + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Here are all the *general* test vase condition functions. + +%% The idea is that this function shall test if the test host has +%% support for IPv6. If not, there is no point in running IPv6 tests. +%% Currently we just skip. +has_support_ipv6() -> + %% case socket:supports(ipv6) of + %% true -> + %% ok; + %% false -> + %% {error, not_supported} + %% end. + not_yet_implemented(). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +not_yet_implemented() -> + skip("not yet implemented"). + +skip(Reason) -> + throw({skip, Reason}). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +t() -> + ts(ms). + +ts(ms) -> + erlang:monotonic_time(milli_seconds). + +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) -> + OldVal = process_flag(trap_exit, true), + put(old_trap_exit, OldVal), + set_tc_name(TC), + tc_print("begin ***", + "~n----------------------------------------------------~n", ""). + +tc_end(Result) when is_list(Result) -> + OldVal = erase(old_trap_exit), + process_flag(trap_exit, OldVal), + tc_print("done: ~s", [Result], + "", "----------------------------------------------------~n~n"), + ok. + +%% *** tc_try/2,3 *** +%% Case: Basically the test case name +%% TCCondFun: A fun that is evaluated before the actual test case +%% The point of this is that it can performs checks to +%% see if we shall run the test case at all. +%% For instance, the test case may only work in specific +%% conditions. +%% FCFun: The test case fun +tc_try(Case, TCFun) -> + TCCondFun = fun() -> ok end, + tc_try(Case, TCCondFun, TCFun). + +tc_try(Case, TCCondFun, TCFun) + when is_atom(Case) andalso + is_function(TCCondFun, 0) andalso + is_function(TCFun, 0) -> + tc_begin(Case), + try TCCondFun() of + ok -> + try + begin + TCFun(), + ?SLEEP(?SECS(1)), + tc_end("ok") + end + catch + C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) -> + %% i("catched[tc] (skip): " + %% "~n C: ~p" + %% "~n SKIP: ~p" + %% "~n", [C, SKIP]), + tc_end( f("skipping(catched,~w,tc)", [C]) ), + SKIP; + C:E:S -> + %% i("catched[tc]: " + %% "~n C: ~p" + %% "~n E: ~p" + %% "~n S: ~p" + %% "~n", [C, E, S]), + tc_end( f("failed(catched,~w,tc)", [C]) ), + erlang:raise(C, E, S) + end; + {skip, _} = SKIP -> + tc_end("skipping(tc)"), + SKIP; + {error, Reason} -> + tc_end("failed(tc)"), + exit({tc_cond_failed, Reason}) + catch + C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) -> + %% i("catched[cond] (skip): " + %% "~n C: ~p" + %% "~n SKIP: ~p" + %% "~n", [C, SKIP]), + tc_end( f("skipping(catched,~w,cond)", [C]) ), + SKIP; + C:E:S -> + %% i("catched[cond]: " + %% "~n C: ~p" + %% "~n E: ~p" + %% "~n S: ~p" + %% "~n", [C, E, S]), + tc_end( f("failed(catched,~w,cond)", [C]) ), + erlang:raise(C, E, S) + 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_test_evaluator.erl b/erts/emulator/test/socket_test_evaluator.erl new file mode 100644 index 0000000000..694f0d5f1e --- /dev/null +++ b/erts/emulator/test/socket_test_evaluator.erl @@ -0,0 +1,664 @@ +%% +%% %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_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 = erlang:spawn_link( + fun() -> init(Name, Seq, InitState2) end), + %% MRef = erlang:monitor(process, Pid), + #ev{name = Name, pid = Pid}%, mref = MRef} + end. + +init(Name, Seq, Init) -> + put(sname, Name), + process_flag(trap_exit, true), + 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} -> + ?SEV_IPRINT("command ~w skip: " + "~n ~p", [ID, Reason]), + exit({skip, Reason}); + {error, Reason} -> + ?SEV_EPRINT("command ~w failed: " + "~n ~p", [ID, Reason]), + exit({command_failed, ID, Reason, State}) + catch + C:{skip, command} = E:_ when ((C =:= throw) orelse (C =:= exit)) -> + %% Secondary skip + exit(E); + C:{skip, R} = E:_ when ((C =:= throw) orelse (C =:= exit)) -> + ?SEV_IPRINT("command ~w skip catched(~w): " + "~n Reason: ~p", [ID, C, R]), + exit(E); + C:E:S -> + ?SEV_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([], _OK, Fails) -> + ?SEV_EPRINT("Fails: " + "~n ~p", [Fails]), + Fails; +await_finish(Evs, OK, Fails) -> + receive + %% Successfull termination of evaluator + {'DOWN', _MRef, process, Pid, normal} -> + {Evs2, OK2, Fails2} = await_finish_normal(Pid, Evs, OK, Fails), + await_finish(Evs2, OK2, Fails2); + {'EXIT', Pid, normal} -> + {Evs2, OK2, Fails2} = await_finish_normal(Pid, Evs, OK, Fails), + await_finish(Evs2, OK2, Fails2); + + %% The evaluator can skip the test case: + {'DOWN', _MRef, process, Pid, {skip, Reason}} -> + %% ?SEV_IPRINT("await_finish -> skip (down) received: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), + await_finish_skip(Pid, Reason, Evs, OK); + {'EXIT', Pid, {skip, Reason}} -> + %% ?SEV_IPRINT("await_finish -> skip (exit) received: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), + await_finish_skip(Pid, Reason, Evs, OK); + + %% Evaluator failed + {'DOWN', _MRef, process, Pid, Reason} -> + %% ?SEV_IPRINT("await_finish -> fail (down) received: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), + {Evs2, OK2, Fails2} = + await_finish_fail(Pid, Reason, Evs, OK, Fails), + await_finish(Evs2, OK2, Fails2); + {'EXIT', Pid, Reason} -> + %% ?SEV_IPRINT("await_finish -> fail (exit) received: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), + {Evs2, OK2, Fails2} = + await_finish_fail(Pid, Reason, Evs, OK, Fails), + await_finish(Evs2, OK2, Fails2) + end. + + +await_finish_normal(Pid, Evs, OK, Fails) -> + 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), + {NewEvs, [Pid|OK], Fails}; + false -> + case lists:member(Pid, OK) of + true -> + ok; + false -> + iprint("unknown process ~p died (normal)", [Pid]), + ok + end, + {Evs, OK, Fails} + end. + +await_finish_skip(Pid, Reason, Evs, OK) -> + Evs2 = + case lists:keysearch(Pid, #ev.pid, Evs) of + {value, #ev{name = Name}} -> + ?SEV_IPRINT("evaluator '~s' (~p) issued SKIP: " + "~n ~p", [Name, Pid, Reason]), + lists:keydelete(Pid, #ev.pid, Evs); + false -> + case lists:member(Pid, OK) of + true -> + ?SEV_IPRINT("already terminated (ok) process ~p skip" + "~n ~p", [Pid]), + ok; + false -> + ?SEV_IPRINT("unknown process ~p issued SKIP: " + "~n ~p", [Pid, Reason]), + iprint("unknown process ~p issued SKIP: " + "~n ~p", [Pid, Reason]) + end, + Evs + end, + await_evs_terminated(Evs2), + ?LIB:skip(Reason). + +await_evs_terminated(Evs) -> + Instructions = + [ + %% Just wait for the evaluators to die on their own + {fun() -> ?SEV_IPRINT("await (no action) evs termination") end, + fun(_) -> ok end}, + + %% Send them a skip message, causing the evaluators to + %% die with a skip reason. + {fun() -> ?SEV_IPRINT("await (send skip message) evs termination") end, + fun(#ev{pid = Pid}) -> Pid ! skip end}, + + %% And if nothing else works, try to kill the remaining evaluators + {fun() -> ?SEV_IPRINT("await (issue exit kill) evs termination") end, + fun(#ev{pid = Pid}) -> exit(Pid, kill) end}], + + await_evs_terminated(Evs, Instructions). + +await_evs_terminated([], _) -> + ok; +await_evs_terminated(Evs, []) -> + {error, {failed_terminated, [P||#ev{pid=P} <- Evs]}}; +await_evs_terminated(Evs, [{Inform, Command}|Instructions]) -> + Inform(), + lists:foreach(Command, Evs), + RemEvs = await_evs_termination(Evs), + await_evs_terminated(RemEvs, Instructions). + +await_evs_termination(Evs) -> + await_evs_termination(Evs, 2000). + +await_evs_termination([], _Timeout) -> + []; +await_evs_termination(Evs, Timeout) -> + T = t(), + receive + {'DOWN', _MRef, process, Pid, _Reason} -> + %% ?SEV_IPRINT("await_evs_termination -> DOWN: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), + Evs2 = lists:keydelete(Pid, #ev.pid, Evs), + await_evs_termination(Evs2, tdiff(T, t())); + {'EXIT', Pid, _Reason} -> + %% ?SEV_IPRINT("await_evs_termination -> EXIT: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), + Evs2 = lists:keydelete(Pid, #ev.pid, Evs), + await_evs_termination(Evs2, tdiff(T, t())) + + after Timeout -> + Evs + end. + + +await_finish_fail(Pid, Reason, Evs, OK, Fails) -> + 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), + {NewEvs, OK, [{Pid, Reason}|Fails]}; + false -> + case lists:member(Pid, OK) of + true -> + ok; + false -> + iprint("unknown process ~p died: " + "~n ~p", [Pid, Reason]) + end, + {Evs, OK, Fails} + 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_reason, 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 + skip -> + %% This means that another evaluator has issued a skip, + %% and we have been instructed to terminate as a result. + ?LIB:skip(command); + {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, Reason}}; + {'DOWN', _, process, OtherPid, Reason} -> + case check_down(OtherPid, Reason, OtherPids) of + ok -> + iprint("DOWN from unknown process ~p: " + "~n ~p" + "~n when" + "~n OtherPids: " + "~n ~p", [OtherPid, Reason, OtherPids]), + 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, DownReason}}; + 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]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +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. + 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..26610e9ef3 --- /dev/null +++ b/erts/emulator/test/socket_test_logger.erl @@ -0,0 +1,118 @@ +%% +%% %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_logger). + +-export([ + start/0, start/1, + stop/0, + format/2 + ]). + + +-define(QUIET, true). +-define(LIB, socket_test_lib). +-define(LOGGER, ?MODULE). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start() -> + start(?QUIET). + +start(Quiet) -> + case global:whereis_name(?LOGGER) of + Pid when is_pid(Pid) -> + ok; + undefined -> + Self = self(), + Pid = spawn_link(fun() -> init(Self, Quiet) 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, Quiet) -> + put(sname, "logger"), + print("[~s][logger] starting~n", [?LIB:formated_timestamp()]), + loop(#{parent => Parent, quiet => Quiet}). + +loop(#{parent := Parent, + quiet := Quiet} = 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_str(Quiet, Msg), + loop(State) + end. + + +print(F, A) -> + print_str(false, ?LIB:f(F, A)). + +print_str(Quiet, Str) -> + try + begin + if (Quiet =/= true) -> io:format(user, Str ++ "~n", []); + true -> ok + end, + 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/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index ae3099633a..d278ac86c7 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -93,25 +93,34 @@ wall_clock_zero_diff1(0) -> %% statistics(wall_clock) are compatible, and are within a small number %% of ms of the amount of real time we waited for. wall_clock_update(Config) when is_list(Config) -> - wall_clock_update1(6). + N = 10, + Inc = 200, + TotalTime = wall_clock_update1(N, Inc, 0), + Overhead = TotalTime - N * Inc, + IsDebug = test_server:is_debug(), -wall_clock_update1(N) when N > 0 -> - {T1_wc_time, _} = statistics(wall_clock), - receive after 1000 -> ok end, - {T2_wc_time, Wc_Diff} = statistics(wall_clock), - - Wc_Diff = T2_wc_time - T1_wc_time, - io:format("Wall clock diff = ~p; should be = 1000..1040~n", [Wc_Diff]), - case test_server:is_debug() of - false -> - true = Wc_Diff =< 1040; + %% Check that the average overhead is reasonable. + if + Overhead < N * 100 -> + ok; + IsDebug, Overhead < N * 1000 -> + ok; true -> - true = Wc_Diff =< 2000 %Be more tolerant in debug-compiled emulator. - end, - true = Wc_Diff >= 1000, - wall_clock_update1(N-1); -wall_clock_update1(0) -> - ok. + io:format("There was an overhead of ~p ms during ~p rounds.", + [Overhead,N]), + ct:fail(too_much_overhead) + end. + +wall_clock_update1(N, Inc, Total) when N > 0 -> + {Time1, _} = statistics(wall_clock), + receive after Inc -> ok end, + {Time2, WcDiff} = statistics(wall_clock), + WcDiff = Time2 - Time1, + io:format("Wall clock diff = ~p (expected at least ~p)\n", [WcDiff,Inc]), + true = WcDiff >= Inc, + wall_clock_update1(N-1, Inc, Total + WcDiff); +wall_clock_update1(0, _, Total) -> + Total. %%% Test statistics(runtime). 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 diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 253d5fed23..ad802352b9 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -1181,7 +1181,9 @@ undef(X) -> ?MODULE:undef(X, X). % undef lists_reverse(A, B) -> - lists:reverse(A, B). + Res = lists:reverse(A, B), + _ = (catch abs(A)), + Res. |