%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2018-2023. 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_REG:         include
%%         ESOCK_TEST_MON:         include
%%         ESOCK_TEST_IOCTL:       include
%%         ESOCK_TEST_SOCK_CLOSE:  include
%%         ESOCK_TEST_TRAFFIC:     include
%%         ESOCK_TEST_TICKETS:     include
%%         ESOCK_TEST_TTEST:       include
%%
%% 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: 1 second
%%              Format of values: <integer>[<unit>]
%%              Where unit is: ms | s | m
%%                 ms - milli seconds
%%                 s  - seconds (default)
%%                 m  - minutes
%%
%% The ttest takes a long time to run, even when the runtime is small,
%% because there are such a large number of test cases.
%% So, by default only the 'small' test cases are included in a test run.
%% The following environment variables control which are included and 
%% excluded.
%%
%%          ESOCK_TEST_TTEST_SMALL:  included
%%          ESOCK_TEST_TTEST_MEDIUM: excluded
%%          ESOCK_TEST_TTEST_LARGE:  excluded
%%

%% Run the entire test suite: 
%% ts:run(kernel, socket_SUITE, [batch]).
%%
%% Run a specific group:
%% ts:run(kernel, socket_SUITE, {group, foo}, [batch]).
%%
%% Run a specific test case:
%% ts:run(kernel, socket_SUITE, foo, [batch]).
%%
%% (cd /mnt/c/$LOCAL_TESTS/26/kernel_test/ && $ERL_TOP/bin/win32/erl.exe -sname kernel-26-tester -pa c:$LOCAL_TESTS/26/test_server)
%% application:set_env(kernel, test_inet_backends, true).
%% S = fun() -> ts:run(kernel, socket_SUITE, [batch]) end.
%% S = fun(SUITE) -> ts:run(kernel, SUITE, [batch]) end.
%% S = fun() -> ct:run_test([{suite, socket_SUITE}]) end.
%% S = fun(SUITE) -> ct:run_test([{suite, SUITE}]) end.
%% G = fun(GROUP) -> ts:run(kernel, socket_SUITE, {group, GROUP}, [batch]) end.
%% G = fun(SUITE, GROUP) -> ts:run(kernel, SUITE, {group, GROUP}, [batch]) end.
%% G = fun(GROUP) -> ct:run_test([{suite, socket_SUITE}, {group, GROUP}]) end.
%% G = fun(SUITE, GROUP) -> ct:run_test([{suite, SUITE}, {group, GROUP}]) end.
%% T = fun(TC) -> ts:run(kernel, socket_SUITE, TC, [batch]) end.
%% T = fun(TC) -> ct:run_test([{suite, socket_SUITE}, {testcase, TC}]) end.
%% T = fun(S, TC) -> ct:run_test([{suite, S}, {testcase, TC}]) end.
%% T = fun(S, G, TC) -> ct:run_test([{suite, S}, {group, G}, {testcase, TC}]) end.
%%
%% Some official info about AF_UNIX
%% https://devblogs.microsoft.com/commandline/windowswsl-interop-with-af_unix/



-module(socket_SUITE).

-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
-include("socket_test_evaluator.hrl").

%% Suite exports
-export([suite/0, all/0, groups/0]).
-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 Misc ***
         api_m_info/1,
         api_m_debug/1,
         api_m_error_open/1,
         api_m_error_bind/1,

         %% *** API Basic ***
         api_b_simple_open_and_close_udp4/1,
         api_b_simple_open_and_close_udp6/1,
         api_b_simple_open_and_close_tcp4/1,
         api_b_simple_open_and_close_tcp6/1,
         api_b_open_and_info_udp4/1,
         api_b_open_and_info_udp6/1,
         api_b_open_and_info_tcp4/1,
         api_b_open_and_info_tcp6/1,
         api_b_open_and_close_udp4/1,
         api_b_open_and_close_udp6/1,
         api_b_open_and_close_tcp4/1,
         api_b_open_and_close_tcp6/1,
         api_b_open_and_close_udpL/1,
         api_b_open_and_close_tcpL/1,
         api_b_open_and_close_seqpL/1,
	 api_b_open_and_close_sctp4/1,
         api_b_open_and_maybe_close_raw/1,
         api_b_sendto_and_recvfrom_udp4/1,
         api_b_sendto_and_recvfrom_udpL/1,
         api_b_sendmsg_and_recvmsg_udp4/1,
         api_b_sendmsg_and_recvmsg_udpL/1,
         api_b_send_and_recv_tcp4/1,
         api_b_send_and_recv_tcpL/1,
         api_b_send_and_recv_seqpL/1,
         api_b_sendmsg_and_recvmsg_tcp4/1,
         api_b_sendmsg_and_recvmsg_tcpL/1,
         api_b_sendmsg_and_recvmsg_seqpL/1,
         api_b_sendmsg_and_recvmsg_sctp4/1,
         api_b_sendmsg_iov_dgram_inet/1,
         api_b_sendmsg_iov_dgram_inet6/1,
         api_b_sendmsg_iov_dgram_local/1,
         api_b_sendmsg_iov_stream_inet/1,
         api_b_sendmsg_iov_stream_inet6/1,
         api_b_sendmsg_iov_stream_local/1,
         api_b_dgram_connect_udp4/1,
         api_b_dgram_connect_udp6/1,

         %% *** API sendfile ***
         api_sendfile_inet/1,
         api_sendfile_inet6/1,
         api_sendfile_local/1,
         api_sendfile_loop_inet/1,
         api_sendfile_loop_inet6/1,
         api_sendfile_loop_local/1,

         %% *** API socket from FD ***
         api_ffd_open_wod_and_info_udp4/1,
         api_ffd_open_wod_and_info_udp6/1,
         api_ffd_open_wod_and_info_tcp4/1,
         api_ffd_open_wod_and_info_tcp6/1,
         api_ffd_open_wd_and_info_udp4/1,
         api_ffd_open_wd_and_info_udp6/1,
         api_ffd_open_wd_and_info_tcp4/1,
         api_ffd_open_wd_and_info_tcp6/1,
         api_ffd_open_and_open_wod_and_send_udp4/1,
         api_ffd_open_and_open_wod_and_send_udp6/1,
         api_ffd_open_and_open_wd_and_send_udp4/1,
         api_ffd_open_and_open_wd_and_send_udp6/1,
         api_ffd_open_connect_and_open_wod_and_send_tcp4/1,
         api_ffd_open_connect_and_open_wod_and_send_tcp6/1,
         api_ffd_open_connect_and_open_wd_and_send_tcp4/1,
         api_ffd_open_connect_and_open_wd_and_send_tcp6/1,


         %% *** API async ***
         api_a_connect_tcp4/1,
         api_a_connect_tcp6/1,
         api_a_sendto_and_recvfrom_udp4/1,
         api_a_sendto_and_recvfrom_udp6/1,
         api_a_sendmsg_and_recvmsg_udp4/1,
         api_a_sendmsg_and_recvmsg_udp6/1,
         api_a_send_and_recv_tcp4/1,
         api_a_send_and_recv_tcp6/1,
         api_a_sendmsg_and_recvmsg_tcp4/1,
         api_a_sendmsg_and_recvmsg_tcp6/1,
         api_a_recvfrom_cancel_udp4/1,
         api_a_recvfrom_cancel_udp6/1,
         api_a_recvmsg_cancel_udp4/1,
         api_a_recvmsg_cancel_udp6/1,
         api_a_accept_cancel_tcp4/1,
         api_a_accept_cancel_tcp6/1,
         api_a_recv_cancel_tcp4/1,
         api_a_recv_cancel_tcp6/1,
         api_a_recvmsg_cancel_tcp4/1,
         api_a_recvmsg_cancel_tcp6/1,
         api_a_mrecvfrom_cancel_udp4/1,
         api_a_mrecvfrom_cancel_udp6/1,
         api_a_mrecvmsg_cancel_udp4/1,
         api_a_mrecvmsg_cancel_udp6/1,
         api_a_maccept_cancel_tcp4/1,
         api_a_maccept_cancel_tcp6/1,
         api_a_mrecv_cancel_tcp4/1,
         api_a_mrecv_cancel_tcp6/1,
         api_a_mrecvmsg_cancel_tcp4/1,
         api_a_mrecvmsg_cancel_tcp6/1,


         %% *** API Options ***
         api_opt_simple_otp_options/1,
         api_opt_simple_otp_meta_option/1,
         api_opt_simple_otp_rcvbuf_option/1,
         api_opt_simple_otp_controlling_process/1,
         api_opt_sock_acceptconn_udp/1,
         api_opt_sock_acceptconn_tcp/1,
         api_opt_sock_acceptfilter/1,
         api_opt_sock_bindtodevice/1,
         api_opt_sock_broadcast/1,
         api_opt_sock_debug/1,
         api_opt_sock_domain/1,
         api_opt_sock_dontroute/1,
         api_opt_sock_error/1,
         api_opt_sock_keepalive/1,
         api_opt_sock_linger/1,
         api_opt_sock_mark/1,
         api_opt_sock_maxdg/1,
         api_opt_sock_max_msg_size/1,
         api_opt_sock_oobinline/1,
         api_opt_sock_passcred_tcp4/1,
         api_opt_sock_peek_off_tcpL/1,
         api_opt_sock_peercred_tcpL/1,
         api_opt_sock_priority_udp4/1,
         api_opt_sock_priority_tcp4/1,
         api_opt_sock_rcvbuf_udp4/1,
         api_opt_sock_rcvlowat_udp4/1,
         api_opt_sock_rcvtimeo_udp4/1,
         api_opt_sock_reuseaddr/1,
         api_opt_sock_exclusiveaddruse/1,
         api_opt_sock_bsp_state/1,
         api_opt_sock_sndbuf_udp4/1,
         api_opt_sock_sndlowat_udp4/1,
         api_opt_sock_sndtimeo_udp4/1,
         api_opt_sock_timestamp_udp4/1,
         api_opt_sock_timestamp_tcp4/1,
         api_opt_ip_add_drop_membership/0, api_opt_ip_add_drop_membership/1,
         api_opt_ip_pktinfo_udp4/1,
         api_opt_ip_recvopts_udp4/1,
         api_opt_ip_recvorigdstaddr_udp4/1,
         api_opt_ip_recvtos_udp4/1,
         api_opt_ip_recvttl_udp4/1,
         api_opt_ip_tos_udp4/1,
         api_opt_ip_recverr_udp4/1,
         api_opt_ip_mopts_udp4/1,
         api_opt_ipv6_recvpktinfo_udp6/1,
	 api_opt_ipv6_flowinfo_udp6/1,
	 api_opt_ipv6_hoplimit_udp6/1,
	 api_opt_ipv6_tclass_udp6/1,
         api_opt_ipv6_recverr_udp6/1,
	 api_opt_ipv6_mopts_udp6/1,
         api_opt_tcp_congestion_tcp4/1,
         api_opt_tcp_cork_tcp4/1,
         api_opt_tcp_maxseg_tcp4/1,
         api_opt_tcp_nodelay_tcp4/1,
         api_opt_tcp_keepcnt_tcp4/1,
         api_opt_tcp_keepidle_tcp4/1,
         api_opt_tcp_keepintvl_tcp4/1,
         api_opt_udp_cork_udp4/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 Registry
         reg_s_single_open_and_close_and_count/1,
         reg_s_optional_open_and_close_and_count/1,


         %% Socket Monitor
         monitor_simple_open_and_close/1,
	 monitor_simple_open_and_exit/1,
	 monitor_simple_open_and_demon_and_close/1,
	 monitor_open_and_close_multi_socks/1,
	 monitor_open_and_exit_multi_socks/1,
	 monitor_open_and_demon_and_close_multi_socks/1,
	 monitor_open_and_close_multi_mon/1,
	 monitor_open_and_exit_multi_mon/1,
	 monitor_open_and_close_multi_socks_and_mon/1,
	 monitor_open_and_exit_multi_socks_and_mon/1,
	 monitor_closed_socket/1,

         %% *** Socket Closure ***
         sc_cpe_socket_cleanup_tcp4/1,
         sc_cpe_socket_cleanup_tcp6/1,
         sc_cpe_socket_cleanup_tcpL/1,
         sc_cpe_socket_cleanup_udp4/1,
         sc_cpe_socket_cleanup_udp6/1,
         sc_cpe_socket_cleanup_udpL/1,

         sc_lc_recv_response_tcp4/1,
         sc_lc_recv_response_tcp6/1,
         sc_lc_recv_response_tcpL/1,
         sc_lc_recvfrom_response_udp4/1,
         sc_lc_recvfrom_response_udp6/1,
         sc_lc_recvfrom_response_udpL/1,
         sc_lc_recvmsg_response_tcp4/1,
         sc_lc_recvmsg_response_tcp6/1,
         sc_lc_recvmsg_response_tcpL/1,
         sc_lc_recvmsg_response_udp4/1,
         sc_lc_recvmsg_response_udp6/1,
         sc_lc_recvmsg_response_udpL/1,
         sc_lc_acceptor_response_tcp4/1,
         sc_lc_acceptor_response_tcp6/1,
         sc_lc_acceptor_response_tcpL/1,

         sc_rc_recv_response_tcp4/1,
         sc_rc_recv_response_tcp6/1,
         sc_rc_recv_response_tcpL/1,
         sc_rc_recvmsg_response_tcp4/1,
         sc_rc_recvmsg_response_tcp6/1,
         sc_rc_recvmsg_response_tcpL/1,

         sc_rs_recv_send_shutdown_receive_tcp4/1,
         sc_rs_recv_send_shutdown_receive_tcp6/1,
         sc_rs_recv_send_shutdown_receive_tcpL/1,
         sc_rs_recvmsg_send_shutdown_receive_tcp4/1,
         sc_rs_recvmsg_send_shutdown_receive_tcp6/1,
         sc_rs_recvmsg_send_shutdown_receive_tcpL/1,

         %% Socket IOCTL simple
         ioctl_simple1/1,
         ioctl_simple2/1,
         ioctl_nread/1,
         %% Socket IOCTL get requests
         ioctl_get_gifname/1,
         ioctl_get_gifindex/1,
         ioctl_get_gifaddr/1,
         ioctl_get_gifdstaddr/1,
         ioctl_get_gifbrdaddr/1,
         ioctl_get_gifnetmask/1,
         ioctl_get_gifmtu/1,
         ioctl_get_gifhwaddr/1,
         ioctl_get_giftxqlen/1,
         ioctl_get_gifflags/1,
         ioctl_get_gifmap/1,
         ioctl_tcp_info/1,
         %% ioctl_set_requests/1,

         %% *** Traffic ***
         traffic_send_and_recv_counters_tcp4/1,
         traffic_send_and_recv_counters_tcp6/1,
         traffic_send_and_recv_counters_tcpL/1,
         traffic_sendmsg_and_recvmsg_counters_tcp4/1,
         traffic_sendmsg_and_recvmsg_counters_tcp6/1,
         traffic_sendmsg_and_recvmsg_counters_tcpL/1,
         traffic_sendto_and_recvfrom_counters_udp4/1,
         traffic_sendto_and_recvfrom_counters_udp6/1,
         traffic_sendto_and_recvfrom_counters_udpL/1,
         traffic_sendmsg_and_recvmsg_counters_udp4/1,
         traffic_sendmsg_and_recvmsg_counters_udp6/1,
         traffic_sendmsg_and_recvmsg_counters_udpL/1,

         traffic_send_and_recv_chunks_tcp4/1,
         traffic_send_and_recv_chunks_tcp6/1,
         traffic_send_and_recv_chunks_tcpL/1,

         traffic_ping_pong_small_send_and_recv_tcp4/1,
         traffic_ping_pong_small_send_and_recv_tcp6/1,
         traffic_ping_pong_small_send_and_recv_tcpL/1,
         traffic_ping_pong_medium_send_and_recv_tcp4/1,
         traffic_ping_pong_medium_send_and_recv_tcp6/1,
         traffic_ping_pong_medium_send_and_recv_tcpL/1,
         traffic_ping_pong_large_send_and_recv_tcp4/1,
         traffic_ping_pong_large_send_and_recv_tcp6/1,
         traffic_ping_pong_large_send_and_recv_tcpL/1,

         traffic_ping_pong_small_sendto_and_recvfrom_udp4/1,
         traffic_ping_pong_small_sendto_and_recvfrom_udp6/1,
         traffic_ping_pong_small_sendto_and_recvfrom_udpL/1,
         traffic_ping_pong_medium_sendto_and_recvfrom_udp4/1,
         traffic_ping_pong_medium_sendto_and_recvfrom_udp6/1,
         traffic_ping_pong_medium_sendto_and_recvfrom_udpL/1,

         traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4/1,
         traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6/1,
         traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL/1,
         traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4/1,
         traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6/1,
         traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL/1,
         traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4/1,
         traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6/1,
         traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL/1,

         traffic_ping_pong_small_sendmsg_and_recvmsg_udp4/1,
         traffic_ping_pong_small_sendmsg_and_recvmsg_udp6/1,
         traffic_ping_pong_small_sendmsg_and_recvmsg_udpL/1,
         traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4/1,
         traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6/1,
         traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL/1,

         %% *** Time Test ***
         %% Server: transport = gen_tcp, active = false
         %% 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(socket), active = false
         %% Client: transport = socket(tcp)
         ttest_sgsf_csockf_small_tcp4/1,
         ttest_sgsf_csockf_small_tcp6/1,
         ttest_sgsf_csockf_medium_tcp4/1,
         ttest_sgsf_csockf_medium_tcp6/1,
         ttest_sgsf_csockf_large_tcp4/1,
         ttest_sgsf_csockf_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/0, ttest_sgent_cgent_medium_tcp4/1,
         ttest_sgent_cgent_medium_tcp6/0, ttest_sgent_cgent_medium_tcp6/1,
         ttest_sgent_cgent_large_tcp4/0, ttest_sgent_cgent_large_tcp4/1,
         ttest_sgent_cgent_large_tcp6/0, 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 = gen_tcp(socket)
         ttest_ssockf_cgsf_small_tcp4/1,
         ttest_ssockf_cgsf_small_tcp6/1,
         ttest_ssockf_cgsf_medium_tcp4/1,
         ttest_ssockf_cgsf_medium_tcp6/1,
         ttest_ssockf_cgsf_large_tcp4/1,
         ttest_ssockf_cgsf_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_small_tcpL/1,
         ttest_ssockf_csockf_medium_tcp4/1,
         ttest_ssockf_csockf_medium_tcp6/1,
         ttest_ssockf_csockf_medium_tcpL/1,
         ttest_ssockf_csockf_large_tcp4/1,
         ttest_ssockf_csockf_large_tcp6/1,
         ttest_ssockf_csockf_large_tcpL/1,

         ttest_ssockf_csocko_small_tcp4/1,
         ttest_ssockf_csocko_small_tcp6/1,
         ttest_ssockf_csocko_small_tcpL/1,
         ttest_ssockf_csocko_medium_tcp4/1,
         ttest_ssockf_csocko_medium_tcp6/1,
         ttest_ssockf_csocko_medium_tcpL/1,
         ttest_ssockf_csocko_large_tcp4/1,
         ttest_ssockf_csocko_large_tcp6/1,
         ttest_ssockf_csocko_large_tcpL/1,

         ttest_ssockf_csockt_small_tcp4/1,
         ttest_ssockf_csockt_small_tcp6/1,
         ttest_ssockf_csockt_small_tcpL/1,
         ttest_ssockf_csockt_medium_tcp4/1,
         ttest_ssockf_csockt_medium_tcp6/1,
         ttest_ssockf_csockt_medium_tcpL/1,
         ttest_ssockf_csockt_large_tcp4/1,
         ttest_ssockf_csockt_large_tcp6/1,
         ttest_ssockf_csockt_large_tcpL/1,

         %% Server: transport = socket(tcp), active = once
         %% Client: transport = gen_tcp
         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_small_tcpL/1,
         ttest_ssocko_csockf_medium_tcp4/1,
         ttest_ssocko_csockf_medium_tcpL/1,
         ttest_ssocko_csockf_medium_tcp6/1,
         ttest_ssocko_csockf_large_tcp4/1,
         ttest_ssocko_csockf_large_tcp6/1,
         ttest_ssocko_csockf_large_tcpL/1,

         ttest_ssocko_csocko_small_tcp4/1,
         ttest_ssocko_csocko_small_tcp6/1,
         ttest_ssocko_csocko_small_tcpL/1,
         ttest_ssocko_csocko_medium_tcp4/1,
         ttest_ssocko_csocko_medium_tcp6/1,
         ttest_ssocko_csocko_medium_tcpL/1,
         ttest_ssocko_csocko_large_tcp4/1,
         ttest_ssocko_csocko_large_tcp6/1,
         ttest_ssocko_csocko_large_tcpL/1,

         ttest_ssocko_csockt_small_tcp4/1,
         ttest_ssocko_csockt_small_tcp6/1,
         ttest_ssocko_csockt_small_tcpL/1,
         ttest_ssocko_csockt_medium_tcp4/1,
         ttest_ssocko_csockt_medium_tcp6/1,
         ttest_ssocko_csockt_medium_tcpL/1,
         ttest_ssocko_csockt_large_tcp4/1,
         ttest_ssocko_csockt_large_tcp6/1,
         ttest_ssocko_csockt_large_tcpL/1,

         %% Server: transport = socket(tcp), active = true
         %% Client: transport = gen_tcp
         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_small_tcpL/1,
         ttest_ssockt_csockf_medium_tcp4/1,
         ttest_ssockt_csockf_medium_tcp6/1,
         ttest_ssockt_csockf_medium_tcpL/1,
         ttest_ssockt_csockf_large_tcp4/1,
         ttest_ssockt_csockf_large_tcp6/1,
         ttest_ssockt_csockf_large_tcpL/1,

         ttest_ssockt_csocko_small_tcp4/1,
         ttest_ssockt_csocko_small_tcp6/1,
         ttest_ssockt_csocko_small_tcpL/1,
         ttest_ssockt_csocko_medium_tcp4/1,
         ttest_ssockt_csocko_medium_tcp6/1,
         ttest_ssockt_csocko_medium_tcpL/1,
         ttest_ssockt_csocko_large_tcp4/1,
         ttest_ssockt_csocko_large_tcp6/1,
         ttest_ssockt_csocko_large_tcpL/1,

         ttest_ssockt_csockt_small_tcp4/1,
         ttest_ssockt_csockt_small_tcp6/1,
         ttest_ssockt_csockt_small_tcpL/1,
         ttest_ssockt_csockt_medium_tcp4/1,
         ttest_ssockt_csockt_medium_tcp6/1,
         ttest_ssockt_csockt_medium_tcpL/1,
         ttest_ssockt_csockt_large_tcp4/1,
         ttest_ssockt_csockt_large_tcp6/1,
         ttest_ssockt_csockt_large_tcpL/1,

         ttest_simple_ssockt_csocko_small_tcp4/1,
         ttest_simple_ssockt_csocko_small_tcp6/1,
         ttest_simple_ssockt_csocko_small_tcpL/1,

         %% Tickets
         otp16359_maccept_tcp4/1,
         otp16359_maccept_tcp6/1,
         otp16359_maccept_tcpL/1,
         otp18240_accept_mon_leak_tcp4/1,
         otp18240_accept_mon_leak_tcp6/1,
         otp18635/1
        ]).


%% Internal exports
%% -export([]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-define(LIB,        socket_test_lib).
-define(KLIB,       kernel_test_lib).
-define(TTEST_LIB,  socket_test_ttest_lib).
-define(LOGGER,     socket_test_logger).

-define(BASIC_REQ,  <<"hejsan">>).
-define(BASIC_REP,  <<"hoppsan">>).

-define(DATA,       <<"HOPPSAN">>). % Temporary
-define(FAIL(R),    exit(R)).

-define(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(F(F, A),    ?LIB:f(F, A)).
-define(P(F),       ?LIB:print(F)).
-define(P(F, A),    ?LIB:print(F, A)).


-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,  5000).
-define(TPP_MEDIUM_NUM, 500).
-define(TPP_LARGE_NUM,  50).
-define(TPP_NUM(Config, Base), (Base) div lookup(kernel_factor, 1, Config)).

-define(WINDOWS, {win32,nt}).

-define(TTEST_RUNTIME,                       ?SECS(1)).
-define(TTEST_MIN_FACTOR,                    3).
-define(TTEST_MIN_FACTOR_WIN,                ?TTEST_MIN_FACTOR-1).
-define(TTEST_DEFAULT_SMALL_MAX_OUTSTANDING, 50).
-define(TTEST_DEFAULT_MEDIUM_MAX_OUTSTANDING,
        ?TTEST_MK_DEFAULT_MAX_OUTSTANDING(
           ?TTEST_DEFAULT_SMALL_MAX_OUTSTANDING)).
-define(TTEST_DEFAULT_LARGE_MAX_OUTSTANDING,
        ?TTEST_MK_DEFAULT_MAX_OUTSTANDING(
           ?TTEST_DEFAULT_MEDIUM_MAX_OUTSTANDING)).

-define(TTEST_MK_DEFAULT_MAX_OUTSTANDING(__X__),
        if ((__X__) >= 5) ->
                (__X__) div 5;
           true ->
                1
        end).

-define(START_NODE(NamePre),
        ?START_NODE(NamePre, 5000)).
-define(START_NODE(NamePre, Timeout),
        start_node(?CT_PEER_NAME(NamePre), Timeout)).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

suite() ->
    [{ct_hooks, [ts_install_cth]},
     {timetrap, {minutes,1}}].

all() -> 
    Groups = [{api,          "ESOCK_TEST_API",        include},
              {reg,          "ESOCK_TEST_REG",        include},
              {monitor,      "ESOCK_TEST_MON",        include},
              {ioctl,        "ESOCK_TEST_IOCTL",      include},
	      {socket_close, "ESOCK_TEST_SOCK_CLOSE", include},
	      {traffic,      "ESOCK_TEST_TRAFFIC",    include},
	      {ttest,        "ESOCK_TEST_TTEST",      include},
	      {tickets,      "ESOCK_TEST_TICKETS",    include}],
    [use_group(Group, Env, Default) || {Group, Env, Default} <- Groups].

use_group(_Group, undefined, exclude) ->
    [];
use_group(Group, undefined, _Default) ->
    [{group, Group}];
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_misc,                    [], api_misc_cases()},
     {api_basic,                   [], api_basic_cases()},
     {api_sendfile,                [], api_sendfile_cases()},
     {api_from_fd,                 [], api_from_fd_cases()},
     {api_async,                   [], api_async_cases()},
     {api_async_ref,               [], api_async_cases()},
     {api_options,                 [], api_options_cases()},
     {api_options_otp,             [], api_options_otp_cases()},
     {api_options_socket,          [], api_options_socket_cases()},
     {api_option_sock_acceptconn,  [], api_option_sock_acceptconn_cases()},
     {api_option_sock_passcred,    [], api_option_sock_passcred_cases()},
     {api_option_sock_priority,    [], api_option_sock_priority_cases()},
     {api_option_sock_buf,         [], api_option_sock_buf_cases()},
     {api_option_sock_lowat,       [], api_option_sock_lowat_cases()},
     {api_option_sock_timeo,       [], api_option_sock_timeo_cases()},
     {api_option_sock_timestamp,   [], api_option_sock_timestamp_cases()},
     {api_options_ip,              [], api_options_ip_cases()},
     {api_options_ipv6,            [], api_options_ipv6_cases()},
     {api_options_tcp,             [], api_options_tcp_cases()},
     {api_options_udp,             [], api_options_udp_cases()},
     %% {api_options_sctp,            [], api_options_sctp_cases()},
     {api_op_with_timeout,         [], api_op_with_timeout_cases()},
     {reg,                         [], reg_simple_cases()},
     {monitor,                     [], monitor_cases()},
     {socket_close,                [], socket_close_cases()},
     {sc_ctrl_proc_exit,           [], sc_cp_exit_cases()},
     {sc_local_close,              [], sc_lc_cases()},
     {sc_remote_close,             [], sc_rc_cases()},
     {sc_remote_shutdown,          [], sc_rs_cases()},
     {ioctl,                       [], ioctl_cases()},
     {ioctl_simple,                [], ioctl_simple_cases()},
     {ioctl_get,                   [], ioctl_get_cases()},
     {ioctl_set,                   [], ioctl_set_cases()},
     {traffic,                     [], traffic_cases()},
     {traffic_counters,            [], traffic_counters_cases()},
     {traffic_chunks,              [], traffic_chunks_cases()},
     {traffic_ping_pong,           [], traffic_ping_pong_cases()},
     {traffic_pp_send_recv,        [], traffic_pp_send_recv_cases()},
     {traffic_pp_sendto_recvfrom,  [], traffic_pp_sendto_recvfrom_cases()},
     {traffic_pp_sendmsg_recvmsg,  [], traffic_pp_sendmsg_recvmsg_cases()},
     {ttest,                       [], ttest_cases()},
     {ttest_sgenf,                 [], ttest_sgenf_cases()},
     {ttest_sgenf_cgen,            [], ttest_sgenf_cgen_cases()},
     {ttest_sgenf_cgenf,           [], ttest_sgenf_cgenf_cases()},
     {ttest_sgenf_cgeno,           [], ttest_sgenf_cgeno_cases()},
     {ttest_sgenf_cgent,           [], ttest_sgenf_cgent_cases()},
     {ttest_sgenf_csock,           [], ttest_sgenf_csock_cases()},
     {ttest_sgenf_csockf,          [], ttest_sgenf_csockf_cases()},
     {ttest_sgenf_csocko,          [], ttest_sgenf_csocko_cases()},
     {ttest_sgenf_csockt,          [], ttest_sgenf_csockt_cases()},
     {ttest_sgsf,                  [], ttest_sgsf_cases()},
     {ttest_sgsf_csock,            [], ttest_sgsf_csock_cases()},
     {ttest_sgsf_csockf,           [], ttest_sgsf_csockf_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_cgs,            [], ttest_ssockf_cgs_cases()},
     {ttest_ssockf_cgsf,           [], ttest_ssockf_cgsf_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()},
     {ttest_simple_ssockt,         [], ttest_simple_ssockt_cases()},
     {ttest_simple_ssockt_csock,   [], ttest_simple_ssockt_csock_cases()},
     {ttest_simple_ssockt_csocko,  [], ttest_simple_ssockt_csocko_cases()},

     %% Ticket groups
     {tickets,                     [], tickets_cases()},
     {otp16359,                    [], otp16359_cases()},
     {otp18240,                    [], otp18240_cases()}
    ].
     
api_cases() ->
    [
     {group, api_misc},
     {group, api_basic},
     {group, api_sendfile},
     {group, api_async},
     {group, api_async_ref},
     {group, api_options},
     {group, api_op_with_timeout}
    ].

api_misc_cases() ->
    [
     api_m_info,
     api_m_debug,
     api_m_error_open,
     api_m_error_bind
    ].

api_basic_cases() ->
    [
     api_b_simple_open_and_close_udp4,
     api_b_simple_open_and_close_udp6,
     api_b_simple_open_and_close_tcp4,
     api_b_simple_open_and_close_tcp6,
     api_b_open_and_info_udp4,
     api_b_open_and_info_udp6,
     api_b_open_and_info_tcp4,
     api_b_open_and_info_tcp6,
     api_b_open_and_close_udp4,
     api_b_open_and_close_udp6,
     api_b_open_and_close_tcp4,
     api_b_open_and_close_tcp6,
     api_b_open_and_close_udpL,
     api_b_open_and_close_tcpL,
     api_b_open_and_close_seqpL,
     api_b_open_and_close_sctp4,
     api_b_open_and_maybe_close_raw,
     api_b_sendto_and_recvfrom_udp4,
     api_b_sendto_and_recvfrom_udpL,
     api_b_sendmsg_and_recvmsg_udp4,
     api_b_sendmsg_and_recvmsg_udpL,
     api_b_send_and_recv_tcp4,
     api_b_send_and_recv_tcpL,
     api_b_send_and_recv_seqpL,
     api_b_sendmsg_and_recvmsg_tcp4,
     api_b_sendmsg_and_recvmsg_tcpL,
     api_b_sendmsg_and_recvmsg_seqpL,
     api_b_sendmsg_and_recvmsg_sctp4,
     api_b_sendmsg_iov_dgram_inet,
     api_b_sendmsg_iov_dgram_inet6,
     api_b_sendmsg_iov_dgram_local,
     api_b_sendmsg_iov_stream_inet,
     api_b_sendmsg_iov_stream_inet6,
     api_b_sendmsg_iov_stream_local,
     api_b_dgram_connect_udp4,
     api_b_dgram_connect_udp6
    ].

api_sendfile_cases() ->
    [
     api_sendfile_inet,
     api_sendfile_inet6,
     api_sendfile_local,
     api_sendfile_loop_inet,
     api_sendfile_loop_inet6,
     api_sendfile_loop_local
    ].

api_from_fd_cases() ->
    [
     api_ffd_open_wod_and_info_udp4,
     api_ffd_open_wod_and_info_udp6,
     api_ffd_open_wod_and_info_tcp4,
     api_ffd_open_wod_and_info_tcp6,
     api_ffd_open_wd_and_info_udp4,
     api_ffd_open_wd_and_info_udp6,
     api_ffd_open_wd_and_info_tcp4,
     api_ffd_open_wd_and_info_tcp6,
     api_ffd_open_and_open_wod_and_send_udp4,
     api_ffd_open_and_open_wod_and_send_udp6,
     api_ffd_open_and_open_wd_and_send_udp4,
     api_ffd_open_and_open_wd_and_send_udp6,
     api_ffd_open_connect_and_open_wod_and_send_tcp4,
     api_ffd_open_connect_and_open_wod_and_send_tcp6,
     api_ffd_open_connect_and_open_wd_and_send_tcp4,
     api_ffd_open_connect_and_open_wd_and_send_tcp6
    ].

api_async_cases() ->
    [
     api_a_connect_tcp4,
     api_a_connect_tcp6,
     api_a_sendto_and_recvfrom_udp4,
     api_a_sendto_and_recvfrom_udp6,
     api_a_sendmsg_and_recvmsg_udp4,
     api_a_sendmsg_and_recvmsg_udp6,
     api_a_send_and_recv_tcp4,
     api_a_send_and_recv_tcp6,
     api_a_sendmsg_and_recvmsg_tcp4,
     api_a_sendmsg_and_recvmsg_tcp6,
     api_a_recvfrom_cancel_udp4,
     api_a_recvfrom_cancel_udp6,
     api_a_recvmsg_cancel_udp4,
     api_a_recvmsg_cancel_udp6,
     api_a_accept_cancel_tcp4,
     api_a_accept_cancel_tcp6,
     api_a_recv_cancel_tcp4,
     api_a_recv_cancel_tcp6,
     api_a_recvmsg_cancel_tcp4,
     api_a_recvmsg_cancel_tcp6,
     api_a_mrecvfrom_cancel_udp4,
     api_a_mrecvfrom_cancel_udp6,
     api_a_mrecvmsg_cancel_udp4,
     api_a_mrecvmsg_cancel_udp6,
     api_a_maccept_cancel_tcp4,
     api_a_maccept_cancel_tcp6,
     api_a_mrecv_cancel_tcp4,
     api_a_mrecv_cancel_tcp6,
     api_a_mrecvmsg_cancel_tcp4,
     api_a_mrecvmsg_cancel_tcp6
    ].

api_options_cases() ->
    [
     {group, api_options_otp},
     {group, api_options_socket},
     {group, api_options_ip},
     {group, api_options_ipv6},
     {group, api_options_tcp},
     {group, api_options_udp}
     %% {group, api_options_sctp}
    ].

api_options_otp_cases() ->
    [
     api_opt_simple_otp_options,
     api_opt_simple_otp_meta_option,
     api_opt_simple_otp_rcvbuf_option,
     api_opt_simple_otp_controlling_process
    ].

api_options_socket_cases() ->
    [
     {group, api_option_sock_acceptconn},
     api_opt_sock_acceptfilter,
     api_opt_sock_bindtodevice,
     api_opt_sock_broadcast,
     api_opt_sock_debug,
     api_opt_sock_domain,
     api_opt_sock_dontroute,
     api_opt_sock_error,
     api_opt_sock_keepalive,
     api_opt_sock_linger,
     api_opt_sock_mark,
     api_opt_sock_maxdg,
     api_opt_sock_max_msg_size,
     api_opt_sock_oobinline,
     {group, api_option_sock_passcred},
     api_opt_sock_peek_off_tcpL,
     api_opt_sock_peercred_tcpL,
     {group, api_option_sock_priority},
     {group, api_option_sock_buf},
     {group, api_option_sock_lowat},
     {group, api_option_sock_timeo},
     {group, api_option_sock_timestamp},
     api_opt_sock_reuseaddr,
     api_opt_sock_exclusiveaddruse,
     api_opt_sock_bsp_state

    ].

api_option_sock_acceptconn_cases() ->
    [
     api_opt_sock_acceptconn_udp,
     api_opt_sock_acceptconn_tcp
    ].

api_option_sock_passcred_cases() ->
    [
     %% api_opt_sock_passcred_udp4,
     api_opt_sock_passcred_tcp4
    ].

api_option_sock_priority_cases() ->
    [
     api_opt_sock_priority_udp4,
     api_opt_sock_priority_tcp4%,
     %% api_opt_sock_priority_udp6,
     %% api_opt_sock_priority_tcp6
    ].

api_option_sock_buf_cases() ->
    [
     api_opt_sock_rcvbuf_udp4,
     api_opt_sock_sndbuf_udp4
    ].

api_option_sock_lowat_cases() ->
    [
     api_opt_sock_rcvlowat_udp4,
     api_opt_sock_sndlowat_udp4
    ].

api_option_sock_timeo_cases() ->
    [
     api_opt_sock_rcvtimeo_udp4,
     api_opt_sock_sndtimeo_udp4
    ].

api_option_sock_timestamp_cases() ->
    [
     api_opt_sock_timestamp_udp4,
     api_opt_sock_timestamp_tcp4
    ].

api_options_ip_cases() ->
    [
     api_opt_ip_add_drop_membership,
     api_opt_ip_pktinfo_udp4,
     api_opt_ip_recvopts_udp4,
     api_opt_ip_recvorigdstaddr_udp4,
     api_opt_ip_recvtos_udp4,
     api_opt_ip_recvttl_udp4,
     api_opt_ip_tos_udp4,
     api_opt_ip_recverr_udp4,

     %% Should be last!
     api_opt_ip_mopts_udp4
    ].

api_options_ipv6_cases() ->
    [
     api_opt_ipv6_recvpktinfo_udp6,
     api_opt_ipv6_flowinfo_udp6,
     api_opt_ipv6_hoplimit_udp6,
     api_opt_ipv6_tclass_udp6,
     api_opt_ipv6_recverr_udp6,

     %% Should be last!
     api_opt_ipv6_mopts_udp6
    ].

api_options_tcp_cases() ->
    [
     api_opt_tcp_congestion_tcp4,
     %% api_opt_tcp_congestion_tcp6,
     api_opt_tcp_cork_tcp4,
     %% api_opt_tcp_cork_tcp6,
     api_opt_tcp_maxseg_tcp4,
     %% api_opt_tcp_maxseg_tcp6,
     api_opt_tcp_nodelay_tcp4,
     %% api_opt_tcp_nodelay_tcp6
     api_opt_tcp_keepcnt_tcp4,
     api_opt_tcp_keepidle_tcp4,
     api_opt_tcp_keepintvl_tcp4
    ].

api_options_udp_cases() ->
    [
     api_opt_udp_cork_udp4%,
     %% api_opt_udp_cork_udp6
    ].

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
    ].

%% Socket Registry "simple" test cases
reg_simple_cases() ->
    [
     reg_s_single_open_and_close_and_count,
     reg_s_optional_open_and_close_and_count
    ].


%% Socket monitor test cases
monitor_cases() ->
    [
     monitor_simple_open_and_close,
     monitor_simple_open_and_exit,
     monitor_simple_open_and_demon_and_close,
     monitor_open_and_close_multi_socks,
     monitor_open_and_exit_multi_socks,
     monitor_open_and_demon_and_close_multi_socks,
     monitor_open_and_close_multi_mon,
     monitor_open_and_exit_multi_mon,
     monitor_open_and_close_multi_socks_and_mon,
     monitor_open_and_exit_multi_socks_and_mon,
     monitor_closed_socket
    ].


%% These cases tests what happens when the socket is closed/shutdown,
%% locally or remotely.
socket_close_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* explicitly calling socket:close/1.
sc_cp_exit_cases() ->
    [
     sc_cpe_socket_cleanup_tcp4,
     sc_cpe_socket_cleanup_tcp6,
     sc_cpe_socket_cleanup_tcpL,
     sc_cpe_socket_cleanup_udp4,
     sc_cpe_socket_cleanup_udp6,
     sc_cpe_socket_cleanup_udpL
    ].

%% 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_recv_response_tcpL,

     sc_lc_recvfrom_response_udp4,
     sc_lc_recvfrom_response_udp6,
     sc_lc_recvfrom_response_udpL,

     sc_lc_recvmsg_response_tcp4,
     sc_lc_recvmsg_response_tcp6,
     sc_lc_recvmsg_response_tcpL,
     sc_lc_recvmsg_response_udp4,
     sc_lc_recvmsg_response_udp6,
     sc_lc_recvmsg_response_udpL,

     sc_lc_acceptor_response_tcp4,
     sc_lc_acceptor_response_tcp6,
     sc_lc_acceptor_response_tcpL
    ].

%% 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_recv_response_tcpL,

     sc_rc_recvmsg_response_tcp4,
     sc_rc_recvmsg_response_tcp6,
     sc_rc_recvmsg_response_tcpL
    ].

%% 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_recv_send_shutdown_receive_tcpL,

     sc_rs_recvmsg_send_shutdown_receive_tcp4,
     sc_rs_recvmsg_send_shutdown_receive_tcp6,
     sc_rs_recvmsg_send_shutdown_receive_tcpL
    ].


ioctl_cases() ->
    [
     {group, ioctl_simple},
     {group, ioctl_get},
     {group, ioctl_set}
    ].


ioctl_simple_cases() ->
    [
     ioctl_simple1,
     ioctl_simple2,
     ioctl_nread
    ].


ioctl_get_cases() ->
    [
     ioctl_get_gifname,
     ioctl_get_gifindex,
     ioctl_get_gifaddr,
     ioctl_get_gifdstaddr,
     ioctl_get_gifbrdaddr,
     ioctl_get_gifnetmask,
     ioctl_get_gifmtu,
     ioctl_get_gifhwaddr,
     ioctl_get_giftxqlen,
     ioctl_get_gifflags,
     ioctl_get_gifmap,
     ioctl_tcp_info
    ].


ioctl_set_cases() ->
    [
    ].


traffic_cases() ->
    [
     {group, traffic_counters},
     {group, traffic_chunks},
     {group, traffic_ping_pong}
    ].

traffic_counters_cases() ->
    [
     traffic_send_and_recv_counters_tcp4,
     traffic_send_and_recv_counters_tcp6,
     traffic_send_and_recv_counters_tcpL,
     traffic_sendmsg_and_recvmsg_counters_tcp4,
     traffic_sendmsg_and_recvmsg_counters_tcp6,
     traffic_sendmsg_and_recvmsg_counters_tcpL,
     traffic_sendto_and_recvfrom_counters_udp4,
     traffic_sendto_and_recvfrom_counters_udp6,
     traffic_sendto_and_recvfrom_counters_udpL,
     traffic_sendmsg_and_recvmsg_counters_udp4,
     traffic_sendmsg_and_recvmsg_counters_udp6,
     traffic_sendmsg_and_recvmsg_counters_udpL
    ].

traffic_chunks_cases() ->
    [
     traffic_send_and_recv_chunks_tcp4,
     traffic_send_and_recv_chunks_tcp6,
     traffic_send_and_recv_chunks_tcpL
    ].

traffic_ping_pong_cases() ->
    [
     {group, traffic_pp_send_recv},
     {group, traffic_pp_sendto_recvfrom},
     {group, traffic_pp_sendmsg_recvmsg}
    ].

traffic_pp_send_recv_cases() ->
    [
     traffic_ping_pong_small_send_and_recv_tcp4,
     traffic_ping_pong_small_send_and_recv_tcp6,
     traffic_ping_pong_small_send_and_recv_tcpL,
     traffic_ping_pong_medium_send_and_recv_tcp4,
     traffic_ping_pong_medium_send_and_recv_tcp6,
     traffic_ping_pong_medium_send_and_recv_tcpL,
     traffic_ping_pong_large_send_and_recv_tcp4,
     traffic_ping_pong_large_send_and_recv_tcp6,
     traffic_ping_pong_large_send_and_recv_tcpL
    ].    

traffic_pp_sendto_recvfrom_cases() ->
    [
     traffic_ping_pong_small_sendto_and_recvfrom_udp4,
     traffic_ping_pong_small_sendto_and_recvfrom_udp6,
     traffic_ping_pong_small_sendto_and_recvfrom_udpL,
     traffic_ping_pong_medium_sendto_and_recvfrom_udp4,
     traffic_ping_pong_medium_sendto_and_recvfrom_udp6,
     traffic_ping_pong_medium_sendto_and_recvfrom_udpL
    ].

traffic_pp_sendmsg_recvmsg_cases() ->
    [    
     traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4,
     traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6,
     traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL,
     traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4,
     traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6,
     traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL,
     traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4,
     traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6,
     traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL,

     traffic_ping_pong_small_sendmsg_and_recvmsg_udp4,
     traffic_ping_pong_small_sendmsg_and_recvmsg_udp6,
     traffic_ping_pong_small_sendmsg_and_recvmsg_udpL,
     traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4,
     traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6,
     traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL
    ].

%% Condition for running the ttest cases.
%% No point in running these cases unless the machine is
%% reasonably fast.
ttest_condition(Config) ->
    OsType = os:type(),
    case ?config(kernel_factor, Config) of
        Factor when (OsType =:= ?WINDOWS) andalso
                    is_integer(Factor) andalso
                    (Factor =< ?TTEST_MIN_FACTOR_WIN) ->
            ok;
        Factor when is_integer(Factor) andalso (Factor =< ?TTEST_MIN_FACTOR) ->
            ok;
        Factor when is_integer(Factor) ->
            {skip, ?F("Too slow for TTest (~w)", [Factor])};
        _ ->
            {skip, "Too slow for TTest (undef)"}
    end.

ttest_small_max_outstanding(Config) ->
    EnvKey                = "ESOCK_TEST_TTEST_SMALL_MAX_OUTSTANDING",
    Default               = ?TTEST_DEFAULT_SMALL_MAX_OUTSTANDING,
    DefaultMaxOutstanding = ttest_max_outstanding(Config, EnvKey, Default),
    ttest_max_outstanding(Config, DefaultMaxOutstanding).

ttest_medium_max_outstanding(Config) ->
    SmallMaxOutstanding   = ttest_small_max_outstanding(Config),
    EnvKey                = "ESOCK_TEST_TTEST_MEDIUM_MAX_OUTSTANDING",
    Default               = ?TTEST_MK_DEFAULT_MAX_OUTSTANDING(
                               SmallMaxOutstanding),
    DefaultMaxOutstanding = ttest_max_outstanding(Config, EnvKey, Default),
    ttest_max_outstanding(Config, DefaultMaxOutstanding).

ttest_large_max_outstanding(Config) ->
    MediumMaxOutstanding  = ttest_medium_max_outstanding(Config),
    EnvKey                = "ESOCK_TEST_TTEST_LARGE_MAX_OUTSTANDING",
    Default               = ?TTEST_MK_DEFAULT_MAX_OUTSTANDING(
                               MediumMaxOutstanding),
    DefaultMaxOutstanding = ttest_max_outstanding(Config, EnvKey, Default),
    ttest_max_outstanding(Config, DefaultMaxOutstanding).

ttest_max_outstanding(Config, Default)
  when is_integer(Default) andalso (Default > 1) ->
    %% Note that we should not even get here if factor > 4
    case ?config(kernel_factor, Config) of
        1                     -> Default;
        2 when (Default >= 2) -> Default div 2;
        3 when (Default >= 4) -> Default div 4;
        _ when (Default >= 8) -> Default div 8;
        _                     -> 1
    end;
ttest_max_outstanding(_, _) ->
    1.

ttest_max_outstanding(Config, EnvKey, Default) ->
    Key = list_to_atom(string:to_lower(EnvKey)),
    case lists:keysearch(Key, 1, Config) of
        {value, {Key, MO}} when is_integer(MO) andalso (MO > 0) ->
            MO;
        _ ->
            case os:getenv(EnvKey) of
                false ->
                    Default;
                Val ->
                    try list_to_integer(Val) of
                        MO when (MO > 0) ->
                            MO;
                        _ ->
                            1
                    catch
                        _:_:_ ->
                            Default
                    end
            end
    end.

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 = gen_tcp(socket), active = false
     {group, ttest_sgsf},

     %% 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},

     %% simple: Server: transport = socket(tcp), active = true
     {group, ttest_simple_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(socket), active = false
ttest_sgsf_cases() ->
    [
     %% {group, ttest_sgenf_cgen},
     {group, ttest_sgsf_csock}
    ].

%% Server: transport = gen_tcp, active = false
%% Client: transport = gen_tcp, active = false

ttest_conditional_cases(Env, Default, Cases) ->
    case os:getenv(Env) of
        false ->
            Default;
        Val ->
            case list_to_atom(string:to_lower(Val)) of
                Use when (Use =:= include) orelse 
                         (Use =:= enable) orelse 
                         (Use =:= true) ->
                    Cases;
                _ -> % Assumed to be explicitly *disabled*
                    []
            end
    end.

ttest_small_conditional_cases(Cases) ->
    ttest_conditional_cases("ESOCK_TEST_TTEST_SMALL", Cases, Cases).

ttest_medium_conditional_cases(Cases) ->
    ttest_conditional_cases("ESOCK_TEST_TTEST_MEDIUM", [], Cases).

ttest_large_conditional_cases(Cases) ->
    ttest_conditional_cases("ESOCK_TEST_TTEST_LARGE", [], Cases).

ttest_select_conditional_cases(Small, Medium, Large) ->
    ttest_small_conditional_cases(Small) ++
        ttest_medium_conditional_cases(Medium) ++
        ttest_large_conditional_cases(Large).

ttest_sgenf_cgenf_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_sgenf_cgenf_small_tcp4,
       ttest_sgenf_cgenf_small_tcp6],
      %% Medium
      [ttest_sgenf_cgenf_medium_tcp4,
       ttest_sgenf_cgenf_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_sgenf_cgeno_small_tcp4,
       ttest_sgenf_cgeno_small_tcp6],
      %% Medium
      [ttest_sgenf_cgeno_medium_tcp4,
       ttest_sgenf_cgeno_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_sgenf_cgent_small_tcp4,
       ttest_sgenf_cgent_small_tcp6],
      %% Medium
      [ttest_sgenf_cgent_medium_tcp4,
       ttest_sgenf_cgent_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_sgenf_csockf_small_tcp4,
       ttest_sgenf_csockf_small_tcp6],
      %% Medium
      [ttest_sgenf_csockf_medium_tcp4,
       ttest_sgenf_csockf_medium_tcp6],
      %% Large
      [ttest_sgenf_csockf_large_tcp4,
       ttest_sgenf_csockf_large_tcp6]).

ttest_sgenf_csocko_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_sgenf_csocko_small_tcp4,
       ttest_sgenf_csocko_small_tcp6],
      %% Medium
      [ttest_sgenf_csocko_medium_tcp4,
       ttest_sgenf_csocko_medium_tcp6],
      %% Large
      [ttest_sgenf_csocko_large_tcp4,
       ttest_sgenf_csocko_large_tcp6]).

ttest_sgenf_csockt_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_sgenf_csockt_small_tcp4,
       ttest_sgenf_csockt_small_tcp6],
      %% Medium
      [ttest_sgenf_csockt_medium_tcp4,
       ttest_sgenf_csockt_medium_tcp6],
      %% Large
     [ttest_sgenf_csockt_large_tcp4,
      ttest_sgenf_csockt_large_tcp6]).

%% Server: transport = gen_tcp(socket), active = false
%% Client: transport = socket(tcp)
ttest_sgsf_csock_cases() ->
    [
     {group, ttest_sgsf_csockf}%% ,
     %% {group, ttest_sgsf_csocko},
     %% {group, ttest_sgsf_csockt}
    ].

ttest_sgsf_csockf_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_sgsf_csockf_small_tcp4,
       ttest_sgsf_csockf_small_tcp6],
      %% Medium
      [ttest_sgsf_csockf_medium_tcp4,
       ttest_sgsf_csockf_medium_tcp6],
      %% Large
      [ttest_sgsf_csockf_large_tcp4,
       ttest_sgsf_csockf_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_select_conditional_cases(
      %% Small
      [ttest_sgeno_cgenf_small_tcp4,
       ttest_sgeno_cgenf_small_tcp6],
      %% Medium
      [ttest_sgeno_cgenf_medium_tcp4,
       ttest_sgeno_cgenf_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_sgeno_cgeno_small_tcp4,
       ttest_sgeno_cgeno_small_tcp6],
      %% Medium
      [ttest_sgeno_cgeno_medium_tcp4,
       ttest_sgeno_cgeno_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_sgeno_cgent_small_tcp4,
       ttest_sgeno_cgent_small_tcp6],
      %% Medium
      [ttest_sgeno_cgent_medium_tcp4,
       ttest_sgeno_cgent_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_sgeno_csockf_small_tcp4,
       ttest_sgeno_csockf_small_tcp6],
      %% Medium
      [ttest_sgeno_csockf_medium_tcp4,
       ttest_sgeno_csockf_medium_tcp6],
      %% Large
      [ttest_sgeno_csockf_large_tcp4,
       ttest_sgeno_csockf_large_tcp6]).

ttest_sgeno_csocko_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_sgeno_csocko_small_tcp4,
       ttest_sgeno_csocko_small_tcp6],
      %% Medium
      [ttest_sgeno_csocko_medium_tcp4,
       ttest_sgeno_csocko_medium_tcp6],
      %% Large
      [ttest_sgeno_csocko_large_tcp4,
       ttest_sgeno_csocko_large_tcp6]).

ttest_sgeno_csockt_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_sgeno_csockt_small_tcp4,
       ttest_sgeno_csockt_small_tcp6],
      %% Medium
      [ttest_sgeno_csockt_medium_tcp4,
       ttest_sgeno_csockt_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_sgent_cgenf_small_tcp4,
       ttest_sgent_cgenf_small_tcp6],
      %% Medium
      [ttest_sgent_cgenf_medium_tcp4,
       ttest_sgent_cgenf_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_sgent_cgeno_small_tcp4,
       ttest_sgent_cgeno_small_tcp6],
      %% Medium
      [ttest_sgent_cgeno_medium_tcp4,
       ttest_sgent_cgeno_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_sgent_cgent_small_tcp4,
       ttest_sgent_cgent_small_tcp6],
      %% Medium
      [ttest_sgent_cgent_medium_tcp4,
       ttest_sgent_cgent_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_sgent_csockf_small_tcp4,
       ttest_sgent_csockf_small_tcp6],
      %% Medium
      [ttest_sgent_csockf_medium_tcp4,
       ttest_sgent_csockf_medium_tcp6],
      %% Large
      [ttest_sgent_csockf_large_tcp4,
       ttest_sgent_csockf_large_tcp6]).

ttest_sgent_csocko_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_sgent_csocko_small_tcp4,
       ttest_sgent_csocko_small_tcp6],
      %% Medium
      [ttest_sgent_csocko_medium_tcp4,
       ttest_sgent_csocko_medium_tcp6],
      %% Large
      [ttest_sgent_csocko_large_tcp4,
       ttest_sgent_csocko_large_tcp6]).

ttest_sgent_csockt_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_sgent_csockt_small_tcp4,
       ttest_sgent_csockt_small_tcp6],
      %% Medium
      [ttest_sgent_csockt_medium_tcp4,
       ttest_sgent_csockt_medium_tcp6],
      %% Large
      [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},
     {group, ttest_ssockf_cgsf}
    ].

%% 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_select_conditional_cases(
      %% Small
      [ttest_ssockf_cgenf_small_tcp4,
       ttest_ssockf_cgenf_small_tcp6],
      %% Medium
      [ttest_ssockf_cgenf_medium_tcp4,
       ttest_ssockf_cgenf_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_ssockf_cgeno_small_tcp4,
       ttest_ssockf_cgeno_small_tcp6],
      %% Medium
      [ttest_ssockf_cgeno_medium_tcp4,
       ttest_ssockf_cgeno_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_ssockf_cgent_small_tcp4,
       ttest_ssockf_cgent_small_tcp6],
      %% Medium
      [ttest_ssockf_cgent_medium_tcp4,
       ttest_ssockf_cgent_medium_tcp6],
      %% Large
      [ttest_ssockf_cgent_large_tcp4,
       ttest_ssockf_cgent_large_tcp6]).

%% Server: transport = socket(tcp), active = false
%% Client: transport = gen_tcp(socket)
ttest_ssockf_cgs_cases() ->
    [
     {group, ttest_ssockf_cgsf}%% ,
     %% {group, ttest_ssockf_cgeno},
     %% {group, ttest_ssockf_cgent}
    ].

%% Server: transport = socket(tcp), active = false
%% Client: transport = gen_tcp(socket), active = false
ttest_ssockf_cgsf_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_ssockf_cgsf_small_tcp4,
       ttest_ssockf_cgsf_small_tcp6],
      %% Medium
      [ttest_ssockf_cgsf_medium_tcp4,
       ttest_ssockf_cgsf_medium_tcp6],
      %% Large
      [ttest_ssockf_cgsf_large_tcp4,
       ttest_ssockf_cgsf_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_select_conditional_cases(
      %% Small
      [ttest_ssockf_csockf_small_tcp4,
       ttest_ssockf_csockf_small_tcp6,
       ttest_ssockf_csockf_small_tcpL],
      %% Medium
      [ttest_ssockf_csockf_medium_tcp4,
       ttest_ssockf_csockf_medium_tcp6,
       ttest_ssockf_csockf_medium_tcpL],
      %% Large
      [ttest_ssockf_csockf_large_tcp4,
       ttest_ssockf_csockf_large_tcp6,
       ttest_ssockf_csockf_large_tcpL]).

%% Server: transport = socket(tcp), active = false
%% Client: transport = socket(tcp), active = once
ttest_ssockf_csocko_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_ssockf_csocko_small_tcp4,
       ttest_ssockf_csocko_small_tcp6,
       ttest_ssockf_csocko_small_tcpL],
      %% Medium
      [ttest_ssockf_csocko_medium_tcp4,
       ttest_ssockf_csocko_medium_tcp6,
       ttest_ssockf_csocko_medium_tcpL],
      %% Large
      [ttest_ssockf_csocko_large_tcp4,
       ttest_ssockf_csocko_large_tcp6,
       ttest_ssockf_csocko_large_tcpL]).

%% Server: transport = socket(tcp), active = false
%% Client: transport = socket(tcp), active = true
ttest_ssockf_csockt_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_ssockf_csockt_small_tcp4,
       ttest_ssockf_csockt_small_tcp6,
       ttest_ssockf_csockt_small_tcpL],
      %% Medium
      [ttest_ssockf_csockt_medium_tcp4,
       ttest_ssockf_csockt_medium_tcp6,
       ttest_ssockf_csockt_medium_tcpL],
      %% Large
      [ttest_ssockf_csockt_large_tcp4,
       ttest_ssockf_csockt_large_tcp6,
       ttest_ssockf_csockt_large_tcpL]).

%% 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_select_conditional_cases(
      %% Small
      [ttest_ssocko_cgenf_small_tcp4,
       ttest_ssocko_cgenf_small_tcp6],
      %% Medium
      [ttest_ssocko_cgenf_medium_tcp4,
       ttest_ssocko_cgenf_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_ssocko_cgeno_small_tcp4,
       ttest_ssocko_cgeno_small_tcp6],
      %% Medium
      [ttest_ssocko_cgeno_medium_tcp4,
       ttest_ssocko_cgeno_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_ssocko_cgent_small_tcp4,
       ttest_ssocko_cgent_small_tcp6],
      %% Medium
      [ttest_ssocko_cgent_medium_tcp4,
       ttest_ssocko_cgent_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_ssocko_csockf_small_tcp4,
       ttest_ssocko_csockf_small_tcp6,
       ttest_ssocko_csockf_small_tcpL],
     %% Medium
      [ttest_ssocko_csockf_medium_tcp4,
       ttest_ssocko_csockf_medium_tcp6,
       ttest_ssocko_csockf_medium_tcpL],
      %% Large
      [ttest_ssocko_csockf_large_tcp4,
       ttest_ssocko_csockf_large_tcp6,
       ttest_ssocko_csockf_large_tcpL]).

%% Server: transport = socket(tcp), active = once
%% Client: transport = socket(tcp), active = once
ttest_ssocko_csocko_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_ssocko_csocko_small_tcp4,
       ttest_ssocko_csocko_small_tcp6,
       ttest_ssocko_csocko_small_tcpL],
      %% Medium
      [ttest_ssocko_csocko_medium_tcp4,
       ttest_ssocko_csocko_medium_tcp6,
       ttest_ssocko_csocko_medium_tcpL],
      %% Large
      [ttest_ssocko_csocko_large_tcp4,
       ttest_ssocko_csocko_large_tcp6,
       ttest_ssocko_csocko_large_tcpL]).

%% Server: transport = socket(tcp), active = once
%% Client: transport = socket(tcp), active = true
ttest_ssocko_csockt_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_ssocko_csockt_small_tcp4,
       ttest_ssocko_csockt_small_tcp6,
       ttest_ssocko_csockt_small_tcpL],
      %% Medium
      [ttest_ssocko_csockt_medium_tcp4,
       ttest_ssocko_csockt_medium_tcp6,
       ttest_ssocko_csockt_medium_tcpL],
      %% Large
      [ttest_ssocko_csockt_large_tcp4,
       ttest_ssocko_csockt_large_tcp6,
       ttest_ssocko_csockt_large_tcpL]).

%% 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_select_conditional_cases(
      %% Small
      [ttest_ssockt_cgenf_small_tcp4,
       ttest_ssockt_cgenf_small_tcp6],
      %% Medium
      [ttest_ssockt_cgenf_medium_tcp4,
       ttest_ssockt_cgenf_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_ssockt_cgeno_small_tcp4,
       ttest_ssockt_cgeno_small_tcp6],
      %% Medium
      [ttest_ssockt_cgeno_medium_tcp4,
       ttest_ssockt_cgeno_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_ssockt_cgent_small_tcp4,
       ttest_ssockt_cgent_small_tcp6],
      %% Medium
      [ttest_ssockt_cgent_medium_tcp4,
       ttest_ssockt_cgent_medium_tcp6],
      %% Large
      [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_select_conditional_cases(
      %% Small
      [ttest_ssockt_csockf_small_tcp4,
       ttest_ssockt_csockf_small_tcp6,
       ttest_ssockt_csockf_small_tcpL],
      %% Medium
      [ttest_ssockt_csockf_medium_tcp4,
       ttest_ssockt_csockf_medium_tcp6,
       ttest_ssockt_csockf_medium_tcpL],
      %% Large
      [ttest_ssockt_csockf_large_tcp4,
       ttest_ssockt_csockf_large_tcp6,
       ttest_ssockt_csockf_large_tcpL]).

%% Server: transport = socket(tcp), active = true
%% Client: transport = socket(tcp), active = once
ttest_ssockt_csocko_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_ssockt_csocko_small_tcp4,
       ttest_ssockt_csocko_small_tcp6,
       ttest_ssockt_csocko_small_tcpL],
      %% Medium
      [ttest_ssockt_csocko_medium_tcp4,
       ttest_ssockt_csocko_medium_tcp6,
       ttest_ssockt_csocko_medium_tcpL],
      %% Large
      [ttest_ssockt_csocko_large_tcp4,
       ttest_ssockt_csocko_large_tcp6,
       ttest_ssockt_csocko_large_tcpL]).

%% Server: transport = socket(tcp), active = true
%% Client: transport = socket(tcp), active = true
ttest_ssockt_csockt_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_ssockt_csockt_small_tcp4,
       ttest_ssockt_csockt_small_tcp6,
       ttest_ssockt_csockt_small_tcpL],
      %% Medium
      [ttest_ssockt_csockt_medium_tcp4,
       ttest_ssockt_csockt_medium_tcp6,
       ttest_ssockt_csockt_medium_tcpL],
      %% Large
      [ttest_ssockt_csockt_large_tcp4,
       ttest_ssockt_csockt_large_tcp6,
       ttest_ssockt_csockt_large_tcpL]).

%% Server: transport = socket(tcp), active = true
ttest_simple_ssockt_cases() ->
    [
     {group, ttest_simple_ssockt_csock}
    ].

%% Server: transport = socket(tcp), active = true
%% Client: transport = socket(tcp)
ttest_simple_ssockt_csock_cases() ->
    [
     %% {group, ttest_simple_ssockt_csockf},
     {group, ttest_simple_ssockt_csocko}%% ,
     %% {group, ttest_simple_ssockt_csockt}
    ].

%% Server: transport = socket(tcp), active = true
%% Client: transport = socket(tcp), active = once
ttest_simple_ssockt_csocko_cases() ->
    ttest_select_conditional_cases(
      %% Small
      [ttest_simple_ssockt_csocko_small_tcp4,
       ttest_simple_ssockt_csocko_small_tcp6,
       ttest_simple_ssockt_csocko_small_tcpL],
      %% Medium
      [],
      %% Large
      []).

tickets_cases() ->
    [
     {group, otp16359},
     {group, otp18240},
     otp18635
    ].

otp16359_cases() ->
    [
     otp16359_maccept_tcp4,
     otp16359_maccept_tcp6,
     otp16359_maccept_tcpL
    ].


otp18240_cases() ->
    [
     otp18240_accept_mon_leak_tcp4,
     otp18240_accept_mon_leak_tcp6
    ].


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

init_per_suite(Config0) ->
    ?P("init_per_suite -> entry with"
       "~n      Config: ~p"
       "~n      Nodes:  ~p", [Config0, erlang:nodes()]),
    
    try socket:info() of
        #{} ->
            case ?KLIB:init_per_suite(Config0) of
                {skip, _} = SKIP ->
                    SKIP;

                Config1 when is_list(Config1) ->

                    ?P("init_per_suite -> end when "
                       "~n      Config: ~p", [Config1]),

                    %% We need a monitor on this node also
                    kernel_test_sys_monitor:start(),

                    socket:use_registry(false),
                    case quiet_mode(Config1) of
                        default ->
                            case ?LOGGER:start() of
                                ok ->
                                    Config1;
                                {error, Reason} ->
                                    ?P("init_per_suite -> "
                                       "Failed starting logger"
                                       "~n   Reason: ~p"
                                       "~n", [Reason]),
                                    {skip, "Failed starting logger"}
                            end;
                        Quiet ->
                            case ?LOGGER:start(Quiet) of
                                ok ->
                                    [{esock_test_quiet, Quiet} | Config1];
                                {error, Reason} ->
                                    ?P("init_per_suite -> "
                                       "Failed starting logger"
                                       "~n   Reason: ~p"
                                       "~n", [Reason]),
                                    {skip, "Failed starting logger"}
                            end
                    end
            end
    catch
        error : notsup ->
            {skip, "esock not supported"};
        error : undef ->
            {skip, "esock not configured"}
    end.

end_per_suite(Config0) ->

    ?P("end_per_suite -> entry with"
       "~n      Config: ~p"
       "~n      Nodes:  ~p", [Config0, erlang:nodes()]),

    %% Stop the local monitor
    kernel_test_sys_monitor:stop(),

    (catch ?LOGGER:stop()),

    Config1 = ?KLIB:end_per_suite(Config0),

    ?P("end_per_suite -> "
       "~n      Nodes: ~p", [erlang:nodes()]),

    Config1.


init_per_group(api_sendfile = GroupName, Config) ->
    io:format("init_per_group(~w) -> entry with"
              "~n   Config: ~p"
              "~n", [GroupName, Config]),
    case socket:is_supported(sendfile) of
        true ->
            Dir = proplists:get_value(priv_dir, Config),
            HeaderSize = 1021, % I like prime numbers
            DataSize = 1031,  Pow2 = 16, % About 64 MB
            Header = rand:bytes(HeaderSize),
            Filler = rand:bytes(DataSize),
            Trailer = rand:bytes(HeaderSize),
            FileName = filename:join(Dir, "sendfile.bin"),
            file:write_file(
              FileName,
              [Header, double_data(Pow2, Filler), Trailer]),
            N = 1 bsl Pow2,
            [{sendfile_file,
              {FileName,
               HeaderSize + (N * DataSize) + HeaderSize,
               [Header, {N, Filler}, Trailer]}}
             | Config];
        false ->
            Config
    end;
init_per_group(GroupName, Config)
  when (GroupName =:= sc_remote_close) orelse
       (GroupName =:= sc_remote_shutdown) orelse
       (GroupName =:= traffic) ->
    io:format("init_per_group(~w) -> entry with"
              "~n   Config: ~p"
              "~n", [GroupName, Config]),
    %% Maybe we should skip the entire suite for this platform,
    %% but for now we just skip these groups, which seem to 
    %% have problems (node start).
    %% As stated elsewhere, its not really Fedora 16, but 
    %% the *really* slow VM that is the issue.
    try is_old_fedora16() of
        ok ->
            Config
    catch
        throw:{skip, _} = SKIP ->
            SKIP
    end;
init_per_group(ttest = _GroupName, Config) ->
    io:format("init_per_group(~w) -> entry with"
              "~n   Config: ~p"
              "~n", [_GroupName, Config]),
    case ttest_condition(Config) of
        ok ->
            ttest_manager_start(),
            case lists:keysearch(esock_test_ttest_runtime, 1, Config) of
                {value, _} ->
                    Config;
                false ->
                    [{esock_test_ttest_runtime, which_ttest_runtime_env()} |
                     Config]
            end;
        {skip, _} = SKIP ->
            SKIP
    end;
init_per_group(api_async_ref, Config) ->
    [{select_handle, true} | Config];
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]),
    ttest_manager_stop(),
    lists:keydelete(esock_test_ttest_runtime, 1, Config);
end_per_group(api_async_ref, Config) ->
    lists:keydelete(select_handle, 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.

double_data(0, Data) ->
    Data;
double_data(N, Data) ->
    double_data(N - 1, [Data, Data]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                           API MISC                                  %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% This is an extremely rudimentary test case, that just tests
%% that we can call the "global" info function and that it returns
%% a non-empty map...

api_m_info(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_m_info,
           fun() ->
                   ok = api_m_info()
           end).

api_m_info() ->
    case socket:info() of
        Info when is_map(Info) ->
            Sz = maps:size(Info),
            if
                (Sz > 0) ->
                    ok;
                true ->
                    ?FAIL(no_info)
            end;
        Info ->
            ?FAIL({invalid_info, Info})
    end.



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% A simple test case that tests that the global debug can be changed.
%% At the same time, it will test the info function (since it uses it
%% for verification).

api_m_debug(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_m_debug,
           fun() -> has_bugfree_gcc() end,
           fun() ->
                   ok = api_m_debug()
           end).

%% For some reason this test case triggers a gcc bug, which causes
%% a segfault, on an ancient Fedora 16 VM. So, check the version of gcc...
%% Not pretty, but the simplest way to skip (without actually testing
%% for the host).
has_bugfree_gcc() ->
    has_bugfree_gcc(os:type()).

%% Make sure we are on linux
has_bugfree_gcc({unix, linux}) ->
    has_bugfree_gcc2(string:trim(os:cmd("cat /etc/issue")));
has_bugfree_gcc(_) ->
    ok.

%% Make sure we are on Fedora 16
has_bugfree_gcc2("Fedora release 16 " ++ _) ->
    has_bugfree_gcc3(os:cmd("gcc --version"));
has_bugfree_gcc2("Welcome to SUSE Linux " ++ _) ->
    has_bugfree_gcc4(os:cmd("gcc --version"));
has_bugfree_gcc2(_) ->
    ok.

has_bugfree_gcc3("gcc (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2" ++ _) ->
    skip("Buggy GCC");
has_bugfree_gcc3(_) ->
    ok.

has_bugfree_gcc4("gcc (SUSE Linux) 4.3.2" ++ _) ->
    skip("Buggy GCC");
has_bugfree_gcc4(_) ->
    ok.

api_m_debug() ->
    i("get initial info"),
    #{debug := D0} = socket:info(),
    D1 = not D0,
    i("set new debug (~w => ~w)", [D0, D1]),
    ok = socket:debug(D1),
    i("get updated info (~w)", [D1]),
    #{debug := D1} = socket:info(),
    D2 = not D1,
    i("set new debug (~w => ~w)", [D1, D2]),
    ok = socket:debug(D2),
    i("get updated info (~w)", [D2]),
    #{debug := D2} = socket:info(),
    i("ok"),
    ok.
    
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Some tests for API misuses

-define(
   EXCEPTION(Code),
   exception(fun () -> begin Code end end)).

exception(Fun) ->
    try Fun() of
        Result ->
            error({unexpected_return, Result})
    catch
        Class : Reason ->
            {Class, Reason}
    end.


api_m_error_open(Config) when is_list(Config) ->
    %%
    %% open/1
    {error, badarg} =
        ?EXCEPTION(
           socket:open(should_be_fd) ),
    %%
    %% open/2
    {error, badarg} =
        ?EXCEPTION(
           socket:open(should_be_fd, #{}) ),
    %%
    %% A non-atom, non-integer protocol causes an exception
    %% in prim_socket, whilst a non-atom, non-integer type
    %% or domain causes an error return tuple from the NIF
    %% code.  This is a bit inconsistent.  Should we change
    %% that, if so - how, and consolidate in tests?
    %%
    {error,{invalid,{domain,should_be_domain}}} =
        socket:open(should_be_domain, dgram),
    {error,{invalid,{type,should_be_type}}} =
        socket:open(inet, should_be_type),
    %%
    %% open/3
    {error,{invalid,{domain,should_be_domain}}} =
        socket:open(should_be_domain, dgram, #{}),
    {error,{invalid,{type,should_be_type}}} =
        socket:open(inet, should_be_type, #{}),
    {error,{invalid,{protocol,should_be_protocol}}} =
        socket:open(inet, dgram, should_be_protocol),
    %%
    %% open/4
    {error,{invalid,{domain,should_be_domain}}} =
        socket:open(should_be_domain, dgram, default, #{}),
    {error,{invalid,{type,should_be_type}}} =
        socket:open(inet, should_be_type, default, #{}),
    {error,{invalid,{protocol,should_be_protocol}}} =
        socket:open(inet, dgram, should_be_protocol, #{}),
    {error, badarg} =
        ?EXCEPTION(
           socket:open(inet, dgram, default, should_be_options) ).


api_m_error_bind(Config) when is_list(Config) ->
    {ok, S} = socket:open(inet, dgram),
    try
        %%
        %% bind/2
        {error, badarg} =
            ?EXCEPTION(
               socket:bind(should_be_socket, any) ),
        {error, badarg} =
            ?EXCEPTION(
               socket:bind(make_ref(), any) ),
        %%
        %% A non-map, non-atom Addr causes an {invalid,_}
        %% exception.  Should that instead be an error
        %% return?
        %%
        {error,{invalid,{sockaddr,should_be_sockaddr}}} =
            socket:bind(S, should_be_sockaddr),
        EmptyMap = #{},
        {error,{invalid,{sockaddr,family,EmptyMap}}} =
            socket:bind(S, EmptyMap),
        InvalidKey = #{family => inet, invalid_key => []},
        {error,{invalid,{sockaddr,{keys,[invalid_key]},InvalidKey}}} =
            socket:bind(S, InvalidKey),
        InvalidFamily = #{family => invalid_family},
        {error,{invalid,{sockaddr,InvalidFamily}}} =
            socket:bind(S, InvalidFamily)
    after
        _ = socket:close(S)
    end,
    ok.


%% XXX Lots of missing error tests here, for all other API functions...


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                           API BASIC                                 %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and then close.
api_b_simple_open_and_close_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => dgram,
                                 protocol => udp},
                   ok = api_b_simple_open_and_close(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and then close.
api_b_simple_open_and_close_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => dgram,
                                 protocol => udp},
                   ok = api_b_simple_open_and_close(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and then close.
api_b_simple_open_and_close_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = api_b_simple_open_and_close(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and then close.
api_b_simple_open_and_close_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => stream,
                                 protocol => tcp},
                   ok = api_b_simple_open_and_close(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_b_simple_open_and_close(InitState) ->
    Seq = 
        [
         #{desc => "open",
           cmd  => fun(#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol} = State) -> 
                           case socket:open(Domain, Type, Protocol) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = _State) ->
                           socket:close(Sock)
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and info of an IPv4 UDP (dgram) socket.
%% With some extra checks...
api_b_open_and_info_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_open_and_info_udp4,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => dgram,
                                 protocol => udp},
                   ok = api_b_open_and_info(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and info of an IPv6 UDP (dgram) socket.
%% With some extra checks...
api_b_open_and_info_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_open_and_info_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => dgram,
                                 protocol => udp},
                   ok = api_b_open_and_info(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and info of an IPv4 TCP (stream) socket.
%% With some extra checks...
api_b_open_and_info_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_open_and_info_tcp4,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = api_b_open_and_info(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and info of an IPv6 TCP (stream) socket.
%% With some extra checks...
api_b_open_and_info_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_open_and_info_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => stream,
                                 protocol => tcp},
                   ok = api_b_open_and_info(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_b_open_and_info(InitState) ->
    Seq = 
        [
         #{desc => "open",
           cmd  => fun(#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol} = State) -> 
                           case socket:open(Domain, Type, Protocol) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "get socket info",
           cmd  => fun(#{sock := Sock} = State) ->
                           Info = socket:info(Sock),
                           ?SEV_IPRINT("Got (some) Info: "
                                       "~n   ~p", [Info]),
                           {ok, State#{info => Info}}
                   end},
         #{desc => "validate socket info",
           cmd  => fun(#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol,
                         info     := #{domain        := Domain,
                                       type          := Type,
                                       protocol      := Protocol,
                                       counters      := _,
                                       num_readers   := 0,
                                       num_writers   := 0,
                                       num_acceptors := 0}}) ->
                           ok;
                      (#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol,
                         info     := Info}) ->
                           ?SEV_EPRINT("Unexpected Info: "
                                       "~n   (expected) Domain:   ~p"
                                       "~n   (expected) Type:     ~p"
                                       "~n   (expected) Protocol: ~p"
                                       "~n   ~p",
                                       [Domain, Type, Protocol, Info]),
                           {error, unexpected_infio}
                   end},
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = _State) ->
                           socket:close(Sock)
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and close an IPv4 UDP (dgram) socket.
%% With some extra checks...
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 IPv6 UDP (dgram) socket.
%% With some extra checks...
api_b_open_and_close_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_open_and_close_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 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(_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).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and close an IPv6 TCP (stream) socket.
%% With some extra checks...
api_b_open_and_close_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_open_and_close_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = api_b_open_and_close(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and close an Unix Domain dgram (UDP) socket.
%% With some extra checks...
api_b_open_and_close_udpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_open_and_close_udpL,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   InitState = #{domain   => local,
                                 type     => dgram,
                                 protocol => default},
                   ok = api_b_open_and_close(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and close an Unix Domain stream (TCP) socket.
%% With some extra checks...
api_b_open_and_close_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_open_and_close_tcpL,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   InitState = #{domain   => local,
                                 type     => stream,
                                 protocol => default},
                   ok = api_b_open_and_close(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and close an Unix Domain dgram (UDP) socket.
%% With some extra checks...
api_b_open_and_close_seqpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
		   has_support_unix_domain_socket(),
                   is_not_windows()
	   end,
           fun() ->
                   InitState = #{domain   => local,
                                 type     => seqpacket,
                                 protocol => default},
                   ok = api_b_open_and_close(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) and close an IPv4 SCTP (seqpacket) socket.
%% With some extra checks...
api_b_open_and_close_sctp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_open_and_close_sctp4,
           fun() -> has_support_sctp() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => seqpacket,
                                 protocol => sctp},
                   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, epfnosupport = Reason}}) ->
                           {skip, Reason};
                      ({_, {error, eprotonosupport = Reason}}) ->
                           {skip, Reason};
                      ({_, {error, esocktnosupport = Reason}}) ->
                           {skip, Reason};
                      ({_, {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, {invalid, _}} = ERROR}) ->
                           case
                               socket:is_supported(options, socket, domain)
                           of
                               true ->
                                   ERROR;
                               false ->
                                   ?SEV_IPRINT("socket option 'domain' "
                                               "not supported"),
                                   {ok, S}
                           end;
                      ({_, {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 (maybe)",
           cmd  => fun(#{socket := Sock} = State) ->
                           Res = socket:getopt(Sock, socket, protocol),
                           {ok, {State, Res}}
                   end},
         #{desc => "validate protocol",
           cmd  => fun({#{protocol := Protocol} = State, {ok, Protocol}}) ->
                           {ok, State};
                      ({#{domain   := Domain,
			  protocol := ExpProtocol}, {ok, Protocol}}) ->
			   %% On OpenBSD (at least 6.6) something screwy happens
			   %% when domain = local.
			   %% It will report a completely different protocol (icmp)
			   %% but everything still works. So we skip if this happens
			   %% on OpenBSD...
			   case os:type() of
			       {unix, openbsd} when (Domain =:= local) ->
				   {skip, ?F("Unexpected protocol: ~p instead of ~p",
					     [Protocol, ExpProtocol])};
			       _ ->
				   {error, {unexpected_protocol,
					    ExpProtocol, Protocol}}
			   end;
                      %% Some platforms do not support this option
                      ({State, {error, {invalid, _}} = ERROR}) ->
			   case socket:is_supported(options, socket, protocol) of
			       true ->
                                   ERROR;
			       false ->
                                   ?SEV_IPRINT("socket option 'protocol' "
                                               "not supported"),
                                   {ok, State}
			   end;
                      ({_, {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 open (create) and (maybe) close an RAW socket.

api_b_open_and_maybe_close_raw(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_open_and_maybe_close_raw,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => raw,
                                 protocol => {raw, 255}},
                   ok = do_api_b_open_and_maybe_close_raw(InitState)
           end).

do_api_b_open_and_maybe_close_raw(InitState) ->
    Tester = 
        [
         #{desc => "open",
           cmd  => fun(#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol} = State) -> 
                           ?SEV_IPRINT("try open with:"
                                       "~n   Domain:   ~p"
                                       "~n   Type:     ~p"
                                       "~n   Protocol: ~p",
                                       [Domain, Type, Protocol]),
                           case socket:open(Domain, Type, Protocol) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, Reason} when (Reason =:= eperm) orelse
                                                    (Reason =:= eacces) ->
                                   ?SEV_IPRINT("not allowed (~w) => SKIP",
                                               [Reason]),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("open failed:"
                                               "~n   Reason: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{socket := Sock} = State) ->
                           ?SEV_IPRINT("try socket close"),
                           case socket:close(Sock) of
                               ok ->
                                   ?SEV_IPRINT("socket closed"),
                                   {ok, maps:remote(sock, State)};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("close failed:"
                                               "~n   Reason: ~p", [Reason]),
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Tester, 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_sendto_and_recvfrom_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Send = fun(Sock, Data, Dest) ->
                                  socket:sendto(Sock, Data, Dest)
                          end,
                   Recv = fun(Sock) ->
                                  socket:recvfrom(Sock)
                          end,
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive on an IPv4 UDP (dgram) socket using
%% sendto and recvfrom.
api_b_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_sendto_and_recvfrom_udpL,
           fun() ->
                   has_support_unix_domain_socket(),
		   is_not_windows(),
                   unix_domain_socket_host_cond()
           end,
           fun() ->
                   Send = fun(Sock, Data, Dest) ->
                                  socket:sendto(Sock, Data, Dest)
                          end,
                   Recv = fun(Sock) ->
                                  socket:recvfrom(Sock)
                          end,
                   InitState = #{domain => local,
                                 proto  => default,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive on an IPv4 UDP (dgram) socket
%% using sendmsg and recvmsg.
api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_sendmsg_and_recvmsg_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Send = fun(Sock, Data, Dest) ->
                                  %% We need tests for this,
                                  %% but this is not the place it.
                                  %% CMsg  = #{level => ip,
                                  %%              type  => tos,
                                  %%              data  => reliability},
                                  %% CMsgs = [CMsg],
                                  Msg = #{addr => Dest,
                                             %% ctrl => CMsgs,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  %% We have some issues on old darwin...
                                  %% ok = 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,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive on an IPv4 UDP (dgram) socket
%% using sendmsg and recvmsg.
api_b_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_sendmsg_and_recvmsg_udpL,
           fun() ->
                   has_support_unix_domain_socket(),
		   is_not_windows(),
                   unix_domain_socket_host_cond()
           end,
           fun() ->
                   Send = fun(Sock, Data, Dest) ->
                                  %% We need tests for this,
                                  %% but this is not the place it.
                                  %% CMsg  = #{level => ip,
                                  %%              type  => tos,
                                  %%              data  => reliability},
                                  %% CMsgs = [CMsg],
                                  Msg = #{addr => Dest,
                                             %% ctrl => CMsgs,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  %% We have some issues on old darwing...
                                  %% socket:setopt(Sock, otp, debug, true),
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr  := Source,
                                             iov   := [Data]}} ->
                                          %% socket:setopt(Sock, otp, debug, false),
                                          {ok, {Source, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => local,
                                 proto  => default,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_b_send_and_recv_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := local = Domain} = State) ->
                           LSASrc = which_local_socket_addr(Domain),
                           LSADst = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSASrc,
                                       lsa_dst => LSADst}};
                      (#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src socket",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   ?SEV_IPRINT("src bound (to ~p)", [Port]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst socket",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   ?SEV_IPRINT("src bound (to ~p)", [Port]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~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(#{domain   := local,
                         sock_src := Sock,
                         lsa_src  := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() -> maps:remove(lsa_src, State) end,
                                           fun() -> State end),
                           {ok, maps:remove(sock_src, State1)};
                      (#{sock_src := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{domain   := local,
                         sock_dst := Sock,
                         lsa_dst  := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() -> maps:remove(lsa_dst, State) end,
                                           fun() -> State end),
                           {ok, maps:remove(sock_dst, State1)};
                      (#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_b_send_and_recv_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  socket:send(Sock, Data)
                          end,
                   Recv = fun(Sock) ->
                                  socket:recv(Sock)
                          end,
                   InitState = #{domain => inet,
                                 type   => stream,
                                 proto  => tcp,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_conn(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive using the "common" functions (send and recv)
%% on an Unix Domain (stream) socket (TCP).
api_b_send_and_recv_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_b_send_and_recv_tcpL,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  socket:send(Sock, Data)
                          end,
                   Recv = fun(Sock) ->
                                  socket:recv(Sock)
                          end,
                   InitState = #{domain => local,
                                 type   => stream,
                                 proto  => default,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_conn(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive using the "common" functions (send and recv)
%% on an Unix Domain seqpacket socket.
api_b_send_and_recv_seqpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
		   has_support_unix_domain_socket(),
                   is_not_windows()
	   end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  socket:send(Sock, Data)
                          end,
                   Recv = fun(Sock) ->
                                  socket:recv(Sock)
                          end,
                   InitState = #{domain => local,
                                 type   => seqpacket,
                                 proto  => default,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_conn(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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_b_sendmsg_and_recvmsg_tcp4,
           fun() ->
                   is_not_windows(),
                   has_support_ipv4()
           end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  Msg = #{iov => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr  := _} = Addr} ->
                                          {error, {addr, Addr}};
                                      {ok, #{iov   := [Data]}} ->
                                          {ok, Data};
                                      {ok, Msg} ->
                                          {error, {msg, Msg}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 type   => stream,
                                 proto  => tcp,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_conn(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive using the msg functions (sendmsg and recvmsg)
%% on an Unix Domain (stream) socket (TCP).
api_b_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_b_sendmsg_and_recvmsg_tcpL,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  Msg = #{iov => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      %% On some platforms, the address
                                      %% *is* provided (e.g. linux)
                                      {ok, #{addr  := #{family := local},
                                             iov   := [Data]}} ->
                                          socket:setopt(Sock, 
                                                        otp, 
                                                        debug, 
                                                        false),
                                          {ok, Data};
                                      {ok, #{addr := _} = Msg} ->
                                          {error, {msg, Msg}};
                                      %% On some platforms, the address
                                      %% is *not* provided (e.g. FreeBSD)
                                      {ok, #{iov   := [Data]}} ->
                                          {ok, Data};
                                      {ok, Msg} ->
                                          {error, {msg, Msg}};
                                      {error, _} = ERROR ->
                                          socket:setopt(Sock, 
                                                        otp, 
                                                        debug, 
                                                        false),
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => local,
                                 type   => stream,
                                 proto  => default,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_conn(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive using the msg functions (sendmsg and recvmsg)
%% on an Unix Domain (stream) socket (TCP).
api_b_sendmsg_and_recvmsg_seqpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
		   has_support_unix_domain_socket(),
                   is_not_windows()
	   end,
           fun() ->
                   Send =
                       fun(Sock, Data) ->
                               Msg =
                                   #{iov => [Data]},
                               socket:sendmsg(Sock, Msg)
                       end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      %% On some platforms, the address
                                      %% *is* provided (e.g. linux)
                                      {ok,
                                       #{addr  := #{family := local},
                                         iov   := [Data]}} ->
                                          socket:setopt(
                                            Sock, otp, debug, false),
                                          {ok, Data};
                                      {ok, #{addr := _} = Msg} ->
                                          {error, {msg, Msg}};
                                      %% On some platforms, the address
                                      %% is *not* provided (e.g. FreeBSD)
                                      {ok,
                                       #{iov   := [Data]}} ->
                                          {ok, Data};
                                      {ok, Msg} ->
                                          {error, {msg, Msg}};
                                      {error, _} = ERROR ->
                                          socket:setopt(
                                            Sock, otp, debug, false),
                                          ERROR
                                  end
                          end,
                   InitState =
                       #{domain => local,
                         type   => seqpacket,
                         proto  => default,
                         send   => Send,
                         recv   => Recv},
                   ok = api_b_send_and_recv_conn(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_b_send_and_recv_conn(InitState) ->
    process_flag(trap_exit, true),

    ServerSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
			   ?SEV_IPRINT("LSA: ~p", [LSA]),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain,
                         type := Type,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, eprotonosupport = Reason} ->
                                   {skip, Reason};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain := local,
                         lsock  := LSock,
                         lsa    := LSA} = _State) ->
			   ?SEV_IPRINT("try bind to: "
				       "~n   ~p", [LSA]),
			   %% _ = socket:setopt(LSock, otp, debug, true),
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   %% We do not care about the port for local
				   %% _ = socket:setopt(LSock, otp, debug, false),
                                   ok;
                               {error, Reason} = ERROR ->
				   %% _ = socket:setopt(LSock, otp, debug, false),
				   ?SEV_EPRINT("failed binding: "
					       "~n   ~p", [Reason]),
                                   ERROR
                           end;
                      (#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain := local,
                         tester := Tester, lsa := #{path := Path}}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Path),
                           ok;
                      (#{tester := Tester, lport := Port}) ->
                           %% This is actually not used for unix domain socket
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},

         %% 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) ->
			   %% _ = socket:setopt(LSock, otp, debug, true),
			   ?SEV_IPRINT("try accept"),
                           case socket:accept(LSock) of
                               {ok, Sock} ->
				   %% _ = socket:setopt(LSock, otp, debug, false),
                                   ?SEV_IPRINT("accepted: ~n   ~p", [Sock]),
                                   {ok, State#{csock => Sock}};
                               {error, Reason} = ERROR ->
				   %% _ = socket:setopt(LSock, otp, debug, false),
                                   ?SEV_EPRINT("accept failed: "
					       "~n   ~p", [Reason]),
                                   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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(csock, State)}
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{domain   := local,
                         lsock    := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)};
                      (#{lsock := 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 ***
         #{desc => "await start (from tester)",
           cmd  => fun(#{domain := local} = State) ->
                           {Tester, Path} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_path => Path}};
                      (State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain      := local = Domain,
                         server_path := Path} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = #{family => Domain, path => Path},
                           {ok, State#{local_sa => LSA, server_sa => SSA}};
                      (#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         type := Type,
                         proto  := 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, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   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(#{domain   := local,
                         sock     := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(sock, State1)};
                      (#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?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},
         ?SEV_SLEEP(?SECS(1)),
         #{desc => "order client to continue (with connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, connect),
                           ok
                   end},
         #{desc => "await client ready (connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, connect)
                   end},
         #{desc => "await server ready (accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, accept)
                   end},
         #{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]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive on an IPv4 SCTP (seqpacket) socket
%% using sendmsg and recvmsg.
api_b_sendmsg_and_recvmsg_sctp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_b_sendmsg_and_recvmsg_sctp4,
           fun() ->
		   has_support_sctp(),
		   not_yet_implemented()
	   end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  %% CMsg  = #{level => sctp,
                                  %%              type  => tos,
                                  %%              data  => reliability},
                                  %% CMsgs = [CMsg],
                                  Msg = #{%% ctrl => CMsgs,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          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,
                                 proto  => sctp,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_sctp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_b_send_and_recv_sctp(_InitState) ->
%%     Seq = 
%%         [
%%          #{desc => "local address",
%%            cmd  => fun(#{domain := Domain} = State) ->
%%                            LSA = which_local_socket_addr(Domain),
%%                            {ok, State#{lsa_src => LSA,
%%                                        lsa_dst => LSA}}
%%                    end},

%%          #{desc => "open src socket",
%%            cmd  => fun(#{domain := Domain,
%%                          proto  := Proto} = State) ->
%%                            Sock = sock_open(Domain, seqpacket, Proto),
%%                            {ok, State#{sock_src => Sock}}
%%                    end},
%%          #{desc => "bind src",
%%            cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
%%                            case socket:bind(Sock, LSA) of
%%                                ok ->
%%                                    ?SEV_IPRINT("src bound"),
%%                                    ok;
%%                                {error, Reason} = ERROR ->
%%                                    ?SEV_EPRINT("src bind failed: ~p", [Reason]),
%%                                    ERROR
%%                            end
%%                    end},
%%          #{desc => "sockname src socket",
%%            cmd  => fun(#{sock_src := Sock} = State) ->
%%                            SASrc = sock_sockname(Sock),
%%                            ?SEV_IPRINT("src sockaddr: "
%%                                        "~n   ~p", [SASrc]),
%%                            {ok, State#{sa_src => SASrc}}
%%                    end},

%%          #{desc => "open dst socket",
%%            cmd  => fun(#{domain := Domain,
%%                          proto  := Proto} = State) ->
%%                            Sock = sock_open(Domain, seqpacket, Proto),
%%                            {ok, State#{sock_dst => Sock}}
%%                    end},
%%          #{desc => "bind dst",
%%            cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
%%                            case socket:bind(Sock, LSA) of
%%                                ok ->
%%                                    ?SEV_IPRINT("src bound"),
%%                                    ok;
%%                                {error, Reason} = ERROR ->
%%                                    ?SEV_EPRINT("src bind failed: ~p", [Reason]),
%%                                    ERROR
%%                            end
%%                    end},
%%          #{desc => "sockname dst socket",
%%            cmd  => fun(#{sock_dst := Sock} = State) ->
%%                            SADst = sock_sockname(Sock),
%%                            ?SEV_IPRINT("dst sockaddr: "
%%                                        "~n   ~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} = State) ->
%%                            ok = socket:close(Sock),
%%                            {ok, maps:remove(sock_src, State)}
%%                    end},
%%          #{desc => "close dst socket",
%%            cmd  => fun(#{sock_dst := Sock} = State) ->
%%                            ok = socket:close(Sock),
%%                            {ok, maps:remove(sock_dst, State)}
%%                    end},

%%          %% *** We are done ***
%%          ?SEV_FINISH_NORMAL
%%         ],
%%     Evaluator = ?SEV_START("tester", Seq, InitState),
%%     ok = ?SEV_AWAIT_FINISH([Evaluator]).

%%     process_flag(trap_exit, true),
%%     ServerSeq = 
%%         [
%%          %% *** Wait for start order ***
%%          #{desc => "await start (from tester)",
%%            cmd  => fun(State) ->
%%                            Tester = ?SEV_AWAIT_START(),
%%                            {ok, State#{tester => Tester}}
%%                    end},
%%          #{desc => "monitor tester",
%%            cmd  => fun(#{tester := Tester}) ->
%%                            _MRef = erlang:monitor(process, Tester),
%%                            ok
%%                    end},

%%          %% *** Init part ***
%%          #{desc => "which local address",
%%            cmd  => fun(#{domain := Domain} = State) ->
%%                            LSA = which_local_socket_addr(Domain),
%%                            {ok, State#{lsa => LSA}}
%%                    end},
%%          #{desc => "create (listen) socket",
%%            cmd  => fun(#{domain := Domain,
%%                          proto  := Proto} = State) ->
%%                            case socket:open(Domain, seqpacket, Proto) 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 = sock_port(LSock),
%%                                    ?SEV_IPRINT("bound to port: ~w", [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 => "accepting (await connection) FAKE",
%%            cmd  => fun(#{lsock := LSock} = State) ->
%% 			   {ok, State#{csock => LSock}}
%%                    end},
%% %%          #{desc => "accepting (await connection)",
%% %%            cmd  => fun(#{lsock := LSock} = State) ->
%% %% 			   socket:setopt(LSock, otp, debug, true),
%% %%                            case socket:accept(LSock) of
%% %%                                {ok, Sock} ->
%% %% 				   socket:setopt(LSock, otp, debug, false),
%% %%                                    ?SEV_IPRINT("accepted: ~n   ~p", [Sock]),
%% %%                                    {ok, State#{csock => Sock}};
%% %%                                {error, Reason} = ERROR ->
%% %% 				   socket:setopt(LSock, otp, debug, false),
%% %% 				   ?SEV_EPRINT("Failed accepting: "
%% %% 					       "~n   ~p", [Reason]),
%% %%                                    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} = State) ->
%%                            ok = socket:close(Sock),
%%                            {ok, maps:remove(csock, State)}
%%                    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 ***
%%          #{desc => "await start (from tester)",
%%            cmd  => fun(State) ->
%%                            {Tester, Port} = ?SEV_AWAIT_START(),
%%                            {ok, State#{tester => Tester, server_port => Port}}
%%                    end},
%%          #{desc => "monitor tester",
%%            cmd  => fun(#{tester := Tester}) ->
%%                            _MRef = erlang:monitor(process, Tester),
%%                            ok
%%                    end},

%%          %% *** The init part ***
%%          #{desc => "which server (local) address",
%%            cmd  => fun(#{domain := Domain, server_port := Port} = State) ->
%%                            LSA = which_local_socket_addr(Domain),
%%                            SSA = LSA#{port => Port},
%%                            {ok, State#{local_sa => LSA, server_sa => SSA}}
%%                    end},
%%          #{desc => "create socket",
%%            cmd  => fun(#{domain := Domain,
%%                          proto  := Proto} = State) ->
%%                            case socket:open(Domain, seqpacket, Proto) of
%%                                {ok, Sock} ->
%%                                    {ok, State#{sock => Sock}};
%%                                {error, _} = ERROR ->
%%                                    ERROR
%%                            end
%%                    end},
%%          #{desc => "bind to local address",
%%            cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
%%                            case socket:bind(Sock, LSA) of
%%                                ok ->
%%                                    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}) ->
%%                            case socket:connect(Sock, SSA) of
%% 			       ok ->
%% 				   ?SEV_IPRINT("connected"),
%% 				   ok;
%% 			       {error, Reason} = ERROR ->
%% 				   ?SEV_EPRINT("Failed connect: "
%% 					       "~n   ~p", [Reason]),
%% 				   ERROR
%% 			   end
%%                    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} = State) ->
%%                            ok = socket:close(Sock),
%%                            {ok, maps:remove(sock, State)}
%%                    end},

%%          %% *** We are done ***
%%          ?SEV_FINISH_NORMAL
%%         ],

%%     TesterSeq =
%%         [
%%          %% *** Init part ***
%%          #{desc => "monitor server",
%%            cmd  => fun(#{server := Pid} = _State) ->
%%                            _MRef = erlang:monitor(process, Pid),
%%                            ok
%%                    end},
%%          #{desc => "monitor client",
%%            cmd  => fun(#{client := Pid} = _State) ->
%%                            _MRef = erlang:monitor(process, Pid),
%%                            ok
%%                    end},

%%          %% Start the server
%%          #{desc => "order server start",
%%            cmd  => fun(#{server := Pid} = _State) ->
%%                            ?SEV_ANNOUNCE_START(Pid),
%%                            ok
%%                    end},
%%          #{desc => "await server ready (init)",
%%            cmd  => fun(#{server := Pid} = State) ->
%%                            {ok, 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},
%%          ?SEV_SLEEP(?SECS(1)),
%%          #{desc => "order client to continue (with connect)",
%%            cmd  => fun(#{client := Client} = _State) ->
%%                            ?SEV_ANNOUNCE_CONTINUE(Client, connect),
%%                            ok
%%                    end},
%%          #{desc => "await client ready (connect)",
%%            cmd  => fun(#{client := Client} = _State) ->
%%                            ?SEV_AWAIT_READY(Client, client, connect)
%%                    end},
%%          #{desc => "await server ready (accept)",
%%            cmd  => fun(#{server := Server} = _State) ->
%%                            ?SEV_AWAIT_READY(Server, server, accept)
%%                    end},
%%          #{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]).

    ok.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                           API IOV                                   %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_b_sendmsg_iov_dgram_inet(Config) when is_list(Config) ->
    has_support_ipv4(),
    api_b_sendmsg_iov_dgram(inet).
%%
api_b_sendmsg_iov_dgram_inet6(Config) when is_list(Config) ->
    has_support_ipv6(),
    api_b_sendmsg_iov_dgram(inet6).
%%
api_b_sendmsg_iov_dgram_local(Config) when is_list(Config) ->
    has_support_unix_domain_socket(),
    is_not_windows(), % on Windows, local only works for stream sockets
    api_b_sendmsg_iov_dgram(local).

api_b_sendmsg_iov_stream_inet(Config) when is_list(Config) ->
    has_support_ipv4(),
    is_not_windows(), % sendmsg does not work on Windows
    api_b_sendmsg_iov_stream(inet).
%%
api_b_sendmsg_iov_stream_inet6(Config) when is_list(Config) ->
    has_support_ipv6(),
    is_not_windows(), % sendmsg does not work on Windows
    api_b_sendmsg_iov_stream(inet6).
%%
api_b_sendmsg_iov_stream_local(Config) when is_list(Config) ->
    has_support_unix_domain_socket(),
    is_not_windows(), % sendmsg does not work on Windows
    api_b_sendmsg_iov_stream(local).


api_b_sendmsg_iov_dgram(Domain) ->
    ?P("api_b_sendmsg_iov_dgram -> entry with"
       "~n   Domain: ~p", [Domain]),
    ?TT(?SECS(5)),
    #{iov_max := IOVMax} = socket:info(),
    ?P("api_b_sendmsg_iov_dgram -> IOVMax: ~p", [IOVMax]),
    IOV =
        lists:map(
          fun (N) -> <<(rand:uniform(N) - 1)>> end,
          lists:duplicate(IOVMax, 256)),
    IOVTooLarge = IOV ++ IOV,
    Data = erlang:iolist_to_binary(IOV),
    ?P("api_b_sendmsg_iov_dgram -> a: create socket"),
    {ok, Sa} = socket:open(Domain, dgram, #{debug => true}),
    try
	?P("api_b_sendmsg_iov_dgram -> b: create socket"),
        {ok, Sb} = socket:open(Domain, dgram),
        try
	    ?P("api_b_sendmsg_iov_dgram -> a: bind socket"),
            ok = socket:bind(Sa, which_local_socket_addr(Domain)),
	    ?P("api_b_sendmsg_iov_dgram -> b: bind socket"),
            ok = socket:bind(Sb, which_local_socket_addr(Domain)),
	    ?P("api_b_sendmsg_iov_dgram -> a: get sockname"),
            {ok, Aa} = socket:sockname(Sa),
	    ?P("api_b_sendmsg_iov_dgram -> b: get sockname"),
            {ok, Ab} = socket:sockname(Sb),
            %%
	    ?P("api_b_sendmsg_iov_dgram -> a: sendmsg"),
            ok = socket:sendmsg(Sa, #{addr => Ab, iov => IOV}),
	    ?P("api_b_sendmsg_iov_dgram -> b: recvfrom"),
            {ok, {Aa, Data}} = socket:recvfrom(Sb),
            %%
	    ?P("api_b_sendmsg_iov_dgram -> b: sendmsg (too large => fail)"),
            {error, {invalid, _}} =
                socket:sendmsg(Sb, #{addr => Aa, iov => IOVTooLarge}),
	    ?P("api_b_sendmsg_iov_dgram -> done"),
            ok
        after
	    ?P("api_b_sendmsg_iov_dgram -> b: close socket"),
            socket:close(Sb)
        end
    after
	?P("api_b_sendmsg_iov_dgram -> a: close socket"),
        socket:close(Sa)
    end.

api_b_sendmsg_iov_stream(Domain) ->
    ?P("api_b_sendmsg_iov_stream -> entry with"
       "~n   Domain: ~p", [Domain]),
    ?TT(?SECS(5)),
    #{iov_max := IOVMax} = socket:info(),
    ?P("api_b_sendmsg_iov_stream -> IOVMax: ~p", [IOVMax]),
    IOV =
        lists:map(
          fun (N) -> <<(rand:uniform(N) - 1)>> end,
          lists:duplicate(IOVMax, 256)),
    IOVTooLarge = IOV ++ IOV,
    Data = erlang:iolist_to_binary(IOV),
    DataTooLarge = erlang:iolist_to_binary(IOVTooLarge),
    ?P("api_b_sendmsg_iov_stream -> create stream socket a"),
    {ok, Sa} = socket:open(Domain, stream),
    try
        case os:type() of
            {win32,nt} ->
		?P("api_b_sendmsg_iov_stream-> [win] a: bind socket"),
                ok = socket:bind(Sa, which_local_socket_addr(Domain));
            _ ->
                ok
        end,
	?P("api_b_sendmsg_iov_stream -> create stream socket b"),
        {ok, Sb} = socket:open(Domain, stream),
        try
	    ?P("api_b_sendmsg_iov_stream -> b: bind socket"),
            ok = socket:bind(Sb, which_local_socket_addr(Domain)),
	    ?P("api_b_sendmsg_iov_stream -> b: get sockname"),
            {ok, Ab} = socket:sockname(Sb),
	    ?P("api_b_sendmsg_iov_stream -> b: make socket listen"),
            ok = socket:listen(Sb),
	    ?P("api_b_sendmsg_iov_stream -> a: connect socket to b"),
            ok = socket:connect(Sa, Ab),
	    ?P("api_b_sendmsg_iov_stream -> a: get sockname"),
            {ok, Aa} = socket:sockname(Sa),
	    ?P("api_b_sendmsg_iov_stream -> a: get peername"),
            {ok, Ab} = socket:peername(Sa),
	    ?P("api_b_sendmsg_iov_stream -> accept connection (=> c)"),
            {ok, Sc} = socket:accept(Sb),
            try
		?P("api_b_sendmsg_iov_stream -> c: get sockname"),
                {ok, Ab} = socket:sockname(Sc),
		?P("api_b_sendmsg_iov_stream -> c: get peername"),
                {ok, Aa} = socket:peername(Sc),
                %%
		?P("api_b_sendmsg_iov_stream -> a: sendmsg"),
                ok = socket:sendmsg(Sa, #{iov => IOV}),
		?P("api_b_sendmsg_iov_stream -> c: recv"),
                {ok, Data} =
                    socket:recv(Sc, byte_size(Data)),
		?P("api_b_sendmsg_iov_stream -> c: sendmsg (too large)"),
                ok = socket:sendmsg(Sc, #{iov => IOVTooLarge}),
		?P("api_b_sendmsg_iov_stream -> a: recv"),
                {ok, DataTooLarge} =
                    socket:recv(Sa, byte_size(DataTooLarge)),
		?P("api_b_sendmsg_iov_stream -> done"),
                ok
            catch
                error:notsup = Reason:_ ->
		    ?P("api_b_sendmsg_iov_stream -> notsup"),
                    exit({skip, Reason})
            after
                socket:close(Sc)
            end
        after
	    ?P("api_b_sendmsg_iov_stream -> after - b: close socket"),
            socket:close(Sb)
        end
    after
	?P("api_b_sendmsg_iov_stream -> after - a: close socket"),
        socket:close(Sa)
    end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically connect, send and receive using the "common" functions
%% (send and recv) on an IPv4 UDP (dgram) socket.
api_b_dgram_connect_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain => inet,
                                 type   => dgram,
                                 proto  => udp},
                   ok = api_b_dgram_connect(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically connect, send and receive using the "common" functions
%% (send and recv) on an IPv6 UDP (dgram) socket.
api_b_dgram_connect_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain => inet6,
                                 type   => dgram,
                                 proto  => udp},
                   ok = api_b_dgram_connect(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_b_dgram_connect(InitState) ->
    PeerSeq = 
        [
         %% *** 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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},

         #{desc => "open",
           cmd  => fun(#{domain := Domain,
                         type   := Type,
                         proto  := Protocol} = State) ->
                           case socket:open(Domain, Type, Protocol) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, eprotonosupport = Reason}
                                 when (Reason =:= epfnosupport)    orelse
                                      (Reason =:= eprotonosupport) orelse
                                      (Reason =:= esocktnosupport) ->
                                   ?SEV_IPRINT("failed open: ~p => SKIP",
                                               [Reason]),
                                   {skip, Reason};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "bind to local address",
           cmd  => fun(#{domain   := local,
                         sock     := Sock,
                         local_sa := LSA} = State) ->
                           case socket:bind(Sock, LSA) of
                               ok ->
                                   {ok, State#{sock_sa => LSA}};
                               {error, Reason} = ERROR ->
				   ?SEV_EPRINT("failed binding: "
					       "~n   ~p", [Reason]),
                                   ERROR
                           end;
                      (#{sock := Sock, local_sa := LSA} = State) ->
                           case sock_bind(Sock, LSA#{port => 0}) of
                               ok ->
                                   Port = sock_port(Sock),
                                   {ok, State#{sock_sa => LSA#{port => Port}}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester, sock_sa := SSA}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, SSA),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (peer sa)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_CONTINUE(Tester, tester, connect) of
                               {ok, PeerSA} ->
                                   ?SEV_IPRINT("Peer SA:"
                                               "~n   ~p", [PeerSA]),
                                   {ok, State#{peer_sa => PeerSA}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "connect",
           cmd  => fun(#{sock := Sock, peer_sa := PSA}) ->
                           ?SEV_IPRINT("try connect"),
                           socket:connect(Sock, PSA)
                   end},

         #{desc => "announce ready (connected)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, connected),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (send and recv)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_CONTINUE(Tester,
                                                    tester, send_and_recv) of
                               {ok, Role} ->
                                   ?SEV_IPRINT("role: ~p", [Role]),
                                   {ok, State#{role => Role}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "send (client) or recv (server) request",
           cmd  => fun(#{sock := Sock, role := client}) ->
                           ?SEV_IPRINT("[client] try send request"),
                           socket:send(Sock, ?BASIC_REQ);
                      (#{sock := Sock, role := server}) ->
                           ?SEV_IPRINT("[server] try recv request"),
                           case socket:recv(Sock) of
                               {ok, ?BASIC_REQ} ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "recv (client) send (server) reply",
           cmd  => fun(#{sock := Sock, role := client}) ->
                           ?SEV_IPRINT("[client] try recv reply"),
                           case socket:recv(Sock) of
                               {ok, ?BASIC_REP} ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{sock := Sock, role := server}) ->
                           ?SEV_IPRINT("[server] try send reply"),
                           socket:send(Sock, ?BASIC_REP)
                   end},

         #{desc => "announce ready (send_and_recv)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_and_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",
           cmd  => fun(#{sock := Sock} = State) ->
                           socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor peer 1",
           cmd  => fun(#{peer_1 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor peer 2",
           cmd  => fun(#{peer_2 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         #{desc => "order peer 1 start",
           cmd  => fun(#{peer_1 := Pid}) ->
                           ?SEV_ANNOUNCE_START(Pid)
                   end},
         #{desc => "order peer 2 start",
           cmd  => fun(#{peer_2 := Pid}) ->
                           ?SEV_ANNOUNCE_START(Pid)
                   end},

         #{desc => "await peer 1 ready (init)",
           cmd  => fun(#{peer_1 := Pid} = State) ->
                           {ok, PeerSA} = ?SEV_AWAIT_READY(Pid, peer1, init),
                           {ok, State#{peer_1_sa => PeerSA}}
                   end},
         #{desc => "await peer 2 ready (init)",
           cmd  => fun(#{peer_2 := Pid} = State) ->
                           {ok, PeerSA} = ?SEV_AWAIT_READY(Pid, peer2, init),
                           {ok, State#{peer_2_sa => PeerSA}}
                   end},


         #{desc => "order peer 1 to continue (with connect)",
           cmd  => fun(#{peer_1 := Pid, peer_2_sa := PeerSA} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, connect, PeerSA),
                           ok
                   end},
         #{desc => "order peer 2 to continue (with connect)",
           cmd  => fun(#{peer_2 := Pid, peer_1_sa := PeerSA} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, connect, PeerSA),
                           ok
                   end},


         #{desc => "await peer 1 ready (connected)",
           cmd  => fun(#{peer_1 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, peer1, connected)
                   end},
         #{desc => "await peer 2 ready (connected)",
           cmd  => fun(#{peer_2 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, peer2, connected)
                   end},



         %% We should pick one to issue the request
         %% (and the other is then to *await* the request
         %%  and respond).



         #{desc => "order peer 1 to continue (with send_and_recv)",
           cmd  => fun(#{peer_1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, send_and_recv, client),
                           ok
                   end},
         #{desc => "order peer 2 to continue (with send_and_recv)",
           cmd  => fun(#{peer_2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, send_and_recv, server),
                           ok
                   end},


         #{desc => "await peer 1 ready (send_and_recv)",
           cmd  => fun(#{peer_1 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, peer1, send_and_recv)
                   end},
         #{desc => "await peer 2 ready (send_and_recv)",
           cmd  => fun(#{peer_2 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, peer2, send_and_recv)
                   end},


         ?SEV_SLEEP(?SECS(1)),


         %% *** Termination ***
         #{desc => "order peer 1 to terminate",
           cmd  => fun(#{peer_1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await peer 1 termination",
           cmd  => fun(#{peer_1 := Pid} = State) ->
                           ?SEV_AWAIT_TERMINATION(Pid),
                           State1 = maps:remove(peer_1, State),
                           {ok, State1}
                   end},
         #{desc => "order peer 2 to terminate",
           cmd  => fun(#{peer_2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await peer 2 termination",
           cmd  => fun(#{peer_2 := Pid} = State) ->
                           ?SEV_AWAIT_TERMINATION(Pid),
                           State1 = maps:remove(peer_2, State),
                           {ok, State1}
                   end},


         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    Peer1  = ?SEV_START("peer 1", PeerSeq,   InitState),
    Peer2  = ?SEV_START("peer 2", PeerSeq,   InitState),
    Tester = ?SEV_START("tester", TesterSeq, #{peer_1 => Peer1#ev.pid,
                                               peer_2 => Peer2#ev.pid}),
    ok = ?SEV_AWAIT_FINISH([Peer1, Peer2, Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                           API SENDFILE                              %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_sendfile_inet(Config) when is_list(Config) ->
    has_support_sendfile(),
    has_support_ipv4(),
    api_sendfile(inet, Config, fun socket:sendfile/2).

api_sendfile_inet6(Config) when is_list(Config) ->
    has_support_sendfile(),
    has_support_ipv6(),
    api_sendfile(inet6, Config, fun socket:sendfile/2).

api_sendfile_local(Config) when is_list(Config) ->
    has_support_sendfile(),
    has_support_unix_domain_socket(),
    api_sendfile(local, Config, fun socket:sendfile/2).



api_sendfile_loop_inet(Config) when is_list(Config) ->
    has_support_sendfile(),
    has_support_ipv4(),
    api_sendfile(inet, Config, fun sendfile_loop/2).

api_sendfile_loop_inet6(Config) when is_list(Config) ->
    has_support_sendfile(),
    has_support_ipv6(),
    api_sendfile(inet6, Config, fun sendfile_loop/2).

api_sendfile_loop_local(Config) when is_list(Config) ->
    has_support_sendfile(),
    has_support_unix_domain_socket(),
    api_sendfile(local, Config, fun sendfile_loop/2).


sendfile_loop(Sa, F) ->
    sendfile_loop(Sa, F, 0).

sendfile_loop(Sa, Cont, Offset) ->
    SelectHandle = make_ref(),
    case socket:sendfile(Sa, Cont, Offset, 0, SelectHandle) of
        {select, Select} ->
            receive
                {'$socket', Sa, select, SelectHandle} ->
                    case Select of
                        {{select_info, _, _} = Cont_1, BytesSent} ->
                            sendfile_loop(Sa, Cont_1, Offset + BytesSent);
                        {select_info, _, _} = Cont_1 ->
                            sendfile_loop(Sa, Cont_1, Offset)
                    end;
                {'$socket', Sa, abort, {SelectHandle, Reason}} ->
                    {error, {Reason, Offset}}
            end;
        {ok, BytesSent} ->
            {ok, Offset + BytesSent};
        {error, Reason} = Error ->
            io:format(
              "sendfile_loop(~p, ~p, ~p) -> ~p.~n",
              [Sa, Cont, Offset, Error]),
            {error, {Reason, Offset}}
    end.



api_sendfile(Domain, Config, Sendfile) ->
    case proplists:get_value(sendfile_file, Config) of
        undefined ->
            {skip, sendfile_not_supported};
        {File, Size, Data} ->
            api_sendfile(Domain, File, Size, Data, Sendfile)
    end.

api_sendfile(Domain, File, Size, Data, Sendfile) ->
    ?TT(?SECS(10)),
    TC = self(),
    BufSize = Size bsr 10, % /1k
    BindAddr = which_local_socket_addr(Domain),
    case BindAddr of
        #{family := local, path := Path} ->
            _ =
                spawn(
                  fun () ->
                          Mref = monitor(process, TC),
                          receive
                              {'DOWN', Mref, _, _, _} ->
                                  unlink_path(Path)
                          end
                  end),
            ok;
        #{family := _} ->
            ok
    end,
    io:format("BindAddr = ~p~n", [BindAddr]),
    {ok, F} = file:open(File, [raw, read, binary]),
    io:format("F = ~p~n", [F]),
    {ok, L} = socket:open(Domain, stream),
    io:format("L = ~p~n", [L]),
    ok = socket:bind(L, BindAddr),
    ok = socket:listen(L),
    {ok, Addr} = socket:sockname(L),
    io:format("Addr = ~p~n", [Addr]),
    {ok, Sa} = socket:open(Domain, stream),
    io:format("Sa = ~p~n", [Sa]),
    ok = socket:setopt(Sa, {socket,sndbuf}, BufSize),
    ok = socket:connect(Sa, Addr),
    {ok, Sb} = socket:accept(L),
    io:format("Sb = ~p~n", [Sb]),
    ok = socket:setopt(Sb, {socket,rcvbuf}, BufSize),
    Verifyer =
        spawn_link(
          fun () ->
                  TC ! {self(), api_sendfile_verify(Sb, Data, 0)}
          end),
    io:format("Verifyer = ~p~n", [Verifyer]),
    %%
    SendfileResult = Sendfile(Sa, F),
    io:format("SendfileResult = ~p~n", [SendfileResult]),
    %%
    receive
        {Verifyer, VerifyerResult} ->
            io:format("VerifyerResult = ~p~n", [VerifyerResult]),
            case {SendfileResult, VerifyerResult} of
                {{ok, Size}, {ok, Size}} ->
                    ok;
                Other ->
                    ?FAIL({bad_result, Other})
            end
    end.

api_sendfile_verify(_S, [], M) ->
    {ok, M};
api_sendfile_verify(S, [Block | Data], M) when is_binary(Block) ->
    api_sendfile_verify_block(S, Data, M, Block, 1);
api_sendfile_verify(S, [{N, Block} | Data], M) ->
    api_sendfile_verify_block(S, Data, M, Block, N).

api_sendfile_verify_block(S, Data, M, Block, N) ->
    if
        0 < N ->
            ByteSize = byte_size(Block),
            case socket:recv(S, ByteSize, 2000) of
                {ok, Block} ->
                    api_sendfile_verify_block(
                      S, Data, M + ByteSize, Block, N - 1);
                Other ->
                    {error_at, M, Other}
            end;
        true ->
            api_sendfile_verify(S, Data, M)
    end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                           API FROM FD                               %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) a socket from an already existing 
%% file descriptor (FD) and info of an IPv4 UDP (dgram) socket.
%% With some extra checks...
%% IPv4
%% Without dup
api_ffd_open_wod_and_info_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_wod_and_info_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => dgram,
                                 protocol => udp,
                                 dup      => false},
                   ok = api_ffd_open_and_info(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) a socket from an already existing 
%% file descriptor (FD) and info of an IPv6 UDP (dgram) socket.
%% With some extra checks...
%% IPv6
%% Without dup
api_ffd_open_wod_and_info_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_wod_and_info_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => dgram,
                                 protocol => udp,
                                 dup      => false},
                   ok = api_ffd_open_and_info(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) a socket from an already existing 
%% file descriptor (FD) and info of an IPv4 UDP (dgram) socket.
%% With some extra checks...
%% IPv4
%% With dup
api_ffd_open_wd_and_info_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_wd_open_and_info_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => dgram,
                                 protocol => udp,
                                 dup      => true},
                   ok = api_ffd_open_and_info(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) a socket from an already existing 
%% file descriptor (FD) and info of an IPv4 UDP (dgram) socket.
%% With some extra checks...
%% IPv6
%% With dup
api_ffd_open_wd_and_info_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_wd_open_and_info_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => dgram,
                                 protocol => udp,
                                 dup      => true},
                   ok = api_ffd_open_and_info(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) a socket from an already existing 
%% file descriptor (FD) and info of an IPv4 TCP (stream) socket.
%% With some extra checks...
%% IPv6
%% Without dup
api_ffd_open_wod_and_info_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_wod_and_info_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp,
                                 dup      => false},
                   ok = api_ffd_open_and_info(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) a socket from an already existing 
%% file descriptor (FD) and info of an IPv6 TCP (stream) socket.
%% With some extra checks...
%% IPv6
%% Without dup
api_ffd_open_wod_and_info_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_wod_and_info_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => stream,
                                 protocol => tcp,
                                 dup      => false},
                   ok = api_ffd_open_and_info(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) a socket from an already existing 
%% file descriptor (FD) and info of an IPv4 TCP (stream) socket.
%% With some extra checks...
%% IPv6
%% With dup
api_ffd_open_wd_and_info_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_wd_and_info_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp,
                                 dup      => true},
                   ok = api_ffd_open_and_info(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open (create) a socket from an already existing 
%% file descriptor (FD) and info of an IPv6 TCP (stream) socket.
%% With some extra checks...
%% IPv6
%% With dup
api_ffd_open_wd_and_info_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_wd_and_info_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => stream,
                                 protocol => tcp,
                                 dup      => true},
                   ok = api_ffd_open_and_info(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_ffd_open_and_info(InitState) ->
    Seq = 
        [
         #{desc => "open",
           cmd  => fun(#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol} = State) -> 
                           case socket:open(Domain, Type, Protocol) of
                               {ok, Sock1} ->
                                   {ok, State#{sock1 => Sock1}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "get socket (1) FD",
           cmd  => fun(#{sock1 := Sock1} = State) ->
                           case socket:getopt(Sock1, otp, fd) of
                               {ok, FD} ->
                                   ?SEV_IPRINT("FD: ~w", [FD]),
                                   {ok, State#{fd => FD}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed get FD: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "check if we need to provide protocol or not",
           cmd  => fun(#{sock1 := Sock1} = State) ->
                           case socket:getopt(Sock1, socket, protocol) of
                               {ok, _} ->
                                   ?SEV_IPRINT("protocol accessible"),
                                   {ok, State#{provide_protocol => false}};
                               {error, Reason} ->
                                   ?SEV_IPRINT("failed get protocol: "
                                               "~n   ~p", [Reason]),
                                   {ok, State#{provide_protocol => true}}
                           end
                   end},
         #{desc => "open with FD",
           cmd  => fun(#{fd               := FD,
                         dup              := DUP,
                         provide_protocol := true,
                         protocol         := Protocol} = State) -> 
                           case socket:open(FD, #{dup      => DUP,
                                                  protocol => Protocol}) of
                               {ok, Sock2} ->
                                   ?SEV_IPRINT("socket 2 open"),
                                   {ok, State#{sock2 => Sock2}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed open socket with FD (~w): "
                                               "~n   ~p", [FD, Reason]),
                                   ERROR
                           end;
                      (#{fd               := FD,
                         dup              := DUP,
                         provide_protocol := false} = State) -> 
                           case socket:open(FD, #{dup => DUP}) of
                               {ok, Sock2} ->
                                   ?SEV_IPRINT("socket 2 open"),
                                   {ok, State#{sock2 => Sock2}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed open socket with FD (~w): "
                                               "~n   ~p", [FD, Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "get socket (1) info",
           cmd  => fun(#{sock1 := Sock} = State) ->
                           %% socket:setopt(Sock, otp, debug, true),
                           Info = socket:info(Sock),
                           %% socket:setopt(Sock, otp, debug, false),
                           ?SEV_IPRINT("Got Info: "
                                       "~n   ~p", [Info]),
                           {ok, State#{info1 => Info}}
                   end},
         #{desc => "get socket (2) info",
           cmd  => fun(#{sock2 := Sock} = State) ->
                           %% socket:setopt(Sock, otp, debug, true),
                           Info = socket:info(Sock),
                           %% socket:setopt(Sock, otp, debug, false),
                           ?SEV_IPRINT("Got Info: "
                                       "~n   ~p", [Info]),
                           {ok, State#{info2 => Info}}
                   end},
         #{desc => "validate socket (1) info",
           cmd  => fun(#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol,
                         info1    := #{domain        := Domain,
                                       type          := Type,
                                       protocol      := Protocol,
                                       ctype         := normal,
                                       counters      := _,
                                       num_readers   := 0,
                                       num_writers   := 0,
                                       num_acceptors := 0}}) ->
                           ok;
                      (#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol,
                         info     := Info}) ->
                           ?SEV_EPRINT("Unexpected Info for socket 1: "
                                       "~n   (expected) Domain:      ~p"
                                       "~n   (expected) Type:        ~p"
                                       "~n   (expected) Protocol:    ~p"
                                       "~n   (expected) Create Type: ~p"
                                       "~n   ~p",
                                       [Domain, Type, Protocol, normal, Info]),
                           {error, unexpected_infio}
                   end},
         #{desc => "validate socket (2) info",
           cmd  => fun(#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol,
                         fd       := _FD,
                         dup      := false,
                         info2    := #{domain        := Domain,
                                       type          := Type,
                                       protocol      := Protocol,
                                       ctype         := fromfd,
                                       counters      := _,
                                       num_readers   := 0,
                                       num_writers   := 0,
                                       num_acceptors := 0}}) ->
                           ok;
                      (#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol,
                         fd       := _FD,
                         dup      := false,
                         info     := Info}) ->
                           ?SEV_EPRINT("Unexpected Info for socket 2: "
                                       "~n   (expected) Domain:      ~p"
                                       "~n   (expected) Type:        ~p"
                                       "~n   (expected) Protocol:    ~p"
                                       "~n   (expected) Create Type: ~p"
                                       "~n   ~p",
                                       [Domain, Type, Protocol,
                                        fromfd, Info]),
                           {error, unexpected_info};
                      (#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol,
                         fd       := FD,
                         dup      := true,
                         info2    := #{domain        := Domain,
                                       type          := Type,
                                       protocol      := Protocol,
                                       ctype         := {fromfd, FD},
                                       counters      := _,
                                       num_readers   := 0,
                                       num_writers   := 0,
                                       num_acceptors := 0}}) ->
                           ok;
                      (#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol,
                         fd       := FD,
                         dup      := true,
                         info     := Info}) ->
                           ?SEV_EPRINT("Unexpected Info for socket 2: "
                                       "~n   (expected) Domain:      ~p"
                                       "~n   (expected) Type:        ~p"
                                       "~n   (expected) Protocol:    ~p"
                                       "~n   (expected) Create Type: ~p"
                                       "~n   ~p",
                                       [Domain, Type, Protocol,
                                        {fromfd, FD}, Info]),
                           {error, unexpected_info}
                   end},
         #{desc => "close socket (1)",
           cmd  => fun(#{sock1 := Sock} = _State) ->
                           socket:close(Sock)
                   end},
         #{desc => "close socket (2)",
           cmd  => fun(#{sock2 := Sock} = _State) ->
                           socket:close(Sock)
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open a socket (1) and then create another socket (2) from
%% its file descriptor *without* dup.
%% Exchange some data from via both "client" sockets.
%% Finally close the second socket. Ensure that the original socket
%% has not been closed (test by sending some data).
%% IPv4 UDP (dgram) socket.
%%
%% <WARNING>
%%
%% This is *not* how its intended to be used.
%% That an erlang process creating a socket and then handing over the
%% file descriptor to another erlang process. *But* its a convient way
%% to test it!
%%
%% </WARNING>
%%
api_ffd_open_and_open_wod_and_send_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_and_open_wod_and_send_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => dgram,
                                 protocol => udp,
                                 dup      => false},
                   ok = api_ffd_open_and_open_and_send_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open a socket (1) and then create another socket (2) from
%% its file descriptor *without* dup.
%% Exchange some data from via both "client" sockets.
%% Finally close the second socket. Ensure that the original socket
%% has not been closed (test by sending some data).
%% IPv6 UDP (dgram) socket.
%%
%% <WARNING>
%%
%% This is *not* how its intended to be used.
%% That an erlang process creating a socket and then handing over the
%% file descriptor to another erlang process. *But* its a convient way
%% to test it!
%%
%% </WARNING>
%%
api_ffd_open_and_open_wod_and_send_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_and_open_wod_and_send_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => dgram,
                                 protocol => udp,
                                 dup      => false},
                   ok = api_ffd_open_and_open_and_send_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open a socket (1) and then create another socket (2) from
%% its file descriptor *with* dup.
%% Exchange some data from via both "client" sockets.
%% Finally close the second socket. Ensure that the original socket
%% has not been closed (test by sending some data).
%% IPv4 UDP (dgram) socket.
%%
api_ffd_open_and_open_wd_and_send_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_and_open_wd_and_send_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => dgram,
                                 protocol => udp,
                                 dup      => true},
                   ok = api_ffd_open_and_open_and_send_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open a socket (1) and then create another socket (2) from
%% its file descriptor *with* dup.
%% Exchange some data from via both "client" sockets.
%% Finally close the second socket. Ensure that the original socket
%% has not been closed (test by sending some data).
%% IPv6 UDP (dgram) socket.
%%
api_ffd_open_and_open_wd_and_send_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_and_open_wd_and_send_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => dgram,
                                 protocol => udp,
                                 dup      => true},
                   ok = api_ffd_open_and_open_and_send_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_ffd_open_and_open_and_send_udp(InitState) ->
    Send = fun(Sock, Data, Dest) ->
                   socket:sendto(Sock, Data, Dest)
           end,
    Recv = fun(Sock) ->
                   socket:recvfrom(Sock)
           end,
    api_ffd_open_and_open_and_send_udp2(InitState#{send   => Send,
                                                           recv   => Recv}).

api_ffd_open_and_open_and_send_udp2(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain   := Domain,
                         protocol := Proto} = State) ->
                           case socket:open(Domain, dgram, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, lsa := LSA} = State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   {ok, State#{port => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester, port := Port}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},

         #{desc => "await request 1 (recv)",
           cmd  => fun(#{sock := Sock, recv := Recv} = State) ->
                           case Recv(Sock) of
                               {ok, {Source, ?BASIC_REQ}} ->
                                   ?SEV_IPRINT("received request (1) from: "
                                               "~n   ~p", [Source]),
                                   {ok, State#{source => Source}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 1 (recv request)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_req),
                           ok
                   end},
         #{desc => "await continue 1 (with send reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
                   end},
         #{desc => "send reply 1",
           cmd  => fun(#{sock := Sock, send := Send, source := Source}) ->
                           Send(Sock, ?BASIC_REP, Source)
                   end},
         #{desc => "announce ready 1 (send reply)",
           cmd  => fun(#{tester := Tester} = State) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_reply),
                           {ok, maps:remove(source, State)}
                   end},

         #{desc => "await request 2 (recv)",
           cmd  => fun(#{sock := Sock, recv := Recv} = State) ->
                           case Recv(Sock) of
                               {ok, {Source, ?BASIC_REQ}} -> 
                                   ?SEV_IPRINT("received request (2) from: "
                                               "~n   ~p", [Source]),
                                   {ok, State#{source => Source}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 2 (recv request)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_req),
                           ok
                   end},
         #{desc => "await continue 2 (with send reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
                   end},
         #{desc => "send reply 2",
           cmd  => fun(#{sock := Sock, send := Send, source := Source}) ->
                           Send(Sock, ?BASIC_REP, Source)
                   end},
         #{desc => "announce ready 2 (send reply)",
           cmd  => fun(#{tester := Tester} = State) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_reply),
                           {ok, maps:remove(source, State)}
                   end},

         #{desc => "await request 3 (recv)",
           cmd  => fun(#{sock := Sock, recv := Recv} = State) ->
                           case Recv(Sock) of
                               {ok, {Source, ?BASIC_REQ}} ->
                                   ?SEV_IPRINT("received request (2) from: "
                                               "~n   ~p", [Source]),
                                   {ok, State#{source => Source}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 3 (recv request)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_req),
                           ok
                   end},
         #{desc => "await continue 3 (with send reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
                   end},
         #{desc => "send reply 3",
           cmd  => fun(#{sock := Sock, send := Send, source := Source}) ->
                           Send(Sock, ?BASIC_REP, Source)
                   end},
         #{desc => "announce ready 3 (send reply)",
           cmd  => fun(#{tester := Tester} = State) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_reply),
                           {ok, maps:remove(source, State)}
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    Client1Seq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain   := Domain,
                         protocol := Proto} = State) ->
                           case socket:open(Domain, dgram, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "get socket FD",
           cmd  => fun(#{sock := Sock} = State) ->
                           case socket:getopt(Sock, otp, fd) of
                               {ok, FD} ->
                                   ?SEV_IPRINT("FD: ~w", [FD]),
                                   {ok, State#{fd => FD}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed get FD: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester,
                         fd     := FD}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, FD),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "await continue (send request 1)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 1 (to server)",
           cmd  => fun(#{sock := Sock, send := Send, server_sa := SSA}) ->
                           Send(Sock, ?BASIC_REQ, SSA)
                   end},
         #{desc => "announce ready (send request 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await recv reply 1 (from server)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           {ok, {_, ?BASIC_REP}} = Recv(Sock),
                           ok
                   end},
         #{desc => "announce ready (recv reply 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_reply),
                           ok
                   end},

         #{desc => "await continue (send request 3)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 3 (to server)",
           cmd  => fun(#{sock := Sock, send := Send, server_sa := SSA}) ->
                           Send(Sock, ?BASIC_REQ, SSA)
                   end},
         #{desc => "announce ready (send request 3)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await recv reply 3 (from server)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           {ok, {_, ?BASIC_REP}} = Recv(Sock),
                           ok
                   end},
         #{desc => "announce ready (recv reply 3)",
           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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    Client2Seq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           {Tester, {Port, FD}} = ?SEV_AWAIT_START(),
                           {ok, State#{tester      => Tester,
                                       server_port => Port,
                                       fd          => FD}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{fd  := FD,
                         dup := DUP} = State) ->
                           case socket:open(FD, #{dup => DUP}) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "await continue (send request 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 2 (to server)",
           cmd  => fun(#{sock := Sock, send := Send, server_sa := SSA}) ->
                           Send(Sock, ?BASIC_REQ, SSA)
                   end},
         #{desc => "announce ready (send request 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await recv reply 2 (from server)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           {ok, {_, ?BASIC_REP}} = Recv(Sock),
                           ok
                   end},
         #{desc => "announce ready (recv reply 2)",
           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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor client 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},

         %% 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 1
         #{desc => "order client 1 start",
           cmd  => fun(#{client1 := Pid, server_port := Port} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Port),
                           ok
                   end},
         #{desc => "await client 1 ready (init)",
           cmd  => fun(#{client1 := Pid} = State) ->
                           case ?SEV_AWAIT_READY(Pid, client1, init) of
                               {ok, FD} ->
                                   {ok, State#{fd => FD}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Client 1 init error: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         %% Start the client 2
         #{desc => "order client 2 start",
           cmd  => fun(#{client2     := Pid,
                         server_port := Port,
                         fd          := FD} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, {Port, FD}),
                           ok
                   end},
         #{desc => "await client 2 ready (init)",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, init)
                   end},

         %% *** The actual test ***

         #{desc => "order client 1 to continue (with send request 1)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client 1 ready (with send request 1)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client1, send_req)
                   end},
         #{desc => "await server ready (request recv 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order server to continue (with send reply 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
                           ok
                   end},
         #{desc => "await server ready (with reply 1 sent)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_reply)
                   end},
         #{desc => "await client 1 ready (reply recv 1)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client1, recv_reply)
                   end},


         #{desc => "order client 2 to continue (with send request 2)",
           cmd  => fun(#{client2 := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client 2 ready (with send request 2)",
           cmd  => fun(#{client2 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client2, send_req)
                   end},
         #{desc => "await server ready (request recv 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order server to continue (with send reply 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
                           ok
                   end},
         #{desc => "await server ready (with reply 2 sent)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_reply)
                   end},
         #{desc => "await client 2 ready (reply recv 2)",
           cmd  => fun(#{client2 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client2, recv_reply)
                   end},


         #{desc => "order client 2 to terminate",
           cmd  => fun(#{client2 := Client} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Client),
                           ok
                   end},
         #{desc => "await client 2 termination",
           cmd  => fun(#{client2 := Client} = State) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client2, State),
                           {ok, State1}
                   end},


         #{desc => "order client 1 to continue (with send request 3)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client 1 ready (with send request 3)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client1, send_req)
                   end},
         #{desc => "await server ready (request recv 3)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order server to continue (with send reply 3)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
                           ok
                   end},
         #{desc => "await server ready (with reply 3 sent)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_reply)
                   end},
         #{desc => "await client 1 ready (reply recv 3)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client1, recv_reply)
                   end},


         %% *** Termination ***
         #{desc => "order client 1 to terminate",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Client),
                           ok
                   end},
         #{desc => "await client 1 termination",
           cmd  => fun(#{client1 := Client} = State) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client1, 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, maps:remove(dup, InitState)),

    i("start (socket origin) client 1 evaluator"),
    Client1 = ?SEV_START("client-1", Client1Seq, maps:remove(dup, InitState)),
    i("await evaluator(s)"),

    i("start client 2 evaluator"),
    Client2 = ?SEV_START("client-2", Client2Seq, InitState),
    i("await evaluator(s)"),

    i("start tester evaluator"),
    TesterInitState = #{server  => Server#ev.pid,
                        client1 => Client1#ev.pid,
                        client2 => Client2#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    ok = ?SEV_AWAIT_FINISH([Server, Client1, Client2, Tester]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open a socket (1), connect to a server and then create
%% another socket (2) from its file descriptor *without* dup.
%% Exchange some data from via both "client" sockets.
%% Finally close the second socket. Ensure that the original socket
%% has not been closed (test by sending some data).
%% IPv4 TCP (stream) socket.
%%
%% <WARNING>
%%
%% This is *not* how its intended to be used.
%% That an erlang process creating a socket and then handing over the
%% file descriptor to another erlang process. *But* its a convient way
%% to test it!
%%
%% </WARNING>
%%
api_ffd_open_connect_and_open_wod_and_send_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_connect_and_open_wod_and_send_tcp4,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp,
                                 dup      => false},
                   ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open a socket (1), connect to a server and then create
%% another socket (2) from its file descriptor *without* dup.
%% Exchange some data from via both "client" sockets.
%% Finally close the second socket. Ensure that the original socket
%% has not been closed (test by sending some data).
%% IPv6 TCP (stream) socket.
%%
%% <WARNING>
%%
%% This is *not* how its intended to be used.
%% That an erlang process creating a socket and then handing over the
%% file descriptor to another erlang process. *But* its a convient way
%% to test it!
%%
%% </WARNING>
%%
api_ffd_open_connect_and_open_wod_and_send_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_connect_and_open_wod_and_send_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => stream,
                                 protocol => tcp,
                                 dup      => false},
                   ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open a socket (1), connect to a server and then create
%% another socket (2) from its file descriptor *with* dup.
%% Exchange some data from via both "client" sockets.
%% Finally close the second socket. Ensure that the original socket
%% has not been closed (test by sending some data).
%% IPv4 TCP (stream) socket.
api_ffd_open_connect_and_open_wd_and_send_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_connect_and_open_wd_and_send_tcp4,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp,
                                 dup      => true},
                   ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
           end).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically open a socket (1), connect to a server and then create
%% another socket (2) from its file descriptor *with* dup.
%% Exchange some data from via both "client" sockets.
%% Finally close the second socket. Ensure that the original socket
%% has not been closed (test by sending some data).
%% IPv6 TCP (stream) socket.
api_ffd_open_connect_and_open_wd_and_send_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_ffd_open_connect_and_open_wd_and_send_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => stream,
                                 protocol => tcp,
                                 dup      => true},
                   ok = api_ffd_open_connect_and_open_and_send_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_ffd_open_connect_and_open_and_send_tcp(InitState) ->
    Send = fun(Sock, Data) ->
                   socket:send(Sock, Data)
           end,
    Recv = fun(Sock) ->
                   socket:recv(Sock)
           end,
    api_ffd_open_connect_and_open_and_send_tcp2(InitState#{send   => Send,
                                                           recv   => Recv}).

api_ffd_open_connect_and_open_and_send_tcp2(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain   := Domain,
                         protocol := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) 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 sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [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}) ->
                           %% This is actually not used for unix domain socket
                           ?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 request 1 (recv)",
           cmd  => fun(#{csock := Sock, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, ?BASIC_REQ} ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 1 (recv request)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_req),
                           ok
                   end},
         #{desc => "await continue 1 (with send reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
                   end},
         #{desc => "send reply 1",
           cmd  => fun(#{csock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REP)
                   end},
         #{desc => "announce ready 1 (send reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_reply),
                           ok
                   end},

         #{desc => "await request 2 (recv)",
           cmd  => fun(#{csock := Sock, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, ?BASIC_REQ} ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 2 (recv request)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_req),
                           ok
                   end},
         #{desc => "await continue 2 (with send reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
                   end},
         #{desc => "send reply 2",
           cmd  => fun(#{csock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REP)
                   end},
         #{desc => "announce ready 2 (send reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_reply),
                           ok
                   end},

         #{desc => "await request 3 (recv)",
           cmd  => fun(#{csock := Sock, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, ?BASIC_REQ} ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 3 (recv request)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_req),
                           ok
                   end},
         #{desc => "await continue 3 (with send reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
                   end},
         #{desc => "send reply 3",
           cmd  => fun(#{csock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REP)
                   end},
         #{desc => "announce ready 3 (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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(csock, State)}
                   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
        ],


    Client1Seq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain   := Domain,
                         protocol := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},


         #{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 => "get socket FD",
           cmd  => fun(#{sock := Sock} = State) ->
                           case socket:getopt(Sock, otp, fd) of
                               {ok, FD} ->
                                   ?SEV_IPRINT("FD: ~w", [FD]),
                                   {ok, State#{fd => FD}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed get FD: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (connect)",
           cmd  => fun(#{tester := Tester,
                         fd     := FD}) ->
                           ?SEV_ANNOUNCE_READY(Tester, connect, FD),
                           ok
                   end},


         %% *** The actual test ***
         #{desc => "await continue (send request 1)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 1 (to server)",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REQ)
                   end},
         #{desc => "announce ready (send request 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await recv reply 1 (from server)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           {ok, ?BASIC_REP} = Recv(Sock),
                           ok
                   end},
         #{desc => "announce ready (recv reply 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_reply),
                           ok
                   end},

         #{desc => "await continue (send request 3)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 3 (to server)",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REQ)
                   end},
         #{desc => "announce ready (send request 3)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await recv reply 3 (from server)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           {ok, ?BASIC_REP} = Recv(Sock),
                           ok
                   end},
         #{desc => "announce ready (recv reply 3)",
           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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    Client2Seq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           {Tester, FD} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, fd => FD}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "create socket",
           cmd  => fun(#{fd  := FD,
                         dup := DUP} = State) ->
                           case socket:open(FD, #{dup => DUP}) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "await continue (send request 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 2 (to server)",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REQ)
                   end},
         #{desc => "announce ready (send request 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await recv reply 2 (from server)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           {ok, ?BASIC_REP} = Recv(Sock),
                           ok
                   end},
         #{desc => "announce ready (recv reply 2)",
           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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor client 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},

         %% 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 1
         #{desc => "order client 1 start",
           cmd  => fun(#{client1 := Pid, server_port := Port} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Port),
                           ok
                   end},
         #{desc => "await client 1 ready (init)",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, init)
                   end},

         #{desc => "order server to continue (with accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, accept),
                           ok
                   end},
         ?SEV_SLEEP(?SECS(1)),
         #{desc => "order client 1 to continue (with connect)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, connect),
                           ok
                   end},
         #{desc => "await client 1 ready (connect)",
           cmd  => fun(#{client1 := Pid} = State) ->
                           {ok, FD} = ?SEV_AWAIT_READY(Pid, client1, connect),
                           {ok, State#{fd => FD}}
                   end},
         #{desc => "await server ready (accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, accept)
                   end},         

         %% Start the client 2
         #{desc => "order client 2 start",
           cmd  => fun(#{client2 := Pid, fd := FD} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, FD),
                           ok
                   end},
         #{desc => "await client 2 ready (init)",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, init)
                   end},

         %% *** The actual test ***

         #{desc => "order client 1 to continue (with send request 1)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client 1 ready (with send request 1)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client1, send_req)
                   end},
         #{desc => "await server ready (request recv 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order server to continue (with send reply 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
                           ok
                   end},
         #{desc => "await server ready (with reply 1 sent)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_reply)
                   end},
         #{desc => "await client 1 ready (reply recv 1)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client1, recv_reply)
                   end},


         #{desc => "order client 2 to continue (with send request 2)",
           cmd  => fun(#{client2 := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client 2 ready (with send request 2)",
           cmd  => fun(#{client2 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client2, send_req)
                   end},
         #{desc => "await server ready (request recv 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order server to continue (with send reply 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
                           ok
                   end},
         #{desc => "await server ready (with reply 2 sent)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_reply)
                   end},
         #{desc => "await client 2 ready (reply recv 2)",
           cmd  => fun(#{client2 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client2, recv_reply)
                   end},


         #{desc => "order client 2 to terminate",
           cmd  => fun(#{client2 := Client} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Client),
                           ok
                   end},
         #{desc => "await client 2 termination",
           cmd  => fun(#{client2 := Client} = State) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client2, State),
                           {ok, State1}
                   end},


         #{desc => "order client 1 to continue (with send request 3)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client 1 ready (with send request 3)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client1, send_req)
                   end},
         #{desc => "await server ready (request recv 3)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order server to continue (with send reply 3)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
                           ok
                   end},
         #{desc => "await server ready (with reply 3 sent)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_reply)
                   end},
         #{desc => "await client 1 ready (reply recv 3)",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client1, recv_reply)
                   end},


         %% *** Termination ***
         #{desc => "order client 1 to terminate",
           cmd  => fun(#{client1 := Client} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Client),
                           ok
                   end},
         #{desc => "await client 1 termination",
           cmd  => fun(#{client1 := Client} = State) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client1, 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, maps:remove(dup, InitState)),

    i("start (socket origin) client 1 evaluator"),
    Client1 = ?SEV_START("client-1", Client1Seq, maps:remove(dup, InitState)),
    i("await evaluator(s)"),

    i("start client 2 evaluator"),
    Client2 = ?SEV_START("client-2", Client2Seq, InitState),
    i("await evaluator(s)"),

    i("start tester evaluator"),
    TesterInitState = #{server  => Server#ev.pid,
                        client1 => Client1#ev.pid,
                        client2 => Client2#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    ok = ?SEV_AWAIT_FINISH([Server, Client1, Client2, Tester]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                           API ASYNC                                 %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically establish a TCP connection via an async connect. IPv4.
api_a_connect_tcp4(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    tc_try(api_a_connect_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   ok = api_a_connect_tcpD(inet, nowait(Config))
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically establish a TCP connection via an async connect. IPv6.
api_a_connect_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    tc_try(api_a_connect_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   ok = api_a_connect_tcpD(inet6, nowait(Config))
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_a_connect_tcpD(Domain, Nowait) ->
    Connect = fun(Sock, SockAddr) ->
                      socket:connect(Sock, SockAddr, Nowait)
              end,
    Send = fun(Sock, Data) ->
                   socket:send(Sock, Data)
           end,
    Recv = fun(Sock) ->
                   socket:recv(Sock)
           end,
    InitState = #{domain => Domain,
                  connect => Connect,
                  send => Send,
                  recv => Recv,
                  connect_ref => Nowait},
    api_a_connect_tcp(InitState).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_a_connect_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester, lport := Port}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (accept)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
                   end},
         #{desc => "await connection",
           cmd  => fun(#{lsock := LSock} = State) ->
                           case socket:accept(LSock) of
                               {ok, Sock} ->
                                   ?SEV_IPRINT("accepted: ~n   ~p", [Sock]),
                                   {ok, State#{csock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (accept)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, accept),
                           ok
                   end},

         #{desc => "await continue (recv_req)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_req)
                   end},
         #{desc => "recv req",
           cmd  => fun(#{csock := Sock, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, ?BASIC_REQ} ->
                                   ok;
                               {ok, UnexpData} ->
                                   {error, {unexpected_data, UnexpData}};
                               {error, _} = ERROR ->
                                   %% At the moment there is no way to get
                                   %% status or state for the socket...
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_req)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_req),
                           ok
                   end},
         #{desc => "await continue (send_rep)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_rep)
                   end},
         #{desc => "send rep",
           cmd  => fun(#{csock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REP)
                   end},
         #{desc => "announce ready (send_rep)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_rep),
                           ok
                   end},
	 

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close connection socket",
           cmd  => fun(#{csock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(csock, State)}
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{lsock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(lsock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    ClientSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "await continue (async connect)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, async_connect)
                   end},
         #{desc => "connect (async) to server",
           cmd  => fun(#{sock        := Sock,
                         server_sa   := SSA,
                         connect     := Connect,
                         connect_ref := SR} = State) ->
                           case Connect(Sock, SSA) of
                               ok ->
                                   ?SEV_IPRINT("ok -> "
					       "unexpected success => SKIP", 
                                               []),
                                   {skip, unexpected_success};

                               {select, {select_info, ST, SelectRef}}
                                 when SR =:= nowait ->
                                   ?SEV_IPRINT("select nowait ->"
                                               "~n   tag: ~p"
                                               "~n   ref: ~p",
                                               [ST, SelectRef]),
                                   {ok, State#{asynch_tag  => select,
                                               connect_tag => ST,
                                               connect_ref => SelectRef}};
                               {select, {select_info, ST, SR}}
                                 when is_reference(SR) ->
                                   ?SEV_IPRINT("select ref ->"
                                               "~n   tag: ~p"
                                               "~n   ref: ~p", [ST, SR]),
                                   {ok, State#{asynch_tag  => select,
                                               connect_tag => ST}};

                               {completion,
                                {completion_info, CT, CompletionRef}}
                                 when SR =:= nowait ->
                                   ?SEV_IPRINT("completion nowait ->"
                                               "~n   tag: ~p"
                                               "~n   ref: ~p",
                                               [CT, CompletionRef]),
                                   {ok, State#{asynch_tag  => completion,
                                               connect_tag => CT,
                                               connect_ref => CompletionRef}};
                               {completion,
                                {completion_info, CT, CR}}
                                 when is_reference(CR) ->
                                   ?SEV_IPRINT("completion ref ->"
                                               "~n   tag: ~p"
                                               "~n   ref: ~p", [CT, CR]),
                                   {ok, State#{asynch_tag  => completion,
                                               connect_tag => CT}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (connect select|completion)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, connect_select),
                           ok
                   end},
         #{desc => "await select|completion message",
           cmd  => fun(#{sock        := Sock,
                         asynch_tag  := select,
                         connect_tag := connect,
                         connect_ref := Ref}) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   ?SEV_IPRINT("select message ->"
                                               "~n   ref: ~p", [Ref]),
                                   ok
                           after 5000 ->
                                   ?SEV_EPRINT("timeout: "
                                               "~n   message queue: ~p",
                                               [mq()]),
                                   {error, timeout}
                           end;
                      (#{sock        := Sock,
                         asynch_tag  := completion,
                         connect_tag := connect,
                         connect_ref := Ref}) ->
                           receive
                               {'$socket', Sock, completion, {Ref, ok = Res}} ->
                                   ?SEV_IPRINT("completion message ->"
                                               "~n   ref: ~p"
                                               "~n   res: ~p", [Ref, Res]),
                                   ok
                           after 5000 ->
                                   ?SEV_EPRINT("timeout: "
                                               "~n   message queue: ~p",
                                               [mq()]),
                                   {error, timeout}
                           end
                   end},
         #{desc => "announce ready (select|completion)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, select),
                           ok
                   end},
         #{desc => "(maybe) connect (async) to server",
           cmd  => fun(#{sock        := Sock,
                         server_sa   := SSA,
                         asynch_tag  := select,
                         connect_tag := connect,
                         connect     := Connect}) ->
                           case Connect(Sock, SSA) of
                               ok ->
                                   ok;
                               {select, SelectInfo} ->
                                   {error, {unexpected_select, SelectInfo}};
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{sock        := _Sock,
                         server_sa   := _SSA,
                         asynch_tag  := completion,
                         connect_tag := connect,
                         connect     := _Connect}) ->
                           ok
                   end},
         #{desc => "announce ready (connect)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, connect),
                           ok
                   end},
         #{desc => "get peername",
           cmd  => fun(#{sock := Sock} = _State) ->
                           case socket:peername(Sock) of
                               {ok, SockAddr} ->
                                   ?SEV_IPRINT("Peer Name: ~p", [SockAddr]),
                                   ok;
                                {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "await continue (send_req)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send req",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REQ)
                   end},
         #{desc => "announce ready (send_req)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await continue (recv_rep)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_rep)
                   end},
         #{desc => "recv rep",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, ?BASIC_REP} ->
                                   ok;
                               {ok, UnexpData} ->
                                   {error, {unexpected_data, UnexpData}};
                               {error, _} = ERROR ->
                                   %% At the moment there is no way to get
                                   %% status or state for the socket...
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_rep)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_rep),
                           ok
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           State2 = maps:remove(sock,         State),
                           State3 = maps:remove(connect_stag, State2),
                           State4 = maps:remove(connect_sref, State3),
                           {ok, State4}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor client",
           cmd  => fun(#{client := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = State) ->
                           {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{server_port => Port}}
                   end},

         %% Start the client
         #{desc => "order client start",
           cmd  => fun(#{client := Pid, server_port := Port} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Port),
                           ok
                   end},
         #{desc => "await client ready (init)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, init)
                   end},


         %% *** The actual test ***
         #{desc => "order client to continue (async connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, async_connect),
                           ok
                   end},
         #{desc => "await client ready (connect select)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, connect_select)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "order server to continue (with accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, accept),
                           ok
                   end},
         #{desc => "await client ready (select)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, select)
                   end},
         #{desc => "await client ready (connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, connect)
                   end},
         #{desc => "await server ready (accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, accept)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "order server to recv test req (recv req)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, recv_req),
                           ok
                   end},
         #{desc => "order client to send test req (send req)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client ready (send_req)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_req)
                   end},
         #{desc => "await server ready (recv_req)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order client to recv test rep (send rep)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, recv_rep),
                           ok
                   end},
         #{desc => "order server to send test rep (send rep)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_rep),
                           ok
                   end},
         #{desc => "await server ready (send_rep)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_rep)
                   end},
         #{desc => "await client ready (recv_rep)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, recv_rep)
                   end},


         %% *** Termination ***
         #{desc => "order client to terminate",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Client),
                           ok
                   end},
         #{desc => "await client termination",
           cmd  => fun(#{client := Client} = State) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client, State),
                           {ok, State1}
                   end},
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start client evaluator"),
    Client = ?SEV_START("client", ClientSeq, InitState),
    i("await evaluator(s)"),

    i("start tester evaluator"),
    TesterInitState = #{server => Server#ev.pid,
                        client => Client#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive on an IPv4 UDP (dgram) socket using
%% sendto and recvfrom. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
%% select message). Note that we only do this for the recvfrom,
%% since its much more difficult to "arrange" for sendto.
%%
api_a_sendto_and_recvfrom_udp4(Config) when is_list(Config) ->
    ?TT(?SECS(5)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Send = fun(Sock, Data, Dest) ->
                                  socket:sendto(Sock, Data, Dest)
                          end,
                   Recv = fun(Sock) ->
                                  socket:recvfrom(Sock, 0, Nowait)
                          end,
                   InitState = #{domain   => inet,
                                 send     => Send,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_send_and_recv_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive on an IPv6 UDP (dgram) socket using
%% sendto and recvfrom. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
%% select message). Note that we only do this for the recvfrom,
%% since its much more difficult to "arrange" for sendto.
%%
api_a_sendto_and_recvfrom_udp6(Config) when is_list(Config) ->
    ?TT(?SECS(5)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Send = fun(Sock, Data, Dest) ->
                                  socket:sendto(Sock, Data, Dest)
                          end,
                   Recv = fun(Sock) ->
                                  socket:recvfrom(Sock, 0, Nowait)
                          end,
                   InitState = #{domain   => inet6,
                                 send     => Send,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_send_and_recv_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive on an IPv4 UDP (dgram) socket using
%% sendto and recvfrom. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
%% select message). Note that we only do this for the recvmsg,
%% since its much more difficult to "arrange" for sendmsg.
%%
api_a_sendmsg_and_recvmsg_udp4(Config) when is_list(Config) ->
    ?TT(?SECS(5)),
    Nowait = nowait(Config),
    tc_try(api_a_sendmsg_and_recvmsg_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Send = fun(Sock, Data, Dest) ->
                                  Msg = #{addr => Dest,
                                             %% ctrl => CMsgs,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock, Nowait) of
                                      {ok, #{addr  := Source,
                                             iov   := [Data]}} ->
                                          {ok, {Source, Data}};
                                      {ok, _} = OK ->
                                          OK;
                                      {select, _} = SELECT ->
                                          SELECT;
                                      {completion, _} = COMPLETION ->
                                          COMPLETION;
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain   => inet,
                                 send     => Send,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_send_and_recv_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive on an IPv6 UDP (dgram) socket using
%% sendto and recvfrom. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
%% select message). Note that we only do this for the recvmsg,
%% since its much more difficult to "arrange" for sendmsg.
%%
api_a_sendmsg_and_recvmsg_udp6(Config) when is_list(Config) ->
    ?TT(?SECS(5)),
    Nowait = nowait(Config),
    tc_try(api_a_sendmsg_and_recvmsg_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Send = fun(Sock, Data, Dest) ->
                                  Msg = #{addr => Dest,
                                             %% ctrl => CMsgs,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock, Nowait) of
                                      {ok, #{addr  := Source,
                                             iov   := [Data]}} ->
                                          {ok, {Source, Data}};
                                      {ok, _} = OK ->
                                          OK;
                                      {select, _} = SELECT ->
                                          SELECT;
                                      {completion, _} = COMPLETION ->
                                          COMPLETION;
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain   => inet6,
                                 send     => Send,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_send_and_recv_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_a_send_and_recv_udp(InitState) ->
    ServerSeq = 
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind socket (to local address)",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   {ok, State#{port => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester, local_sa := LSA, port := Port}) ->
                           ServerSA = LSA#{port => Port},
                           ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (recv)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
                   end},
         #{desc => "try recv request (with nowait, expect select)",
           cmd  => fun(#{sock     := Sock,
                         recv     := Recv,
                         recv_ref := Ref} = State) ->
                           case Recv(Sock) of
                               {select, {select_info, Tag, RecvRef}}
                                 when Ref =:= nowait ->
                                   ?SEV_IPRINT("expected select nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, RecvRef]),
                                   {ok, State#{async_tag => select,
                                               recv_tag  => Tag,
                                               recv_ref  => RecvRef}};
                               {select, {select_info, Tag, Ref}}
                                 when is_reference(Ref) ->
                                   ?SEV_IPRINT("expected select ref: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, Ref]),
                                   {ok, State#{async_tag => select,
                                               recv_tag  => Tag}};

                               {completion, {completion_info, Tag, RecvRef}}
                                 when Ref =:= nowait ->
                                   ?SEV_IPRINT("expected select nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, RecvRef]),
                                   {ok, State#{async_tag => completion,
                                               recv_tag  => Tag,
                                               recv_ref  => RecvRef}};
                               {completion, {completion_info, Tag, Ref}}
                                 when is_reference(Ref) ->
                                   ?SEV_IPRINT("expected completion ref: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, Ref]),
                                   {ok, State#{async_tag => completion,
                                               recv_tag  => Tag}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_select),
                           ok
                   end},
         #{desc => "await select|completion message",
           cmd  => fun(#{async_tag := select,
                         sock      := Sock,
                         recv_ref  := RecvRef}) ->
                           receive
                               {'$socket', Sock, select, RecvRef} ->
                                   ok
                           after 5000 ->
                                   ?SEV_EPRINT("timeout when: "
                                               "~n   Socket Info:   ~p"
                                               "~n   Message Queue: ~p",
                                               [socket:info(Sock), mq()]),
                                   {error, timeout}
                           end;
                      (#{async_tag := completion,
                         sock      := Sock,
                         recv_ref  := RecvRef} = State) ->
                           receive
                               %% Recvfrom
                               {'$socket', Sock, completion,
                                {RecvRef, {ok, {Src, ?BASIC_REQ}}}} ->
                                   {ok, State#{req_src => Src}};
                               %% Recvmsg
                               {'$socket', Sock, completion,
                                {RecvRef, {ok, #{addr := Src,
                                                 iov  := [?BASIC_REQ]}}}} ->
                                   {ok, State#{req_src => Src}};
                               {'$socket', Sock, completion,
                                {RecvRef, {ok, Unexpected}}} ->
                                   ?SEV_EPRINT("Unexpected success result: "
                                               "~n   ~p", [Unexpected]),
                                   {error, {unexpected_success_result,
                                            Unexpected}};
                               {'$socket', Sock, completion,
                                {RecvRef, {error, Reason} = ERROR}} ->
                                   ?SEV_EPRINT("completion with error: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           after 5000 ->
                                   ?SEV_EPRINT("timeout when: "
                                               "~n   Socket Info:   ~p"
                                               "~n   Message Queue: ~p",
                                               [socket:info(Sock), mq()]),
                                   {error, timeout}
                           end
                   end},
         #{desc => "announce ready (select)",
           cmd  => fun(#{tester := Tester}) ->
                           %% We are actually done *if* this was
                           %% a completion event, but to make the
                           %% test case simple...
                           ?SEV_ANNOUNCE_READY(Tester, select),
                           ok
                   end},
         #{desc => "now read the data (request), for select",
           cmd  => fun(#{async_tag := select,
                         sock      := Sock,
                         recv      := Recv} = State) ->
                           case Recv(Sock) of
                               {ok, {Src, ?BASIC_REQ}} ->
                                   {ok, State#{req_src => Src}};
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{async_tag := completion} = _State) ->
                           %% We are already done!
                           ?SEV_IPRINT("Already done!"),
                           ok
                   end},
         #{desc => "announce ready (recv request)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_req),
                           ok
                   end},

         #{desc => "await continue (send reply)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
                   end},
         #{desc => "send reply",
           cmd  => fun(#{sock := Sock, req_src := Src, send := Send}) ->
                           Send(Sock, ?BASIC_REP, Src)
                   end},
         #{desc => "announce ready (send)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send),
                           ok
                   end},

         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   State2 = maps:remove(tester,    State),
                                   State3 = maps:remove(async_tag, State2),
                                   State4 = maps:remove(recv_tag,  State3),
                                   State5 = maps:remove(recv_ref,  State4),
                                   State6 = maps:remove(req_src,   State5),
                                   {ok, State6};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    ClientSeq = 
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(State) ->
                           {Tester, ServerSA} = ?SEV_AWAIT_START(),
                           {ok, State#{tester    => Tester, 
                                       server_sa => ServerSA}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "open socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           Sock = sock_open(Domain, dgram, udp),
                           {ok, State#{sock => Sock}}
                   end},
         #{desc => "bind socket (to local address)",
           cmd  => fun(#{sock := Sock, lsa := LSA}) ->
                          case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "sockname",
           cmd  => fun(#{sock := Sock} = State) ->
                           SA = sock_sockname(Sock),
                           {ok, State#{sa => SA}}
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (send request)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request",
           cmd  => fun(#{sock := Sock, server_sa := Server, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Server)
                   end},
         #{desc => "announce ready (send request)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await continue (recv)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
                   end},
         #{desc => "try recv reply (with nowait)",
           cmd  => fun(#{sock     := Sock,
                         recv     := Recv,
                         recv_ref := Ref} = State) ->
                           case Recv(Sock) of
                               {select, {select_info, Tag, RecvRef}}
                                 when Ref =:= nowait ->
                                   {ok, State#{async_tag => select,
                                               recv_tag  => Tag,
                                               recv_ref  => RecvRef}};
                               {select, {select_info, Tag, Ref}}
                                 when is_reference(Ref) ->
                                   {ok, State#{async_tag => select,
                                               recv_tag  => Tag}};
                               {completion, {completion_info, Tag, RecvRef}}
                                 when Ref =:= nowait ->
                                   {ok, State#{async_tag => completion,
                                               recv_tag  => Tag,
                                               recv_ref  => RecvRef}};
                               {completion, {completion_info, Tag, Ref}}
                                 when is_reference(Ref) ->
                                   {ok, State#{async_tag => completion,
                                               recv_tag  => Tag}};
                               {ok, X} ->
                                   {error, {unexpected_select_info, X}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_select),
                           ok
                   end},
         #{desc => "await select message",
           cmd  => fun(#{async_tag := select,
                         sock      := Sock,
                         recv_ref  := RecvRef}) ->
                           receive
                               {'$socket', Sock, select, RecvRef} ->
                                   ok
                           end;
                      (#{async_tag := completion,
                         sock      := Sock,
                         recv_ref  := RecvRef}) ->
                           receive
                               {'$socket', Sock, completion,
                                {RecvRef, {ok, _}}} ->
                                   ok
                           end
                   end},
         #{desc => "announce ready (select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, select),
                           ok
                   end},
         #{desc => "now read the data (reply)",
           cmd  => fun(#{async_tag := select,
                         sock      := Sock,
                         recv      := Recv}) ->
                           case Recv(Sock) of
                               {ok, {_Src, ?BASIC_REP}} ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{async_tag := completion}) ->
                           ?SEV_IPRINT("Already read!"),
                           ok
                   end},
         #{desc => "announce ready (recv reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_rep),
                           ok
                   end},

         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   State2 = maps:remove(tester,    State),
                                   State3 = maps:remove(async_tag, State2),
                                   State4 = maps:remove(recv_tag,  State3),
                                   State5 = maps:remove(recv_ref,  State4),
                                   {ok, State5};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq = 
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor client",
           cmd  => fun(#{client := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = State) ->
                           {ok, ServerSA} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{server_sa => ServerSA}}
                   end},

         %% Start the client
         #{desc => "order client start",
           cmd  => fun(#{client    := Pid, 
                         server_sa := ServerSA} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, ServerSA),
                           ok
                   end},
         #{desc => "await client ready (init)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, init)
                   end},
 
         %% The actual test
         #{desc => "order server continue (recv)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
                           ok
                   end},
         #{desc => "await server ready (recv_select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, recv_select)
                   end},

         #{desc => "order client continue (send request)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, send_req),
                           ok
                   end},
         #{desc => "await client ready (send request)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, send_req)
                   end},
         #{desc => "await server ready (select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, select)
                   end},
         #{desc => "await server ready (recv request)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, recv_req)
                   end},

         #{desc => "order client continue (recv)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
                           ok
                   end},
         #{desc => "await client ready (recv_select)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, recv_select)
                   end},
         #{desc => "order server continue (send reply)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, send_reply),
                           ok
                   end},
         #{desc => "await server ready (send)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, send)
                   end},
         #{desc => "await client ready (select)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, select)
                   end},
         #{desc => "await client ready (recv reply)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, recv_rep)
                   end},

         %% Terminations
         #{desc => "order client to terminate",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await client termination",
           cmd  => fun(#{client := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(client, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(server, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    ServerInitState = InitState,
    Server = ?SEV_START("server", ServerSeq, ServerInitState),

    i("start client evaluator(s)"),
    ClientInitState = InitState,
    Client = ?SEV_START("client", ClientSeq, ClientInitState),

    i("start 'tester' evaluator"),
    TesterInitState = #{server => Server#ev.pid,
                        client => Client#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator"),
    ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive using the "common" functions (send and recv)
%% on an IPv4 TCP (stream) socket. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
%% select message). Note that we only do this for the recv,
%% since its much more difficult to "arrange" for send.
%% We *also* test async for accept.
api_a_send_and_recv_tcp4(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(api_a_send_and_recv_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  socket:send(Sock, Data)
                          end,
                   Recv = fun(Sock) ->
                                  socket:recv(Sock, 0, Nowait)
                          end,
                   InitState = #{domain => inet,
                                 send => Send,
                                 recv => Recv,
                                 recv_sref => Nowait},
                   ok = api_a_send_and_recv_tcp(Config, InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive using the "common" functions (send and recv)
%% on an IPv6 TCP (stream) socket. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
%% select message). Note that we only do this for the recv,
%% since its much more difficult to "arrange" for send.
%% We *also* test async for accept.
api_a_send_and_recv_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(api_a_send_and_recv_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  socket:send(Sock, Data)
                          end,
                   Recv = fun(Sock) ->
                                  socket:recv(Sock, 0, Nowait)
                          end,
                   InitState = #{domain => inet6,
                                 send => Send,
                                 recv => Recv,
                                 recv_sref => Nowait},
                   ok = api_a_send_and_recv_tcp(Config, InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive using the msg functions (sendmsg and recvmsg)
%% on an IPv4 TCP (stream) socket. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
%% select message). Note that we only do this for the recvmsg,
%% since its much more difficult to "arrange" for sendmsg.
%% We *also* test async for accept.
api_a_sendmsg_and_recvmsg_tcp4(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(api_a_sendmsg_and_recvmsg_tcp4,
           fun() ->
                   is_not_windows(),
                   has_support_ipv4()
           end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  Msg = #{iov => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock, Nowait) of
                                      {ok, #{iov   := [Data]}} ->
                                          {ok, Data};
                                      {select, _} = SELECT ->
                                          SELECT;
				      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 send => Send,
                                 recv => Recv,
                                 recv_sref => Nowait},
                   ok = api_a_send_and_recv_tcp(Config, InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically send and receive using the msg functions (sendmsg and recvmsg)
%% on an IPv6 TCP (stream) socket. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
%% select message). Note that we only do this for the recvmsg,
%% since its much more difficult to "arrange" for sendmsg.
%% We *also* test async for accept.
api_a_sendmsg_and_recvmsg_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(api_a_sendmsg_and_recvmsg_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  Msg = #{iov => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock, Nowait) of
                                      {ok, #{iov   := [Data]}} ->
                                          {ok, Data};
                                      {select, _} = SELECT ->
                                          SELECT;
				      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet6,
                                 send => Send,
                                 recv => Recv,
                                 recv_sref => Nowait},
                   ok = api_a_send_and_recv_tcp(Config, InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_a_send_and_recv_tcp(Config, InitState) ->
    process_flag(trap_exit, true),
    ServerSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester, lport := Port}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (accept)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
                   end},
         #{desc => "await connection (nowait)",
           cmd  => fun(#{lsock := LSock} = State) ->
                           Nowait = nowait(Config),
                           case socket:accept(LSock, Nowait) of
                               {select, {select_info, Tag, Ref}}
                                 when Nowait =:= nowait ->
                                   ?SEV_IPRINT("select accept message: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, Ref]),
                                   {ok, State#{sorc        => select,
                                               accept_stag => Tag,
                                               accept_sref => Ref}};
                               {select, {select_info, Tag, Nowait}}
                                 when is_reference(Nowait) ->
                                   ?SEV_IPRINT("select accept result: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, Nowait]),
                                   {ok, State#{sorc        => select,
                                               accept_stag => Tag,
                                               accept_sref => Nowait}};

                               {completion, {completion_info, Tag, Ref}}
                                 when Nowait =:= nowait ->
                                   ?SEV_IPRINT("completion accept result: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, Ref]),
                                   {ok, State#{sorc        => completion,
                                               accept_stag => Tag,
                                               accept_sref => Ref}};
                               {completion, {completion_info, Tag, Nowait}}
                                 when is_reference(Nowait) ->
                                   ?SEV_IPRINT("completion accept result: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, Nowait]),
                                   {ok, State#{sorc        => completion,
                                               accept_stag => Tag,
                                               accept_sref => Nowait}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (accept_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, accept_select),
                           ok
                   end},
         #{desc => "await select|completion message",
           cmd  => fun(#{lsock := Sock, accept_sref := Ref} = State) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   ?SEV_IPRINT("select message: "
                                               "ready for accept"),
                                   ok;
                               {'$socket', Sock, completion,
                                {Ref, {ok, CSock}}} ->
                                   ?SEV_IPRINT("completion message: accepted: "
                                               "~n   CSock: ~p", [Sock]),
                                   {ok, State#{csock => CSock}}
                           after 5000 ->
                                   ?SEV_EPRINT("select|completion message timeout:"
                                               "~n   Sock:          ~p"
                                               "~n   Ref:           ~p"
                                               "~n   Message Queue: ~p",
                                               [Sock, Ref, mq()]),
                                   {error, timeout}
                           end
                   end},
         #{desc => "announce ready (select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, select),
                           ok
                   end},
         #{desc => "try accept (again)",
           cmd  => fun(#{lsock := LSock, sorc := select} = State) ->
                           ?SEV_IPRINT("try accept again"),
                           case socket:accept(LSock, nowait) of
                               {ok, Sock} ->
                                   ?SEV_IPRINT("accepted: "
                                               "~n   Sock: ~p", [Sock]),
                                   {ok, State#{csock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                       (#{sorc := completion})->
                           ?SEV_IPRINT("already accepted"),
                           ok
                   end},
         #{desc => "announce ready (accept)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, accept),
                           ok
                   end},

         #{desc => "await continue (recv request)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_req)
                   end},
         #{desc => "try recv request (with nowait, expect select)",
           cmd  => fun(#{csock     := Sock,
                         recv      := Recv,
                         recv_sref := SR} = State) ->
                           case Recv(Sock) of
                               {select, {select_info, Tag, Ref}}
                                 when SR =:= nowait ->
                                   ?SEV_IPRINT("recv select nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, Ref]),
                                   {ok, State#{recv_stag => Tag,
                                               recv_sref => Ref}};
                               {select, {select_info, Tag, SR}}
                                 when is_reference(SR) ->
                                   ?SEV_IPRINT("recv select ref: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, SR]),
                                   {ok, State#{recv_stag => Tag}};

                               {completion, {completion_info, Tag, Ref}}
                                 when SR =:= nowait ->
                                   ?SEV_IPRINT("recv completion nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, Ref]),
                                   {ok, State#{recv_stag => Tag,
                                               recv_sref => Ref}};
                               {completion, {completion_info, Tag, SR}}
                                 when is_reference(SR) ->
                                   ?SEV_IPRINT("recv completion ref: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, SR]),
                                   {ok, State#{recv_stag => Tag}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_select),
                           ok
                   end},
         #{desc => "await select|completion message",
           cmd  => fun(#{csock := Sock, recv_sref := RecvRef}) ->
                           receive
                               {'$socket', Sock, select, RecvRef} ->
                                   ok;
                               {'$socket', Sock, completion,
                                {RecvRef, {ok, ?BASIC_REQ}}} ->
                                   ?SEV_IPRINT("received expected data"),
                                   ok;
                               {'$socket', Sock, completion,
                                {RecvRef, {error, Reason} = ERROR}} ->
                                   ?SEV_EPRINT("received unexpected error: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, select),
                           ok
                   end},
         #{desc => "now read the data (request)",
           cmd  => fun(#{sorc  := select,
                         csock := Sock,
                         recv  := Recv} = _State) ->
                           case Recv(Sock) of
                               {ok, ?BASIC_REQ} ->
                                   ?SEV_IPRINT("read expected data"),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{sorc := completion}) ->
                           ?SEV_IPRINT("already received"),
                           ok
                   end},
         #{desc => "announce ready (recv request)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_req),
                           ok
                   end},

         #{desc => "await continue (send reply)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_rep)
                   end},
         #{desc => "send reply",
           cmd  => fun(#{csock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REP)
                   end},
         #{desc => "announce ready (send reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_rep),
                           ok
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close connection socket",
           cmd  => fun(#{csock := Sock}) ->
                           socket:close(Sock)
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{lsock := Sock}) ->
                           socket:close(Sock)
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    ClientSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "await continue (connect)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, connect)
                   end},
         #{desc => "connect to server",
           cmd  => fun(#{sock := Sock, server_sa := SSA}) ->
                           socket:connect(Sock, SSA)
                   end},
         #{desc => "announce ready (connect)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, connect),
                           ok
                   end},

         #{desc => "await continue (send request)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request (to server)",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           ok = Send(Sock, ?BASIC_REQ)
                   end},
         #{desc => "announce ready (send request)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},

         #{desc => "try recv reply (with nowait, expect select|completion)",
           cmd  => fun(#{sock := Sock,
                         recv := Recv,
                         recv_sref := SR} = State) ->
                           case Recv(Sock) of
                               {select, {select_info, Tag, Ref}}
                                 when SR =:= nowait ->
                                   ?SEV_IPRINT("recv select nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, Ref]),
                                   {ok, State#{sorc      => select,
                                               recv_stag => Tag,
                                               recv_sref => Ref}};
                               {select, {select_info, Tag, SR}}
                                 when is_reference(SR) ->
                                   ?SEV_IPRINT("recv select ref: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, SR]),
                                   {ok, State#{sorc      => select,
                                               recv_stag => Tag}};

                               {completion, {completion_info, Tag, Ref}}
                                 when SR =:= nowait ->
                                   ?SEV_IPRINT("recv completion nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, Ref]),
                                   {ok, State#{sorc      => completion,
                                               recv_stag => Tag,
                                               recv_sref => Ref}};
                               {completion, {completion_info, Tag, SR}}
                                 when is_reference(SR) ->
                                   ?SEV_IPRINT("recv completion ref: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [Tag, SR]),
                                   {ok, State#{sorc      => completion,
                                               recv_stag => Tag}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_select),
                           ok
                   end},
         #{desc => "await select message",
           cmd  => fun(#{sock := Sock, recv_sref := RecvRef}) ->
                           receive
                               {'$socket', Sock, select, RecvRef} ->
                                   ok;
                               {'$socket', Sock, completion,
                                {RecvRef, {ok, ?BASIC_REP}}} ->
                                   ?SEV_IPRINT("received expected reply"),
                                   ok
                           end
                   end},
         #{desc => "announce ready (select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, select),
                           ok
                   end},
         #{desc => "now read the data (reply)",
           cmd  => fun(#{sorc := select, sock := Sock, recv := Recv}) ->
                           {ok, ?BASIC_REP} = Recv(Sock),
                           ?SEV_IPRINT("[select] received expected reply"),
                           ok;
                      (#{sorc := completion}) ->
                           ?SEV_IPRINT("[completion] "
                                       "expected reply already received"),
                           ok
                   end},
         #{desc => "announce ready (recv reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_rep),
                           ok
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock}) ->
                           socket:close(Sock)
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor client",
           cmd  => fun(#{client := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = State) ->
                           {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{server_port => Port}}
                   end},

         %% Start the client
         #{desc => "order client start",
           cmd  => fun(#{client := Pid, server_port := Port} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Port),
                           ok
                   end},
         #{desc => "await client ready (init)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, init)
                   end},

         %% *** The actual test ***
         #{desc => "order server to continue (with accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, accept),
                           ok
                   end},
         #{desc => "await server ready (accept select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, accept_select)
                   end},
         #{desc => "order client to continue (with connect)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
                           ok
                   end},
         #{desc => "await server ready (select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, select)
                   end},
         #{desc => "await server ready (accept)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, accept)
                   end},
         #{desc => "await client ready (connect)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client, connect)
                   end},

         #{desc => "order server to continue (recv request)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv_req),
                           ok
                   end},
         #{desc => "await server ready (recv select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, recv_select)
                   end},
         #{desc => "order client to continue (send request)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, send_req),
                           ok
                   end},
         #{desc => "await client ready (send request)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_req)
                   end},
         #{desc => "await server ready (select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, select)
                   end},
         #{desc => "await server ready (recv request)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, recv_req)
                   end},

         #{desc => "order client to continue (recv reply)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv_rep),
                           ok
                   end},
         #{desc => "await client ready (recv select)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client, recv_select)
                   end},
         #{desc => "order server to continue (send reply)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, send_rep),
                           ok
                   end},
         #{desc => "await server ready (send reply)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, send_rep)
                   end},
         #{desc => "await client ready (select)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client, select)
                   end},
         #{desc => "await client ready (reply recv)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, recv_rep)
                   end},


         %% *** Termination ***
         #{desc => "order client to terminate",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Client),
                           ok
                   end},
         #{desc => "await client termination",
           cmd  => fun(#{client := Client} = State) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client, State),
                           {ok, State1}
                   end},
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start client evaluator"),
    Client = ?SEV_START("client", ClientSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{server => Server#ev.pid,
                        client => Client#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).





%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make an async (Timeout = nowait) call to recvfrom,
%% wait some time and then cancel. IPv4
%%
api_a_recvfrom_cancel_udp4(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  case socket:recvfrom(Sock, 0, Nowait) of
                                      {ok, _} = OK ->
                                          OK;
                                      {select, _} = SELECT ->
                                          SELECT;
                                      {completion, _} = COMPLETION ->
                                          COMPLETION;
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain   => inet,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_recv_cancel_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make an async (Timeout = nowait) call to recvfrom,
%% wait some time and then cancel. IPv6
%%
api_a_recvfrom_cancel_udp6(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  case socket:recvfrom(Sock, 0, Nowait) of
                                      {ok, _} = OK ->
                                          OK;
                                      {select, _} = SELECT ->
                                          SELECT;
                                      {completion, _} = COMPLETION ->
                                          COMPLETION;
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain   => inet6,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_recv_cancel_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make an async (Timeout = nowait) call to recvmsg,
%% wait some time and then cancel. IPv4
%%
api_a_recvmsg_cancel_udp4(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock, Nowait) of
                                      {ok, _} = OK ->
                                          OK;
                                      {select, _} = SELECT ->
                                          SELECT;
                                      {completion, _} = COMPLETION ->
                                          COMPLETION;
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain   => inet,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_recv_cancel_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make an async (Timeout = nowait) call to recvmsg,
%% wait some time and then cancel. IPv6
%%
api_a_recvmsg_cancel_udp6(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock, Nowait) of
                                      {ok, _} = OK ->
                                          OK;
                                      {select, _} = SELECT ->
                                          SELECT;
                                      {completion, _} = COMPLETION ->
                                          COMPLETION;
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain   => inet6,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_recv_cancel_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_a_recv_cancel_udp(InitState) ->
    ServerSeq = 
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind socket (to local address)",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   {ok, State#{port => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester, local_sa := LSA, port := Port}) ->
                           ServerSA = LSA#{port => Port},
                           ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (recv)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
                   end},
         #{desc => "try recv request (with nowait, expect select|completion)",
           cmd  => fun(#{sock     := Sock,
                         recv     := Recv,
                         recv_ref := Ref} = State) ->
                           case Recv(Sock) of
                               {select, SI} when Ref =:= nowait ->
                                   {ok, State#{recv_select_info => SI}};
                               {select,
                                {select_info, _Tag, Ref} = SI}
                                 when is_reference(Ref) ->
                                   {ok, State#{recv_select_info => SI}};

                               {completion, CI} when Ref =:= nowait ->
                                   {ok, State#{recv_completion_info => CI}};
                               {completion,
                                {completion_info, _Tag, Ref} = CI}
                                 when is_reference(Ref) ->
                                   {ok, State#{recv_completion_info => CI}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_select),
                           ok
                   end},
         #{desc => "wait for select message - without success",
           cmd  => fun(#{sock := Sock}) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   {error, {unexpected_select, Ref}};

                               {'$socket', Sock, completion, C} ->
                                   {error, {unexpected_completion, C}}

                           after 5000 ->
                                   ok
                           end
                   end},
         #{desc => "announce ready (no select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, no_select),
                           ok
                   end},
         #{desc => "await continue (cancel)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, cancel)
                   end},
         #{desc => "cancel",
           cmd  => fun(#{sock := Sock, recv_select_info := SI}) ->
                           ok = socket:cancel(Sock, SI);
                      (#{sock := Sock, recv_completion_info := CI}) ->
                           ok = socket:cancel(Sock, CI)
                   end},

         #{desc => "announce ready (cancel)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, cancel),
                           ok
                   end},

         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   State2 = maps:remove(tester,   State),
                                   State3 = maps:remove(recv_ref, State2),
                                   State4 = maps:remove(req_src,  State3),
                                   {ok, State4};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq = 
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = State) ->
                           {ok, ServerSA} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{server_sa => ServerSA}}
                   end},

         %% The actual test
         #{desc => "order server continue (recv)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
                           ok
                   end},
         #{desc => "await server ready (recv select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, recv_select)
                   end},
         #{desc => "await server ready (no select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, no_select)
                   end},
         #{desc => "order server continue (cancel)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, cancel),
                           ok
                   end},
         #{desc => "await server ready (cancel)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, cancel)
                   end},

         %% Terminations
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(server, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    ServerInitState = InitState,
    Server = ?SEV_START("server", ServerSeq, ServerInitState),

    i("start 'tester' evaluator"),
    TesterInitState = #{server => Server#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator"),
    ok = ?SEV_AWAIT_FINISH([Server, Tester]).





%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make an async (Timeout = nowait) call to accept,
%% wait some time and then cancel. IPv4
%%
api_a_accept_cancel_tcp4(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Accept = fun(Sock) ->
                                    case socket:accept(Sock, Nowait) of
                                        {ok, _} = OK ->
                                            OK;
                                        {select, _} = SELECT ->
                                            SELECT;
                                        {completion, _} = COMPLETION ->
                                            COMPLETION;
                                        {error, _} = ERROR ->
                                            ERROR
                                    end
                            end,
                   InitState = #{domain     => inet,
                                 accept     => Accept,
                                 accept_ref => Nowait},
                   ok = api_a_accept_cancel_tcp(InitState)
           end).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make an async (Timeout = nowait) call to accept,
%% wait some time and then cancel. IPv6
%%
api_a_accept_cancel_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Accept = fun(Sock) ->
                                    case socket:accept(Sock, Nowait) of
                                        {ok, _} = OK ->
                                            OK;
                                        {select, _} = SELECT ->
                                            SELECT;
                                        {completion, _} = COMPLETION ->
                                            COMPLETION;
                                        {error, _} = ERROR ->
                                            ERROR
                                    end
                            end,
                   InitState = #{domain     => inet6,
                                 accept     => Accept,
                                 accept_ref => Nowait},
                   ok = api_a_accept_cancel_tcp(InitState)
           end).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_a_accept_cancel_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester, lport := Port}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (accept)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
                   end},
         #{desc => "await connection (nowait)",
           cmd  => fun(#{lsock      := LSock,
                         accept     := Accept,
                         accept_ref := Ref} = State) ->
                           case Accept(LSock) of
                               {select, {select_info, T, R} = SI}
                                 when Ref =:= nowait ->
                                   ?SEV_IPRINT("accept select nowait: "
                                               "~n   T: ~p"
                                               "~n   R: ~p", [T, R]),
                                   {ok, State#{accept_select_info => SI}};
                               {select, {select_info, T, Ref} = SI}
                                 when is_reference(Ref) ->
                                   ?SEV_IPRINT("accept select ref: "
                                               "~n   T: ~p"
                                               "~n   R: ~p", [T, Ref]),
                                   {ok, State#{accept_select_info => SI}};

                               {completion, {completion_info, T, R} = CI}
                                 when Ref =:= nowait ->
                                   ?SEV_IPRINT("accept completion nowait: "
                                               "~n   T: ~p"
                                               "~n   R: ~p", [T, R]),
                                   {ok, State#{accept_completion_info => CI}};
                               {completion, {completion_info, T, Ref} = CI}
                                 when is_reference(Ref) ->
                                   ?SEV_IPRINT("accept completion ref: "
                                               "~n   T: ~p"
                                               "~n   R: ~p", [T, Ref]),
                                   {ok, State#{accept_completion_info => CI}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (accept_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, accept_select),
                           ok
                   end},
         #{desc => "await select message (without success)",
           cmd  => fun(#{lsock := Sock}) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   {error, {unexpected_select, Ref}};

                               {'$socket', Sock, completion, C} ->
                                   {error, {unexpected_completion, C}}

                           after 5000 ->
                                   ok
                           end
                   end},
         #{desc => "announce ready (no select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, no_select),
                           ok
                   end},
         #{desc => "await continue (cancel)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, cancel)
                   end},
         #{desc => "cancel",
           cmd  => fun(#{lsock              := Sock,
                         accept_select_info := SelectInfo}) ->
                           ok = socket:cancel(Sock, SelectInfo);
                      (#{lsock                  := Sock,
                         accept_completion_info := CompletionInfo}) ->
                           ok = socket:cancel(Sock, CompletionInfo)
                   end},
         #{desc => "announce ready (cancel)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, cancel),
                           ok
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{lsock := Sock}) ->
                           socket:close(Sock)
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = State) ->
                           {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{server_port => Port}}
                   end},

         %% *** The actual test ***
         #{desc => "order server to continue (with accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, accept),
                           ok
                   end},
         #{desc => "await server ready (accept select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, accept_select)
                   end},
         #{desc => "await server ready (no select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, no_select)
                   end},
         #{desc => "order server to continue (cancel)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, cancel),
                           ok
                   end},
         #{desc => "await server ready (cancel)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, cancel)
                   end},

         %% *** Termination ***
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{server => Server#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Server, Tester]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make an async (Timeout = nowait) call to recv,
%% wait some time and then cancel. IPv4
%%
api_a_recv_cancel_tcp4(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  socket:recv(Sock, 0, Nowait)
                          end,
                   InitState = #{domain   => inet,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_recv_cancel_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make an async (Timeout = nowait) call to recv,
%% wait some time and then cancel. IPv6
%%
api_a_recv_cancel_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  socket:recv(Sock, 0, Nowait)
                          end,
                   InitState = #{domain   => inet6,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_recv_cancel_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make an async (Timeout = nowait) call to recvmsg,
%% wait some time and then cancel. IPv4
%%
api_a_recvmsg_cancel_tcp4(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   is_not_windows(),
                   has_support_ipv4()
           end,
           fun() ->
                   Recv = fun(Sock) ->
                                  socket:recvmsg(Sock, Nowait)
                          end,
                   InitState = #{domain   => inet,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_recv_cancel_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make an async (Timeout = nowait) call to recvmsg,
%% wait some time and then cancel. IPv6
%%
api_a_recvmsg_cancel_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   is_not_windows(),
                   has_support_ipv6()
           end,
           fun() ->
                   Recv = fun(Sock) ->
                                  socket:recvmsg(Sock, Nowait)
                          end,
                   InitState = #{domain   => inet6,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_recv_cancel_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_a_recv_cancel_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {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, CSock} ->
                                   {ok, State#{csock => CSock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (accept)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, accept),
                           ok
                   end},

         #{desc => "await continue (nowait recv)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
                   end},

         #{desc => "try recv request (with nowait, expect select|completion)",
           cmd  => fun(#{csock    := Sock,
                         recv     := Recv,
                         recv_ref := Ref} = State) ->
                           case Recv(Sock) of
                               {select, {select_info, T, R} = SI}
                                 when Ref =:= nowait ->
                                   ?SEV_IPRINT("recv select nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [T, R]),
                                   {ok, State#{recv_select_info => SI}};
                               {select, {select_info, T, Ref} = SI}
                                 when is_reference(Ref) ->
                                   ?SEV_IPRINT("recv select ref: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [T, Ref]),
                                   {ok, State#{recv_select_info => SI}};

                               {completion,
                                {completion_info, T, R} = CI}
                                 when Ref =:= nowait ->
                                   ?SEV_IPRINT("recv completion nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [T, R]),
                                   {ok, State#{recv_completion_info => CI}};
                               {completion,
                                {completion_info, T, Ref} = CI}
                                 when is_reference(Ref) ->
                                   ?SEV_IPRINT("recv completion ref: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [T, Ref]),
                                   {ok, State#{recv_completion_info => CI}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_select),
                           ok
                   end},
         #{desc => "await select message",
           cmd  => fun(#{csock := Sock}) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   {error, {unexpected_select, Ref}};

                               {'$socket', Sock, completion, C} ->
                                   {error, {unexpected_completion, C}}

                           after 5000 ->
                                   ok
                           end
                   end},
         #{desc => "announce ready (no select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, no_select),
                           ok
                   end},
         #{desc => "await continue (cancel)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, cancel)
                   end},
         #{desc => "cancel",
           cmd  => fun(#{csock := Sock, recv_select_info := SI}) ->
                           ok = socket:cancel(Sock, SI);

                      (#{csock := Sock, recv_completion_info := CI}) ->
                           ok = socket:cancel(Sock, CI)
                   end},
         #{desc => "announce ready (cancel)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, cancel),
                           ok
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close connection socket",
           cmd  => fun(#{csock := Sock}) ->
                           socket:close(Sock)
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{lsock := Sock}) ->
                           socket:close(Sock)
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    ClientSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "await continue (connect)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, connect)
                   end},
         #{desc => "connect to server",
           cmd  => fun(#{sock := Sock, server_sa := SSA}) ->
                           socket:connect(Sock, SSA)
                   end},
         #{desc => "announce ready (connect)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, connect),
                           ok
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock}) ->
                           socket:close(Sock)
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor client",
           cmd  => fun(#{client := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = State) ->
                           {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{server_port => Port}}
                   end},

         %% Start the client
         #{desc => "order client start",
           cmd  => fun(#{client := Pid, server_port := Port} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Port),
                           ok
                   end},
         #{desc => "await client ready (init)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, init)
                   end},

         %% *** The actual test ***
         #{desc => "order server to continue (with accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, accept),
                           ok
                   end},
         #{desc => "order client to continue (connect)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
                           ok
                   end},
         #{desc => "await client ready (connect)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client, connect)
                   end},
         #{desc => "await server ready (accept)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, accept)
                   end},

         #{desc => "order server to continue (recv)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
                           ok
                   end},
         #{desc => "await server ready (recv select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, recv_select)
                   end},
         #{desc => "await server ready (no select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, no_select)
                   end},
         #{desc => "order server to continue (send request)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, cancel),
                           ok
                   end},
         #{desc => "await server ready (cancel)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, server, cancel)
                   end},

         %% *** Termination ***
         #{desc => "order client to terminate",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Client),
                           ok
                   end},
         #{desc => "await client termination",
           cmd  => fun(#{client := Client} = State) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client, State),
                           {ok, State1}
                   end},
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start client evaluator"),
    Client = ?SEV_START("client", ClientSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{server => Server#ev.pid,
                        client => Client#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make multiple async (Timeout = nowait) call(s) to recvfrom
%% (from *several* processes), wait some time and then cancel.
%% This should result in abort messages to the 'other' processes. IPv4
%%
api_a_mrecvfrom_cancel_udp4(Config) when is_list(Config) ->
    ?TT(?SECS(20)),
    Nowait = nowait(Config),
    tc_try(api_a_mrecvfrom_cancel_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  case socket:recvfrom(Sock, 0, Nowait) of
                                      {ok, _} = OK ->
                                          OK;
                                      {select, _} = SELECT ->
                                          SELECT;
                                      {completion, _} = COMPLETION ->
                                          COMPLETION;
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain   => inet,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_mrecv_cancel_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make multiple async (Timeout = nowait) call(s) to recvfrom
%% (from *several* processes), wait some time and then cancel.
%% This should result in abort messages to the 'other' processes. IPv6
%%
api_a_mrecvfrom_cancel_udp6(Config) when is_list(Config) ->
    ?TT(?SECS(20)),
    Nowait = nowait(Config),
    tc_try(api_a_mrecvfrom_cancel_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  case socket:recvfrom(Sock, 0, Nowait) of
                                      {ok, _} = OK ->
                                          OK;
                                      {select, _} = SELECT ->
                                          SELECT;
                                      {completion, _} = COMPLETION ->
                                          COMPLETION;
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain   => inet6,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_mrecv_cancel_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg
%% (from *several* processes), wait some time and then cancel.
%% This should result in abort messages to the 'other' processes. IPv4
%%
api_a_mrecvmsg_cancel_udp4(Config) when is_list(Config) ->
    ?TT(?SECS(20)),
    Nowait = nowait(Config),
    tc_try(api_a_mrecvmsg_cancel_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock, Nowait) of
                                      {ok, _} = OK ->
                                          OK;
                                      {select, _} = SELECT ->
                                          SELECT;
                                      {completion, _} = COMPLETION ->
                                          COMPLETION;
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain   => inet,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_mrecv_cancel_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg
%% (from *several* processes), wait some time and then cancel.
%% This should result in abort messages to the 'other' processes. IPv6
%%
api_a_mrecvmsg_cancel_udp6(Config) when is_list(Config) ->
    ?TT(?SECS(20)),
    Nowait = nowait(Config),
    tc_try(api_a_mrecvmsg_cancel_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock, Nowait) of
                                      {ok, _} = OK ->
                                          OK;
                                      {select, _} = SELECT ->
                                          SELECT;
                                      {completion, _} = COMPLETION ->
                                          COMPLETION;
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain   => inet6,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_mrecv_cancel_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_a_mrecv_cancel_udp(InitState) ->
    ServerSeq = 
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind socket (to local address)",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   {ok, State#{port => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester, sock := Sock}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Sock),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (recv)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
                   end},
         #{desc => "try recv request (with nowait, expect select)",
           cmd  => fun(#{sock     := Sock,
                         recv     := Recv,
                         recv_ref := Ref} = State) ->
                           case Recv(Sock) of
                               {select, SI}
                                 when Ref =:= nowait ->
                                   {ok, State#{recv_select_info => SI}};
                               {select, {select_info, _Tag, Ref} = SI}
                                 when is_reference(Ref) ->
                                   {ok, State#{recv_select_info => SI}};

                               {completion, CI}
                                 when Ref =:= nowait ->
                                   {ok, State#{recv_completion_info => CI}};
                               {completion, {completion_info, _Tag, Ref} = CI}
                                 when is_reference(Ref) ->
                                   {ok, State#{recv_completion_info => CI}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_select),
                           ok
                   end},
         #{desc => "await abort message",
           cmd  => fun(#{sock             := Sock,
                         recv_select_info := {select_info, _, Ref}} =
                           State) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   {error, {unexpected_select, Ref}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(sock, State)}
                           after 5000 ->
                                   ?SEV_EPRINT("message queue: ~p", [mq()]),
                                   {error, timeout}
                           end;
                      (#{sock                 := Sock,
                         recv_completion_info := {completion_info, _, Ref}} = 
                           State) ->
                           receive
                               {'$socket', Sock, completion, {Ref, CS}} ->
                                   {error, {unexpected_completion, CS}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(sock, State)}
                           after 5000 ->
                                   ?SEV_EPRINT("message queue: ~p", [mq()]),
                                   {error, timeout}
                           end
                   end},
         #{desc => "announce ready (abort)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, abort),
                           ok
                   end},

         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   State2 = maps:remove(tester,   State),
                                   State3 = maps:remove(recv_ref, State2),
                                   {ok, State3};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    AltServerSeq = 
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(State) ->
                           {Tester, Sock} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, sock => Sock}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (recv)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
                   end},
         #{desc => "try recv request (with nowait, expect select)",
           cmd  => fun(#{sock     := Sock,
                         recv     := Recv,
                         recv_ref := Ref} = State) ->
                           case Recv(Sock) of
                               {select, SI} when Ref =:= nowait ->
                                   {ok, State#{recv_select_info => SI}};
                               {select,
                                {select_info, _Tag, Ref} = SI}
                                 when is_reference(Ref) ->
                                   {ok, State#{recv_select_info => SI}};

                               {completion, CI} when Ref =:= nowait ->
                                   {ok, State#{recv_completion_info => CI}};
                               {completion,
                                {completion_info, _Tag, Ref} = CI}
                                 when is_reference(Ref) ->
                                   {ok, State#{recv_completion_info => CI}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_select),
                           ok
                   end},
         #{desc => "await abort message",
           cmd  => fun(#{sock             := Sock,
                         recv_select_info := {select_info, _, Ref}} = State) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   {error, {unexpected_select, Ref}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(sock, State)}
                           after 5000 ->
                                   ?SEV_EPRINT("message queue: ~p", [mq()]),
                                   {error, timeout}
                           end;

                      (#{sock                 := Sock,
                         recv_completion_info := {completion_info, _, Ref}} =
                           State) ->
                           receive
                               {'$socket', Sock, completion, {Ref, CS}} ->
                                   {error, {unexpected_completion, CS}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(sock, State)}
                           after 5000 ->
                                   ?SEV_EPRINT("message queue: ~p", [mq()]),
                                   {error, timeout}
                           end
                   end},
         #{desc => "announce ready (abort)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, abort),
                           ok
                   end},

         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   ?SEV_IPRINT("terminating"),
                                   State1 = maps:remove(recv_select_info, State),
                                   State2 = maps:remove(recv_completion_info, State1),
                                   State3 = maps:remove(tester,           State2),
                                   State4 = maps:remove(sock,             State3),
                                   {ok, State4};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],



    TesterSeq = 
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor alt-server 1",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor alt-server 2",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = State) ->
                           {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{sock => Sock}}
                   end},

         %% Start the alt-server 1
         #{desc => "order alt-server 1 start",
           cmd  => fun(#{alt_server1 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Sock),
                           ok
                   end},
         #{desc => "await alt-server 1 ready (init)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, alt_server1, init)
                   end},

         %% Start the alt-server 2
         #{desc => "order alt-server 2 start",
           cmd  => fun(#{alt_server2 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Sock),
                           ok
                   end},
         #{desc => "await alt-server 2 ready (init)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, alt_server2, init)
                   end},


         %% The actual test
         #{desc => "order server continue (recv)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
                           ok
                   end},
         #{desc => "await server ready (recv select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, recv_select)
                   end},

         #{desc => "order alt-server 1 continue (recv)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
                           ok
                   end},
         #{desc => "await alt-server 1 ready (recv select)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server1, recv_select)
                   end},

         #{desc => "order alt-server 2 continue (recv)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
                           ok
                   end},
         #{desc => "await alt-server 2 ready (recv select)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server2, recv_select)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "close the socket",
           cmd  => fun(#{sock := Sock} = _State) ->
                           socket:close(Sock)
                   end},

         #{desc => "await server ready (abort)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, abort)
                   end},
         #{desc => "await alt-server 1 ready (abort)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort)
                   end},
         #{desc => "await alt-server 2 ready (abort)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort)
                   end},

         %% Terminations
         #{desc => "order alt-server 2 to terminate",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await alt-server 2 termination",
           cmd  => fun(#{alt_server2 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(alt_server2, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order alt-server 1 to terminate",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await alt-server 1 termination",
           cmd  => fun(#{alt_server1 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(alt_server1, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order server to terminate",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(server, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start alt-server 1 evaluator"),
    AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState),

    i("start alt-server 2 evaluator"),
    AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState),

    i("start 'tester' evaluator"),
    TesterInitState = #{server      => Server#ev.pid,
                        alt_server1 => AltServer1#ev.pid,
                        alt_server2 => AltServer2#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Tester]).






%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make multiple async (Timeout = nowait) call(s) to accept
%% (from *several* processes), wait some time and then cancel,
%% This should result in abort messages to the 'other' processes. IPv4
%%
api_a_maccept_cancel_tcp4(Config) when is_list(Config) ->
    ?TT(?SECS(20)),
    Nowait = nowait(Config),
    tc_try(api_a_maccept_cancel_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Accept = fun(Sock) ->
                                    case socket:accept(Sock, Nowait) of
                                        {ok, _} = OK ->
                                            OK;
                                        {select, _} = SELECT ->
                                            SELECT;
                                        {completion, _} = COMPLETION ->
                                            COMPLETION;
                                        {error, _} = ERROR ->
                                            ERROR
                                    end
                            end,
                   InitState = #{domain     => inet,
                                 accept     => Accept,
                                 accept_ref => Nowait},
                   ok = api_a_maccept_cancel_tcp(InitState)
           end).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make multiple async (Timeout = nowait) call(s) to accept
%% (from *several* processes), wait some time and then cancel,
%% This should result in abort messages to the 'other' processes. IPv6
%%
api_a_maccept_cancel_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(20)),
    Nowait = nowait(Config),
    tc_try(api_a_maccept_cancel_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Accept = fun(Sock) ->
                                    case socket:accept(Sock, Nowait) of
                                        {ok, _} = OK ->
                                            OK;
                                        {select, _} = SELECT ->
                                            SELECT;
                                        {completion, _} = COMPLETION ->
                                            COMPLETION;
                                        {error, _} = ERROR ->
                                            ERROR
                                    end
                            end,
                   InitState = #{domain     => inet6,
                                 accept     => Accept,
                                 accept_ref => Nowait},
                   ok = api_a_maccept_cancel_tcp(InitState)
           end).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_a_maccept_cancel_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester, lsock := Sock}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Sock),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (accept)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
                   end},
         #{desc => "await connection (nowait)",
           cmd  => fun(#{lsock      := LSock,
                         accept     := Accept,
                         accept_ref := Ref} = State) ->
                           case Accept(LSock) of
                               {select, {select_info, T, R} = SI}
                                 when Ref =:= nowait ->
                                   ?SEV_IPRINT("accept select nowait: "
                                               "~n   T: ~p"
                                               "~n   R: ~p", [T, R]),
                                   {ok, State#{accept_select_info => SI}};
                               {select, {select_info, T, Ref} = SI}
                                 when is_reference(Ref) ->
                                   ?SEV_IPRINT("accept select ref: "
                                               "~n   T: ~p"
                                               "~n   R: ~p", [T, Ref]),
                                   {ok, State#{accept_select_info => SI}};

                               {completion, {completion_info, T, R} = CI}
                                 when Ref =:= nowait ->
                                   ?SEV_IPRINT("accept completion nowait: "
                                               "~n   T: ~p"
                                               "~n   R: ~p", [T, R]),
                                   {ok, State#{accept_completion_info => CI}};
                               {completion, {completion_info, T, Ref} = CI}
                                 when is_reference(Ref) ->
                                   ?SEV_IPRINT("accept completion ref: "
                                               "~n   T: ~p"
                                               "~n   R: ~p", [T, Ref]),
                                   {ok, State#{accept_completion_info => CI}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (accept_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, accept_select),
                           ok
                   end},
         #{desc => "await select message (without success)",
           cmd  => fun(#{lsock              := Sock,
                         accept_select_info := {select_info, _, Ref}} = State) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   {error, {unexpected_select, Ref}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(lsock, State)}
                           after 5000 ->
                                   ?SEV_EPRINT("timeout when: "
                                               "~n   Sock:          ~p"
                                               "~n   Ref:           ~p"
                                               "~n   message queue: ~p",
                                               [Sock, Ref, mq()]),
                                   {error, timeout}
                           end;
                      (#{lsock                  := Sock,
                         accept_completion_info := {completion_info, _, Ref}} = State) ->
                           receive
                               {'$socket', Sock, completion, {Ref, _} = C} ->
                                   {error, {unexpected_completion, C}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(lsock, State)}
                           after 5000 ->
                                   ?SEV_EPRINT("timeout when: "
                                               "~n   Sock:          ~p"
                                               "~n   Ref:           ~p"
                                               "~n   message queue: ~p",
                                               [Sock, Ref, mq()]),
                                   {error, timeout}
                           end
                   end},
         #{desc => "announce ready (abort)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, abort),
                           ok
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    AltServerSeq =
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(State) ->
                           {Tester, Sock} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, lsock => Sock}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (accept)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
                   end},
         #{desc => "try accept request (with nowait, expect select)",
           cmd  => fun(#{lsock      := Sock,
                         accept     := Accept,
                         accept_ref := Ref} = State) ->
                           case Accept(Sock) of
                               {select, SI} when Ref =:= nowait ->
                                   {ok, State#{accept_select_info => SI}};
                               {select, {select_info, _Tag, Ref} = SI}
                                 when is_reference(Ref) ->
                                   {ok, State#{accept_select_info => SI}};

                               {completion, CI} when Ref =:= nowait ->
                                   {ok, State#{accept_completion_info => CI}};
                               {completion, {completion_info, _Tag, Ref} = CI}
                                 when is_reference(Ref) ->
                                   {ok, State#{accept_completion_info => CI}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (accept_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, accept_select),
                           ok
                   end},
         #{desc => "await abort message",
           cmd  => fun(#{lsock              := Sock,
                         accept_select_info := {select_info, _, Ref}} = State) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   {error, {unexpected_select, Ref}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(sock, State)}
                           after 5000 ->
                                   ?SEV_EPRINT("timeout when: "
                                               "~n   Sock:          ~p"
                                               "~n   Ref:           ~p"
                                               "~n   message queue: ~p",
                                               [Sock, Ref, mq()]),
                                   {error, timeout}
                           end;
                      (#{lsock              := Sock,
                         accept_completion_info := {completion_info, _, Ref}} = State) ->
                           receive
                               {'$socket', Sock, completion, {Ref, _} = C} ->
                                   {error, {unexpected_completion, C}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(sock, State)}
                           after 5000 ->
                                   ?SEV_EPRINT("timeout when: "
                                               "~n   Sock:          ~p"
                                               "~n   Ref:           ~p"
                                               "~n   message queue: ~p",
                                               [Sock, Ref, mq()]),
                                   {error, timeout}
                           end
                   end},
         #{desc => "announce ready (abort)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, abort),
                           ok
                   end},

         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   ?SEV_IPRINT("terminating"),
                                   State1 = maps:remove(tester,             State),
                                   State2 = maps:remove(accept_select_info, State1),
                                   State3 = maps:remove(accept_completion_info, State2),
                                   State4 = maps:remove(lsock,              State3),
                                   {ok, State4};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor alt-server 1",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor alt-server 2",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = State) ->
                           {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{sock => Sock}}
                   end},

         %% Start the alt-server 1
         #{desc => "order alt-server 1 start",
           cmd  => fun(#{alt_server1 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Sock),
                           ok
                   end},
         #{desc => "await alt-server 1 ready (init)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, alt_server1, init)
                   end},

         %% Start the alt-server 2
         #{desc => "order alt-server 2 start",
           cmd  => fun(#{alt_server2 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Sock),
                           ok
                   end},
         #{desc => "await alt-server 2 ready (init)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, alt_server2, init)
                   end},


         %% *** The actual test ***
         #{desc => "order server continue (accept)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
                           ok
                   end},
         #{desc => "await server ready (accept select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, accept_select)
                   end},

         #{desc => "order alt-server 1 continue (accept)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
                           ok
                   end},
         #{desc => "await alt-server 1 ready (accept select)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server1, accept_select)
                   end},

         #{desc => "order alt-server 2 continue (accept)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, accept),
                           ok
                   end},
         #{desc => "await alt-server 2 ready (accept select)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server2, accept_select)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "close the socket",
           cmd  => fun(#{sock := Sock} = _State) ->
                           socket:close(Sock)
                   end},

         #{desc => "await server ready (abort)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, abort)
                   end},
         #{desc => "await alt-server 1 ready (abort)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort)
                   end},
         #{desc => "await alt-server 2 ready (abort)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort)
                   end},


         %% *** Termination ***
         #{desc => "order alt-server 2 to terminate",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await alt-server 2 termination",
           cmd  => fun(#{alt_server2 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(alt_server2, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order alt-server 1 to terminate",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await alt-server 1 termination",
           cmd  => fun(#{alt_server1 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(alt_server1, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start alt-server 1 evaluator"),
    AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState),

    i("start alt-server 2 evaluator"),
    AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{server      => Server#ev.pid,
                        alt_server1 => AltServer1#ev.pid,
                        alt_server2 => AltServer2#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Tester]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make multiple async (Timeout = nowait) call(s) to recv
%% (from *several* processes), wait some time and then cancel,
%% This should result in abort messages to the 'other' processes. IPv4
%%
api_a_mrecv_cancel_tcp4(Config) when is_list(Config) ->
    ?TT(?SECS(20)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  socket:recv(Sock, 0, Nowait)
                          end,
                   InitState = #{domain   => inet,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_mrecv_cancel_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make multiple async (Timeout = nowait) call(s) to recv
%% (from *several* processes), wait some time and then cancel,
%% This should result in abort messages to the 'other' processes. IPv6
%%
api_a_mrecv_cancel_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(20)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv = fun(Sock) ->
                                  socket:recv(Sock, 0, Nowait)
                          end,
                   InitState = #{domain   => inet6,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_mrecv_cancel_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg
%% (from *several* processes), wait some time and then cancel,
%% This should result in abort messages to the 'other' processes. IPv4
%%
api_a_mrecvmsg_cancel_tcp4(Config) when is_list(Config) ->
    ?TT(?SECS(20)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   is_not_windows(),
                   has_support_ipv4()
           end,
           fun() ->
                   Recv = fun(Sock) ->
                                  socket:recvmsg(Sock, Nowait)
                          end,
                   InitState = #{domain   => inet,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_mrecv_cancel_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg
%% (from *several* processes), wait some time and then cancel,
%% This should result in abort messages to the 'other' processes. IPv6
%%
api_a_mrecvmsg_cancel_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(20)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   is_not_windows(),
                   has_support_ipv6()
           end,
           fun() ->
                   Recv = fun(Sock) ->
                                  socket:recvmsg(Sock, Nowait)
                          end,
                   InitState = #{domain   => inet6,
                                 recv     => Recv,
                                 recv_ref => Nowait},
                   ok = api_a_mrecv_cancel_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_a_mrecv_cancel_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {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, CSock} ->
                                   {ok, State#{csock => CSock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (accept)",
           cmd  => fun(#{tester := Tester, csock := Sock}) ->
                           ?SEV_ANNOUNCE_READY(Tester, accept, Sock),
                           ok
                   end},

         #{desc => "await continue (nowait recv)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
                   end},
         #{desc => "try recv request (with nowait, expect select|completion)",
           cmd  => fun(#{csock    := Sock,
                         recv     := Recv,
                         recv_ref := Ref} = State) ->
                           case Recv(Sock) of
                               {select, {select_info, T, R} = SI}
                                 when Ref =:= nowait ->
                                   ?SEV_IPRINT("recv select nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [T, R]),
                                   {ok, State#{recv_select_info => SI}};
                               {select, {select_info, T, Ref} = SI}
                                 when is_reference(Ref) ->
                                   ?SEV_IPRINT("recv select nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [T, Ref]),
                                   {ok, State#{recv_select_info => SI}};

                               {completion, {completion_info, T, R} = CI}
                                 when Ref =:= nowait ->
                                   ?SEV_IPRINT("recv completion nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [T, R]),
                                   {ok, State#{recv_completion_info => CI}};
                               {completion, {completion_info, T, Ref} = CI}
                                 when is_reference(Ref) ->
                                   ?SEV_IPRINT("recv completion nowait: "
                                               "~n   Tag: ~p"
                                               "~n   Ref: ~p", [T, Ref]),
                                   {ok, State#{recv_completion_info => CI}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_select),
                           ok
                   end},
         #{desc => "await select|completion message",
           cmd  => fun(#{csock            := Sock,
                         recv_select_info := {select_info, _, Ref}} = State) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   {error, {unexpected_select, Ref}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(sock, State)}
                           after 5000 ->
                                   ok
                           end;
                      (#{csock                := Sock,
                         recv_completion_info := {completion_info, _, Ref}} = State) ->
                           receive
                               {'$socket', Sock, completion, {Ref, _} = C} ->
                                   {error, {unexpected_completion, C}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(sock, State)}
                           after 5000 ->
                                   ok
                           end
                   end},
         #{desc => "announce ready (abort)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, abort),
                           ok
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{lsock := Sock}) ->
                           socket:close(Sock)
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    AltServerSeq =
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(State) ->
                           {Tester, Sock} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, sock => Sock}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (recv)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
                   end},
         #{desc => "try recv request (with nowait, expect select)",
           cmd  => fun(#{sock     := Sock,
                         recv     := Recv,
                         recv_ref := Ref} = State) ->
                           case Recv(Sock) of
                               {select, SI} when Ref =:= nowait ->
                                   {ok, State#{recv_select_info => SI}};
                               {select, {select_info, _Tag, Ref} = SI}
                                 when is_reference(Ref) ->
                                   {ok, State#{recv_select_info => SI}};

                               {completion, CI} when Ref =:= nowait ->
                                   {ok, State#{recv_completion_info => CI}};
                               {completion, {completion_info, _Tag, Ref} = CI}
                                 when is_reference(Ref) ->
                                   {ok, State#{recv_completion_info => CI}};

                               {ok, X} ->
                                   {error, {unexpected_success, X}};

                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv_select)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_select),
                           ok
                   end},
         #{desc => "await abort message",
           cmd  => fun(#{sock             := Sock,
                         recv_select_info := {select_info, _, Ref}} = State) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   {error, {unexpected_select, Ref}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(sock, State)}
                           after 5000 ->
                                   ?SEV_EPRINT("message queue: ~p", [mq()]),
                                   {error, timeout}
                           end;
                      (#{sock             := Sock,
                         recv_completion_info := {completion_info, _, Ref}} = State) ->
                           receive
                               {'$socket', Sock, completion, {Ref, _} = C} ->
                                   {error, {unexpected_completion, C}};
                               {'$socket', Sock, abort, {Ref, closed}} ->
                                   {ok, maps:remove(sock, State)}
                           after 5000 ->
                                   ?SEV_EPRINT("message queue: ~p", [mq()]),
                                   {error, timeout}
                           end
                   end},
         #{desc => "announce ready (abort)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, abort),
                           ok
                   end},

         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   ?SEV_IPRINT("terminating"),
                                   State1 = maps:remove(recv_select_info, State),
                                   State2 = maps:remove(recv_completion_info, State1),
                                   State3 = maps:remove(tester,           State2),
                                   State4 = maps:remove(sock,             State3),
                                   {ok, State4};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    ClientSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "await continue (connect)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, connect)
                   end},
         #{desc => "connect to server",
           cmd  => fun(#{sock := Sock, server_sa := SSA}) ->
                           socket:connect(Sock, SSA)
                   end},
         #{desc => "announce ready (connect)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, connect),
                           ok
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock}) ->
                           socket:close(Sock)
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor alt-server 1",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor alt-server 2",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor client",
           cmd  => fun(#{client := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = State) ->
                           {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{server_port => Port}}
                   end},

         %% Start the client
         #{desc => "order client start",
           cmd  => fun(#{client := Pid, server_port := Port} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Port),
                           ok
                   end},
         #{desc => "await client ready (init)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, init)
                   end},

         #{desc => "order server to continue (with accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, accept),
                           ok
                   end},
         #{desc => "order client to continue (connect)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
                           ok
                   end},
         #{desc => "await client ready (connect)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client, connect)
                   end},
         #{desc => "await server ready (accept)",
           cmd  => fun(#{server := Pid} = State) ->
                           {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, accept),
                           {ok, State#{sock => Sock}}
                   end},

         %% Start the alt server 1
         #{desc => "order alt-server 1 start",
           cmd  => fun(#{alt_server1 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Sock),
                           ok
                   end},
         #{desc => "await alt-server 1 ready (init)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server1, init)
                   end},

         %% Start the alt server 2
         #{desc => "order alt-server 2 start",
           cmd  => fun(#{alt_server2 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Sock),
                           ok
                   end},
         #{desc => "await alt-server 2 ready (init)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server2, init)
                   end},


         %% *** The actual test ***
         #{desc => "order server continue (recv)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
                           ok
                   end},
         #{desc => "await server ready (recv select)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, recv_select)
                   end},

         #{desc => "order alt-server 1 continue (recv)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
                           ok
                   end},
         #{desc => "await alt-server 1 ready (recv select)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server1, recv_select)
                   end},

         #{desc => "order alt-server 2 continue (recv)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, recv),
                           ok
                   end},
         #{desc => "await alt-server 2 ready (recv select)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server2, recv_select)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "close the socket",
           cmd  => fun(#{sock := Sock} = _State) ->
                           socket:close(Sock)
                   end},

         #{desc => "await server ready (abort)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, abort)
                   end},
         #{desc => "await alt-server 1 ready (abort)",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort)
                   end},
         #{desc => "await alt-server 2 ready (abort)",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort)
                   end},

         %% Terminations
         #{desc => "order client to terminate",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Client),
                           ok
                   end},
         #{desc => "await client termination",
           cmd  => fun(#{client := Client} = State) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client, State),
                           {ok, State1}
                   end},

         #{desc => "order alt-server 2 to terminate",
           cmd  => fun(#{alt_server2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await alt-server 2 termination",
           cmd  => fun(#{alt_server2 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(alt_server2, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order alt-server 1 to terminate",
           cmd  => fun(#{alt_server1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await alt-server 1 termination",
           cmd  => fun(#{alt_server1 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(alt_server1, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order server to terminate",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   State1 = maps:remove(server, State),
                                   {ok, State1};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start alt-server 1 evaluator"),
    AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState),

    i("start alt-server 2 evaluator"),
    AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState),

    i("start client evaluator"),
    Client = ?SEV_START("client", ClientSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{server      => Server#ev.pid,
                        alt_server1 => AltServer1#ev.pid,
                        alt_server2 => AltServer2#ev.pid,
                        client      => Client#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Client, Tester]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                           API OPTIONS                               %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Perform some simple getopt and setopt with the level = otp options
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, _} = OK ->
                                   {error, OK};
                               {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, _} = OK->
                                   {error, OK};
                               {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, _} = OK ->
                                   {error, OK};
                               {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, _} = OK ->
                                   {error, OK};
                               {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, _} = OK ->
                                   {error, OK};
                               {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, _} = OK ->
                                   {error, OK};
                               {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, _} = OK ->
                                   {error, OK};
                               {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, _} = OK ->
                                   {error, OK};
                               {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, _} = OK ->
                                   {error, OK};
                               {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, _} = OK->
                                   {error, OK};
                               {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, _} = OK ->
                                   {error, OK};
                               {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, _} = OK ->
                                   {error, OK};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         #{desc => "finish",
           cmd  => fun(#{ dummy := Dummy }) ->
                           Dummy ! die,
                           {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 getopt and setopt otp meta option
api_opt_simple_otp_meta_option(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_simple_otp_meta_option,
           fun() -> api_opt_simple_otp_meta_option() end).

api_opt_simple_otp_meta_option() ->
    Get = fun(S) ->
                  socket:getopt(S, otp, meta)
          end,
    Set = fun(S, Val) ->
                  socket:setopt(S, otp, meta, Val)
          end,

    MainSeq =
        [
         #{desc => "monitor helper",
           cmd => fun(#{helper := Pid}) ->
                          _ = erlang:monitor(process, Pid),
                          ok
                  end},

         #{desc => "create socket",
           cmd  => fun(#{domain   := Domain,
                         type     := Type,
                         protocol := Protocol} = State) ->
                           Sock = sock_open(Domain, Type, Protocol),
                           {ok, State#{sock => Sock}}
                   end},

         #{desc => "get default",
           cmd => fun(#{sock := Sock}) ->
                          case Get(Sock) of
                              {ok, undefined} ->
                                  ok;
                              {ok, _} = OK ->
                                  {error, OK};
                              {error, _} = ERROR ->
                                  ERROR
                          end
                  end},

         #{desc => "set value",
           cmd => fun(#{sock := Sock} = State) ->
                          Value = make_ref(),
                          case Set(Sock, Value) of
                              ok ->
                                  {ok, State#{value => Value}};
                              {error, _} = ERROR ->
                                  ERROR
                          end
                  end},

         #{desc => "get value",
           cmd => fun(#{sock := Sock, value := Value}) ->
                          case Get(Sock) of
                              {ok, Value} ->
                                  ok;
                              {ok, _} = OK ->
                                  {error, OK};
                              {error, _} = ERROR ->
                                  ERROR
                          end
                  end},

         #{desc => "set complex value",
           cmd => fun(#{sock := Sock} = State) ->
                          Value =
                              #{a => 1,
                                b => {2, 3},
                                c => make_ref(),
                                d => self(),
                                e => State},
                          case Set(Sock, Value) of
                              ok ->
                                  {ok, State#{value := Value}};
                              {error, _} = ERROR ->
                                  ERROR
                          end
                  end},

         #{desc => "get complex value",
           cmd => fun(#{sock := Sock, value := Value}) ->
                          case Get(Sock) of
                              {ok, Value} ->
                                  ok;
                              {ok, _} = OK->
                                  {error, OK};
                              {error, _} = ERROR ->
                                  ERROR
                          end
                  end},

         #{desc => "start helper",
           cmd => fun(#{helper := Pid,  sock := Sock, value := Value}) ->
                          ?SEV_ANNOUNCE_START(Pid, {Sock, Value}),
                          ok
                  end},

         #{desc => "wait for helper ready",
           cmd => fun(#{helper := Pid}) ->
                          ?SEV_AWAIT_READY(Pid, helper, test)
                  end},

         #{desc => "socket close",
           cmd => fun(#{sock := Sock}) ->
                          socket:close(Sock)
                  end},

        ?SEV_FINISH_NORMAL],

    HelperSeq =
        [#{desc => "await start",
           cmd => fun (State) ->
                          {Main, {Sock, Value}} = ?SEV_AWAIT_START(),
                          {ok, State#{main => Main,
                                      sock => Sock,
                                      value => Value}}
                  end},
         #{desc => "monitor main",
           cmd => fun(#{main := Main}) ->
                          _ = erlang:monitor(process, Main),
                          ok
                  end}

         #{desc => "get value",
           cmd => fun(#{sock := Sock, value := Value}) ->
                          case Get(Sock) of
                              {ok, Value} ->
                                  ok;
                              {ok, _} = OK->
                                  {error, OK};
                              {error, _} = ERROR ->
                                  ERROR
                          end
                  end},

         #{desc => "set and fail",
           cmd => fun(#{sock := Sock}) ->
                          Value = self(),
                          case Set(Sock, Value) of
                              ok ->
                                  {error, only_owner_may_set};
                              {error, {invalid, not_owner}} ->
                                  ok;
                              {error, _} = ERROR ->
                                  ERROR
                          end
                  end},

         #{desc => "announce ready (test)",
           cmd  => fun(#{main := Main}) ->
                           ?SEV_ANNOUNCE_READY(Main, test),
                           ok
                   end},

         ?SEV_FINISH_NORMAL],


    i("start tcp helper evaluator"),
    Helper = ?SEV_START("tcp-helper", HelperSeq, #{}),

    i("start tcp main evaluator"),
    MainState = #{domain => inet, type => stream, protocol => tcp,
                  helper => Helper#ev.pid},
    Main = ?SEV_START("tcp-main", MainSeq, MainState),

    i("await tcp evaluators"),
    ok = ?SEV_AWAIT_FINISH([Helper, Main]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(?FUNCTION_NAME,
           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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, local_sa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {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) ->
                           ?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 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}} ->
                                   ?SEV_IPRINT("set new rcvbuf:"
                                               "~n   New RcvBuf:  ~p"
                                               "~n   Expect Size: ~p",
                                               [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) ->
                           LSA = which_local_socket_addr(Domain),
                           {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 sock_bind(Sock, LSA) of
                               ok ->
                                   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 =
                               case os:type() of
                                   {win32, nt} ->
                                       (((2 * MsgSz) div 1024) + 1) * 1024;
                                   _ ->
                                       {2 + (MsgSz div 1024), 1024}
                               end,
                           ?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 = 
                               case os:type() of
                                   {win32, nt} ->
                                       (((3 * MsgSz) div 1024) + 1) * 1024;
                                   _ ->
                                       {2 + (MsgSz div 2048), 2048}
                               end,
                           ?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,
                           {ExpSz, NewRcvBuf} =
                               case os:type() of
                                   {win32, nt} ->
                                       {N*BufSz, N*BufSz};
                                   _ ->
                                       {N*BufSz, {N, BufSz}}
                               end,
                           ?SEV_ANNOUNCE_CONTINUE(Server, recv,
                                                  {ExpSz, 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_or_inet6(),
                  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(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    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, _} = OK ->
                                   {error, OK};
                               {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, {invalid, 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, _} = OK ->
                                   {error, OK};
                               {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, {invalid, 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, _} = OK ->
                                   {error, OK};
                               {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, _} = OK ->
                                   {error, OK};
                               {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, {invalid, 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, _} = OK ->
                                   {error, OK};
                               {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]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option acceptconn for UDP.
%% This should be possible to get but not set.

api_opt_sock_acceptconn_udp(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(api_opt_sock_acceptconn_udp,
           fun() ->
                   has_support_sock_acceptconn()
           end,
           fun() -> api_opt_sock_acceptconn_udp() end).



api_opt_sock_acceptconn_udp() ->
    Opt = acceptconn,
    Set = fun(S, Val) ->
                  socket:setopt(S, socket, Opt, Val)
          end,
    Get = fun(S) ->
                  socket:getopt(S, socket, Opt)
          end,

    TesterSeq =
        [
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "[get] verify socket (before bind)",
           cmd  => fun(#{sock := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, false} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "Not accepting connections"),
                                   ok;
                               {ok, true} ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Accepting connections"),
                                   {error, {unexpected_success, {Opt, true}}};
                               {error, enoprotoopt = Reason} ->
                                   %% On some platforms this is not accepted
                                   %% for UDP, so skip this part (UDP).
                                   ?SEV_EPRINT("Expected Failure: "
                                               "~p => SKIP", [Reason]),
                                   (catch socket:close(Sock)),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[set] verify socket (before bind)",
           cmd  => fun(#{sock := Sock} = _State) ->
                           case Set(Sock, true) of
                               {error, Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p",
                                               [Reason]),
                                   ok;
                               ok ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Set acceptconn (=true)"),
                                   {error, unexpected_success}
                           end
                   end},

         #{desc => "bind socket to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "[get] verify socket (after bind)",
           cmd  => fun(#{sock := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, false} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "Not accepting connections"),
                                   ok;
                               {ok, true} ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Accepting connections"),
                                   {error, {unexpected_success, {Opt, true}}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[set] verify socket (after bind)",
           cmd  => fun(#{sock := Sock} = _State) ->
                           case Set(Sock, true) of
                               {error, Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p",
                                               [Reason]),
                                   ok;
                               ok ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Set acceptconn (=true)"),
                                   {error, unexpected_success}
                           end
                   end},

         %% *** Termination ***
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    Domain = inet_or_inet6(),

    i("start tester evaluator"),
    InitState = #{domain  => Domain},
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option acceptconn for TCP.
%% This should be possible to get but not set.

api_opt_sock_acceptconn_tcp(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(api_opt_sock_acceptconn_tcp,
           fun() ->
                   has_support_sock_acceptconn()
           end,
           fun() -> api_opt_sock_acceptconn_tcp() end).



api_opt_sock_acceptconn_tcp() ->
    Opt = acceptconn,
    Set = fun(S, Val) ->
                  socket:setopt(S, socket, Opt, Val)
          end,
    Get = fun(S) ->
                  socket:getopt(S, socket, Opt)
          end,

    TesterSeq =
        [
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},

         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "[get] verify listen socket (before bind)",
           cmd  => fun(#{lsock := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, false} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "Not accepting connections"),
                                   ok;
                               {ok, true} ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Accepting connections"),
                                   {error, {unexpected_success, {Opt, true}}};
                               {error, enoprotoopt = Reason} ->
                                   ?SEV_EPRINT("Expected Failure: "
                                               "~p => SKIP", [Reason]),
                                   (catch socket:close(Sock)),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[set] verify listen socket (before bind)",
           cmd  => fun(#{lsock := Sock} = _State) ->
                           case Set(Sock, true) of
                               {error, Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
                                   ok;
                               ok ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Set acceptconn (=true)"),
                                   {error, unexpected_success}
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "bind listen socket to local address",
           cmd  => fun(#{lsock := Sock, local_sa := LSA} = State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   {ok, State#{server_sa => LSA#{port => Port}}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "[get] verify listen socket (after bind)",
           cmd  => fun(#{lsock := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, false} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "Not accepting connections"),
                                   ok;
                               {ok, true} ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Accepting connections"),
                                   {error, {unexpected_success, {Opt, true}}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[set] verify listen socket (after bind)",
           cmd  => fun(#{lsock := Sock} = _State) ->
                           case Set(Sock, true) of
                               {error, Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
                                   ok;
                               ok ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Set acceptconn (=true)"),
                                   {error, unexpected_success}
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "make listen socket accept connections",
           cmd  => fun(#{lsock := Sock} = _State) ->
                           case socket:listen(Sock) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "[get] verify listen socket (after listen)",
           cmd  => fun(#{lsock := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, true} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "Accepting connections"),
                                   ok;
                               {ok, false} ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Not accepting connections"),
                                   {error, {unexpected_success, {Opt, false}}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[set] verify listen socket (after listen)",
           cmd  => fun(#{lsock := Sock} = _State) ->
                           case Set(Sock, false) of
                               {error, Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
                                   ok;
                               ok ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Set acceptconn (=false)"),
                                   {error, unexpected_success}
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "create (connecting) socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{csockc => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "bind connecting socket to local address",
           cmd  => fun(#{csockc := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "[get] verify connecting socket (before connect)",
           cmd  => fun(#{csockc := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, false} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "Not accepting connections"),
                                   ok;
                               {ok, true} ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Accepting connections"),
                                   {error, {unexpected_success, {Opt, true}}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[set] verify connecting socket (before connect)",
           cmd  => fun(#{csockc := Sock} = _State) ->
                           case Set(Sock, true) of
                               {error, Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
                                   ok;
                               ok ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Set acceptconn (=true)"),
                                   {error, unexpected_success}
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "connect to server",
           cmd  => fun(#{csockc := Sock, server_sa := SSA} = _State) ->
                           case socket:connect(Sock, SSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "accept connection",
           cmd  => fun(#{lsock := Sock} = State) ->
                           case socket:accept(Sock) of
                               {ok, CSock} ->
                                   {ok, State#{csocks => CSock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "[get] verify connecting socket (after connect)",
           cmd  => fun(#{csockc := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, false} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "Not accepting connections"),
                                   ok;
                               {ok, true} ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Accepting connections"),
                                   {error, {unexpected_success, {Opt, true}}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[set] verify connecting socket (after connect)",
           cmd  => fun(#{csockc := Sock} = _State) ->
                           case Set(Sock, true) of
                               {error, Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
                                   ok;
                               ok ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Set acceptconn (=true)"),
                                   {error, unexpected_success}
                           end
                   end},

         #{desc => "[get] verify connected socket",
           cmd  => fun(#{csocks := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, false} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "Not accepting connections"),
                                   ok;
                               {ok, true} ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Accepting connections"),
                                   {error, {unexpected_success, {Opt, true}}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[set] verify connected socket",
           cmd  => fun(#{csocks := Sock} = _State) ->
                           case Set(Sock, true) of
                               {error, Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
                                   ok;
                               ok ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Set acceptconn (=true)"),
                                   {error, unexpected_success}
                           end
                   end},

         #{desc => "[get] verify listen socket (after connect)",
           cmd  => fun(#{lsock := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, true} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "Accepting connections"),
                                   ok;
                               {ok, false} ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Not accepting connections"),
                                   {error, {unexpected_success, {Opt, false}}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[set] verify listen socket (after connect)",
           cmd  => fun(#{lsock := Sock} = _State) ->
                           case Set(Sock, false) of
                               {error, Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
                                   ok;
                               ok ->
                                   ?SEV_EPRINT("Unexpected Success: "
                                               "Set acceptconn (=false)"),
                                   {error, unexpected_success}
                           end
                   end},

         %% *** Termination ***
         #{desc => "close connecting socket(s)",
           cmd  => fun(#{csockc := Sock} = State0) ->
                           socket:close(Sock),
                           State1 = maps:remove(csockc, State0),
                           State2 = maps:remove(csocks, State1), %% Auto-close
                           {ok, maps:remove(csockc, State2)}
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{lsock := Sock} = State) ->
                           socket:close(Sock),
                           {ok, maps:remove(lsock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    Domain = inet_or_inet6(),

    i("start tester evaluator"),
    InitState = #{domain  => Domain},
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option acceptfilter. PLACEHOLDER!

api_opt_sock_acceptfilter(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(api_opt_sock_acceptfilter,
           fun() -> not_yet_implemented() end,
           fun() -> ok end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option bindtodevice.
%% It has not always been possible to 'get' this option
%% (at least on linux).

api_opt_sock_bindtodevice(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(api_opt_sock_bindtodevice,
           fun() -> has_support_sock_bindtodevice() end,
           fun() -> api_opt_sock_bindtodevice() end).


api_opt_sock_bindtodevice() ->
    Opt = bindtodevice,
    Set = fun(S, Val) ->
                  socket:setopt(S, socket, Opt, Val)
          end,
    Get = fun(S) ->
                  socket:getopt(S, socket, Opt)
          end,

    TesterSeq =
        [
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           case which_local_host_info(Domain) of
                               {ok, #{name := Name, addr := Addr}} ->
                                   ?SEV_IPRINT("local host info (~p): "
                                               "~n   Name: ~p"
                                               "~n   Addr: ~p",
                                               [Domain, Name, Addr]),
                                   LSA = #{family => Domain,
                                           addr   => Addr},
                                   {ok, State#{dev      => Name,
                                               local_sa => LSA}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create UDP socket 1",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{usock1 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create UDP socket 2",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{usock2 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create TCP socket 1",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{tsock1 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create TCP socket 2",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{tsock2 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "[get] verify UDP socket 1 (before bindtodevice)",
           cmd  => fun(#{usock1 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, Dev} ->
                                   ?SEV_IPRINT("Expected Success: ~p", [Dev]),
                                   ok;
                               {error, enoprotoopt = Reason} ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p => SKIP",
					       [Reason]),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[get] verify UDP socket 2 (before bind)",
           cmd  => fun(#{usock2 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, Dev} ->
                                   ?SEV_IPRINT("Expected Success: ~p", [Dev]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[get] verify TCP socket 1 (before bindtodevice)",
           cmd  => fun(#{tsock1 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, Dev} ->
                                   ?SEV_IPRINT("Expected Success: ~p", [Dev]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[get] verify TCP socket 2 (before bind)",
           cmd  => fun(#{tsock2 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, Dev} ->
                                   ?SEV_IPRINT("Expected Success: ~p", [Dev]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "Bind UDP socket 1 to device",
           cmd  => fun(#{usock1 := Sock, dev := Dev} = State) ->
                           case Set(Sock, Dev) of
                               ok ->
                                   ?SEV_IPRINT("Expected Success"),
                                   ok;
                               {error, eperm = Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
                                   (catch socket:close(Sock)),
                                   {ok, State#{usock1 => skip}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Bind UDP socket 2 to local address",
           cmd  => fun(#{usock2 := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("Expected Success"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Bind TCP socket 1 to device",
           cmd  => fun(#{usock1 := USock1,
                         tsock1 := Sock, dev := Dev} = State) ->
                           case Set(Sock, Dev) of
                               ok ->
                                   ?SEV_IPRINT("Expected Success"),
                                   ok;
                               {error, eperm = Reason} when (USock1 =:= skip) ->
                                   ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
                                   {skip, Reason};
                               {error, eperm = Reason} ->
                                   ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
                                   (catch socket:close(Sock)),
                                   {ok, State#{tsock1 => skip}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Bind TCP socket 2 to local address",
           cmd  => fun(#{tsock2 := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("Expected Success"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "[get] verify UDP socket 1 (after bindtodevice)",
           cmd  => fun(#{usock1 := skip} = _State) ->
                           ?SEV_IPRINT("SKIP'ed (previous eperm)"),
                           ok;
                      (#{usock1 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, Dev} ->
                                   ?SEV_IPRINT("Expected Success: ~p", [Dev]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[get] verify UDP socket 2 (after bind)",
           cmd  => fun(#{usock2 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, Dev} ->
                                   ?SEV_IPRINT("Expected Success: ~p", [Dev]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[get] verify TCP socket 1 (after bindtodevice)",
           cmd  => fun(#{tsock1 := skip} = _State) ->
                           ?SEV_IPRINT("SKIP'ed (previous eperm)"),
                           ok;
                      (#{tsock1 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, Dev} ->
                                   ?SEV_IPRINT("Expected Success: ~p", [Dev]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[get] verify TCP socket 2 (after bind)",
           cmd  => fun(#{tsock2 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, Dev} ->
                                   ?SEV_IPRINT("Expected Success: ~p", [Dev]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
                                   ERROR
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         %% *** Termination ***
         #{desc => "close UDP socket 1",
           cmd  => fun(#{usock1 := skip} = State) ->
                           ?SEV_IPRINT("SKIP'ed (already closed)"),
                           {ok, maps:remove(usock1, State)};
                      (#{usock1 := Sock} = State) ->
                           socket:close(Sock),
                           {ok, maps:remove(usock1, State)}
                   end},
         #{desc => "close UDP socket 2",
           cmd  => fun(#{usock2 := Sock} = State) ->
                           socket:close(Sock),
                           {ok, maps:remove(usock2, State)}
                   end},
         #{desc => "close TCP socket 1",
           cmd  => fun(#{tsock1 := skip} = State) ->
                           ?SEV_IPRINT("SKIP'ed (already closed)"),
                           {ok, maps:remove(tsock1, State)};
                      (#{tsock1 := Sock} = State) ->
                           socket:close(Sock),
                           {ok, maps:remove(tsock1, State)}
                   end},
         #{desc => "close TCP socket 2",
           cmd  => fun(#{tsock2 := Sock} = State) ->
                           socket:close(Sock),
                           {ok, maps:remove(tsock2, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    Domain = inet_or_inet6(),

    i("start tester evaluator"),
    InitState = #{domain  => Domain},
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option broadcast.
%% Make it possible for datagram sockets to send packets to a broadcast
%% address (IPv4 only).

api_opt_sock_broadcast(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(api_opt_sock_broadcast,
           fun() -> has_support_sock_broadcast() end,
           fun() -> api_opt_sock_broadcast() end).


api_opt_sock_broadcast() ->
    Opt    = broadcast,
    Set    = fun(S, Val) when is_boolean(Val) ->
                     socket:setopt(S, socket, Opt, Val)
             end,
    Get    = fun(S) ->
                     socket:getopt(S, socket, Opt)
             end,

    TesterSeq =
        [
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           case which_local_host_info(Domain) of
                               {ok, #{name      := Name,
                                      addr      := Addr,
                                      broadaddr := BAddr}}
				 when (BAddr =/= undefined) ->
                                   ?SEV_IPRINT("local host info: "
                                               "~n   Name:           ~p"
                                               "~n   Addr:           ~p"
                                               "~n   Broadcast Addr: ~p",
                                               [Name, Addr, BAddr]),
                                   LSA = #{family => Domain,
                                           addr   => Addr},
                                   BSA = #{family => Domain,
                                           addr   => BAddr},
                                   {ok, State#{lsa => LSA,
                                               bsa => BSA}};
			       {ok, _} ->
				   {skip, no_broadcast_address};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "[socket 1] create UDP socket (listening 1)",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock1 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "[socket 1] Bind UDP socket (to limited broadcast address)",
           cmd  => fun(#{sock1 := Sock} = State) ->
			   BSA = #{family => inet,
				   addr   => broadcast},
                           ?SEV_IPRINT("Try bind (socket 1) to: "
                                       "~n   ~p", [BSA]),
                           case socket:bind(Sock, BSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   ?SEV_IPRINT("Expected Success (bound): ~p",
                                               [Port]),
                                   {ok, State#{sa1 => BSA#{port => Port}}};
                               {error, eaddrnotavail = Reason} ->
                                   ?SEV_IPRINT("~p => "
					       "SKIP limited broadcast test",
					       [Reason]),
                                   {ok, State#{sa1 => skip}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[socket 1] UDP socket sockname",
           cmd  => fun(#{sa1 := skip} = _State) ->
                           ?SEV_IPRINT("SKIP limited broadcast test"),
                           ok;
		      (#{sock1 := Sock} = _State) ->
			   case socket:sockname(Sock) of
			       {ok, SA} ->
				   ?SEV_IPRINT("SA: ~p", [SA]),
				   ok;
			       {error, _} = ERROR ->
				   ERROR
			   end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "[socket 2] create UDP socket (listening 2)",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock2 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "[socket 2] Bind UDP socket (to subnet-directed broadcast address)",
           cmd  => fun(#{sock2 := Sock,
			 bsa   := BSA} = State) ->
                           ?SEV_IPRINT("Try bind (socket 1) to: "
                                       "~n   ~p", [BSA]),
                           case socket:bind(Sock, BSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   ?SEV_IPRINT("Expected Success (bound): ~p",
                                               [Port]),
                                   {ok, State#{sa2 => BSA#{port => Port}}};
                               {error, eaddrnotavail = Reason} ->
                                   ?SEV_IPRINT("~p => "
					       "SKIP subnet-directed broadcast test",
					       [Reason]),
                                   {ok, State#{sa2 => skip}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[socket 2] UDP socket sockname",
           cmd  => fun(#{sa2 := skip} = _State) ->
                           ?SEV_IPRINT("SKIP subnet-directed broadcast test"),
                           ok;
		      (#{sock2 := Sock} = _State) ->
                           case socket:sockname(Sock) of
                               {ok, SA} ->
				   ?SEV_IPRINT("SA: ~p", [SA]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "[socket 3] create UDP socket (sender)",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock3 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "[socket 3][get] verify UDP socket (before bind and set)",
           cmd  => fun(#{sock3 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, false} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "broadcast not allowed"),
                                   ok;
                               {ok, true} ->
                                   ?SEV_IPRINT("Unexpected Success result: "
                                               "broadcast already allowed"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[socket 3] Try make broadcast allowed",
           cmd  => fun(#{sock3 := Sock} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "broadcast now allowed"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[socket 3] verify UDP socket broadcast allowed",
           cmd  => fun(#{sock3 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, true} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "broadcast allowed"),
                                   ok;
                               {ok, false} ->
                                   ?SEV_IPRINT("Unexpected Success result: "
                                               "broadcast *not* allowed"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[socket 3] Bind UDP socket (to local address)",
           cmd  => fun(#{sock3 := Sock, lsa := LSA} = State) ->
                           ?SEV_IPRINT("Try bind (socket 2) to: "
                                       "~n   ~p", [LSA]),
                           case socket:bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   ?SEV_IPRINT("Expected Success (bound): ~p",
                                               [Port]),
                                   {ok, State#{sa3 => LSA#{port => Port}}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[socket 3] verify UDP socket (after set)",
           cmd  => fun(#{sock3 := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, true} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "broadcast allowed"),
                                   ok;
                               {ok, false} ->
                                   ?SEV_IPRINT("Unexpected Success result: "
                                               "broadcast not allowed"),
                                   {error, not_allowed};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         #{desc => "[socket 3] try send to limited broadcast address",
           cmd  => fun(#{sa1 := skip} = _State) ->
                           ?SEV_IPRINT("SKIP limited broadcast test (send)"),
			   ok;
		      (#{sock3 := Sock,
			 sa1   := Dest} = _State) ->
			   Data = list_to_binary("hejsan"),
			   ?SEV_IPRINT("try send to broadcast address: "
				       "~n   ~p", [Dest]),
			   case socket:sendto(Sock, Data, Dest) of
			       ok ->
				   ?SEV_IPRINT("Expected Success: "
					       "broadcast message sent"),
				   ok;
			       {error, Reason} = ERROR ->
				   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
				   ERROR
			   end
		   end},
         #{desc => "[socket 1] try recv",
           cmd  => fun(#{sa1 := skip} = _State) ->
			   ?SEV_IPRINT("SKIP limited broadcast test (recv)"),
			   ok;
		      (#{sock1 := Sock} = State) ->
                           case socket:recvfrom(Sock, 0, 5000) of
                               {ok, _} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "received message"),
                                   ok;
                               {error, timeout = Reason} ->
                                   %% Some platforms seem to balk at this.
                                   %% It spossible to bind to this, and
                                   %% send to it, but no data is received.
                                   %% At some point we should investigate...
                                   %% For now, we just skip this part of
                                   %% the test...
                                   ?SEV_IPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   {ok, State#{sa1 => skip}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         #{desc => "[socket 2] try send to subnet-directed broadcast address",
           cmd  => fun(#{sa2 := skip} = _State) ->
			   ?SEV_IPRINT("SKIP subnet-directed broadcast test "
                                       "(send)"),
			   ok;
		      (#{sock2 := Sock,
                         sa2   := Dest} = _State) ->
                           Data = list_to_binary("hejsan"),
                           ?SEV_IPRINT("try send to broadcast address: "
                                       "~n   ~p", [Dest]),
                           case socket:sendto(Sock, Data, Dest) of
                               ok ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "broadcast message sent"),
                                   ok;
                               {error, eaddrnotavail = Reason} ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p => SKIP",
                                               [Reason]),
                                   {skip, Reason};
                               {error, eacces = Reason} ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p => SKIP",
					       [Reason]),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "[socket 2] try recv",
           cmd  => fun(#{sa2 := skip} = _State) ->
			   ?SEV_IPRINT("SKIP subnet-directed broadcast test "
                                         "(recv)"),
			   ok;
		      (#{sock2 := Sock, sa2 := SA2} = _State) ->
                           case socket:recvfrom(Sock, 0, 5000) of
                               {ok, _} ->
                                   ?SEV_IPRINT("Expected Success: "
                                               "received message"),
                                   ok;
                               {error, timeout = Reason} when (SA2 =:= skip) ->
                                   ?SEV_IPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   {skip, "receive timeout"};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},

         %% *** Termination ***
         #{desc => "[socket 3] close UDP socket (sender)",
           cmd  => fun(#{sock3 := Sock} = State0) ->
                           socket:close(Sock),
			   State1 = maps:remove(sock3, State0),
			   State2 = maps:remove(sa3,   State1),
			   {ok, State2}
                   end},
         #{desc => "[socket 2] close UDP socket (listener 2)",
           cmd  => fun(#{sock2 := Sock} = State0) ->
                           socket:close(Sock),
			   State1 = maps:remove(sock2, State0),
			   State2 = maps:remove(sa2,   State1),
                           {ok, State2}
                   end},
         #{desc => "[socket 1] close UDP socket (listener 1)",
           cmd  => fun(#{sock1 := Sock} = State0) ->
                           socket:close(Sock),
			   State1 = maps:remove(sock1, State0),
			   State2 = maps:remove(sa1,   State1),
                           {ok, State2}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    Domain = inet_or_inet6(),

    i("start tester evaluator"),
    InitState = #{domain => Domain},
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option debug.
%% On linux, this test requires that the user running the test to have
%% CAP_NET_ADMIN capabilities or be root (effective user ID of 0), 
%% therefore we explicitly test for the result eacces when attempting to
%% set, and skip if we get it.

api_opt_sock_debug(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_opt_sock_debug,
           fun() -> has_support_sock_debug() end,
           fun() -> api_opt_sock_debug() end).


api_opt_sock_debug() ->
    Opt    = debug,
    Set    = fun(S, Val) when is_integer(Val) ->
                     socket:setopt(S, socket, Opt, Val)
             end,
    Get    = fun(S) ->
                     socket:getopt(S, socket, Opt)
             end,

    TesterSeq =
        [
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           case which_local_host_info(Domain) of
                               {ok, #{name      := Name,
                                      addr      := Addr}} ->
                                   ?SEV_IPRINT("local host info: "
                                               "~n   Name:           ~p"
                                               "~n   Addr:           ~p",
                                               [Name, Addr]),
                                   LSA = #{family => Domain,
                                           addr   => Addr},
                                   {ok, State#{lsa => LSA}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "create UDP socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "Get current debug value",
           cmd  => fun(#{sock := Sock} = State) ->
                           case Get(Sock) of
                               {ok, Debug} when is_integer(Debug) ->
                                   ?SEV_IPRINT("Success: ~p", [Debug]),
                                   {ok, State#{debug => Debug}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Try enable socket debug",
           cmd  => fun(#{sock := Sock, debug := Debug} = State) ->
			   NewDebug = Debug + 1,
                           case Set(Sock, NewDebug) of
                               ok ->
                                   ?SEV_IPRINT("Expected Success"),
                                   {ok, State#{debug => NewDebug}};
                               {error, eacces = Reason} ->
                                   ?SEV_EPRINT("NO ACCESS => SKIP"),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Get current (new) debug value",
           cmd  => fun(#{sock := Sock, debug := Debug} = _State) ->
                           case Get(Sock) of
                               {ok, Debug} when is_integer(Debug) ->
                                   ?SEV_IPRINT("Success: ~p", [Debug]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},

         %% *** Termination ***
         #{desc => "close UDP socket",
           cmd  => fun(#{sock := Sock} = State0) ->
                           socket:close(Sock),
			   State1 = maps:remove(sock, State0),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    Domain = inet_or_inet6(),

    i("start tester evaluator"),
    InitState = #{domain => Domain},
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option domain.
%% This is a read only option. Also not available on all platforms.

api_opt_sock_domain(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_opt_sock_domain,
           fun() -> has_support_sock_domain() end,
           fun() -> api_opt_sock_domain() end).


api_opt_sock_domain() ->
    Opt = domain,
    Get = fun(S) ->
                  socket:getopt(S, socket, Opt)
          end,

    TesterSeq =
        [
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           case which_local_host_info(Domain) of
                               {ok, #{name := Name,
                                      addr := Addr}} ->
                                   ?SEV_IPRINT("local host info: "
                                               "~n   Name: ~p"
                                               "~n   Addr: ~p",
                                               [Name, Addr]),
                                   LSA = #{family => Domain,
                                           addr   => Addr},
                                   {ok, State#{lsa => LSA}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "create IPv4 UDP socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{usock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "Get domain for the UDP socket",
           cmd  => fun(#{domain := Domain, usock := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, Domain} ->
                                   ?SEV_IPRINT("Success: ~p", [Domain]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "create TCP socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{tsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "Get domain for the TCP socket",
           cmd  => fun(#{domain := Domain, tsock := Sock} = _State) ->
                           case Get(Sock) of
                               {ok, Domain} ->
                                   ?SEV_IPRINT("Success: ~p", [Domain]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},

         %% *** Termination ***
         #{desc => "close UDP socket",
           cmd  => fun(#{usock := Sock} = State0) ->
                           socket:close(Sock),
			   State1 = maps:remove(usock, State0),
                           {ok, State1}
                   end},
         #{desc => "close TCP socket",
           cmd  => fun(#{tsock := Sock} = State0) ->
                           socket:close(Sock),
			   State1 = maps:remove(tsock, State0),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    Domain = inet_or_inet6(),

    i("start tester evaluator"),
    InitState = #{domain => Domain},
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option dontroute.
%% The man page has the following to say:
%% "Don't send via a gateway, send only to directly connected hosts.
%%  The same effect can be achieved by setting the MSG_DONTROUTE
%%  flag on a socket send(2) operation."
%% Since its "kind of" difficult to check if it actually takes an 
%% effect (you would need a gateway for that and a machine "on the
%% other side"), we only test if we can set and get the value.
%% Better then nothing.

api_opt_sock_dontroute(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_opt_sock_dontroute,
           fun() -> has_support_sock_dontroute() end,
           fun() -> api_opt_sock_dontroute() end).


api_opt_sock_dontroute() ->
    Opt    = dontroute,
    Set    = fun(S, Val) when is_boolean(Val) ->
                     socket:setopt(S, socket, Opt, Val)
             end,
    Get    = fun(S) ->
                     socket:getopt(S, socket, Opt)
             end,

    TesterSeq =
        [
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           case which_local_host_info(Domain) of
                               {ok, #{name := Name,
                                      addr := Addr}} ->
                                   ?SEV_IPRINT("local host info: "
                                               "~n   Name: ~p"
                                               "~n   Addr: ~p",
                                               [Name, Addr]),
                                   LSA = #{family => Domain,
                                           addr   => Addr},
                                   {ok, State#{lsa => LSA}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "create UDP socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "Get current value",
           cmd  => fun(#{sock := Sock} = State) ->
                           case Get(Sock) of
                               {ok, Val} when is_boolean(Val) ->
                                   ?SEV_IPRINT("Success: ~p", [Val]),
                                   {ok, State#{dontroute => Val}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Try change value",
           cmd  => fun(#{sock := Sock, dontroute := Current} = State) ->
			   New = not Current,
                           ?SEV_IPRINT("Change from ~p to ~p", [Current, New]),
                           case Set(Sock, New) of
                               ok ->
                                   ?SEV_IPRINT("Expected Success"),
                                   {ok, State#{dontroute => New}};
                               {error, eopnotsupp = Reason} ->
                                   ?SEV_EPRINT("Expected Failure: ~p",
					       [Reason]),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Verify changed value",
           cmd  => fun(#{sock := Sock, dontroute := Val} = _State) ->
                           case Get(Sock) of
                               {ok, Val} ->
                                   ?SEV_IPRINT("Expected Success"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},

         %% *** Termination ***
         #{desc => "close UDP socket",
           cmd  => fun(#{sock := Sock} = State0) ->
                           socket:close(Sock),
			   State1 = maps:remove(sock, State0),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    Domain = inet_or_inet6(),

    i("start tester evaluator"),
    InitState = #{domain => Domain},
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option error. PLACEHOLDER!

api_opt_sock_error(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_opt_sock_error,
           fun() -> not_yet_implemented() end,
           fun() -> ok end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option keepalive.
%% This is bit tricky to test, partly because we have no control over
%% the underlying TCP timeouts. So, for now, we just test that we can
%% change the value.

api_opt_sock_keepalive(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_opt_sock_keepalive,
           fun() -> has_support_sock_keepalive() end,
           fun() -> api_opt_sock_keepalive() end).


api_opt_sock_keepalive() ->
    Opt    = keepalive,
    Set    = fun(S, Val) when is_boolean(Val) ->
                     socket:setopt(S, socket, Opt, Val)
             end,
    Get    = fun(S) ->
                     socket:getopt(S, socket, Opt)
             end,

    TesterSeq =
        [
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           case which_local_host_info(Domain) of
                               {ok, #{name      := Name,
                                      addr      := Addr}} ->
                                   ?SEV_IPRINT("local host info: "
                                               "~n   Name: ~p"
                                               "~n   Addr: ~p",
                                               [Name, Addr]),
                                   LSA = #{family => Domain,
                                           addr   => Addr},
                                   {ok, State#{lsa => LSA}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "create TCP socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "Get current value",
           cmd  => fun(#{sock := Sock} = State) ->
                           case Get(Sock) of
                               {ok, Val} when is_boolean(Val) ->
                                   ?SEV_IPRINT("Success: ~p", [Val]),
                                   {ok, State#{keepalive => Val}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Try change the value",
           cmd  => fun(#{sock := Sock, keepalive := Current} = State) ->
			   New = not Current,
                           ?SEV_IPRINT("Try change value from ~p to ~p", 
                                       [Current, New]),
                           case Set(Sock, New) of
                               ok ->
                                   ?SEV_IPRINT("Expected Success"),
                                   {ok, State#{keepalive => New}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Verify (new) current value",
           cmd  => fun(#{sock := Sock, keepalive := Val} = _State) ->
                           case Get(Sock) of
                               {ok, Val} ->
                                   ?SEV_IPRINT("Expected Success (~p)", [Val]),
                                   ok;
                               {ok, OtherVal} ->
                                   ?SEV_IPRINT("Unexpected Success: ~p",
                                               [OtherVal]),
                                   {error, {unexpected_success_value,
                                            Val, OtherVal}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},

         %% *** Termination ***
         #{desc => "close UDP socket",
           cmd  => fun(#{sock := Sock} = State0) ->
                           socket:close(Sock),
			   State1 = maps:remove(sock, State0),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    Domain = inet_or_inet6(),

    i("start tester evaluator"),
    InitState = #{domain => Domain},
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option reuseaddr.
%% This is the most basic of tests. We only test that we can set the
%% option and then read back.

api_opt_sock_reuseaddr(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   %% [IPv4] Nothing to do with the option,
                   %% [IPv4] but we use it the test so make
                   %% [IPv4] use we have it.
                   has_support_ipv4(),
                   has_support_sock_reuseaddr()
           end,
           fun() -> api_opt_sock_reuseaddr() end).


api_opt_sock_reuseaddr() ->
    api_opt_simple_bool(inet, socket, stream, reuseaddr,
                       #{bind => false}).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option exclusiveaddruse.
%% This is the most basic of tests. We only test that we can set the
%% option and then read back.

api_opt_sock_exclusiveaddruse(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   %% [IPv4] Nothing to do with the option,
                   %% [IPv4] but we use it the test so make
                   %% [IPv4] use we have it.
                   has_support_ipv4(),
                   has_support_sock_exclusiveaddruse()
           end,
           fun() -> api_opt_sock_exclusiveaddruse() end).


api_opt_sock_exclusiveaddruse() ->
    api_opt_simple_bool(inet, socket, stream, exclusiveaddruse,
			#{bind => false}).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% *Simple* test for a bool option.
%% This basically just tests that we can set and get the option.
%% This assumes that the option is supported.
%% What if its required that the socket is bound before set/get?
%% What if its required that set/get is done before bind?

api_opt_simple_bool(Domain, Level, Type, Option, InitState) ->

    Set    = fun(S, Val) when is_boolean(Val) ->
                     socket:setopt(S, Level, Option, Val)
             end,
    Get    = fun(S) ->
                     socket:getopt(S, Level, Option)
             end,

    TesterSeq =
        [
         #{desc => "(maybe) which local address",
           cmd  => fun(#{bind := true} = State) ->
                           case ?LIB:which_local_host_info(Domain) of
                               {ok, #{name      := Name,
                                      addr      := Addr}} ->
                                   ?SEV_IPRINT("local host info: "
                                               "~n   Name: ~p"
                                               "~n   Addr: ~p",
                                               [Name, Addr]),
                                   LSA = #{family => Domain,
                                           addr   => Addr},
                                   {ok, State#{lsa => LSA}};
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (_State) ->
                           ?SEV_IPRINT("ignore get local address"),
                           ok
                   end},

         #{desc => "create socket",
           cmd  => fun(State) ->
                           case socket:open(Domain, Type) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "(maybe) bind",
           cmd  => fun(#{bind := true, lsa := LSA, sock := Sock} = _State) ->
                           ?SEV_IPRINT("try binding"),
                           socket:bind(Sock, LSA);
                      (_State) ->
                           ?SEV_IPRINT("ignore binding"),
                           ok
                   end},

         #{desc => "Get current value",
           cmd  => fun(#{sock := Sock} = State) ->
                           case Get(Sock) of
                               {ok, Val} when is_boolean(Val) ->
                                   ?SEV_IPRINT("Success: ~p", [Val]),
                                   {ok, State#{Option => Val}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Try change the value",
           cmd  => fun(#{sock := Sock} = State) ->
                           Current = maps:get(Option, State),
			   New     = not Current,
                           ?SEV_IPRINT("Try change value from ~p to ~p", 
                                       [Current, New]),
                           case Set(Sock, New) of
                               ok ->
                                   ?SEV_IPRINT("Expected Success"),
                                   {ok, State#{Option => New}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected Failure: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "Verify (new) current value",
           cmd  => fun(#{sock := Sock} = State) ->
                           Val = maps:get(Option, State),
                           case Get(Sock) of
                               {ok, Val} ->
                                   ?SEV_IPRINT("Expected Success (~p)", [Val]),
                                   ok;
                               {ok, OtherVal} ->
                                   ?SEV_IPRINT("Unexpected Success: ~p",
                                               [OtherVal]),
                                   {error, {unexpected_success_value,
                                            Val, OtherVal}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},

         %% *** Termination ***
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State0) ->
                           socket:close(Sock),
			   State1 = maps:remove(sock, State0),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start tester evaluator"),
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Tester]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option bsp_state.
%% This is the most basic of tests. We test that we can,
%% create sockets, bind and connect and extract bsp-state
%% in the various state(s) of the socket.
%% For both dgram and stream sockets.

api_opt_sock_bsp_state(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
		   %% This is not a 'IPv4' option,
		   %% but since we used it in the test...
                   has_support_ipv4(),
                   has_support_sock_bsp_state()
           end,
           fun() -> api_opt_sock_bsp_state() end).


api_opt_sock_bsp_state() ->
    LSA        = which_local_socket_addr(inet),
    BspState   = fun(S) ->
			 case socket:getopt(S, socket, bsp_state) of
			     {ok, BS} ->
				 BS;
			     {error, Reason} ->
				 ?FAIL({getopt_bsp_state, Reason})
			 end
		 end,
    CreateSock = fun(T, P) ->
			 case socket:open(inet, T, P) of
			     {ok, Sock}      ->
				 Sock;
			     {error, Reason} ->
				 skip({socket_create_fail, Reason})
			 end
		 end,
    CloseSock = fun(S) ->
			socket:close(S)
		end,
    BindSock   = fun(S, SA) ->
			 case socket:bind(S, SA) of
			     ok ->
				 ok;
			     {error, Reason} ->
				 ?FAIL({bind, Reason})
			 end
		 end,
    Sockname   = fun(S) ->
			 case socket:sockname(S) of
			     {ok, SA} ->
				 SA;
			     {error, Reason} ->
				 ?FAIL({sockname, Reason})
			 end
		 end,
    %% Setopt      = fun(S, L, O, V) ->
    %% 			  socket:setopt(S, L, O, V)
    %% 		  end,
    %% SetOtpOpt = fun(S, O, V) -> Setopt(S, otp, O, V) end,
    %% SetDebug  = fun(S, D) when is_boolean(D) -> SetOptOpt(S, debug, D) end,
    %% EnableDebug = fun(S) -> SetDebug(S, true) end,
    ConnectSock = fun(S, SA) ->
			  case socket:connect(S, SA) of
			      ok ->
				  ok;
			      {error, Reason} ->
				  ?FAIL({connect, Reason})
			  end
		  end,
    ListenSock = fun(S) ->
			 case socket:listen(S) of
			     ok ->
				 ok;
			     {error, Reason} ->
				 ?FAIL({listen, Reason})
			 end
		 end,
    AcceptSock = fun(S) ->
			 case socket:accept(S) of
			     {ok, A} ->
				 A;
			     {error, Reason} ->
				 ?FAIL({accept, Reason})
			 end
		 end,

    VerifyBspState = fun(S, Type, Proto,
			 Bound, Connected) ->
			     verify_bsp_state(S, Type, Proto,
					      Bound, Connected)
		     end,

    ?P("Create UDP socket 1:"),
    US1 = CreateSock(dgram, udp),
    ?P("UDP[1] [Unbound | Unconnected] => ~p", [BspState(US1)]),
    VerifyBspState(BspState(US1), dgram, udp, false, false),

    ?P("Create UDP socket 2:"),
    US2 = CreateSock(dgram, udp),
    ?P("UDP[2] [Unbound | Unconnected] => ~p", [BspState(US2)]),
    VerifyBspState(BspState(US2), dgram, udp, false, false),

    ?P("Bind UDP socket 1"),
    BindSock(US1, LSA),
    ?P("UDP[1] [Bound | Unconnected]   => ~p", [BspState(US1)]),
    VerifyBspState(BspState(US1), dgram, udp, true, false),

    ?P("Bind UDP socket 2"),
    BindSock(US2, LSA),
    ?P("UDP[2] [Bound | Unconnected]   => ~p", [BspState(US2)]),
    VerifyBspState(BspState(US2), dgram, udp, true, false),

    %% We have not yet implemented 'connect' for UDP on Windows,
    %% so we leave this commented for now:

    %% ?P("socknames"),
    %% USN1 = Sockname(US1),
    %% USN2 = Sockname(US2),

    %% ?P("enable debug for US1"),
    %% EnableDebug(US1),

    %% ?P("Connect UDP socket 1 to"
    %%    "~n   ~p", [USN2]),
    %% ConnectSock(US1, USN2),
    %% ?P("UDP[1] [Bound | Connected]     => ~p", [BspState(US1)]),

    %% ?P("Connect UDP socket 2 to"
    %%    "~n   ~p", [USN1]),
    %% ConnectSock(US2, USN1),
    %% ?P("UDP[2] [Bound | Connected]     => ~p", [BspState(US2)]),


    ?P("Create TCP socket 1:"),
    TS1 = CreateSock(stream, tcp),
    ?P("TCP[1] [Unbound | Unconnected] => ~p", [BspState(TS1)]),
    VerifyBspState(BspState(TS1), stream, tcp, false, false),

    ?P("Create TCP socket 2 (listen):"),
    TS2 = CreateSock(stream, tcp),
    ?P("TCP[2] [Unbound | Unconnected] => ~p", [BspState(TS2)]),
    VerifyBspState(BspState(TS2), stream, tcp, false, false),

    ?P("Bind TCP socket 1"),
    BindSock(TS1, LSA),
    ?P("TCP[1] [Bound | Unconnected]   => ~p", [BspState(TS1)]),
    VerifyBspState(BspState(TS1), stream, tcp, true, false),

    ?P("Bind TCP socket 2"),
    BindSock(TS2, LSA),
    ?P("TCP[2] [Bound | Unconnected]   => ~p", [BspState(TS2)]),
    VerifyBspState(BspState(TS2), stream, tcp, true, false),

    ?P("Make TCP socket 2 listen"),
    ListenSock(TS2),

    ?P("socknames"),
    TSN2 = Sockname(TS2),

    ?P("Connect TCP socket 1 to"
       "~n   ~p", [TSN2]),
    ConnectSock(TS1, TSN2),
    ?P("TCP[1] [Bound | Connected]   => ~p", [BspState(TS1)]),
    VerifyBspState(BspState(TS1), stream, tcp, true, true),

    ?P("Accept TCP socket 3"),
    TS3 = AcceptSock(TS2),
    ?P("TCP[3] [Bound | Connected]   => ~p", [BspState(TS3)]),
    VerifyBspState(BspState(TS3), stream, tcp, true, true),

    ?P("Close socket(s)"),
    CloseSock(TS3),
    CloseSock(TS2),
    CloseSock(TS1),

    ?P("done"),
    ok.


verify_bsp_state(#{type        := T,
		   protocol    := P,
		   local_addr  := LA,
		   remote_addr := RA},
		 Type, Proto,
		 Bound, Connected) when (T =:= Type) andalso (P =:= Proto) ->
    case {Bound, LA} of
	{false, undefined} ->
	    ok;
	{true, _} when (LA =/= undefined) ->
	    ok;
	_ ->
	    ?FAIL({invalid_bound_la, Bound, LA})
    end,
    case {Connected, RA} of
	{false, undefined} ->
	    ok;
	{true, _} when (RA =/= undefined) ->
	    ok;
	_ ->
	    ?FAIL({invalid_connected_ra, Connected, RA})
    end,
    ok;
verify_bsp_state(#{type     := T,
		   protocol := P},
		 Type, Proto,
		 _Bound, _Connected) ->
    ?FAIL({invalid_type_or_proto, {T, Type}, {P, Proto}}).


    
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option linger. PLACEHOLDER!

api_opt_sock_linger(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_opt_sock_linger,
           fun() -> not_yet_implemented() end,
           fun() -> ok end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option mark. PLACEHOLDER!

api_opt_sock_mark(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_opt_sock_mark,
           fun() -> not_yet_implemented() end,
           fun() -> ok end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option maxdg.

api_opt_sock_maxdg(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
	   fun() ->
		   %% This is not a 'IPv4' option,
		   %% but since we used it in the test...
                   has_support_ipv4(),
                   has_support_sock_maxdg()
           end,
           fun() -> do_api_opt_sock_maxdg() end).

do_api_opt_sock_maxdg() ->
    ?P("create DGRAM socket"),
    {ok, S} = socket:open(inet, dgram),
    ?P("get maxdg"),
    case socket:getopt(S, socket, maxdg) of
	{ok, Sz} ->
	    ?P("success: Sz = ~p", [Sz]),
	    ok;
	{error, Reason} ->
	    ?FAIL({failed_get_maxdg, Reason})
    end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the socket option max_msg_size.

api_opt_sock_max_msg_size(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
	   fun() ->
		   %% This is not a 'IPv4' option,
		   %% but since we used it in the test...
                   has_support_ipv4(),
                   has_support_sock_max_msg_size()
           end,
           fun() -> do_api_opt_sock_max_msg_size() end).

do_api_opt_sock_max_msg_size() ->
    {ok, S} = socket:open(inet, dgram),
    case socket:getopt(S, socket, max_msg_size) of
	{ok, Sz} ->
	    ?P("success: Sz = ~p", [Sz]),
	    ok;
	{error, Reason} ->
	    ?FAIL({failed_get_max_msg_size, Reason})
    end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% This test case tries to test that the oobinline socket 'socket' option
%% works.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, socket, oobinline, boolean()).
%%
%% This works on linux of some version (at least linux kernel 4.15.0),
%% but not on FreeBSD (12) for some reason. Until we have figured out
%% exctly why, we skip a bunch of OSs...
%%
%% Do we need to make sure the two entities does not run in the same
%% process? This test case does not currently do that (which works in'
%% linux but maybe not in, say, FreeBSD).
%%

api_opt_sock_oobinline(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_ooinline,
           fun() ->
                   has_support_sock_oobinline(),
                   has_support_msg_flag(oob),
                   is_valid_oobinline_platform()
           end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, socket, oobinline, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, socket, oobinline)
                          end,
                   Send = fun(Sock, Data, true) ->
                                  socket:send(Sock, Data, [oob]);
                             (Sock, Data, false) ->
                                     socket:send(Sock, Data)
                          end,
                   Recv = fun(Sock, true) ->
                                  socket:recv(Sock, 0, [oob]);
                             (Sock, false) ->
                                  socket:recv(Sock)
                          end,
                   InitState = #{domain => inet_or_inet6(),
                                 proto  => tcp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = do_api_opt_sock_oobinline(InitState)
           end).

%% Hopefully this is a temporary solution...
is_valid_oobinline_platform() ->
    case os:type() of
        {unix, linux} ->
            ok;

        Type ->
            %% Actually, all we know is that the
            %% test case only work for linux, but
            %% it *should* for FreeBSD and Solaris
            %% also...
            not_supported(Type)
    end.
    



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

do_api_opt_sock_oobinline(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain := local,
                         lsock  := LSock,
                         lsa    := LSA} = _State) ->
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain := local,
                         tester := Tester, lsa := #{path := Path}}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Path),
                           ok;
                      (#{tester := Tester, lport := Port}) ->
                           %% This is actually not used for unix domain socket
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},


         %% 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},

         %% *** no oobinline ***

         #{desc => "await continue (verify no oobinline)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, verify_oobinline)
                   end},
         #{desc => "verify no oobinline",
           cmd  => fun(#{csock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = _Value} ->
                                   ?SEV_IPRINT("oobinline: ~p", [_Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected oobinline: ~p",
                                               [Unexpected]),
                                   {error, {unexpected_oobinline, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting oobinline:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end                                   
                   end},
         #{desc => "announce ready (no oobinline)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, no_oobinline),
                           ok
                   end},

         #{desc => "await continue (recv no oobinline)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
                   end},
         #{desc => "await plain data",
           cmd  => fun(#{csock := Sock, recv := Recv}) ->
                           case Recv(Sock, false) of
                               {ok, <<"a">>} ->
                                   ?SEV_IPRINT("received plain data"),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (plain data)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_plain),
                           ok
                   end},
         #{desc => "await oob data",
           cmd  => fun(#{csock := Sock, recv := Recv}) ->
                           case Recv(Sock, true) of
                               {ok, <<"b">>} ->
                                   ?SEV_IPRINT("received oob data"),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (oob data)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_oob),
                           ok
                   end},

         %% *** oobinline ***

          #{desc => "await continue (enable oobinline)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, enable_oobinline)
                   end},
         #{desc => "enable oobinline",
           cmd  => fun(#{csock := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("oobinline enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed enable oobinline:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end                                   
                   end},
         #{desc => "announce ready (oobinline)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, oobinline),
                           ok
                   end},

         #{desc => "await continue (recv)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv)
                   end},
       #{desc => "await (recv) data",
           cmd  => fun(#{csock := Sock, recv := Recv}) ->
                           case Recv(Sock, false) of
                               {ok, <<"ba">>} ->
                                   ?SEV_IPRINT("received expected message: "
                                               "both plain and oob data"),
                                   ok;
                               {ok, BadMsg} ->
                                   ?SEV_EPRINT("received unexpected message: ~p",
                                               [BadMsg]),
                                   {error, {unexpected_msg, BadMsg}};
                               {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 connection socket",
           cmd  => fun(#{csock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(csock, State)}
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{domain   := local,
                         lsock    := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)};
                      (#{lsock := 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 ***
         #{desc => "await start (from tester)",
           cmd  => fun(#{domain := local} = State) ->
                           {Tester, Path} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_path => Path}};
                      (State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain      := local = Domain,
                         server_path := Path} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = #{family => Domain, path => Path},
                           {ok, State#{local_sa => LSA, server_sa => SSA}};
                      (#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   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},

         %% *** First batch of data (no oobinline) ***

         #{desc => "await continue (send data)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send)
                   end},
         #{desc => "send plain data",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, <<"a">>, false)
                   end},
         %% #{desc => "enable (socket & global) debug",
         %%   cmd  => fun(#{sock := Sock}) ->
         %%                   ok = socket:setopt(Sock, otp, debug, true),
         %%                   ok = socket:debug(true)
         %%           end},
         #{desc => "send oob data",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, <<"b">>, true)
                   end},
         %% #{desc => "disable (socket) debug",
         %%   cmd  => fun(#{sock := Sock}) ->
         %%                   ok = socket:debug(true),
         %%                   ok = socket:setopt(Sock, otp, debug, false)
         %%           end},
         #{desc => "announce ready (send)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send),
                           ok
                   end},

         %% *** Second batch of data (oobinline) ***

         #{desc => "await continue (send data)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send)
                   end},
         #{desc => "send plain data",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, <<"a">>, false)
                   end},
         #{desc => "send oob data",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, <<"b">>, true)
                   end},
         #{desc => "announce ready (send)",
           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(#{domain   := local,
                         sock     := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(sock, State1)};
                      (#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?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},
         ?SEV_SLEEP(?SECS(1)),
         #{desc => "order client to continue (with connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, connect),
                           ok
                   end},
         #{desc => "await client ready (connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, connect)
                   end},
         #{desc => "await server ready (accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, accept)
                   end},

         %% *** First batch of data (no oobinline) ***

         #{desc => "order server to continue (with verify no oobinline)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, verify_oobinline),
                           ok
                   end},
         #{desc => "await server ready (no oobinline)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, no_oobinline)
                   end},

         #{desc => "order client to continue (with send)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send),
                           ok
                   end},
         #{desc => "await client ready (with send)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send)
                   end},
         #{desc => "order server to continue (with recv plain)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, recv),
                           ok
                   end},
         #{desc => "await server ready (recv plain)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_plain)
                   end},
         #{desc => "await server ready (recv oob)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_oob)
                   end},

         %% Second message (w timestamp)

         #{desc => "order server to continue (with enable oobinline)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, enable_oobinline),
                           ok
                   end},
         #{desc => "await server ready (oobinline)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, oobinline)
                   end},

         #{desc => "order client to continue (with send)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send),
                           ok
                   end},
         #{desc => "await client ready (with send)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send)
                   end},
         #{desc => "order server to continue (with recv)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, recv),
                           ok
                   end},
         #{desc => "await server ready (recv plain)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, 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) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client, State),
                           {ok, State1}
                   end},
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start client evaluator"),
    Client = ?SEV_START("client", ClientSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{server => Server#ev.pid,
                        client => Client#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the credentials control message header is received when
%% setting the socket 'socket' option true when using sendmsg/recvmsg
%% on an IPv4 TCP (stream) socket.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, socket, passcred, boolean()).
%%
%% We *may* need to run the different entities (server and client) in 
%% separate VM (os processes) for this to actually work.
%% As it is now, the client does *not* get any credentials!
%% Until this has been done, this case is skipped!.

api_opt_sock_passcred_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_passcred_tcp4,
           fun() -> has_support_sock_passcred(),
                    not_yet_implemented()
           end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, socket, passcred, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, socket, passcred)
                          end,
                   Send = fun(Sock, Data) ->
                                  Msg = #{iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_sock_passcred_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_sock_passcred_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain := local,
                         lsock  := LSock,
                         lsa    := LSA} = _State) ->
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain := local,
                         tester := Tester, lsa := #{path := Path}}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Path),
                           ok;
                      (#{tester := Tester, lport := Port}) ->
                           %% This is actually not used for unix domain socket
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},


         %% 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},

         %% *** First message ***

         #{desc => "await (recv) request 1",
           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},

         %% *** Second message ***

         #{desc => "await (recv) request 2",
           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},

         %% *** Third message ***

         #{desc => "await (recv) request 3",
           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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(csock, State)}
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{domain   := local,
                         lsock    := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)};
                      (#{lsock := 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 ***
         #{desc => "await start (from tester)",
           cmd  => fun(#{domain := local} = State) ->
                           {Tester, Path} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_path => Path}};
                      (State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain      := local = Domain,
                         server_path := Path} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = #{family => Domain, path => Path},
                           {ok, State#{local_sa => LSA, server_sa => SSA}};
                      (#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   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},

         %% *** First message (default=wo passcred) ***

         #{desc => "await continue (verify timestamp off)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, verify_passcred)
                   end},
         #{desc => "verify passcred off",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = _Value} ->
                                   ?SEV_IPRINT("passcred: ~p", [_Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected passcred: ~p",
                                               [Unexpected]),
                                   {error, {unexpected_passcred, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting passcred:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end                                   
                   end},
         #{desc => "announce ready (passcred off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, passcred_off),
                           ok
                   end},

         #{desc => "await continue (send request)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 1 (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 1 (from server, wo passcred)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {[], ?BASIC_REP}} ->
                                   ok;
                               {ok, {[], UnexpData}} ->
                                   {error, {unexpected_reply_data, UnexpData}};
                               {ok, {BadCMsgs, ?BASIC_REP}} ->
                                   {error, {unexpected_reply_cmsgs,
                                            BadCMsgs}};
                               {ok, BadReply} ->
                                   {error, {unexpected_reply, BadReply}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 1 (recv reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_reply),
                           ok
                   end},

         %% *** Second message (w passcred) ***

         #{desc => "await continue (enable passcred)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, enable_passcred)
                   end},
         #{desc => "enable passcred",
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("passcred enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed enable passcred:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end                                   
                   end},
         #{desc => "announce ready (passcred on)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, passcred_on),
                           ok
                   end},

         #{desc => "await continue (send request 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 2 (to server)",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REQ)
                   end},
         #{desc => "announce ready (send request 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await recv reply 2 (from server, w passcred)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           %% socket:setopt(Sock, otp, debug, true),
                           case Recv(Sock) of
                               {ok, {[#{level := socket,
                                        type  := passcred,
                                        value := Cred}], ?BASIC_REP}} ->
                                   %% socket:setopt(Sock, otp, debug, false),
                                   ?SEV_IPRINT("received reply *with* "
                                               "expected passcred: "
                                               "~n   ~p", [Cred]),
                                   ok;
                               {ok, {BadCMsgs, ?BASIC_REP}} ->
                                   %% socket:setopt(Sock, otp, debug, false),
                                   {error, {unexpected_reply_cmsgs,
                                            BadCMsgs}};
                               {ok, {[#{level := socket,
                                        type  := passcred,
                                        value := _Cred}], BadData}} ->
                                   %% socket:setopt(Sock, otp, debug, false),
                                   {error, {unexpected_reply_data,
                                            BadData}};
                               {ok, BadReply} ->
                                   %% socket:setopt(Sock, otp, debug, false),
                                   {error, {unexpected_reply, BadReply}};
                               {error, _} = ERROR ->
                                   %% socket:setopt(Sock, otp, debug, false),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 2 (recv reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_reply),
                           ok
                   end},

         %% *** Third message (wo passcred) ***

         #{desc => "await continue (disable passcred)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, disable_passcred)
                   end},
         #{desc => "disable passcred",
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, false) of
                               ok ->
                                   ?SEV_IPRINT("passcred disabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed disable timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end                                   
                   end},
         #{desc => "announce ready (passcred off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, passcred_off),
                           ok
                   end},

         #{desc => "await continue (send request 3)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 3 (to server)",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REQ)
                   end},
         #{desc => "announce ready (send request 3)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await recv reply 3 (from server, wo passcred)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {[], ?BASIC_REP}} ->
                                   ?SEV_IPRINT("received reply *without* "
                                               "passcred"),
                                   ok;
                               {ok, {BadCMsgs, ?BASIC_REP}} ->
                                   {error, {unexpected_reply_cmsgs,
                                            BadCMsgs}};
                               {ok, {[], BadData}} ->
                                   {error, {unexpected_reply_data,
                                            BadData}};
                               {ok, BadReply} ->
                                   {error, {unexpected_reply, BadReply}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 3 (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(#{domain   := local,
                         sock     := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(sock, State1)};
                      (#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?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},
         ?SEV_SLEEP(?SECS(1)),
         #{desc => "order client to continue (with connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, connect),
                           ok
                   end},
         #{desc => "await client ready (connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, connect)
                   end},
         #{desc => "await server ready (accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, accept)
                   end},

         %% *** First message (default=wo passcred) ***

         #{desc => "order client to continue (with verify timestamp off)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, verify_passcred),
                           ok
                   end},
         #{desc => "await client ready (passcred off)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, passcred_off)
                   end},

         #{desc => "order client to continue (with send request 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client ready (with send request 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_req)
                   end},
         #{desc => "await server ready (request recv 1)",
           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},

         %% Second message (w passcred)

         #{desc => "order client to continue (with enable passcred)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, enable_passcred),
                           ok
                   end},
         #{desc => "await client ready (passcred on)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, passcred_on)
                   end},

         #{desc => "order client to continue (with send request 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client ready (with send request 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_req)
                   end},
         #{desc => "await server ready (request recv 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order server to continue (with send reply 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
                           ok
                   end},
         #{desc => "await server ready (with reply sent 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_reply)
                   end},
         #{desc => "await client ready (reply recv 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, recv_reply)
                   end},

         %% Third message (wo passcred)

         #{desc => "order client to continue (with disable passcred)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, disable_passcred),
                           ok
                   end},
         #{desc => "await client ready (passcred off)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, passcred_off)
                   end},

         #{desc => "order client to continue (with send request 3)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client ready (with send request 3)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_req)
                   end},
         #{desc => "await server ready (request recv 3)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order server to continue (with send reply 3)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
                           ok
                   end},
         #{desc => "await server ready (with reply sent 3)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_reply)
                   end},
         #{desc => "await client ready (reply recv 3)",
           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("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]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the the peek-off socket option for a unix domain socket
%% (stream TCP in this case).
%%
%% THIS IS A PLACEHOLDER!!
%%
%%

api_opt_sock_peek_off_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_peek_off_tcpL,
           fun() ->
                   has_support_unix_domain_socket(),
                   has_support_sock_peek_off(),
                   has_support_msg_flag(peek)
           end,
           fun() ->
                   Set  = fun(Sock, Val) when is_integer(Val) ->
                                  socket:setopt(Sock, socket, peek_off, Val)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, socket, peek_off)
                          end,
                   Send = fun(Sock, Data) ->
                                  socket:send(Sock, Data)
                          end,
                   Recv = fun(Sock, L, false) ->
                                  socket:recv(Sock, L);
                             (Sock, L, true) ->
                                  socket:recv(Sock, L, [peek])
                          end,
                   InitState = #{domain => local,
                                 proto  => default, % Type = stream => tcp
                                 set    => Set,
                                 get    => Get,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_opt_sock_peek_off(InitState)
           end).

api_opt_sock_peek_off(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) 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 sock_bind(LSock, LSA) of
                               ok ->
                                   %% We do not care about the port for local
                                   ok;
                               {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, lsa := #{path := Path}}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Path),
                           ok
                   end},

         #{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},


         %% The actual test

         %% 1) peek (0 = everything: 1,2,3,4,5,6,7,8)
         #{desc => "1a: await continue (peek)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, peek)
                   end},
         #{desc => "1a: peek read",
           cmd  => fun(#{csock := Sock,
                         recv  := Recv} = _State) ->
                           case Recv(Sock, 0, true) of
                               {ok, <<1,2,3,4,5,6,7,8>>} ->
                                   ?SEV_IPRINT("peek'ed expected data"),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "1a: announce ready (peek)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, peek),
                           ok
                   end},

         #{desc => "1b: await continue (verify peek-off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, verify_peek_off)
                   end},
         #{desc => "1b: verify peek-off",
           cmd  => fun(#{csock := Sock,
                         get   := Get} = _State) ->
                           case Get(Sock) of
                               {ok, DefaultPeekOff} ->
                                   ?SEV_IPRINT("verify peek-off: ~w",
                                               [DefaultPeekOff]),
                                   ok;
                               {error, {not_supported, {socket, peek_off}}} ->
                                   {skip, "Not supported"};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "1b: announce ready (verify peek-off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, verify_peek_off),
                           ok
                   end},


         %% 2) set peek-off to 4
         #{desc => "2a: await continue (set peek-off: 4)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, set_peek_off)
                   end},
         #{desc => "2a: set peek-off: 4",
           cmd  => fun(#{csock := Sock,
                         set   := Set} = _State) ->
                           case Set(Sock, 4) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "2a: announce ready (set peek-off: 4)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, set_peek_off),
                           ok
                   end},

         #{desc => "2b: await continue (verify peek-off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, verify_peek_off)
                   end},
         #{desc => "2b: verify peek-off",
           cmd  => fun(#{csock := Sock,
                         get   := Get} = _State) ->
                           case Get(Sock) of
                               {ok, 4 = PeekOff} ->
                                   ?SEV_IPRINT("verify peek-off: ~w", [PeekOff]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "2b: announce ready (verify peek-off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, verify_peek_off),
                           ok
                   end},


         %% 3) peek (0 = everything: 5,6,7,8)
         %%    NOTE THAT THIS WILL MOVE THE PEEK-OFF "POINTER" TO THE END OF 
         %%    THE *CURRENT* DATA POSITION IN THE BUFFER (READY FOR NEXT BATCH
         %%    OF DATA).
         #{desc => "3a: await continue (peek)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, peek)
                   end},
         #{desc => "3a: peek read",
           cmd  => fun(#{csock := Sock,
                         recv  := Recv} = _State) ->
                           case Recv(Sock, 0, true) of
                               {ok, <<5,6,7,8>>} ->
                                   ?SEV_IPRINT("peek'ed expected data"),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "3a: announce ready (peek)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, peek),
                           ok
                   end},

         #{desc => "3b: await continue (verify peek-off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, verify_peek_off)
                   end},
         #{desc => "3b: verify peek-off",
           cmd  => fun(#{csock := Sock,
                         get   := Get} = _State) ->
                           case Get(Sock) of
                               {ok, 8 = PeekOff} ->
                                   ?SEV_IPRINT("verify peek-off: ~w", [PeekOff]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "3b: announce ready (verify peek-off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, verify_peek_off),
                           ok
                   end},


         %% 4) read two byte(s): 1,2
         #{desc => "4a: await continue (read 2 byte)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, read)
                   end},
         #{desc => "4a: read (2 bytes)",
           cmd  => fun(#{csock := Sock,
                         recv  := Recv} = _State) ->
                           case Recv(Sock, 2, false) of
                               {ok, <<1,2>>} ->
                                   ?SEV_IPRINT("read expected data"),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "4a: announce ready (read)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, read),
                           ok
                   end},

         #{desc => "4b: await continue (verify peek-off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, verify_peek_off)
                   end},
         #{desc => "4b: verify peek-off",
           cmd  => fun(#{csock := Sock,
                         get   := Get} = _State) ->
                           case Get(Sock) of
                               {ok, 6 = PeekOff} ->
                                   ?SEV_IPRINT("verify peek-off: ~w", [PeekOff]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "4b: announce ready (verify peek-off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, verify_peek_off),
                           ok
                   end},


         %% 5) read the rest: 3,4,5,6,7,8)
         #{desc => "5a: await continue (read the rest)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, read)
                   end},
         #{desc => "5a: read (the rest)",
           cmd  => fun(#{csock := Sock,
                         recv  := Recv} = _State) ->
                           case Recv(Sock, 0, false) of
                               {ok, <<3,4,5,6,7,8>>} ->
                                   ?SEV_IPRINT("read expected data"),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "5a: announce ready (read)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, read),
                           ok
                   end},

         #{desc => "5b: await continue (verify peek-off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, verify_peek_off)
                   end},
         #{desc => "5b: verify peek-off",
           cmd  => fun(#{csock := Sock,
                         get   := Get} = _State) ->
                           case Get(Sock) of
                               {ok, PeekOff} ->
                                   ?SEV_IPRINT("verify peek-off: ~w", [PeekOff]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "5b: announce ready (verify peek-off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, verify_peek_off),
                           ok
                   end},


         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close connection socket",
           cmd  => fun(#{csock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(csock, State)}
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{lsock := Sock,
                         lsa   := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    ClientSeq = 
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(#{domain := local} = State) ->
                           {Tester, Path} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_path => Path}}
                   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      := local = Domain,
                         server_path := Path} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = #{family => Domain, path => Path},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         #{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},


         %% *** The actual test ***
         #{desc => "await continue (send data)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_data)
                   end},
         #{desc => "send data (to server)",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, <<1:8/integer,
                                        2:8/integer,
                                        3:8/integer,
                                        4:8/integer,
                                        5:8/integer,
                                        6:8/integer,
                                        7:8/integer,
                                        8:8/integer>>)
                   end},
         #{desc => "announce ready (send data)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_data),
                           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,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(sock, State1)}
                   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},

         %% Establish the connection
         #{desc => "order server to continue (with accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, accept),
                           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},


         %% *** The actual test ***
         #{desc => "order client to continue (with send data)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_data),
                           ok
                   end},
         #{desc => "await client ready (with send data)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_data)
                   end},

         %% There is no way to be sure that the data has actually arrived,
         %% and with no data on the server side, the peek will fail.
         %% Hopefully a sleep will take care of this...
         ?SEV_SLEEP(?SECS(1)),

         %% 1) peek
         #{desc => "1a: order server to continue (peek)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, peek),
                           ok
                   end},
         #{desc => "1a: await server ready (peek)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, peek)
                   end},

         #{desc => "1b: order server to continue (verify peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, verify_peek_off),
                           ok
                   end},
         #{desc => "1b: await server ready (verify peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, verify_peek_off)
                   end},


         %% 2) set peek-off
         #{desc => "2a: order server to continue (set peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, set_peek_off),
                           ok
                   end},
         #{desc => "2a: await server ready (set peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, set_peek_off)
                   end},

         #{desc => "2b: order server to continue (verify peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, verify_peek_off),
                           ok
                   end},
         #{desc => "2b: await server ready (verify peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, verify_peek_off)
                   end},



         %% 3) peek
         #{desc => "3a: order server to continue (peek)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, peek),
                           ok
                   end},
         #{desc => "3a: await server ready (peek)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, peek)
                   end},

         #{desc => "3b: order server to continue (verify peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, verify_peek_off),
                           ok
                   end},
         #{desc => "3b: await server ready (verify peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, verify_peek_off)
                   end},



         %% 4) read part
         #{desc => "4a: order server to continue (read part)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, read),
                           ok
                   end},
         #{desc => "4a: await server ready (peek)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, read)
                   end},

         #{desc => "4b: order server to continue (verify peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, verify_peek_off),
                           ok
                   end},
         #{desc => "4b: await server ready (verify peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, verify_peek_off)
                   end},


         %% 5) read (the rest)
         #{desc => "5a: order server to continue (read the rest)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, read),
                           ok
                   end},
         #{desc => "5a: await server ready (peek)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, read)
                   end},

         #{desc => "5b: order server to continue (verify peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, verify_peek_off),
                           ok
                   end},
         #{desc => "5b: await server ready (verify peek-off)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, verify_peek_off)
                   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]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that we get the peer credentials for a connected unix domain
%% TCP (stream) socket.
%% That is, all we need to do is to create a node, and have 
%% process connect from that to a local (unix domain socket) socket.
%%
%% THIS IS A PLACEHOLDER!!
%%
%% We need to figure out what the ucred structure looks like,
%% and decode it...
%%

api_opt_sock_peercred_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_peercred_tcpL,
           fun() ->
                   has_support_unix_domain_socket(),
                   has_support_sock_peercred(),
                   not_yet_implemented()
           end,
           fun() ->
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, socket, peercred)
                          end,
                   InitState = #{domain => local,
                                 proto  => default, % Type = stream => tcp
                                 get    => Get},
                   ok = api_opt_sock_peercred_tcp(InitState)
           end).


api_opt_sock_peercred_tcp(_InitState) ->
    %% 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) ->
    %%                        LSA = which_local_socket_addr(Domain),
    %%                        {ok, State#{lsa => LSA}}
    %%                end},
    %%      #{desc => "create listen socket",
    %%        cmd  => fun(#{domain := Domain, proto := Proto} = State) ->
    %%                        case socket:open(Domain, stream, Proto) of
    %%                            {ok, Sock} ->
    %%                                {ok, State#{lsock => Sock}};
    %%                            {error, _} = ERROR ->
    %%                                ERROR
    %%                        end
    %%                end},
    %%      #{desc => "bind to local address",
    %%        cmd  => fun(#{domain := local,
    %%                      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(#{domain := local,
    %%                      tester := Tester, lsa := #{path := Path}}) ->
    %%                        ?SEV_ANNOUNCE_READY(Tester, init, Path),
    %%                        ok
    %%                end},


    %%      %% The actual test
    %%      #{desc => "await continue (accept)",
    %%        cmd  => fun(#{tester := Tester}) ->
    %%                        ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
    %%                end},
    %%      #{desc => "await connection",
    %%        cmd  => fun(#{lsock := LSock} = State) ->
    %%                        case socket:accept(LSock) of
    %%                            {ok, Sock} ->
    %%                                ?SEV_IPRINT("accepted: ~n   ~p", [Sock]),
    %%                                {ok, State#{csock => Sock}};
    %%                            {error, _} = ERROR ->
    %%                                ERROR
    %%                        end
    %%                end},
    %%      #{desc => "announce ready (accept)",
    %%        cmd  => fun(#{tester := Tester}) ->
    %%                        ?SEV_ANNOUNCE_READY(Tester, accept),
    %%                        ok
    %%                end},

    %%      #{desc => "await continue (peercred)",
    %%        cmd  => fun(#{tester := Tester}) ->
    %%                        ?SEV_AWAIT_CONTINUE(Tester, tester, peercred)
    %%                end},
    %%      #{desc => "get peercred",
    %%        cmd  => fun(#{csock := Sock, get := Get} = _State) ->
    %%                        case Get(Sock) of
    %%                            {ok, PeerCred} ->
    %%                                ?SEV_IPRINT("PeerCred: ~n   ~p", [PeerCred]),
    %%                                ok;
    %%                            {error, _} = ERROR ->
    %%                                ERROR
    %%                        end
    %%                end},
    %%      #{desc => "announce ready (peercred)",
    %%        cmd  => fun(#{tester := Tester}) ->
    %%                        ?SEV_ANNOUNCE_READY(Tester, peercred),
    %%                        ok
    %%                end},


    %%      %% Termination
    %%      #{desc => "await terminate",
    %%        cmd  => fun(#{tester := Tester} = State) ->
    %%                        case ?SEV_AWAIT_TERMINATE(Tester, tester) of
    %%                            ok ->
    %%                                {ok, maps:remove(tester, State)};
    %%                            {error, _} = ERROR ->
    %%                                ERROR
    %%                        end
    %%                end},
    %%      #{desc => "close connection socket",
    %%        cmd  => fun(#{csock := Sock} = State) ->
    %%                        ok = socket:close(Sock),
    %%                        {ok, maps:remove(csock, State)}
    %%                end},
    %%      #{desc => "close listen socket",
    %%        cmd  => fun(#{domain := local,
    %%                      lsock  := Sock,
    %%                      lsa    := #{path := Path}} = State) ->
    %%                        ok = socket:close(Sock),
    %%                        State1 =
    %%                            unlink_path(Path,
    %%                                        fun() ->
    %%                                                maps:remove(lsa, State)
    %%                                        end,
    %%                                        fun() -> State end),
    %%                        {ok, maps:remove(lsock, State1)}
    %%                end},

    %%      %% *** We are done ***
    %%      ?SEV_FINISH_NORMAL
    %%     ],


    %% ClientSeq =
    %%     [
    %%      %% *** Wait for start order part ***
    %%      #{desc => "await start",
    %%        cmd  => fun(#{domain := local} = State) ->
    %%                        {Tester, Path} = ?SEV_AWAIT_START(),
    %%                        {ok, State#{tester    => Tester,
    %%                                    server_path => Path}}
    %%                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      := local = Domain,
    %%                      server_path := Path} = State) ->
    %%                        LSA = which_local_socket_addr(Domain),
    %%                        SSA = #{family => Domain, path => Path},
    %%                        {ok, State#{local_sa => LSA, server_sa => SSA}}
    %%                end},
    %%      #{desc => "create node",
    %%        cmd  => fun(#{host := Host} = State) ->
    %%     		   ?SEV_IPRINT("try create node on ~p", [Host]),
    %%                        case ?CT_PEER() of
    %%                            {ok, Peer, Node} ->
    %%                                ?SEV_IPRINT("client node ~p started",
    %%                                            [Node]),
    %%                                {ok, State#{node => Node, peer => Peer}};
    %%                            {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_opt_sock_peercred_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,
    %%                     proto      := Proto,
    %%                      server_sa := ServerSA}) ->
    %%                        ?SEV_ANNOUNCE_START(Client, {Proto, 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(#{peer := Peer} = _State) ->
    %%                        peer:stop(Peer)
    %%                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) ->
    %%                        LSA = which_local_socket_addr(Domain),
    %%                        {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 accepts the connect from the client, announces
    %%      %% this to us (accept) and then attempts to get peercred.
    %%      #{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},
    %%      #{desc => "await server ready (accept)",
    %%        cmd  => fun(#{server := Server,
    %%                      client := Client} = _State) ->
    %%                        case ?SEV_AWAIT_READY(Server, server, accept,
    %%                                              [{client, Client}]) of
    %%                            ok ->
    %%                                ok;
    %%                            {error, _} = ERROR ->
    %%                                ERROR
    %%                        end
    %%                end},
    %%      #{desc => "await server ready (peercred)",
    %%        cmd  => fun(#{server := Server,
    %%                      client := Client} = _State) ->
    %%                        case ?SEV_AWAIT_READY(Server, server, peercred,
    %%                                              [{client, Client}]) 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]).


    %% This should actually never be called (the conditions should cause a skip),
    %% but just to be on the safe side...
    skip.


%% api_opt_sock_peercred_tcp_client_start(Node) ->
%%     Self = self(),
%%     Fun  = fun() -> api_opt_sock_peercred_tcp_client(Self) end,
%%     erlang:spawn(Node, Fun).

%% api_opt_sock_peercred_tcp_client(Parent) ->
%%     api_opt_sock_peercred_tcp_client_init(Parent),
%%     {Proto, ServerSA} = api_opt_sock_peercred_tcp_client_await_start(Parent),
%%     Domain   = maps:get(family, ServerSA),
%%     api_opt_sock_peercred_tcp_client_announce_ready(Parent, init),
%%     api_opt_sock_peercred_tcp_client_await_continue(Parent, connect),
%%     Result = api_opt_sock_peercred_tcp_client_connect(Domain, Proto, ServerSA),
%%     ?SEV_IPRINT("result: ~p", [Result]),
%%     api_opt_sock_peercred_tcp_client_announce_ready(Parent, connect, Result),
%%     Reason = api_opt_sock_peercred_tcp_client_await_terminate(Parent),
%%     api_opt_sock_peercred_tcp_client_close(Result),
%%     exit(Reason).

%% api_opt_sock_peercred_tcp_client_init(Parent) ->
%%     put(sname, "rclient"),
%%     _MRef = erlang:monitor(process, Parent),
%%     ok.

%% api_opt_sock_peercred_tcp_client_await_start(Parent) ->
%%     ?SEV_AWAIT_START(Parent).

%% api_opt_sock_peercred_tcp_client_announce_ready(Parent, Slogan) ->
%%     ?SEV_ANNOUNCE_READY(Parent, Slogan).
%% api_opt_sock_peercred_tcp_client_announce_ready(Parent, Slogan, Result) ->
%%     ?SEV_ANNOUNCE_READY(Parent, Slogan, Result).

%% api_opt_sock_peercred_tcp_client_await_continue(Parent, Slogan) ->
%%     case ?SEV_AWAIT_CONTINUE(Parent, parent, Slogan) of
%%         ok ->
%%             ok;
%%         {ok, Extra} ->
%%             Extra;
%%         {error, Reason} ->
%%             exit({await_continue, Slogan, Reason})
%%     end.

%% api_opt_sock_peercred_tcp_client_await_terminate(Parent) ->
%%     case ?SEV_AWAIT_TERMINATE(Parent, parent) of
%%         ok ->
%%             ok;
%%         {error, Reason} ->
%%             Reason
%%     end.

%% api_opt_sock_peercred_tcp_client_connect(Domain, Proto, ServerSA) ->
%%     LSA  = which_local_socket_addr(Domain),
%%     Sock = case socket:open(Domain, stream, Proto) of
%%                {ok, S} ->
%%                    S;
%%                {error, OReason} ->
%%                    ?FAIL({open, OReason})
%%            end,
%%     case socket:bind(Sock, LSA) of
%%         ok ->
%%             ok;
%%         {error, BReason} ->
%%             (catch socket:close(Sock)),
%%             ?FAIL({bind, BReason})
%%     end,
%%     case socket:connect(Sock, ServerSA) of
%%         ok ->
%%             {ok, Sock};
%%         {error, Reason} ->
%%             (catch socket:close(Sock)),
%%             ?FAIL({connect, Reason})
%%     end.

%% api_opt_sock_peercred_tcp_client_close({ok, Sock}) ->
%%     (catch socket:close(Sock));
%% api_opt_sock_peercred_tcp_client_close(_) ->
%%     ok.




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the 'PRIORITY' socket 'socket' option with IPv4 UDP:
%%
%%               socket:setopt(Sock, socket, priority, integer()).
%%
%%

api_opt_sock_priority_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_priority_udp4,
           fun() -> has_support_ipv4(), has_support_sock_priority() end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, socket, priority, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, socket, priority)
                          end,
                   InitState = #{domain => inet,
                                 type   => dgram,
                                 proto  => udp,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_sock_priority(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the 'PRIORITY' socket 'socket' option with IPv4 TCP:
%%
%%               socket:setopt(Sock, socket, priority, integer()).
%%
%%

api_opt_sock_priority_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_priority_tcp4,
           fun() -> has_support_ipv4(), has_support_sock_priority() end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, socket, priority, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, socket, priority)
                          end,
                   InitState = #{domain => inet,
                                 type   => stream,
                                 proto  => tcp,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_sock_priority(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_sock_priority(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},

         #{desc => "open socket",
           cmd  => fun(#{domain := Domain,
                         type   := Type,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, Type, Proto),
                           {ok, State#{sock => Sock}}
                   end},
         #{desc => "bind",
           cmd  => fun(#{sock := Sock, lsa := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "get current (default) priority",
           cmd  => fun(#{sock := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, Prio} ->
                                   ?SEV_IPRINT("(default) priority: ~p",
                                               [Prio]),
                                   {ok, State#{default => Prio}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) "
                                               "priority:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "change priority (to within non-root range)",
           cmd  => fun(#{sock    := Sock,
                         default := DefaultPrio,
                         set     := Set} = _State) ->
                           NewPrio =
                               if
                                   (DefaultPrio =< 0) andalso
                                   (DefaultPrio < 6) ->
                                       DefaultPrio+1;
                                   (DefaultPrio =:= 6) ->
                                       DefaultPrio-1;
                                   true ->
                                       3 % ...
                               end,
                           ?SEV_IPRINT("try set new priority (to ~p)",
                                       [NewPrio]),
                           case Set(Sock, NewPrio) of
                               ok ->
                                   ?SEV_IPRINT("priority changed (to ~p)",
                                               [NewPrio]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "change priority (to outside root-range)",
           cmd  => fun(#{sock := Sock,
                         set  := Set} = _State) ->
                           NewPrio = 42,
                           ?SEV_IPRINT("try set new priority (to ~p)",
                                       [NewPrio]),
                           case Set(Sock, NewPrio) of
                               ok ->
                                   ?SEV_IPRINT("priority changed (to ~p)",
                                               [NewPrio]),
                                   ok;
                               {error, eperm} ->
                                   ?SEV_IPRINT("priority change not allowed"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the 'SNDBUF' socket 'socket' option with IPv4 UDP:
%%
%%               socket:setopt(Sock, socket, sndbuf, integer()).
%%
%%

api_opt_sock_rcvbuf_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_rcvbuf_udp4,
           fun() -> has_support_ipv4(), has_support_sock_rcvbuf() end,
           fun() ->
                   ok = api_opt_sock_buf_udp4(rcvbuf)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the 'RCVBUF' socket 'socket' option with IPv4 UDP:
%%
%%               socket:setopt(Sock, socket, rcvbuf, integer()).
%%
%%

api_opt_sock_sndbuf_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_sndbuf_udp4,
           fun() -> has_support_ipv4(), has_support_sock_sndbuf() end,
           fun() ->
                   ok = api_opt_sock_buf_udp4(sndbuf)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_sock_buf_udp4(Opt) ->
    Set  = fun(Sock, Value) ->
                   socket:setopt(Sock, socket, Opt, Value)
           end,
    Get  = fun(Sock) ->
                   socket:getopt(Sock, socket, Opt)
           end,
    InitState = #{domain => inet,
                  type   => dgram,
                  proto  => udp,
                  set    => Set,
                  get    => Get},
    ok = api_opt_sock_buf(InitState).


api_opt_sock_buf(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},

         #{desc => "open socket",
           cmd  => fun(#{domain := Domain,
                         type   := Type,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, Type, Proto),
                           {ok, State#{sock => Sock}}
                   end},
         #{desc => "bind",
           cmd  => fun(#{sock := Sock, lsa := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "get current (default) buffer size",
           cmd  => fun(#{sock := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, Sz} ->
                                   ?SEV_IPRINT("(default) buffer: ~p",
                                               [Sz]),
                                   {ok, State#{default_sz => Sz}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) "
                                               "buffer size:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "change buffer size (default + 1024)",
           cmd  => fun(#{sock       := Sock,
                         default_sz := DefaultSz,
                         set        := Set} = State) ->
                           NewSz = DefaultSz + 1024,
                           ?SEV_IPRINT("try set new buffer size to ~w", [NewSz]),
                           case Set(Sock, NewSz) of
                               ok ->
                                   ?SEV_IPRINT("Buffer size change success", []),
                                   {ok, State#{new_sz => NewSz}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed changing buffer size:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "validate buffer change",
           cmd  => fun(#{sock   := Sock,
                         get    := Get,
                         new_sz := ExpSz} = _State) ->
                           ?SEV_IPRINT("try validate buffer size (~w)", [ExpSz]),
                           case Get(Sock) of
                               {ok, Sz} when (Sz >= ExpSz) ->
                                   ?SEV_IPRINT("buffer size validated:"
                                               "~n   Sz: ~w (~w)", [Sz, ExpSz]),
                                   ok;
                               {ok, Sz} ->
                                   ?SEV_EPRINT("buffer size invalid:"
                                               "~n   Sz:          ~w"
                                               "~n   Expected Sz: ~w", [Sz, ExpSz]),
                                   {error, {invalid_size, Sz, ExpSz}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed get buffer size:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the 'RCVTIMEO' socket 'socket' option with IPv4 UDP:
%%
%%               socket:setopt(Sock, socket, rcvtimeo, #{sec  => integer(),
%%                                                       usec => integer()}).
%%
%% We should really test that the receive behaves as expected,
%% but we don't (we just set the value and read it back...)
%%

api_opt_sock_rcvtimeo_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_rcvtimeo_udp4,
           fun() -> has_support_ipv4(), has_support_sock_rcvtimeo() end,
           fun() ->
                   ok = api_opt_sock_timeo_udp4(rcvtimeo)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the 'SNDTIMEO' socket 'socket' option with IPv4 UDP:
%%
%%               socket:setopt(Sock, socket, sndtimeo, integer()).
%%
%%

api_opt_sock_sndtimeo_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_sndtimeo_udp4,
           fun() -> has_support_ipv4(), has_support_sock_sndtimeo() end,
           fun() ->
                   ok = api_opt_sock_timeo_udp4(sndtimeo)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_sock_timeo_udp4(Opt) ->
    Set  = fun(Sock, Value) ->
                   socket:setopt(Sock, socket, Opt, Value)
           end,
    Get  = fun(Sock) ->
                   socket:getopt(Sock, socket, Opt)
           end,
    InitState = #{domain => inet,
                  type   => dgram,
                  proto  => udp,
		  opt   => Opt,
                  set    => Set,
                  get    => Get},
    ok = api_opt_sock_timeo(InitState).


api_opt_sock_timeo(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},

         #{desc => "open socket",
           cmd  => fun(#{domain := Domain,
                         type   := Type,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, Type, Proto),
                           {ok, State#{sock => Sock}}
                   end},
         #{desc => "bind",
           cmd  => fun(#{sock := Sock, lsa := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "get current (default) timeout",
           cmd  => fun(#{sock := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, #{sec := _, usec := _} = TO} ->
                                   ?SEV_IPRINT("(default) timeout: ~p", [TO]),
                                   {ok, State#{default_timeo => TO}};
                               {error, enoprotoopt = Reason} ->
                                   ?SEV_IPRINT("Failed getting (default) timeout:"
                                               "   ~p", [Reason]),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) timeout:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "change timeout",
           cmd  => fun(#{sock          := Sock,
                         default_timeo := #{sec := DefaultSec} = DefaultTO,
                         set           := Set} = State) ->
                           NewTO = DefaultTO#{sec => DefaultSec + 100},
                           ?SEV_IPRINT("try set new timeout to ~w", [NewTO]),
                           case Set(Sock, NewTO) of
                               ok ->
                                   ?SEV_IPRINT("Timeout change success", []),
                                   {ok, State#{new_timeo => NewTO}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed changing timeout:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "validate timeout change",
           cmd  => fun(#{sock      := Sock,
                         get       := Get,
                         new_timeo := #{sec := ExpSec} = ExpTO} = _State) ->
                           ?SEV_IPRINT("try validate timeout (~w)", [ExpTO]),
                           case Get(Sock) of
                               {ok, ExpTO} ->
                                   ?SEV_IPRINT("timeout (exactly) validated"),
                                   ok;
                               {ok, #{sec := Sec}} when (ExpSec =:= Sec) ->
				   %% For some reason OpenBSD "adjusts" the timeout,
				   %% so that usec does not (always match)
                                   ?SEV_IPRINT("timeout (approx) validated"),
                                   ok;
                               {ok, TO} ->
                                   ?SEV_EPRINT("timeout invalid:"
                                               "~n   Timeout:          ~w"
                                               "~n   Expected Timeout: ~w",
                                               [TO, ExpTO]),
                                   {error, {invalid_timeo, TO, ExpTO}};
                               {error, edom = Reason} ->
				   %% On OpenBSD (at least) its possible that if the value
				   %% is too far "out of bounds", this will be the result:
				   %%
				   %%     "Numerical argument out of domain"
				   %%
                                   ?SEV_IPRINT("Failed get timeout:"
                                               "   ~p", [Reason]),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed get timeout:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the 'RCVLOWAT' socket 'socket' option with IPv4 UDP:
%%
%%               socket:setopt(Sock, socket, rcvlowat, integer()).
%%
%%

api_opt_sock_rcvlowat_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_rcvlowat_udp4,
           fun() ->
                   is_not_windows(), % einval on Windows
                   has_support_ipv4(),
                   has_support_sock_rcvlowat()
           end,
           fun() ->
                   ok = api_opt_sock_lowat_udp4(rcvlowat)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the 'SNDLOWAT' socket 'socket' option with IPv4 UDP:
%%
%%               socket:setopt(Sock, socket, sndlowat, integer()).
%%
%% This is (currently) not changeable on linux (among others),
%% so we skip if we get ENOPROTOOPT when attempting a change.
%%

api_opt_sock_sndlowat_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_sndlowat_udp4,
           fun() ->
                   is_not_windows(), % einval on Windows
                   has_support_ipv4(),
                   has_support_sock_sndlowat()
           end,
           fun() ->
                   ok = api_opt_sock_lowat_udp4(sndlowat)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_sock_lowat_udp4(Opt) ->
    Set  = fun(Sock, Value) ->
                   socket:setopt(Sock, socket, Opt, Value)
           end,
    Get  = fun(Sock) ->
                   socket:getopt(Sock, socket, Opt)
           end,
    InitState = #{domain => inet,
                  type   => dgram,
                  proto  => udp,
                  set    => Set,
                  get    => Get},
    ok = api_opt_sock_lowat(InitState).


api_opt_sock_lowat(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},

         #{desc => "open socket",
           cmd  => fun(#{domain := Domain,
                         type   := Type,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, Type, Proto),
                           {ok, State#{sock => Sock}}
                   end},
         #{desc => "bind",
           cmd  => fun(#{sock := Sock, lsa := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "get current (default) lowat",
           cmd  => fun(#{sock := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, LOWAT} ->
                                   ?SEV_IPRINT("(default) lowat: ~p",
                                               [LOWAT]),
                                   {ok, State#{default_lowat => LOWAT}};
                               {error, enoprotoopt = Reason} ->
                                   ?SEV_IPRINT("Failed getting (default) lowat:"
                                               "   ~p", [Reason]),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) lowat:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "change lowat ( + 1 )",
           cmd  => fun(#{sock          := Sock,
                         default_lowat := DefaultLOWAT,
                         set           := Set} = State) ->
                           NewLOWAT = DefaultLOWAT + 1,
                           ?SEV_IPRINT("try set new lowat to ~w", [NewLOWAT]),
                           case Set(Sock, NewLOWAT) of
                               ok ->
                                   ?SEV_IPRINT("LOWAT change success", []),
                                   {ok, State#{new_lowat => NewLOWAT}};
                               {error, enoprotoopt} ->
                                   ?SEV_IPRINT("LOWAT not changeable", []),
                                   {skip, "Not changeable"};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed changing buffer size:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "validate lowat",
           cmd  => fun(#{sock      := Sock,
                         get       := Get,
                         new_lowat := ExpLOWAT} = _State) ->
                           ?SEV_IPRINT("try validate lowat (~w)", [ExpLOWAT]),
                           case Get(Sock) of
                               {ok, ExpLOWAT} ->
                                   ?SEV_IPRINT("lowat validated:"
                                               "~n   LOWAT: ~w", [ExpLOWAT]),
                                   ok;
                               {ok, LOWAT} ->
                                   ?SEV_EPRINT("lowat invalid:"
                                               "~n   LOWAT:          ~w"
                                               "~n   Expected LOWAT: ~w",
                                               [LOWAT, ExpLOWAT]),
                                   {error, {invalid_lowat, LOWAT, ExpLOWAT}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed get lowat:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the timestamp control message header is received when
%% setting the socket 'socket' option true when using sendmsg/recvmsg
%% on an IPv4 UDP (dgram) socket.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, socket, timestamp, boolean()).
%%
%% All subsequent *received* messages will be timestamped.
%%

api_opt_sock_timestamp_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_timestamp_udp4,
           fun() -> has_support_ipv4(), has_support_sock_timestamp() end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, socket, timestamp, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, socket, timestamp)
                          end,
                   Send = fun(Sock, Data, Dest) ->
                                  Msg = #{addr => Dest,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_sock_timestamp_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_sock_timestamp_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := local = Domain} = State) ->
                           LSASrc = which_local_socket_addr(Domain),
                           LSADst = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSASrc,
                                       lsa_dst => LSADst}};
                      (#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},
         #{desc => "get current (default) timestamp for src socket",
           cmd  => fun(#{sock_src := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("src timestamp: ~p", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected src timestamp: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},

         #{desc => "send req (to dst) (WO TIMESTAMP)",
           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, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 => "enable timestamp on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("dst timestamp enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},


         #{desc => "send req 1 (to dst) (W TIMESTAMP)",
           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, [#{level := socket,
                                             type  := timestamp,
                                             value := TS}], ?BASIC_REQ}} ->
                                   ?SEV_IPRINT("received req *with* "
                                               "expected timestamp: "
                                               "~n   ~p", [TS]),
                                   ok;
                               {ok, {Src, CMsgs, ?BASIC_REQ}} ->
                                   ?SEV_EPRINT("Unexpected control message(s):"
                                               "~n   CMsgs: ~p",
                                               [CMsgs]),
                                   {error, {unexpected_cmsgs, CMsgs}};
                               {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 => "send req 2 (to dst) (W TIMESTAMP)",
           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, [#{level := socket,
                                             type  := timestamp,
                                             value := TS}], ?BASIC_REQ}} ->
                                   ?SEV_IPRINT("received req *with* "
                                               "expected timestamp: "
                                               "~n   ~p", [TS]),
                                   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 => "disable timestamps on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = _State) ->
                           case Set(Sock, false) of
                               ok ->
                                   ?SEV_IPRINT("dst timestamp disabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},


         #{desc => "send req (to dst) (WO TIMESTAMP)",
           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}} ->
                                   ?SEV_IPRINT("received req *without* timestamp"),
                                   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(#{domain   := local,
                         sock_src := Sock,
                         lsa_src  := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() -> maps:remove(lsa_src, State) end,
                                           fun() -> State end),
                           {ok, maps:remove(sock_src, State1)};
                      (#{sock_src := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{domain   := local,
                         sock_dst := Sock,
                         lsa_dst  := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() -> maps:remove(lsa_dst, State) end,
                                           fun() -> State end),
                           {ok, maps:remove(sock_dst, State1)};
                      (#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the timestamp control message header is received when
%% setting the socket 'socket' option true when using sendmsg/recvmsg
%% on an IPv4 TCP (stream) socket.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, socket, timestamp, boolean()).
%%
%% All subsequent *received* messages will be timestamped.
%%
%% There is no mention of this not working for TCP in the man page
%% on a SLES 11 SP4 machine (=> 3.0.101-108.87), but it does not
%% (we don't get a timestamp control message header when its enabled).
%% It also does not work on SLES 12 SP2 (=> 4.4.120-92.70), 
%% so we start by skipping from that version (4.4.120) or older!
%% Don't actually know if its the distro or the (kernel) version...
%%

api_opt_sock_timestamp_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_sock_timestamp_tcp4,
           fun() ->
                   has_support_ipv4(),
                   has_support_sock_timestamp(),
                   is_good_enough_linux({4,4,120}),
                   is_not_freebsd(),
                   is_not_openbsd(),
                   is_not_netbsd(),
                   is_not_darwin()
           end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, socket, timestamp, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, socket, timestamp)
                          end,
                   Send = fun(Sock, Data) ->
                                  Msg = #{iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_sock_timestamp_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_sock_timestamp_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain := local,
                         lsock  := LSock,
                         lsa    := LSA} = _State) ->
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain := local,
                         tester := Tester, lsa := #{path := Path}}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Path),
                           ok;
                      (#{tester := Tester, lport := Port}) ->
                           %% This is actually not used for unix domain socket
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},


         %% 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},

         %% *** First message ***

         #{desc => "await (recv) request 1",
           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},

         %% *** Second message ***

         #{desc => "await (recv) request 2",
           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},

         %% *** Third message ***

         #{desc => "await (recv) request 3",
           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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(csock, State)}
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{domain   := local,
                         lsock    := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)};
                      (#{lsock := 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 ***
         #{desc => "await start (from tester)",
           cmd  => fun(#{domain := local} = State) ->
                           {Tester, Path} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_path => Path}};
                      (State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain      := local = Domain,
                         server_path := Path} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = #{family => Domain, path => Path},
                           {ok, State#{local_sa => LSA, server_sa => SSA}};
                      (#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   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},

         %% *** First message (default=wo timestamp) ***

         #{desc => "await continue (verify timestamp off)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, verify_timestamp)
                   end},
         #{desc => "verify timestamp off",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = _Value} ->
                                   ?SEV_IPRINT("timestamp: ~p", [_Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected timestamp: ~p",
                                               [Unexpected]),
                                   {error, {unexpected_timestamp, Unexpected}};
                               {error, enoprotoopt = Reason} ->
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end                                   
                   end},
         #{desc => "announce ready (timestamp off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, timestamp_off),
                           ok
                   end},

         #{desc => "await continue (send request)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 1 (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 1 (from server, wo timestamp)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {[], ?BASIC_REP}} ->
                                   ok;
                               {ok, {[], UnexpData}} ->
                                   {error, {unexpected_reply_data, UnexpData}};
                               {ok, {BadCMsgs, ?BASIC_REP}} ->
                                   {error, {unexpected_reply_cmsgs,
                                            BadCMsgs}};
                               {ok, BadReply} ->
                                   {error, {unexpected_reply, BadReply}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 1 (recv reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_reply),
                           ok
                   end},

         %% *** Second message (w timestamp) ***

         #{desc => "await continue (enable timestamp)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, enable_timestamp)
                   end},
         #{desc => "enable timestamp",
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("timestamp enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed enable timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end                                   
                   end},
         %% Linux peculiarity observed here...
         %% Detected on Kernel 4.15.0-72 x96_64.
         %% The option set to enable receiving timestamps just above
         %% has failed to be effective down in "await recv reply 2
         %% (from server, w timestamp)" below, unless we put the
         %% sleep between setting the option and informing
         %% the writer that it shall write to the other socket end.
         %% A sleep 1 ms improves a lot but does not remove
         %% problem completely. Believe it or not.
         ?SEV_SLEEP(100),
         #{desc => "announce ready (timestamp on)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, timestamp_on),
                           ok
                   end},

         #{desc => "await continue (send request 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 2 (to server)",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REQ)
                   end},
         #{desc => "announce ready (send request 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await recv reply 2 (from server, w timestamp)",
           cmd  => fun(#{sock := Sock, recv := Recv, get := Get}) ->
                           case Recv(Sock) of
                               {ok, {[#{level := socket,
                                        type  := timestamp,
                                        value := TS}], ?BASIC_REP}} ->
                                   ?SEV_IPRINT("received reply *with* "
                                               "expected timestamp: "
                                               "~n   ~p", [TS]),
                                   ok;
                               {ok, {BadCMsgs, ?BASIC_REP}} ->
                                   ?SEV_EPRINT("received reply *with* "
                                               "unexpected cmsg headers:"
                                               "~n   ~p"
                                               "Current timestamp value: "
                                               "~n   ~p",
                                               [BadCMsgs, Get(Sock)]),
                                   {error, {unexpected_reply_cmsgs,
                                            BadCMsgs}};
                               {ok, BadReply} ->
                                   {error, {unexpected_reply, BadReply}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 2 (recv reply)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_reply),
                           ok
                   end},

         %% *** Third message (wo timestamp) ***

         #{desc => "await continue (disable timestamp)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, disable_timestamp)
                   end},
         #{desc => "disable timestamp",
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, false) of
                               ok ->
                                   ?SEV_IPRINT("timestamp disabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed disable timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end                                   
                   end},
         #{desc => "announce ready (timestamp off)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, timestamp_off),
                           ok
                   end},

         #{desc => "await continue (send request 3)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
                   end},
         #{desc => "send request 3 (to server)",
           cmd  => fun(#{sock := Sock, send := Send}) ->
                           Send(Sock, ?BASIC_REQ)
                   end},
         #{desc => "announce ready (send request 3)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_req),
                           ok
                   end},
         #{desc => "await recv reply 3 (from server, wo timestamp)",
           cmd  => fun(#{sock := Sock, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {[], ?BASIC_REP}} ->
                                   ?SEV_IPRINT("received reply *without* "
                                               "timestamp"),
                                   ok;
                               {ok, {BadCMsgs, ?BASIC_REP}} ->
                                   {error, {unexpected_reply_cmsgs,
                                            BadCMsgs}};
                               {ok, {[], BadData}} ->
                                   {error, {unexpected_reply_data,
                                            BadData}};
                               {ok, BadReply} ->
                                   {error, {unexpected_reply, BadReply}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready 3 (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(#{domain   := local,
                         sock     := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(sock, State1)};
                      (#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?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},
%%%         ?SEV_SLEEP(?SECS(1)),
         #{desc => "order client to continue (with connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, connect),
                           ok
                   end},
         #{desc => "await client ready (connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, connect)
                   end},
         #{desc => "await server ready (accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, accept)
                   end},

         %% *** First message (default=wo timestamp) ***

         #{desc => "order client to continue (with verify timestamp off)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, verify_timestamp),
                           ok
                   end},
         #{desc => "await client ready (timestamp off)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, timestamp_off)
                   end},

         #{desc => "order client to continue (with send request 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client ready (with send request 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_req)
                   end},
         #{desc => "await server ready (request recv 1)",
           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},

         %% Second message (w timestamp)

         #{desc => "order client to continue (with enable timestamp)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, enable_timestamp),
                           ok
                   end},
         #{desc => "await client ready (timestamp on)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, timestamp_on)
                   end},

         #{desc => "order client to continue (with send request 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client ready (with send request 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_req)
                   end},
         #{desc => "await server ready (request recv 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order server to continue (with send reply 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
                           ok
                   end},
         #{desc => "await server ready (with reply sent 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_reply)
                   end},
         #{desc => "await client ready (reply recv 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, recv_reply)
                   end},

         %% Third message (wo timestamp)

         #{desc => "order client to continue (with disable timestamp)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, disable_timestamp),
                           ok
                   end},
         #{desc => "await client ready (timestamp off)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, timestamp_off)
                   end},

         #{desc => "order client to continue (with send request 3)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
                           ok
                   end},
         #{desc => "await client ready (with send request 3)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_req)
                   end},
         #{desc => "await server ready (request recv 3)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_req)
                   end},
         #{desc => "order server to continue (with send reply 3)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
                           ok
                   end},
         #{desc => "await server ready (with reply sent 3)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_reply)
                   end},
         #{desc => "await client ready (reply recv 3)",
           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("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]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the add_mambership and drop_membership ip options work.
%% We create one server and two clients. The server only send messages,
%% the clients only receives messages.
%% An UDP datagram is forbidden (RFC 1122) from having a source address
%% that is a multicast address (or a broadcast address).
%% So, the server create a socket "for sending" and the clients sockets
%% "for receiving".
%% Sending socket:   Bound to the local address (and any port).
%%                   When sending, the dest will be the multicast address
%%                   and port of the receiving socket.
%% Receiving socket: Bound to the multicast address and port.
api_opt_ip_add_drop_membership() ->
    [{doc, "OTP-15908 (ERL-980)"}].

api_opt_ip_add_drop_membership(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(api_opt_ip_add_drop_membership,
           fun() ->
                   has_support_ip_add_membership(),
                   has_support_ip_drop_membership(),
                   has_support_ip_multicast()
           end,
           fun() -> api_opt_ip_add_drop_membership_do() end).


api_opt_ip_add_drop_membership_do() ->
    Set = fun(S, Key, Val) ->
                  socket:setopt(S, ip, Key, Val)
          end,
    AddMembership  = fun(S, Val) -> Set(S, add_membership,  Val) end,
    DropMembership = fun(S, Val) -> Set(S, drop_membership, Val) end,

    ServerSeq =
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(State) ->
                           {Tester, MSA} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, msa => MSA}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make recv socket reuse addr",
           cmd  => fun(#{sock := Sock} = _State) ->
                           case socket:setopt(Sock, socket, reuseaddr, true) of
                               ok ->
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed set reuseaddr: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "bind recv socket to multicast address",
           cmd  => fun(#{sock := Sock, msa := MSA} = State) ->
                           case sock_bind(Sock, MSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   ?SEV_IPRINT("bound to:"
                                               "~n   ~p", [Port]),
                                   {ok, State#{msa => MSA#{port => Port}}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (add_membership)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, add_membership)
                   end},
         #{desc => "add membership",
           cmd  => fun(#{sock     := Sock,
                         msa      := #{addr := MAddr},
                         local_sa := #{addr := Addr}} = State) ->
                           MReq = #{multiaddr => MAddr,
                                    interface => Addr},
                           ?SEV_IPRINT("try add membership to:"
                                       "~n   ~p", [MReq]),
                           case AddMembership(Sock, MReq) of
                               ok ->
                                   ?SEV_IPRINT("membership added"),
                                   {ok, State#{mreq => MReq}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed adding membership to: "
                                               "~n   ~p"
                                               "~n   Reason:  ~p",
                                               [MReq, Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (add-membership)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, add_membership),
                           ok
                   end},

         #{desc => "await continue (drop_membership)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, drop_membership)
                   end},
         #{desc => "drop membership",
           cmd  => fun(#{sock := Sock,
                         mreq := MReq} = State) ->
                           ?SEV_IPRINT("try drop membership from:"
                                       "~n   ~p", [MReq]),
                           case DropMembership(Sock, MReq) of
                               ok ->
                                   ?SEV_IPRINT("membership dropped"),
                                   {ok, maps:remove(mreq, State)};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed drop membership from: "
                                               "~n   ~p"
                                               "~n   Reason:  ~p",
                                               [MReq, Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (drop-membership)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, drop_membership),
                           ok
                   end},


         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Server} = _State) ->
                           _MRef = erlang:monitor(process, Server),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid, msa := MSA} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, MSA),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = _State) ->
                           case ?SEV_AWAIT_READY(Pid, server, init) of
                               ok ->
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Start of server failed: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},


         %% *** The actual test ***
         #{desc => "order server to continue (add-membership)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, add_membership),
                           ok
                   end},
         #{desc => "await server ready (add-membership)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, add_membership)
                   end},

         #{desc => "order server to continue (drop-membership)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, drop_membership),
                           ok
                   end},
         #{desc => "await server ready (drop-membership)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, drop_membership)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         %% *** Termination ***
         #{desc => "order server terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           {ok, maps:remove(server, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    Domain = inet,
    i("get multicast address"),
    MAddr  = which_ip_multicast_address(),
    MSA    = #{family => Domain, addr => MAddr},

    i("start server evaluator"),
    ServerInitState = #{domain => Domain},
    Server = ?SEV_START("server", ServerSeq, ServerInitState),

    i("start tester evaluator"),
    TesterInitState = #{domain  => Domain,
                        msa     => MSA,
                        server  => Server#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Tester, Server]).



which_ip_multicast_address() ->
    which_multicast_address(inet).

which_multicast_address(Domain) ->
    case os:type() of
        {unix, linux} ->
            WhichMAddr = fun([_, _, MAddr]) -> MAddr end,
            which_multicast_address2(Domain, WhichMAddr);

        {unix, sunos} ->
            WhichMAddr = fun([_, MAddr, _]) -> MAddr end,
            which_multicast_address2(Domain, WhichMAddr);

        Type ->
            %% Actually, what is "not supported". is netstat!
            not_supported({multicast, Type})
    end.

%% Note that the 'netstat -g' table looks different on linux and SunOS
%% Linux: IfName - RefCnt - Group
%% SunOS: IfName - Group  - RefCnt

which_multicast_address2(Domain, WhichMAddr) ->
    IfName = which_local_host_ifname(Domain),
    %% On some platforms the netstat barfs out some crap on stderr
    %% before the actual info...
    %% ...without the 'n' (that is; just '-g') this command can take a
    %% *long* time. *With* the 'n' its much better. But just to be on
    %% the safe side, we add a timeout of 10 seconds.
    case ?LIB:os_cmd("netstat -gn 2>/dev/null | grep " ++ IfName, ?SECS(10)) of
        {error, timeout} ->
            skip({netstat, timeout});
        {ok, []} ->
            %% Can't figure out if we support multicast or not...
            not_supported(no_netstat);
        {ok, NetstatGroupsStr} ->
            try
                begin
                    NetstatGroups0   = string:tokens(NetstatGroupsStr, [$\n]),
                    NetstatGroups    = [string:tokens(G, [$ ]) || 
                                           G <- NetstatGroups0],
                    MAddrs           = [WhichMAddr(NetstatGroup) || 
                                           NetstatGroup <- NetstatGroups],
                    which_multicast_address3(Domain, MAddrs)
                end
            catch
                throw:E:_ ->
                    throw(E);
                C:E:S ->
                    not_supported({multicast, {C,E,S}})
            end
    end.

which_multicast_address3(_Domain, []) ->
    not_supported({multicast, no_valid_addrs});
which_multicast_address3(Domain, [MAddrStr|MAddrs]) ->
    %% Even on linux some of these are not actually addresses, but
    %% "host names", such as all-systems.mcast.net. But both
    %% address strings, such as "224.0.0.251" and host name strings
    %% gets translated into an address by the inet:inet:getaddr/2.
    case inet:getaddr(MAddrStr, Domain) of
        {ok, MAddr} ->
            MAddr;
        {error, _} ->
            which_multicast_address3(Domain, MAddrs)
    end.
    
which_local_host_ifname(Domain) ->
    case ?LIB:which_local_host_info(Domain) of
        {ok, #{name := Name}} ->
            Name;
        {error, Reason} ->
            not_supported({multicast, Reason})
    end.

    

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the pktinfo control message header is received when
%% setting the socket 'ip' option pktinfo is set to true when using
%% sendmsg/recvmsg on an IPv4 UDP (dgram) socket.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, ip, pktinfo, boolean()).
%%
%% For all subsequent *received* messages, the pktinfo control message
%% header will be with the message.
%%
%% Note that it *should* be possible to explicitly send pktinfo also,
%% but this have not yet been implemented (in socket), so that part
%% we do not test!!
%%

api_opt_ip_pktinfo_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4(), has_support_ip_pktinfo() end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, ip, pktinfo, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, ip, pktinfo)
                          end,
                   Send = fun(Sock, Data, Dest, default) ->
                                  Msg = #{addr => Dest,
                                          iov  => [Data]},
                                  socket:sendmsg(Sock, Msg);
                             (Sock, Data, Dest, Info) ->
                                  %% We do not support this at the moment!!!
                                  CMsg = #{level => ip,
                                           type  => pktinfo,
                                           data  => Info},
                                  Msg  = #{addr => Dest,
                                           ctrl => [CMsg],
                                           iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_ip_pktinfo_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ip_pktinfo_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := local = Domain} = State) ->
                           LSASrc = which_local_socket_addr(Domain),
                           LSADst = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSASrc,
                                       lsa_dst => LSADst}};
                      (#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},
         #{desc => "get default pktinfo for dst socket",
           cmd  => fun(#{sock_dst := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("dst recvttl: ~p", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected src pktinfo: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) pktinfo:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo (explicit) pktinfo)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   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, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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},


         %% *** We do not *yet* support sending pktinfo ***

         %% #{desc => "send req (to dst) (w explicit pktinfo)",
         %%   cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
         %%                   Send(Sock, ?BASIC_REQ, Dst, PktInfo)
         %%           end},
         %% #{desc => "recv req (from src) - wo pktinfo",
         %%   cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
         %%                   case Recv(Sock) of
         %%                       {ok, {Src, [], ?BASIC_REQ}} ->
         %%                           ok;
         %%                       {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
         %%                           ?SEV_EPRINT("Unexpected msg: "
         %%                                       "~n   Expect Source: ~p"
         %%                                       "~n   Recv Source:   ~p"
         %%                                       "~n   Expect CHdrs:  ~p"
         %%                                       "~n   Recv CHdrs:    ~p"
         %%                                       "~n   Expect Msg:    ~p"
         %%                                       "~n   Recv Msg:      ~p",
         %%                                       [Src, BadSrc,
         %%                                        [], BadCHdrs,
         %%                                        ?BASIC_REQ, BadReq]),
         %%                           {error, {unexpected_data, UnexpData}};
         %%                       {ok, UnexpData} ->
         %%                           ?SEV_EPRINT("Unexpected msg: "
         %%                                       "~n   Expect Source: ~p"
         %%                                       "~n   Expect CHdrs:  ~p"
         %%                                       "~n   Expect Msg:    ~p"
         %%                                       "~n   Unexp Data:    ~p",
         %%                                       [Src, [], ?BASIC_REQ, 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 => "enable pktinfo on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("dst pktinfo enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting pktinfo:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo explicit pktinfo)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   end},
         #{desc => "recv req (from src) - w default pktinfo",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [#{level := ip,
                                             type  := pktinfo,
                                             value := #{addr     := Addr,
                                                        ifindex  := IfIdx,
                                                        spec_dst := SpecDst}}],
                                     ?BASIC_REQ}} ->
                                   ?SEV_IPRINT("Got (default) Pkt Info: "
                                               "~n   Addr:      ~p"
                                               "~n   If Index:  ~p"
                                               "~n   Spec Dst:  ~p",
                                               [Addr, IfIdx, SpecDst]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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},


         %% *** We do not *yet* support sending pktinfo ***

         %% #{desc => "send req (to dst) (w explicit pktinfo)",
         %%   cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
         %%                   Send(Sock, ?BASIC_REQ, Dst, PktInfo)
         %%           end},
         %% #{desc => "recv req (from src) - w ttl = 100",
         %%   cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
         %%                   case Recv(Sock) of
         %%                       {ok, {Src, [#{level := ip,
         %%                                     type  := ttl,
         %%                                     data  := PktInfo}], ?BASIC_REQ}} ->
         %%                           ?SEV_IPRINT("Got Pkt Info: "
         %%                                       "~n   ~p", [Info]),
         %%                           ok;
         %%                       {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
         %%                           ?SEV_EPRINT("Unexpected msg: "
         %%                                       "~n   Expect Source: ~p"
         %%                                       "~n   Recv Source:   ~p"
         %%                                       "~n   Expect CHdrs:  ~p"
         %%                                       "~n   Recv CHdrs:    ~p"
         %%                                       "~n   Expect Msg:    ~p"
         %%                                       "~n   Recv Msg:      ~p",
         %%                                       [Src, BadSrc,
         %%                                        [], BadCHdrs,
         %%                                        ?BASIC_REQ, BadReq]),
         %%                           {error, {unexpected_data, UnexpData}};
         %%                       {ok, UnexpData} ->
         %%                           ?SEV_EPRINT("Unexpected msg: "
         %%                                       "~n   Expect Source: ~p"
         %%                                       "~n   Expect CHdrs:  ~p"
         %%                                       "~n   Expect Msg:    ~p"
         %%                                       "~n   Unexp Data:    ~p",
         %%                                       [Src, [], ?BASIC_REQ, 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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the options control message header is received when
%% setting the socket 'ip' option recvopts is set to true when using
%% sendmsg/recvmsg on an IPv4 UDP (dgram) socket.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, ip, recvopts, boolean()).
%%
%% For all subsequent *received* messages, the options control message
%% header will be with the message.
%%
%% Note that it *should* be possible to explicitly send options also,
%% but this have not yet been implemented (in socket), so that part
%% we do not test!!
%%
%%
%% <NOTE>
%%
%% This test does not currently work. The recvopts is supposed to
%% result in a IP_OPTIONS control message header but does not!
%% So, exactly how we are suppose to use this option is unknown.
%% So, let the test code remain, but skip until we have figured out
%% how to test this.
%%
%% </NOTE>
%%

api_opt_ip_recvopts_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_ip_recvopts_udp4,
           fun() ->
                   has_support_ipv4(),
                   has_support_ip_recvopts(),
                   %% We also use the recvtos and timestamp options
                   %% in this test, so at least one of them must
                   %% be supported
                   has_support_ip_recvtos_and_or_sock_timestamp(),
                   not_yet_implemented()

           end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, ip, recvopts, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, ip, recvopts)
                          end,
                   Send = fun(Sock, Data, Dest, default) ->
                                  Msg = #{addr => Dest,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg);
                             (Sock, Data, Dest, Info) ->
                                  %% We do not support this at the moment!!!
                                  CMsg = #{level => ip,
                                              type  => options,
                                              data  => Info},
                                  Msg  = #{addr => Dest,
                                              ctrl => [CMsg],
                                              iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_ip_recvopts_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ip_recvopts_udp(InitState) ->
    Seq = 
        [
         %% Start by figure out which of the ip:recvtos and/or socket:timestamp
         %% options we can use.
         #{desc => "test for ip:recvtos",
           cmd  => fun(State) ->
                           ?SEV_IPRINT("test for ip:recvtos"),
                           case socket:is_supported(options, ip, recvtos) of
                               true ->
                                   ?SEV_IPRINT("use ip:recvtos"),
                                   {ok, State#{recvtos => true}};
                               false -> 
                                   ?SEV_IPRINT("do *not* use ip:recvtos"),
                                   {ok, State#{recvtos => false}}
                           end
                   end},
         #{desc => "test for socket:timestamp",
           cmd  => fun(State) ->
                           ?SEV_IPRINT("test for socket:timestamp"),
                           case socket:is_supported(options, socket, timestamp) of
                               true ->
                                   ?SEV_IPRINT("use socket:timestamp"),
                                   {ok, State#{timestamp => true}};
                               false -> 
                                   ?SEV_IPRINT("do *not* use socket:timestamp"),
                                   {ok, State#{timestamp => false}}
                           end
                   end},

         #{desc => "local address",
           cmd  => fun(#{domain := local = Domain} = State) ->
                           LSASrc = which_local_socket_addr(Domain),
                           LSADst = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSASrc,
                                       lsa_dst => LSADst}};
                      (#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},
         #{desc => "default recvopts for dst socket",
           cmd  => fun(#{sock_dst := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("dst recvopts: ~p", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected src recvtos: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo (explicit) options)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   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, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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},


         %% *** We do not *yet* support sending options ***

         %% #{desc => "send req (to dst) (w explicit options)",
         %%   cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
         %%                   Send(Sock, ?BASIC_REQ, Dst, Opts)
         %%           end},
         %% #{desc => "recv req (from src) - wo options",
         %%   cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
         %%                   case Recv(Sock) of
         %%                       {ok, {Src, [], ?BASIC_REQ}} ->
         %%                           ok;
         %%                       {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
         %%                           ?SEV_EPRINT("Unexpected msg: "
         %%                                       "~n   Expect Source: ~p"
         %%                                       "~n   Recv Source:   ~p"
         %%                                       "~n   Expect CHdrs:  ~p"
         %%                                       "~n   Recv CHdrs:    ~p"
         %%                                       "~n   Expect Msg:    ~p"
         %%                                       "~n   Recv Msg:      ~p",
         %%                                       [Src, BadSrc,
         %%                                        [], BadCHdrs,
         %%                                        ?BASIC_REQ, BadReq]),
         %%                           {error, {unexpected_data, UnexpData}};
         %%                       {ok, UnexpData} ->
         %%                           ?SEV_EPRINT("Unexpected msg: "
         %%                                       "~n   Expect Source: ~p"
         %%                                       "~n   Expect CHdrs:  ~p"
         %%                                       "~n   Expect Msg:    ~p"
         %%                                       "~n   Unexp Data:    ~p",
         %%                                       [Src, [], ?BASIC_REQ, 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 => "enable recvopts on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("dst recvopts enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting recvopts:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         %% This specific option, recvtos, is tested in another test case
         %% Note that this may not actually be supported here!!
         #{desc => "maybe enable ip:recvtos on dst socket",
           cmd  => fun(#{recvtos := true, sock_dst := Sock} = _State) ->
                           ?SEV_IPRINT("enable ip:recvtos"),
                           ok = socket:setopt(Sock, ip, recvtos, true);
                      (#{recvtos := false} = _State) ->
                           ok
                   end},
         %% This specific option, timestamp, is tested in another test case
         #{desc => "maybe enable socket:timestamp on dst socket",
           cmd  => fun(#{timestamp := true, sock_dst := Sock} = _State) ->
                           ?SEV_IPRINT("enable socket:timestamp"),
                           ok = socket:setopt(Sock, socket, timestamp, true);
                      (#{timestamp := false} = _State) ->
                           ok
                   end},

         #{desc => "send req (to dst) (wo explicit options)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   end},
         #{desc => "recv req (from src) - w default options",
           cmd  => fun(#{recvtos := true, timestamp := true,
                         sock_dst := Sock,
                         sa_src   := Src,
                         recv     := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, Opts, ?BASIC_REQ}} 
                                 when (length(Opts) =:= 2) ->
                                   ?SEV_IPRINT("Got (default) Options: "
                                               "~n   Opts:  ~p", [Opts]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, UnexpData]),
                                   {error, {unexpected_data, UnexpData}};
                               {error, _} = ERROR ->
                                   %% At the moment there is no way to get
                                   %% status or state for the socket...
                                   ERROR
                           end;
                      (#{timestamp := true,
                         sock_dst := Sock,
                         sa_src   := Src,
                         recv     := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, Opts, ?BASIC_REQ}} 
                                 when (length(Opts) =:= 1) ->
                                   ?SEV_IPRINT("Got (default) Options: "
                                               "~n   Opts:  ~p", [Opts]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, UnexpData]),
                                   {error, {unexpected_data, UnexpData}};
                               {error, _} = ERROR ->
                                   %% At the moment there is no way to get
                                   %% status or state for the socket...
                                   ERROR
                           end;
                      (#{recvtos := true,
                         sock_dst := Sock,
                         sa_src   := Src,
                         recv     := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, Opts, ?BASIC_REQ}} 
                                 when (length(Opts) =:= 1) ->
                                   ?SEV_IPRINT("Got (default) Options: "
                                               "~n   Opts:  ~p", [Opts]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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},


         %% *** We do not *yet* support sending options ***

         %% #{desc => "send req (to dst) (w explicit options)",
         %%   cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
         %%                   Send(Sock, ?BASIC_REQ, Dst, Opts)
         %%           end},
         %% #{desc => "recv req (from src) - w options",
         %%   cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
         %%                   case Recv(Sock) of
         %%                       {ok, {Src, Opts, ?BASIC_REQ}} ->
         %%                           ?SEV_IPRINT("Got Options: "
         %%                                       "~n   ~p", [Opts]),
         %%                           ok;
         %%                       {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
         %%                           ?SEV_EPRINT("Unexpected msg: "
         %%                                       "~n   Expect Source: ~p"
         %%                                       "~n   Recv Source:   ~p"
         %%                                       "~n   Expect CHdrs:  ~p"
         %%                                       "~n   Recv CHdrs:    ~p"
         %%                                       "~n   Expect Msg:    ~p"
         %%                                       "~n   Recv Msg:      ~p",
         %%                                       [Src, BadSrc,
         %%                                        [], BadCHdrs,
         %%                                        ?BASIC_REQ, BadReq]),
         %%                           {error, {unexpected_data, UnexpData}};
         %%                       {ok, UnexpData} ->
         %%                           ?SEV_EPRINT("Unexpected msg: "
         %%                                       "~n   Expect Source: ~p"
         %%                                       "~n   Expect CHdrs:  ~p"
         %%                                       "~n   Expect Msg:    ~p"
         %%                                       "~n   Unexp Data:    ~p",
         %%                                       [Src, [], ?BASIC_REQ, 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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the origdstaddr control message header is received when
%% setting the socket 'ip' option recvorigdstaddr is set to true when
%% using sendmsg/recvmsg on an IPv4 UDP (dgram) socket.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, ip, recvorigdstaddr, boolean()).
%%
%% For all subsequent *received* messages, the origdstaddr control
%% message header will be with the message.
%%
%%

api_opt_ip_recvorigdstaddr_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_ip_recvorigdstaddr_udp4,
           fun() -> has_support_ipv4(), has_support_ip_recvorigdstaddr() end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, ip, recvorigdstaddr, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, ip, recvorigdstaddr)
                          end,
                   Send = fun(Sock, Data, Dest) ->
                                  Msg = #{addr => Dest,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_ip_recvorigdstaddr_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ip_recvorigdstaddr_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := local = Domain} = State) ->
                           LSASrc = which_local_socket_addr(Domain),
                           LSADst = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSASrc,
                                       lsa_dst => LSADst}};
                      (#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},
         #{desc => "get default recvorigdstaddr for dst socket",
           cmd  => fun(#{sock_dst := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("dst recvorigdstaddr: ~p", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected src recvorigdstaddr: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) "
                                               "recvorigdstaddr:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (when recvorigdstaddr disabled)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst)
                   end},
         #{desc => "recv req (from src) - wo origdstaddr",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [], ?BASIC_REQ}} ->
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 => "enable recvorigdstaddr on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("dst recvorigdstaddr enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed enable recvorigdstaddr:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (when recvorigdstaddr enabled)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst)
                   end},
         #{desc => "recv req (from src) - w origdstaddr",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [#{level := ip,
                                             type  := origdstaddr,
                                             value := Addr}], ?BASIC_REQ}} ->
                                   ?SEV_IPRINT("got origdstaddr "
                                               "control message header: "
                                               "~n   ~p", [Addr]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [#{level => ip,
                                                   type  => origdstaddr,
                                                   data  => "something"}], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src,
                                                [#{level => ip,
                                                   type  => origdstaddr,
                                                   data  => "something"}],
                                                ?BASIC_REQ, 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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the tos control message header is received when
%% setting the socket 'ip' option recvtos is set to true when using
%% sendmsg/recvmsg on an IPv4 UDP (dgram) socket.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, ip, recvtos, boolean()).
%%
%% For all subsequent *received* messages, the tos control message
%% header will be with the message.
%%
%% On some platforms it works sending TOS with the message (sendmsg with 
%% a control message header), but since its not universal, we can't use
%% that method. Instead, set tos (true) on the sending socket.
%%

api_opt_ip_recvtos_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_ip_recvtos_udp4,
           fun() ->
                   is_not_windows(), % IP_TOS on windows
                   has_support_ipv4(),
                   has_support_ip_recvtos(),
                   has_support_ip_tos() % Used in the test
           end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, ip, recvtos, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, ip, recvtos)
                          end,
                   Send = fun(Sock, Data, Dest, default) ->
                                  Msg = #{addr => Dest,
                                          iov  => [Data]},
                                  socket:sendmsg(Sock, Msg);
                             (Sock, Data, Dest, TOS) ->
                                  CMsg = #{level => ip,
                                           type  => tos,
                                           data  => TOS},
                                  Msg  = #{addr => Dest,
                                           ctrl => [CMsg],
                                           iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_ip_recvtos_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ip_recvtos_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := local = Domain} = State) ->
                           LSASrc = which_local_socket_addr(Domain),
                           LSADst = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSASrc,
                                       lsa_dst => LSADst}};
                      (#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},
         #{desc => "default recvtos for dst socket",
           cmd  => fun(#{sock_dst := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("dst recvtos: ~p", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected src recvtos: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo explicit tos)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   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, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 => "set tos = reliability on src sock",
           cmd  => fun(#{sock_src := Sock}) ->
                           ok = socket:setopt(Sock, ip, tos, reliability)
                   end},
         #{desc => "send req (to dst) (w tos = reliability)",
           cmd  => fun(#{sock_src := Sock,
                         sa_dst   := Dst,
                         send     := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   end},

         %% #{desc => "send req (to dst) (w explicit tos = reliability)",
         %%   cmd  => fun(#{sock_src := Sock,
         %%                 sa_dst   := Dst,
         %%                 send     := Send}) ->
         %%                   socket:setopt(Sock, otp, debug, true),
         %%                   case Send(Sock, ?BASIC_REQ, Dst, reliability) of
         %%                       ok ->
         %%                           socket:setopt(Sock, otp, debug, false),
         %%                           ok;
         %%                       {error, Reason} ->
         %%                           ?SEV_EPRINT("Failed sending message with tos: "
         %%                                       "~n   Reason: ~p", [Reason]),
         %%                           socket:setopt(Sock, otp, debug, false),
         %%                           {skip, "Failed sending message with TOS"}
         %%                   end
         %%           end},

         #{desc => "recv req (from src) - wo explicit tos",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [], ?BASIC_REQ}} ->
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 => "set tos = 0 on src sock (\"disabled\")",
           cmd  => fun(#{sock_src := Sock}) ->
                           ok = socket:setopt(Sock, ip, tos, 0)
                   end},

         #{desc => "enable recvtos on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("dst recvtos enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting recvtos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo explicit tos)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   end},
         #{desc => "recv req (from src) - w default tos",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [#{level := ip,
                                             type  := TOS,
                                             value := 0}], ?BASIC_REQ}}
                                 when ((TOS =:= tos) orelse (TOS =:= recvtos)) ->
                                   ?SEV_IPRINT("got default TOS (~w) "
                                               "control message header", [TOS]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 => "set tos = reliability on src sock",
           cmd  => fun(#{sock_src := Sock}) ->
                           ok = socket:setopt(Sock, ip, tos, reliability)
                   end},

         #{desc => "send req (to dst) (w tos = mincost)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   end},
         #{desc => "recv req (from src) - w tos = reliability",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [#{level := ip,
                                             type  := TOS,
                                             value := reliability = TOSData}],
                                     ?BASIC_REQ}} 
                                 when ((TOS =:= tos) orelse (TOS =:= recvtos)) ->
                                   ?SEV_IPRINT("got expected TOS (~w) = ~w "
                                               "control message header", 
                                               [TOS, TOSData]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the ttl control message header is received when
%% setting the socket 'ip' option recvttl is set to true when using
%% sendmsg/recvmsg on an IPv4 UDP (dgram) socket.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, ip, recvttl, boolean()).
%%
%% For all subsequent *received* messages, the ttl control message
%% header will be with the message.
%%
%% On darwin we don't actually get the TTL we send even after we have
%% enabled TTL. Instead we get the default value (which was 64).
%% Possibly this is because we run the test in the same OS process and
%% even the same erlang process....
%% The same issue on OpenBSD (6.6).
%% Maybe we should send and receive from different VMs, until then
%% skip darwin and OpenBSD.
%%
%% Windows:
%% It seems like its possible to set and get the recvttl option,
%% but not to use the ttl control message header when sending.
%% The following is the list of types (for level ip) which are listed
%% as supported: IP_ORIGINAL_ARRIVAL_IF, IP_PKTINFO and IP_ECN

api_opt_ip_recvttl_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_ipv4(),
		   has_support_ip_recvttl(),
		   is_not_openbsd(),
		   is_not_darwin()
	   end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, ip, recvttl, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, ip, recvttl)
                          end,
                   Send = fun(Sock, Data, Dest, default) ->
                                  Msg = #{addr => Dest,
                                          iov  => [Data]},
                                  socket:sendmsg(Sock, Msg);
                             (Sock, Data, Dest, TTL) ->
                                  CMsg = #{level => ip,
                                           type  => ttl,
                                           value => TTL},
                                  Msg  = #{addr => Dest,
                                           ctrl => [CMsg],
                                           iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_ip_recvttl_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ip_recvttl_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := local = Domain} = State) ->
                           LSASrc = which_local_socket_addr(Domain),
                           LSADst = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSASrc,
                                       lsa_dst => LSADst}};
                      (#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},
         #{desc => "default recvttl for dst socket",
           cmd  => fun(#{sock_dst := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("dst recvttl: ~p", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected src recvttl: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) timestamp:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo explicit ttl)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   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, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 req (to dst) (w explicit ttl = 100)",
           cmd  => fun(#{sock_src := SSock,
                         sock_dst := DSock, sa_dst := Dst,
                         send     := Send}) ->
                           case Send(SSock, ?BASIC_REQ, Dst, 100) of
                               ok ->
                                   ok;
                               {error, einval = Reason} ->
                                   %% IF we can't send it the test will not work
                                   ?SEV_EPRINT("Cannot send TTL: "
                                               "~p => SKIP", [Reason]),
                                   (catch socket:close(SSock)),
                                   (catch socket:close(DSock)),
                                   {skip,
				    ?F("Cannot send with TTL: ~p", [Reason])};
                               {error, enoprotoopt = Reason} ->
                                   %% On some platforms this is not
                                   %% accepted (FreeBSD), so skip.
                                   ?SEV_EPRINT("Expected Failure: "
                                               "~p => SKIP", [Reason]),
                                   (catch socket:close(SSock)),
                                   (catch socket:close(DSock)),
                                   {skip, Reason};

                               {error,
                                {get_overlapped_result,
                                 #{file     := File,
                                   function := Function,
                                   line     := Line,
                                   raw_info := RawInfo,
                                   info     := invalid_parameter = Info}}} ->
                                   %% IF we can't send it the test will not work
                                   ?SEV_EPRINT("Cannot send TTL: "
                                               "~p => SKIP: "
                                               "~n   File:     ~s"
                                               "~n   Function: ~s"
                                               "~n   Line:     ~p"
                                               "~n   Raw Info: ~p",
                                               [Info,
                                                File, Function, Line,
                                                RawInfo]),
                                   (catch socket:close(SSock)),
                                   (catch socket:close(DSock)),
                                   {skip,
                                    ?F("Cannot send with TTL: ~p", [Info])};
                               {error, {get_overlapped_result,
                                        invalid_parameter = Info}} ->
                                   %% IF we can't send it the test will not work
                                   ?SEV_EPRINT("Cannot send TTL: "
                                               "~p => SKIP", [Info]),
                                   (catch socket:close(SSock)),
                                   (catch socket:close(DSock)),
                                   {skip,
                                    ?F("Cannot send with TTL: ~p", [Info])};

                               {error,
                                {completion_status,
                                 #{file     := File,
                                   function := Function,
                                   line     := Line,
                                   raw_info := RawInfo,
                                   info     := invalid_parameter = Info}}} ->
                                   %% IF we can't send it the test will not work
                                   ?SEV_EPRINT("Cannot send TTL: "
                                               "~p => SKIP: "
                                               "~n   File:     ~s"
                                               "~n   Function: ~s"
                                               "~n   Line:     ~p"
                                               "~n   Raw Info: ~p",
                                               [Info,
                                                File, Function, Line,
                                                RawInfo]),
                                   (catch socket:close(SSock)),
                                   (catch socket:close(DSock)),
                                   {skip,
                                    ?F("Cannot send with TTL: ~p", [Info])};
                               {error, {completion_status,
                                        invalid_parameter = Info}} ->
                                   %% IF we can't send it the test will not work
                                   ?SEV_EPRINT("Cannot send TTL: "
                                               "~p => SKIP", [Info]),
                                   (catch socket:close(SSock)),
                                   (catch socket:close(DSock)),
                                   {skip,
                                    ?F("Cannot send with TTL: ~p", [Info])};

                               {error, _Reason} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "recv req (from src) - wo ttl",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [], ?BASIC_REQ}} ->
                                   ok;
                               {ok, {Src, [#{level := ip,
                                             type  := TTLType,
                                             value := TTL}], ?BASIC_REQ}}
                               when ((TTLType =:= recvttl) andalso
                                     (TTL =:= 255)) ->
                                   %% This is the behaviopur on Solaris (11)
                                   %% and maybe on other platforms...
                                   ?SEV_IPRINT("Got (default) TTL (~w): ~p",
                                               [TTLType, TTL]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 => "enable recvttl on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("dst recvttl enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed enabling recvttl:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo explicit ttl)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   end},
         #{desc => "recv req (from src) - w default ttl",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [#{level := ip,
                                             type  := TTLType,
                                             value := TTL}], ?BASIC_REQ}}
                               when ((TTLType =:= ttl) orelse 
                                     (TTLType =:= recvttl)) ->
                                   ?SEV_IPRINT("Got (default) TTL (~w): ~p",
                                               [TTLType, TTL]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [#{level => ip,
						   type  => ttl,
						   value => "something"}],
						BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [#{level => ip,
							type  => ttl,
							value => "something"}],
						?BASIC_REQ, 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 req (to dst) (w explicit ttl = 100)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, 100)
                   end},
         #{desc => "recv req (from src) - w ttl = 100",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [#{level := ip,
                                             type  := TTLType,
                                             value := 100 = TTL}], ?BASIC_REQ}}
                               when ((TTLType =:= ttl) orelse
                                     (TTLType =:= recvttl)) ->
                                   ?SEV_IPRINT("Got TTL (~w): ~p",
                                               [TTLType, TTL]),
                                   ok;
                               {ok, {Src, [#{level := ip,
                                             type  := TTLType,
                                             value := BadTTL}], ?BASIC_REQ}}
                               when ((TTLType =:= ttl) orelse
                                     (TTLType =:= recvttl)) ->
                                   ?SEV_EPRINT("Unexpected TTL: "
                                               "~n   Expect TTL: ~p"
                                               "~n   Recv TTL:   ~p",
                                               [100, BadTTL]),
                                   {error, {unexpected_ttl, {100, BadTTL}}};
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [#{level => ip,
						   type  => ttl,
						   value => 100}], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [#{level => ip,
							type  => ttl,
							value => 100}],
						?BASIC_REQ, 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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the ip socket option 'tos' can be set and retrieved from a
%% the socket its set on. It sets the type-of-server field in the IP
%% header for a TCP or UDP socket.
%% There is no way to fetch the value a received IP datagram.
%% Default value is supposed to be '0'.
%% 

api_opt_ip_tos_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   is_not_windows(), % IP_TOS on windows
                   has_support_ipv4(),
                   has_support_ip_tos()
           end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, ip, tos, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, ip, tos)
                          end,
                   InitState = #{set => Set,
                                 get => Get},
                   ok = api_opt_ip_tos_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ip_tos_udp(InitState) ->
    process_flag(trap_exit, true),
    %% mincost is not supported on all platforms.
    %% For instance, Solaris 10, does not have that constant.
    %% Instead it has two others with, what appears to be,
    %% completely different meanings...
    %% So, avoid the complication by not using this value...
    %% TOS1 = mincost,     TOS1Str = atom_to_list(TOS1),
    TOS2 = throughput,  TOS2Str = atom_to_list(TOS2),
    TOS3 = reliability, TOS3Str = atom_to_list(TOS3),
    TOS4 = lowdelay,    TOS4Str = atom_to_list(TOS4),
    TOS5 = 42,          TOS5Str = integer_to_list(TOS5),
    Seq =
        [
         #{desc => "local address",
           cmd  => fun(State) ->
                           LSA = which_local_socket_addr(inet),
                           {ok, State#{lsa => LSA}}
                   end},

         #{desc => "open socket",
           cmd  => fun(State) ->
                           Sock = sock_open(inet, dgram, udp),
                           {ok, State#{sock => Sock}}
                   end},
         #{desc => "bind",
           cmd  => fun(#{sock := Sock, lsa := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("socket bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "get default tos",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, 0 = Value} ->
                                   ?SEV_IPRINT("expected default tos: ~p", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected default tos: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) tos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         %% #{desc => "set tos " ++ TOS1Str,
         %%   cmd  => fun(#{sock := Sock, set := Set} = _State) ->
         %%                   socket:setopt(Sock, otp, debug, true),
         %%                   case Set(Sock, TOS1) of
         %%                       ok ->
         %%                           socket:setopt(Sock, otp, debug, false),
         %%                           ?SEV_IPRINT("tos set to ~p", [TOS1]),
         %%                           ok;
         %%                       {error, Reason} = ERROR ->
         %%                           socket:setopt(Sock, otp, debug, false),
         %%                           ?SEV_EPRINT("Failed setting tos:"
         %%                                       "   ~p", [Reason]),
         %%                           ERROR
         %%                   end
         %%           end},
         %% #{desc => "get tos (expect " ++ TOS1Str ++ ")",
         %%   cmd  => fun(#{sock := Sock, get := Get} = _State) ->
         %%                   case Get(Sock) of
         %%                       {ok, TOS1 = Value} ->
         %%                           ?SEV_IPRINT("expected tos (~p)", [Value]),
         %%                           ok;
         %%                       {ok, Unexpected} ->
         %%                           ?SEV_EPRINT("Unexpected tos: ~p",
         %%                                       [Unexpected]),
         %%                           {error, {unexpected, Unexpected}};
         %%                       {error, Reason} = ERROR ->
         %%                           ?SEV_EPRINT("Failed getting (default) tos:"
         %%                                       "   ~p", [Reason]),
         %%                           ERROR
         %%                   end
         %%           end},

         #{desc => "set tos " ++ TOS2Str,
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, TOS2) of
                               ok ->
                                   ?SEV_IPRINT("tos set to ~p", [TOS2]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting tos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "get tos (expect " ++ TOS2Str ++ ")",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, TOS2 = Value} ->
                                   ?SEV_IPRINT("expected tos (~p)", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected tos: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) tos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "set tos " ++ TOS3Str,
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, TOS3) of
                               ok ->
                                   ?SEV_IPRINT("tos set to ~p", [TOS3]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting tos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "get tos (expect " ++ TOS3Str ++ ")",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, TOS3 = Value} ->
                                   ?SEV_IPRINT("expected tos (~p)", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected tos: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) tos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "set tos " ++ TOS4Str,
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, TOS4) of
                               ok ->
                                   ?SEV_IPRINT("tos set to ~p", [TOS4]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting tos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "get tos (expect " ++ TOS4Str ++ ")",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, TOS4 = Value} ->
                                   ?SEV_IPRINT("expected tos (~p)", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected tos: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) tos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "set tos " ++ TOS5Str,
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, TOS5) of
                               ok ->
                                   ?SEV_IPRINT("tos set to ~p", [TOS5]),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting tos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "get tos (expect " ++ TOS5Str ++ ")",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, TOS5 = Value} ->
                                   ?SEV_IPRINT("expected tos (~p)", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected tos: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) tos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the ip socket option 'recverr' can be set and that the error
%% queue can be read.
%% 

api_opt_ip_recverr_udp4(Config) when is_list(Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_ip_recverr_udp4,
           fun() ->
                   has_support_ipv4(),
                   has_support_ip_recverr()
           end,
           fun() ->
                   Set  = fun(Sock, Key, Value) ->
                                  socket:setopt(Sock, ip, Key, Value)
                          end,
                   Get  = fun(Sock, Key) ->
                                  socket:getopt(Sock, ip, Key)
                          end,
                   Send = fun(Sock, Data, Dest, Tmo) ->
                                  socket:sendto(Sock, Data, Dest, [], Tmo)
                          end,
                   Recv = fun(Sock, Tmo) ->
                                  socket:recvfrom(Sock, 0, [], Tmo)
                          end,
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_recverr_udp(Config, InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests the ipv6 socket option 'recverr' can be set and that the error
%% queue can be read.
%% 

api_opt_ipv6_recverr_udp6(Config) when is_list(Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_ipv6_recverr_udp6,
           fun() ->
                   has_support_ipv6(),
                   has_support_ipv6_recverr()
           end,
           fun() ->
                   Set  = fun(Sock, Key, Value) ->
                                  socket:setopt(Sock, ipv6, Key, Value)
                          end,
                   Get  = fun(Sock, Key) ->
                                  socket:getopt(Sock, ipv6, Key)
                          end,
                   Send = fun(Sock, Data, Dest, Tmo) ->
                                  socket:sendto(Sock, Data, Dest, [], Tmo)
                          end,
                   Recv = fun(Sock, Tmo) ->
                                  socket:recvfrom(Sock, 0, [], Tmo)
                          end,
                   InitState = #{domain => inet6,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_recverr_udp(Config, InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_recverr_udp(Config, InitState) ->
    Seq = 
        [
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           ?SEV_IPRINT("test for ip:recvtos"),
                           case socket:open(Domain, dgram, udp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR -> 
                                   ERROR
                           end
                   end},
         #{desc => "bind (to loopback)",
           cmd  => fun(#{sock := Sock} = State) ->
                           case socket:bind(Sock, loopback) of
                               ok ->
                                   case socket:sockname(Sock) of
                                       {ok, Addr} ->
                                           ?SEV_IPRINT(
                                              "bound to ~p", [Addr]),
                                           {ok, State#{addr => Addr}};
                                       {error, _} = ERROR_1 ->
                                           ERROR_1
                                   end;
                               {error, _} = ERROR_2 ->
                                   ERROR_2
                           end
                   end},

         #{desc => "enable recverr",
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           Set(Sock, recverr, true)
                   end},

         #{desc => "disable mtu_discover",
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           Set(Sock, mtu_discover, dont)
                   end},

         #{desc => "try (async) read (=> select)",
           cmd  => fun(#{sock := Sock,
                         recv := Recv} = State) ->
                           RecvRef = nowait(Config),
                           case Recv(Sock, RecvRef) of
                               {select, SelectInfo} when RecvRef =:= nowait ->
                                   ?SEV_IPRINT("expected select nowait: "
					       "~n   ~p", [SelectInfo]),
                                   {ok, State#{async_tag => select,
                                               rselect   => SelectInfo}};
                               {select,
                                {select_info, _Tag, RecvRef} = SelectInfo}
                                 when is_reference(RecvRef) ->
                                   ?SEV_IPRINT("expected select ref: "
					       "~n   ~p", [SelectInfo]),
                                   {ok, State#{async_tag => select,
                                               rselect   => SelectInfo}};

                               {completion, CI} when RecvRef =:= nowait ->
                                   ?SEV_IPRINT("expected completion nowait: "
					       "~n   ~p", [CI]),
                                   {ok, State#{asynch_tag  => completion,
                                               rcompletion => CI}};
                               {completion,
                                {completion_info, _Tag, RecvRef} = CI}
                                 when is_reference(RecvRef) ->
                                   ?SEV_IPRINT("expected completion ref: "
					       "~n   ~p", [CI]),
                                   {ok, State#{asynch_tag  => completion,
                                               rcompletion => CI}};

                               {ok, _} ->
                                   ?SEV_EPRINT("unexpected success"),
                                   {error, unexpected_success};

                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("unexpected error: ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "try send to nowhere",
           cmd  => fun(#{domain := Domain,
                         sock   := Sock,
                         send   := Send} = State) ->
                           SendRef = nowait(Config),
                           Dest = #{family => Domain,
                                    addr   => if
                                                  (Domain =:= inet) ->
                                                      {127,0,0,1};
                                                  (Domain =:= inet6) ->
                                                      {0,0,0,0,0,0,0,1}
                                              end,
                                    port   => 44444},
                           case Send(Sock, <<"ping">>, Dest, SendRef) of
                               ok ->
                                   ?SEV_IPRINT("sent"),
                                   {ok, State#{sent => true}};

                               {select, SelectInfo}
                                 when SendRef =:= nowait ->
                                   ?SEV_IPRINT("expected select nowait: ~p",
					       [SelectInfo]),
                                   {ok, State#{sent       => false,
                                               asynch_tag => select,
                                               sselect    => SelectInfo}};
                               {select,
                                {select_info, _Tag, SendRef} = SelectInfo}
                                 when is_reference(SendRef) ->
                                   ?SEV_IPRINT("expected select ref: ~p",
					       [SelectInfo]),
                                   {ok, State#{sent       => false,
                                               asynch_tag => select,
                                               sselect    => SelectInfo}};

                               {completion, CI}
                                 when SendRef =:= nowait ->
                                   ?SEV_IPRINT("expected completion nowait: ~p",
					       [CI]),
                                   {ok, State#{sent       => false,
                                               asynch_tag  => completion,
                                               scompletion => CI}};
                               {completion,
                                {completion_info, _Tag, SendRef} = CI}
                                 when is_reference(SendRef) ->
                                   ?SEV_IPRINT("expected completion ref: ~p",
					       [CI]),
                                   {ok, State#{sent       => false,
                                               asynch_tag  => completion,
                                               scompletion => CI}};

                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("unexpected error: ~p",
					       [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "await receive select|completion message",
           cmd  => fun(#{sent       := false,
                         asynch_tag := select,
                         sock       := Sock,
                         rselect    := {select_info, _, Ref}} = _State) ->
                           receive
                               {'$socket', Sock, select, Ref} ->
                                   ?SEV_IPRINT("received expected (read) "
                                               "select message: "
                                               "~n   ~p", [Ref]),
                                   ok
                           end;
                      (#{sent        := false,
                         asynch_tag  := completion,
                         sock        := Sock,
                         rcompletion := {completion_info, _, Ref}} = _State) ->
                           receive
                               {'$socket', Sock, completion,
                                {Ref, {error, econnrefused = Reason}}} ->
                                   ?SEV_IPRINT("expected failure: ~p",
                                               [Reason]),
                                   ok;

                               {'$socket', Sock, completion,
                                {Ref, {ok, _}}} ->
                                   ?SEV_EPRINT("unexpected success"),
                                   {error, unexpected_success};
                               {'$socket', Sock, completion,
                                {Ref, {error, Reason} = ERROR}} ->
                                   ?SEV_IPRINT("unexpected failure: ~p",
                                               [Reason]),
                                   ERROR

                           end;
                      (#{sent := true} = _State) ->
                           ?SEV_IPRINT("no action needed"),
                           ok
                   end},

         #{desc => "try recv - expect econnrefused",
           cmd  => fun(#{asynch_tag := completion} = _State) ->
                           ?SEV_IPRINT("already processed"),
                           ok;
                      (#{sock := Sock,
                         recv := Recv} = _State) ->
                           case Recv(Sock, infinity) of
                               {error, econnrefused = Reason} ->
                                   ?SEV_IPRINT("expected failure: ~p",
                                               [Reason]),
                                   ok;
                               {ok, _} ->
                                   ?SEV_EPRINT("unexpected success"),
                                   {error, unexpected_success};
                               {select, SelectInfo} ->
                                   ?SEV_EPRINT("unexpected select: ~p",
                                               [SelectInfo]),
                                   {error, unexpected_success};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("unexpected error: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send to self",
           cmd  =>
               fun(#{sock := Sock,
                     send := Send,
                     addr := Addr} = _State) ->
                       case Send(Sock, <<"ring">>, Addr, infinity) of
                           ok ->
                               ?SEV_IPRINT("sent to self"),
                               ok;
                           {error, _} = Error ->
                               Error
                       end
               end},

         #{desc => "try recv - expect data sent to self",
           cmd  => fun(#{sock := Sock,
                         addr := Addr,
                         recv := Recv} = _State) ->
                           case Recv(Sock, infinity) of
                               {ok, {Addr, <<"ring">>}} ->
                                   ?SEV_IPRINT("receive expected"),
                                   ok;

                               {select, SelectInfo} ->
                                   ?SEV_EPRINT("unexpected select: ~p",
                                               [SelectInfo]),
                                   {error, unexpected_success};

                               {completion, CompletionInfo} ->
                                   ?SEV_EPRINT("unexpected completion: ~p",
                                               [CompletionInfo]),
                                   {error, unexpected_success};

                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("unexpected error: ~p",
                                               [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "try recv error queue",
           cmd  => fun(#{domain := Domain, sock := Sock} = State) ->
                           %% Note that not all platforms that support
                           %% recverr, actually supports "encoding" the data
                           %% part, so we need to adjust for that.
			   Origin = 
			       if (Domain =:= inet)  -> icmp;
				  (Domain =:= inet6) -> icmp6
			       end,
			   Level =
			       if (Domain =:= inet)  -> ip;
				  (Domain =:= inet6) -> ipv6
			       end,
                           case socket:recvmsg(Sock, [errqueue], 0) of
                               {ok, #{addr  := #{family := Domain,
						 addr   := Addr},
                                      flags := [errqueue],
                                      iov   := [<<"ping">>],
                                      ctrl  := [#{level := Level,
                                                  type  := recverr,
                                                  value :=
                                                      #{code     := port_unreach,
                                                        data     := 0,
                                                        error    := econnrefused,
                                                        info     := 0,
                                                        offender := #{family := Domain,
								      addr   := Addr},
                                                        origin   := Origin,
                                                        type     := dest_unreach}
                                                 }]} = Msg} ->
                                   ?SEV_IPRINT("expected error queue (decoded): "
                                               "~n   ~p", [Msg]),
                                   {ok, State#{asynch_tag => none}};
                               {ok, #{addr  := #{family := Domain,
						 addr   := _Addr},
                                      flags := [errqueue],
                                      iov   := [<<"ping">>],
                                      value := [#{level := Level,
                                                  type  := recverr}]} = _Msg} ->
                                   ?SEV_IPRINT("expected error queue"),
                                   {ok, State#{asynch_tag => none}};

                               {completion, CI} ->
                                   ?SEV_IPRINT("completion: "
                                               "~n   ~p", [CI]),
                                   {ok, State#{asynch_tag => completion,
                                               completion => CI}};

                               {error, timeout = Reason} = ERROR ->
                                   case os:type() of
                                       {win32, nt} ->
                                           ?SEV_IPRINT("failed reading "
                                                       "error queue: "
                                                       "~n   ~p", [Reason]),
                                           {skip,
                                            "Test case does not "
                                            "work on Windows"};
                                       _ ->
                                           ?SEV_EPRINT("failed reading "
                                                       "error queue: "
                                                       "~n   ~p", [Reason]),
                                           ERROR
                                   end;

                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed reading error queue: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "await receive select message",
           cmd  => fun(#{asynch_tag := completion,
                         sock       := Sock,
                         completion := {completion_info, _, Ref}} = _State) ->
                           receive
                               {'$socket', Sock, completion,
                                {Ref, {ok, Info}}} ->
                                   ?SEV_EPRINT("expected success: "
                                               "~n   ~p", [Info]),
                                   ok;

                               {'$socket', Sock, completion,
                                {Ref, {error, Reason} = ERROR}} ->
                                   ?SEV_IPRINT("unexpected failure: ~p",
                                               [Reason]),
                                   ERROR

                           end;
                      (#{asynch_tag := none} = _State) ->
                           ?SEV_IPRINT("no action needed"),
                           ok
                   end},

         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% This intended to test "all" of the (currently) supported IPv4
%% options that results in control message header(s).
%% So, this is done on the receiving side:
%%
%%      socket:setopt(Sock, ip, Flag, boolean()).
%%
%% For all subsequent *received* messages, a control message header
%% for each of the enabled options will be received with the message.
%%
%% Only allowed for dgram and raw,
%% although we only test this with dgram.
%%
%% Currently we *try* to use the following opts:
%%
%%      pktinfo         => pktinfo
%%      recvorigdstaddr => origdstaddr
%%      recvtos         => tos
%%      recvttl         => ttl
%%
%%
%% Every time we add a test case for a new option (that results in
%% a control message hedare), we should also add it here.
%%
%% Even though this is a IPv4 test case, we add the 'socket' timestamp
%% option (just to fill up), but in the test to see if we should run
%% the test (since its a IPv4 test case).
%%

api_opt_ip_mopts_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_ipv4(),
                   Opts =
                       [{ip, pktinfo},
                        {ip, recvorigdstaddr}] ++
                       case os:type() of
                           {win32, nt} ->
                               [];
                           _ ->
                               [{ip, recvtos},
                                {ip, recvttl}]
                       end,
		   case is_any_options_supported(Opts) of
		       true ->
			   ok;
		       false ->
			   skip("None of the needed options are supported")
		   end
           end,
           fun() ->
		   %% If we get this far, we *know* that at least one of the
		   %% options are available.

		   %% This is list of all the options and there resulting
		   %% control message header type(s):
		   %%   [{level,
		   %%     'ipv6 socket option',
                   %%     'control message header type',
		   %%     default | value()}]
		   Opts =
		       case socket:is_supported(options, socket, timestamp) of
			   true ->
			       [{socket, timestamp, timestamp, default}];
			   false ->
			       []
		       end ++
		       case socket:is_supported(options, ip, pktinfo) of
			   true ->
			       [{ip, pktinfo, pktinfo, default}];
			   false ->
			       []
		       end ++
		       case socket:is_supported(options, ip, recvorigdstaddr) of
			   true ->
			       [{ip, recvorigdstaddr, origdstaddr, default}];
			   false ->
			       []
		       end ++
                       case os:type() of
                           {win32, nt} ->
                               [];
                           _ ->
                               case socket:is_supported(options, ip, recvtos) of
                                   true ->
                                       %% It seems that sending any of the
                                       %% TOS or TTL values will fail on: 
                                       %%    FreeBSD
                                       %%    Linux when
                                       %%      version =< 3.12.60 (at least)
                                       %%      Don't know when this starts
                                       %%      working, but it works on: 
                                       %%         Ubunto 16.04.6 => 4.15.0-65
                                       %%         SLES 12 SP2 => 4.4.120-92.70
                                       %% so don't!
                                       %%
                                       %% The latest we know it not to work
                                       %% was a SLES 12 (plain) at 3.12.50-52.54
                                       %%
                                       [{ip, recvtos, tos, 
                                         case os:type() of
                                             {unix, freebsd} ->
                                                 default;
                                             {unix, linux} ->
                                                 case os:version() of
                                                     Vsn when Vsn > {3,12,60} ->
                                                         42;
                                                     _ ->
                                                         default
                                                 end;
                                             _ -> 
                                                 42
                                         end}];
                                   false ->
                                       []
                               end
		       end ++
                       case os:type() of
                           {unix, darwin} ->
                               [];
                           {win32, nt} ->
                               [];
                           _ ->
                               case socket:is_supported(options, ip, recvttl) of
                                   true ->
				       %% It seems that sending any of the
				       %% TOS or TTL values will fail on: 
				       %%    FreeBSD and NetBSD
				       %%    Linux when
				       %%      version =< 3.12.60 (at least)
				       %% so don't!
                                       %% See recvtos above for more info.
                                       [{ip, recvttl, ttl,
                                         case os:type() of
                                             {unix, BSD} 
                                               when (BSD =:= freebsd) orelse
                                                    (BSD =:= netbsd) ->
                                                 default;
                                             {unix, netbsd} ->
                                                 default;
					     {unix, linux} ->
						 case os:version() of
						     Vsn when Vsn > {3,12,60} ->
							 42;
						     _ ->
							 default
						 end;
                                             _ -> 
                                                 42
                                         end}];
                                   false ->
                                       []
                               end
		       end,

                   Enable = fun(Sock, Level, Opt) ->
				    ?SEV_IPRINT("try enable [~w] ~p",
                                                [Level, Opt]),
				    socket:setopt(Sock, Level, Opt, true)
                            end,
                   Send = fun(Sock, Data, Dest, []) ->
                                  Msg = #{addr => Dest,
                                          iov  => [Data]},
                                  socket:sendmsg(Sock, Msg);
			     (Sock, Data, Dest, Hdrs) when is_list(Hdrs) ->
				  CMsgs = [#{level => Level,
                                             type  => Type,
                                             value => Val} ||
                                              {Level, Type, Val} <- Hdrs],
                                  Msg   = #{addr => Dest,
                                            ctrl => CMsgs,
                                            iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 proto  => udp,
				 opts   => Opts,
                                 send   => Send,
                                 recv   => Recv,
                                 enable => Enable},
                   ok = api_opt_ip_mopts_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ip_mopts_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},

         #{desc => "enable options on dst socket",
           cmd  => fun(#{sock_dst := DSock,
			 sock_src := SSock,
			 opts     := Opts,
			 enable   := Enable} = _State) ->
			   %% If we fail to enable *any* of the options,
			   %% we give up.
			   E = fun({Level, Opt, _, _}) -> 
                                       case Enable(DSock, Level, Opt) of
                                           ok ->
                                               ?SEV_IPRINT("dst [~w] ~w enabled",
                                                           [Level, Opt]),
                                               ok;
                                           {error, enoprotoopt = Reason} ->
                                               ?SEV_EPRINT("Expected "
                                                           "Failure: "
                                                           "~p => SKIP",
                                                           [Reason]),
                                               (catch socket:close(DSock)),
                                               (catch socket:close(SSock)),
                                               {skip, Reason};
                                           {error, Reason} = ERROR ->
                                               ?SEV_EPRINT("Failed "
                                                           "setting ~w:"
                                                           "   ~p",
                                                           [Opt, Reason]),
                                               throw(ERROR)
                                       end
			       end,
			   lists:foreach(E, Opts),
			   ok
                   end},

         #{desc => "send req (to dst)",
           cmd  => fun(#{sock_src := Sock,
			 sa_dst   := Dst,
			 opts     := Opts,
			 send     := Send}) ->
			   Hdrs = [{Level, Type, Data} ||
				      {Level, _, Type, Data} <- 
					  Opts, (Data =/= default)],
                           Send(Sock, ?BASIC_REQ, Dst, Hdrs)
                   end},
         #{desc => "recv req (from src)",
           cmd  => fun(#{sock_dst := Sock,
			 sa_src   := Src,
			 recv     := Recv,
			 opts     := Opts}) ->
                           case Recv(Sock) of
                               {ok, {Src, CMsgs, ?BASIC_REQ}}
                                 when length(CMsgs) =:= length(Opts)  ->
                                   ?SEV_IPRINT("Got (expected) cmsg headers: "
					       "~n   ~p", [CMsgs]),
				   %% We should really verify the headers:
				   %% values, types and so on...
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [{Level, Type} ||
                                                    {Level, _, Type, _} <- Opts],
						BadCHdrs,
						?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src,
                                                [{Level, Type} ||
                                                    {Level, _, Type, _} <- Opts],
						?BASIC_REQ,
                                                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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the IPv6 pktinfo control message header is received on
%% incoming datagrams (UDP and RAW) when setting the socket 'ipv6'
%% option recvpktinfo is set to true when using.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, ipv6, recvpktinfo, boolean()).
%%
%% For all subsequent *received* messages, the pktinfo control message
%% header will be with the message.
%%
%% Only allowed for dgram and raw,
%% although we only test this with dgram.
%%

api_opt_ipv6_recvpktinfo_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_ipv6_recvpktinfo_udp6,
           fun() ->
                   has_support_ipv6(),
                   has_support_ipv6_recvpktinfo()
           end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, ipv6, recvpktinfo, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, ipv6, recvpktinfo)
                          end,
                   Send = fun(Sock, Data, Dest, default) ->
                                  Msg = #{addr => Dest,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg);
                             (Sock, Data, Dest, Info) ->
                                  %% We do not support this at the moment!!!
                                  CMsg = #{level => ipv6,
                                              type  => pktinfo,
                                              data  => Info},
                                  Msg  = #{addr => Dest,
                                              ctrl => [CMsg],
                                              iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet6,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_ipv6_recvpktinfo_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ipv6_recvpktinfo_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},
         #{desc => "default pktinfo for dst socket",
           cmd  => fun(#{sock_dst := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("dst recvttl: ~p", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected src recvtos: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) recvtos:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo (explicit) pktinfo)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   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, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 => "enable pktinfo on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("dst pktinfo enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting pktinfo:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo explicit pktinfo)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   end},
         #{desc => "recv req (from src) - w default pktinfo",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [#{level := ipv6,
                                             type  := pktinfo,
                                             value := #{addr    := Addr,
                                                        ifindex := IfIdx}}],
                                     ?BASIC_REQ}} ->
                                   ?SEV_IPRINT("Got (default) Pkt Info: "
                                               "~n   Addr:      ~p"
                                               "~n   If Index:  ~p",
                                               [Addr, IfIdx]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                #{level => ipv6,
                                                  type  => pktinfo,
                                                  value => "something"} , BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the 'flow info' control message header is received when
%% setting the socket 'ipv6' option flowinfo  is set to true when using
%% sendmsg/recvmsg on an IPv6 UDP (dgram) socket.
%% So, this is done on the receiving side: 
%%
%%               socket:setopt(Sock, ipv6, flowinfo, boolean()).
%%
%% For all subsequent *received* messages, the 'flow info' control message
%% header will be with the message.
%%
%% Only allowed for dgram and raw,
%% although we only test this with dgram.
%%
%% There seem to be some weirdness with the definition of this
%% option, so its defined in an include file we don't include
%% (directly or indirectly). And since some of the defines
%% occur in a file we *do* include (via netinet/in.h), we
%% leave it as is for now...
%%

api_opt_ipv6_flowinfo_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_ipv6_flowinfo_udp6,
           fun() ->
                   has_support_ipv6(),
                   has_support_ipv6_flowinfo()
           end,
           fun() ->
                   Set  = fun(Sock, Value) ->
                                  socket:setopt(Sock, ipv6, flowinfo, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, ipv6, flowinfo)
                          end,
                   Send = fun(Sock, Data, Dest) ->
                                  Msg = #{addr => Dest,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet6,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_ipv6_flowinfo_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ipv6_flowinfo_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},
         #{desc => "default flowinfo for dst socket",
           cmd  => fun(#{sock_dst := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("dst flowinfo: ~p", [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected src flowinfo: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) flowinfo:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   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, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 => "enable flowinfo on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("dst flowinfo enabled"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting flowinfo:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   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, [#{level := ipv6,
                                             type  := flowinfo,
                                             value := FlowID}], ?BASIC_REQ}} ->
                                   ?SEV_IPRINT("Got flow info: "
					       "~n   Flow ID: ~p", [FlowID]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                #{level => ipv6,
						  type  => flowinfo,
						  value => "something"},
						BadCHdrs,
						?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the 'hop limit' control message header is received when
%% setting the socket 'ipv6' hoplimit or recvhoplimit option is set to 
%% true when using sendmsg/recvmsg on an IPv6 UDP (dgram) socket.
%% So, this is done on the receiving side: 
%%
%%      socket:setopt(Sock, ipv6, recvhoplimit | hoplimit, boolean()).
%%
%% For all subsequent *received* messages, the 'hop limit' control message
%% header will be with the message.
%% We make the assumption, that if 'recvhoplimit' is supported, then
%% that option is used to order the hoplimit control message, otherwise
%% hoplimit is used.
%%
%% Only allowed for dgram and raw,
%% although we only test this with dgram.
%%
%% <Note>
%%
%% There is also an IPV6_RECVHOPLIMIT option defined in the header
%% file (bits/in.h) with a different value. This is not mentioned
%% in the man page. Deprecated? More testing needed...
%%
%% </Note>
%%

api_opt_ipv6_hoplimit_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_ipv6_hoplimit_udp6,
           fun() ->
                   has_support_ipv6(),
                   has_support_ipv6_hoplimit_or_recvhoplimit(),
		   is_good_enough_darwin({9,8,0}),
                   is_good_enough_montavista("4.0.1")
           end,
           fun() ->
		   %% Begin by choosing which of the options we shall use
		   Opt = case socket:is_supported(options, ipv6, recvhoplimit) of
			     true  -> recvhoplimit;
			     false -> hoplimit
			 end,
                   Set  = fun(Sock, Value) ->
				  ?SEV_IPRINT("try set ~p: ~p", [Opt, Value]),
                                  socket:setopt(Sock, ipv6, Opt, Value)
                          end,
                   Get  = fun(Sock) ->
				  ?SEV_IPRINT("try get ~p", [Opt]),
                                  socket:getopt(Sock, ipv6, Opt)
                          end,
                   Send = fun(Sock, Data, Dest) ->
                                  Msg = #{addr => Dest,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet6,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_ipv6_hoplimit_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ipv6_hoplimit_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},
         #{desc => "default [recv]hoplimit for dst socket",
           cmd  => fun(#{sock_dst := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("dst [recv]hoplimit: ~p",
					       [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected src [recv]hoplimit: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, enoprotoopt = Reason} ->
                                   %% On some platforms this is not accepted
                                   %% for UDP, so skip this part (UDP).
                                   ?SEV_EPRINT("Expected Failure: "
                                               "~p => SKIP", [Reason]),
                                   (catch socket:close(Sock)),
                                   (catch socket:close(maps:get_value(sock_src,
								      State))),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) hoplimit:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   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, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 => "enable [recv]hoplimit on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("dst [recv]hoplimit enabled"),
                                   ok;
                               {error, enoprotoopt = Reason} ->
                                   %% On some platforms this is not accepted
                                   %% for UDP, so skip this part (UDP).
                                   ?SEV_EPRINT("Expected Failure: "
                                               "~p => SKIP", [Reason]),
                                   (catch socket:close(Sock)),
                                   (catch socket:close(maps:get_value(sock_src,
								      State))),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting hoplimit:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo explicit ttl)",
           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, [#{level := ipv6,
                                             type  := hoplimit,
                                             value := HL}], ?BASIC_REQ}}
                                 when is_integer(HL) ->
                                   ?SEV_IPRINT("Got hop limit: "
					       "~n   Hop Limit: ~p", [HL]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                #{level => ipv6,
						  type  => hoplimit,
						  value => "something"},
						BadCHdrs,
						?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, #{level => ipv6,
						       type  => hoplimit,
						       value => "something"},
						?BASIC_REQ, 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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the 'tclass' control message header is received when
%% setting the socket 'ipv6' tclass or recvtclass option is set to 
%% true when using sendmsg/recvmsg on an IPv6 UDP (dgram) socket.
%% So, this is done on the receiving side: 
%%
%%      socket:setopt(Sock, ipv6, recvtclass | tclass, boolean()).
%%
%% For all subsequent *received* messages, the 'tclass' control message
%% header will be with the message.
%% We make the assumption, that if 'recvtclass' is supported, then
%% that option is used to order the tclass control message, otherwise
%% tclass is used.
%%
%% Only allowed for dgram and raw,
%% although we only test this with dgram.
%%
%% <Note>
%%
%% There is also an IPV6_RECVTCLASS option defined in the header
%% file (bits/in.h) with a different value. This is not mentioned
%% in the man page. Deprecated? More testing needed...
%%
%% </Note>
%%

api_opt_ipv6_tclass_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_ipv6_tclass_udp6,
           fun() ->
                   has_support_ipv6(),
                   has_support_ipv6_tclass_or_recvtclass()
           end,
           fun() ->
		   %% Begin by choosing which of the options we shall use
		   Opt = case socket:is_supported(options, ipv6, recvtclass) of
			     true  -> recvtclass;
			     false -> tclass
			 end,
                   Set  = fun(Sock, Value) ->
				  ?SEV_IPRINT("try set ~p: ~p", [Opt, Value]),
                                  socket:setopt(Sock, ipv6, Opt, Value)
                          end,
                   Get  = fun(Sock) ->
				  ?SEV_IPRINT("try get ~p", [Opt]),
                                  socket:getopt(Sock, ipv6, Opt)
                          end,
                   Send = fun(Sock, Data, Dest, default) ->
                                  Msg = #{addr => Dest,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg);
			     (Sock, Data, Dest, TC) ->
                                  TCHdr    = #{level => ipv6,
					       type  => tclass,
					       data  => TC},
				  CMsgs = [TCHdr],
                                  Msg   = #{addr => Dest,
					       ctrl => CMsgs,
					       iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet6,
                                 proto  => udp,
                                 send   => Send,
                                 recv   => Recv,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_ipv6_tclass_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ipv6_tclass_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},
         #{desc => "default [recv]tclass for dst socket",
           cmd  => fun(#{sock_dst := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("dst [recv]tclass: ~p",
					       [Value]),
                                   ok;
                               {ok, Unexpected} ->
                                   ?SEV_EPRINT("Unexpected src [recv]tclass: ~p",
                                               [Unexpected]),
                                   {error, {unexpected, Unexpected}};
                               {error, enoprotoopt = Reason} ->
                                   %% On some platforms this is not accepted
                                   %% for UDP, so skip this part (UDP).
                                   ?SEV_EPRINT("Expected Failure: "
                                               "~p => SKIP", [Reason]),
                                   (catch socket:close(Sock)),
                                   (catch socket:close(maps:get_value(sock_src,
								      State))),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed getting (default) tclass:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   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, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [], BadCHdrs,
                                                ?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, [], ?BASIC_REQ, 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 => "enable [recv]tclass on dst socket",
           cmd  => fun(#{sock_dst := Sock, set := Set} = State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("dst [recv]tclass enabled"),
                                   ok;
                               {error, enoprotoopt = Reason} ->
                                   %% On some platforms this is not accepted
                                   %% for UDP, so skip this part (UDP).
                                   ?SEV_EPRINT("Expected Failure: "
                                               "~p => SKIP", [Reason]),
                                   (catch socket:close(Sock)),
                                   (catch socket:close(maps:get_value(sock_src,
								      State))),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed setting tclass:"
                                               "   ~p", [Reason]),
                                   ERROR
                           end
                   end},

         #{desc => "send req (to dst) (wo explicit tc)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           Send(Sock, ?BASIC_REQ, Dst, default)
                   end},
         #{desc => "recv req (from src)",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [#{level := ipv6,
                                             type  := tclass,
                                             value := TClass}], ?BASIC_REQ}}
			       when is_integer(TClass) ->
                                   ?SEV_IPRINT("Got tclass: "
					       "~n   TClass: ~p", [TClass]),
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                #{level => ipv6,
						  type  => tclass,
						  value => "something"},
						BadCHdrs,
						?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, #{level => ipv6,
						       type  => tclass,
						       value => "something"},
						?BASIC_REQ, 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 req (to dst) (w explicit tc = 1)",
           cmd  => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) ->
                           case Send(Sock, ?BASIC_REQ, Dst, 1) of
                               {error,
                                {get_overlapped_result,
                                 #{file     := File,
                                   function := Function,
                                   line     := Line,
                                   raw_info := RawInfo,
                                   info     := invalid_parameter = Info}}} ->
                                   %% IF we can't send it the test will not work
                                   ?SEV_EPRINT("Cannot send TClass: "
                                               "~p => SKIP: "
                                               "~n   File:     ~s"
                                               "~n   Function: ~s"
                                               "~n   Line:     ~p"
                                               "~n   Raw Info: ~p",
                                               [Info,
                                                File, Function, Line,
                                                RawInfo]),
                                   (catch socket:close(Sock)),
                                   {skip,
                                    ?F("Cannot send with TClass: ~p", [Info])};
                               {error, {get_overlapped_result,
                                        invalid_parameter = Info}} ->
                                   %% IF we can't send it the test will not work
                                   ?SEV_EPRINT("Cannot send TClass: "
                                               "~p => SKIP", [Info]),
                                   (catch socket:close(Sock)),
                                   {skip,
                                    ?F("Cannot send with TClass: ~p", [Info])};

                               {error,
                                {completion_status,
                                 #{file     := File,
                                   function := Function,
                                   line     := Line,
                                   raw_info := RawInfo,
                                   info     := invalid_parameter = Info}}} ->
                                   %% IF we can't send it the test will not work
                                   ?SEV_EPRINT("Cannot send TClass: "
                                               "~p => SKIP: "
                                               "~n   File:     ~s"
                                               "~n   Function: ~s"
                                               "~n   Line:     ~p"
                                               "~n   Raw Info: ~p",
                                               [Info,
                                                File, Function, Line,
                                                RawInfo]),
                                   (catch socket:close(Sock)),
                                   {skip,
                                    ?F("Cannot send with TClass: ~p", [Info])};
                               {error, {completion_status,
                                        invalid_parameter = Info}} ->
                                   %% IF we can't send it the test will not work
                                   ?SEV_EPRINT("Cannot send TClass: "
                                               "~p => SKIP", [Info]),
                                   (catch socket:close(Sock)),
                                   {skip,
                                    ?F("Cannot send with TClass: ~p", [Info])};

                               Other ->
                                   Other
                           end
                   end},
         #{desc => "recv req (from src)",
           cmd  => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) ->
                           case Recv(Sock) of
                               {ok, {Src, [#{level := ipv6,
                                             type  := tclass,
                                             value := 1 = TClass}], ?BASIC_REQ}}
			       when is_integer(TClass) ->
                                   ?SEV_IPRINT("Got (expected) tclass: "
					       "~n   TClass: ~p", [TClass]),
                                   ok;
                               {ok, {_Src, [#{level := ipv6,
					      type  := tclass,
					      value := TClass}], ?BASIC_REQ}}
			       when is_integer(TClass) ->
                                   ?SEV_EPRINT("Unexpected tclass: "
                                               "~n   Expect TClass: ~p"
                                               "~n   Recv TClass:   ~p",
                                               [1, TClass]),
                                   {error, {unexpected_tclass, TClass}};
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                #{level => ipv6,
						  type  => tclass,
						  value => "something"},
						BadCHdrs,
						?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src, #{level => ipv6,
						       type  => tclass,
						       value => "something"},
						?BASIC_REQ, 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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% This intended to test "all" of the (currently) supported IPv6
%% options that results in control message header(s).
%% So, this is done on the receiving side: 
%%
%%      socket:setopt(Sock, ipv6, Flag, boolean()).
%%
%% For all subsequent *received* messages, a control message header
%% for each of the enabled options will be received with the message.
%%
%% Only allowed for dgram and raw,
%% although we only test this with dgram.
%%
%% Currently we *try* to use the following opts:
%%
%%      recvpktinfo | pktinfo   => pktinfo
%%      flowinfo                => flowinfo
%%      recvhoplimit | hoplimit => hoplimit
%%      recvtclass | tclass     => tclass
%%
%%
%% Every time we add a test case for a new option (that results in
%% a control message hedare), we should also add it here.
%%
%% Even though this is a IPv6 test case, we add the 'socket' timestamp
%% option (just to fill up), but in the test to see if we should run
%% the test (since its a IPv6 test case).
%%

api_opt_ipv6_mopts_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_ipv6_mopts_udp6,
           fun() ->
                   has_support_ipv6(),
                   Opts =
                       [{ipv6, recvpktinfo},
                        {ipv6, flowinfo},
                        {ipv6, recvhoplimit},
                        {ipv6, hoplimit}] ++
                       case os:type() of
                           {win32, nt} ->
                               [];
                           _ ->
                               [{ipv6, recvtclass},
                                {ipv6, tclass}]
                       end,
		   case is_any_options_supported(Opts) of
		       true ->
			   ok;
		       false ->
			   skip("None of the needed options are supported")
		   end,
		   %% The problem here is hoplimit on darwin 9.8.0,
		   %% but I can't be bothered to adjust the test case,
		   %% just skip on that machine (there is only one)...
		   is_good_enough_darwin({9,8,0}),
                   is_good_enough_montavista("4.0.1")
           end,
           fun() ->
		   %% If we get this far, we *know* that at least one of the
		   %% options are available.

		   %% This is list of all the options and there resulting
		   %% control message header type(s):
		   %%   [{'ipv6 socket option', 'control message header type'}]
		   Opts =
		       case socket:is_supported(options, socket, timestamp) of
			   true ->
			       [{socket, timestamp, timestamp, default}];
			   false ->
			       []
		       end ++
		       case socket:is_supported(options, ipv6, recvpktinfo) of
			   true ->
			       [{ipv6, recvpktinfo, pktinfo, default}];
			   false ->
			       []
		       end ++
		       case socket:is_supported(options, ipv6, flowinfo) of
			   true ->
			       [{ipv6, flowinfo, flowinfo, default}];
			   false ->
			       []
		       end ++
		       case socket:is_supported(options, ipv6, recvhoplimit) of
			   true ->
			       [{ipv6, recvhoplimit, hoplimit, default}];
			   false ->
			       case socket:is_supported(options, ipv6, hoplimit) of
				   true ->
				       [{ipv6, hoplimit, hoplimit, default}];
				   false ->
				       []
			       end
		       end ++
                       case os:type() of
                           {win32, nt} ->
                               [];
                           _ ->
                               case socket:is_supported(options,
                                                        ipv6, recvtclass) of
                                   true ->
                                       [{ipv6, recvtclass, tclass, 42}];
                                   false ->
                                       case socket:is_supported(options,
                                                                ipv6, tclass) of
                                           true ->
                                               [{ipv6, tclass, tclass, 42}];
                                           false ->
                                               []
                                       end
                               end
		       end,

                   Enable = fun(Sock, Level, Opt) ->
				    ?SEV_IPRINT("try enable [~w] ~p",
                                                [Level, Opt]),
				    socket:setopt(Sock, Level, Opt, true)
                            end,
                   Send = fun(Sock, Data, Dest, []) ->
                                  Msg = #{addr => Dest,
                                             iov  => [Data]},
                                  socket:sendmsg(Sock, Msg);
			     (Sock, Data, Dest, Hdrs) when is_list(Hdrs) ->
				  CMsgs = [#{level => Level,
                                             type  => Type,
                                             data  => Val} ||
                                              {Level, Type, Val} <- Hdrs],
                                  Msg   = #{addr => Dest,
                                            ctrl => CMsgs,
                                            iov  => [Data]},
                                  socket:sendmsg(Sock, Msg)
                          end,
                   Recv = fun(Sock) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{addr := Source,
                                             ctrl := CMsgs,
                                             iov  := [Data]}} ->
                                          {ok, {Source, CMsgs, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet6,
                                 proto  => udp,
				 opts   => Opts,
                                 send   => Send,
                                 recv   => Recv,
                                 enable => Enable},
                   ok = api_opt_ipv6_mopts_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_ipv6_mopts_udp(InitState) ->
    Seq = 
        [
         #{desc => "local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa_src => LSA,
                                       lsa_dst => LSA}}
                   end},

         #{desc => "open src socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_src => Sock}}
                   end},
         #{desc => "bind src",
           cmd  => fun(#{sock_src := Sock, lsa_src := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname src socket",
           cmd  => fun(#{sock_src := Sock} = State) ->
                           SASrc = sock_sockname(Sock),
                           ?SEV_IPRINT("src sockaddr: "
                                       "~n   ~p", [SASrc]),
                           {ok, State#{sa_src => SASrc}}
                   end},

         #{desc => "open dst socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock_dst => Sock}}
                   end},
         #{desc => "bind dst",
           cmd  => fun(#{sock_dst := Sock, lsa_dst := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "sockname dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           SADst = sock_sockname(Sock),
                           ?SEV_IPRINT("dst sockaddr: "
                                       "~n   ~p", [SADst]),
                           {ok, State#{sa_dst => SADst}}
                   end},

         #{desc => "enable options on dst socket",
           cmd  => fun(#{sock_dst := DSock,
			 sock_src := SSock,
			 opts     := Opts,
			 enable   := Enable} = _State) ->
			   %% If we fail to enable *any* of the options,
			   %% we give up.
			   E = fun({Level, Opt, _, _}) -> 
                                       case Enable(DSock, Level, Opt) of
                                           ok ->
                                               ?SEV_IPRINT("dst [~w] ~w enabled",
                                                           [Level, Opt]),
                                               ok;
                                           {error, enoprotoopt = Reason} ->
                                               ?SEV_EPRINT("Expected "
                                                           "Failure: "
                                                           "~p => SKIP",
                                                           [Reason]),
                                               (catch socket:close(DSock)),
                                               (catch socket:close(SSock)),
                                               {skip, Reason};
                                           {error, Reason} = ERROR ->
                                               ?SEV_EPRINT("Failed "
                                                           "setting ~w:"
                                                           "   ~p",
                                                           [Opt, Reason]),
                                               throw(ERROR)
                                       end
			       end,
			   lists:foreach(E, Opts),
			   ok
                   end},

         #{desc => "send req (to dst)",
           cmd  => fun(#{sock_src := Sock,
			 sa_dst   := Dst,
			 opts     := Opts,
			 send     := Send}) ->
			   Hdrs = [{Level, Type, Data} ||
				      {Level, _, Type, Data} <- 
					  Opts, (Data =/= default)],
                           Send(Sock, ?BASIC_REQ, Dst, Hdrs)
                   end},
         #{desc => "recv req (from src)",
           cmd  => fun(#{sock_dst := Sock,
			 sa_src   := Src,
			 recv     := Recv,
			 opts     := Opts}) ->
                           case Recv(Sock) of
                               {ok, {Src, CMsgs, ?BASIC_REQ}}
                                 when length(CMsgs) =:= length(Opts)  ->
                                   ?SEV_IPRINT("Got (expected) cmsg headers: "
					       "~n   ~p", [CMsgs]),
				   %% We should really verify the headers:
				   %% values, types and so on...
                                   ok;
                               {ok, {BadSrc, BadCHdrs, BadReq} = UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Recv Source:   ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                [{Level, Type} ||
                                                    {Level, _, Type, _} <- Opts],
						BadCHdrs,
						?BASIC_REQ, BadReq]),
                                   {error, {unexpected_data, UnexpData}};
                               {ok, UnexpData} ->
                                   ?SEV_EPRINT("Unexpected msg: "
                                               "~n   Expect Source: ~p"
                                               "~n   Expect CHdrs:  ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Unexp Data:    ~p",
                                               [Src,
                                                [{Level, Type} ||
                                                    {Level, _, Type, _} <- Opts],
						?BASIC_REQ,
                                                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} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_src, State)}
                   end},
         #{desc => "close dst socket",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock_dst, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],
    Evaluator = ?SEV_START("tester", Seq, InitState),
    ok = ?SEV_AWAIT_FINISH([Evaluator]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the congestion tcp socket option.
%%
%% According to the man page (on linux) for this option it *should* be
%% possible to both get and set *allowed* algorithms. But when we attempt
%% to set, we get 'enoent'.
%% According to /proc/sys/net/ipv4/tcp_allowed_congestion_control that
%% allgorithm was allowed, so...
%% For now, we only test that we can get (it could be a bug in our code)

api_opt_tcp_congestion_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_tcp_congestion_tcp4,
           fun() -> has_support_ipv4(), has_support_tcp_congestion() end,
           fun() ->
                   Set  = fun(Sock, Value) when is_list(Value) ->
                                  socket:setopt(Sock, tcp, congestion, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, tcp, congestion)
                          end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_tcp_congestion_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_tcp_congestion_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := LSock, lsa := LSA} = _State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [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 (get congestion)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, get_congestion)
                   end},
         #{desc => "get congestion",
           cmd  => fun(#{sock := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, Algorithm} ->
                                   ?SEV_IPRINT("algorithm: ~s", [Algorithm]),
                                   {ok, State#{alg => Algorithm}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (get congestion)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, get_congestion),
                           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(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, init),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "order server to continue (with get-congestion)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, get_congestion),
                           ok
                   end},
         #{desc => "await server ready (get-congestion)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, get_congestion)
                   end},


         %% *** Termination ***
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{server => Server#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    ok = ?SEV_AWAIT_FINISH([Server, Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the cork tcp socket option.
%%
%% This is a very simple test. We simple set and get the value.
%% To test that it has an effect is just "to much work"...
%%
%% Reading the man page it seems like (on linux) that the
%% value resets itself after some (short) time...

api_opt_tcp_cork_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_tcp_cork_tcp4,
           fun() -> has_support_ipv4(), has_support_tcp_cork() end,
           fun() ->
                   Set  = fun(Sock, Value) when is_boolean(Value) ->
                                  socket:setopt(Sock, tcp, cork, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, tcp, cork)
                          end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_tcp_cork_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_tcp_cork_tcp(InitState) ->
    process_flag(trap_exit, true),
    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := LSock, lsa := LSA} = _State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% The actual test
         #{desc => "get (default) cork (= false)",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("cork default: ~p", [Value]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "enable cork (=> true)",
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("cork enabled"),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "get cork (= true)",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, true = Value} ->
                                   ?SEV_IPRINT("cork: ~p", [Value]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% *** Termination ***
         #{desc => "close connection socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start tester evaluator"),
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the maxseg tcp socket option.
%%
%% This is a very simple test. We simple set and get the value.
%% To test that it has an effect is just "to much work"...
%%
%% Note that there is no point in reading this value back,
%% since the kernel imposes its own rules with regard
%% to what is an acceptable value.
%%

api_opt_tcp_maxseg_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_ipv4(),
                   has_support_tcp_maxseg()
           end,
           fun() ->
                   Set  = fun(Sock, Value) when is_integer(Value) ->
                                  socket:setopt(Sock, tcp, maxseg, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, tcp, maxseg)
                          end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_tcp_maxseg_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_tcp_maxseg_tcp(InitState) ->
    process_flag(trap_exit, true),
    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := LSock, lsa := LSA} = _State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% The actual test
         #{desc => "get (default) maxseg",
           cmd  => fun(#{sock := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, DefMaxSeg} ->
                                   ?SEV_IPRINT("maxseg default: ~p", [DefMaxSeg]),
                                   {ok, State#{def_maxseg => DefMaxSeg}};
                               {error, enoprotoopt = Reason} ->
                                   {skip, Reason};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         
         %% Note that there is no point in reading this value back,
         %% since the kernel imposes its own rules with regard
         %% to what is an acceptable value.
         %% So, even if the set operation is a success, the value
         %% still might not have changed.
         %%
         %% Note that not all platforms allow this to be set!
         %% Since this is the *last* operation in the test sequence
         %% (before termination) we also accept error reason = einval
         %% as success (rather then skip).
         %% The same goes for the error reason = enoprotoopt (Solaris).
         #{desc => "(maybe) change maxseg (default + 16)",
           cmd  => fun(#{sock       := Sock,
                         set        := Set,
                         def_maxseg := DefMaxSeg} = _State) ->
                           NewMaxSeg = DefMaxSeg + 16,
                           case Set(Sock, NewMaxSeg) of
                               ok ->
                                   ?SEV_IPRINT("maxseg (maybe) changed (to ~w)",
                                               [NewMaxSeg]),
                                   ok;
                               {error, Reason} when (Reason =:= einval) orelse
                                                    (Reason =:= enoprotoopt) ->
                                   ?SEV_IPRINT("change not allowed (~w)",
                                               [Reason]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** Termination ***
         #{desc => "close connection socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start tester evaluator"),
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the nodelay tcp socket option.
%%
%% This is a very simple test. We simple set and get the value.
%% To test that it has an effect is just "to much work"...

api_opt_tcp_nodelay_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_tcp_nodelay_tcp4,
           fun() -> has_support_ipv4(), has_support_tcp_nodelay() end,
           fun() ->
                   Set  = fun(Sock, Value) when is_boolean(Value) ->
                                  socket:setopt(Sock, tcp, nodelay, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, tcp, nodelay)
                          end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_tcp_nodelay_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_tcp_nodelay_tcp(InitState) ->
    process_flag(trap_exit, true),
    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, lsa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% The actual test
         #{desc => "get (default) nodelay (= false)",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("nodelay default: ~p", [Value]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "enable nodelay (=> true)",
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("nodelay enabled"),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "get nodelay (= true)",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, true = Value} ->
                                   ?SEV_IPRINT("nodelay: ~p", [Value]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% *** Termination ***
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start tester evaluator"),
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    ok = ?SEV_AWAIT_FINISH([Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the keepcnt tcp socket option.
%%
%% "The maximum number of keepalive probes TCP should send before
%%  dropping the connection"
%%

api_opt_tcp_keepcnt_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4(), has_support_tcp_keepcnt() end,
           fun() ->
                   Set  = fun(Sock, Value) when is_integer(Value) ->
                                  socket:setopt(Sock, tcp, keepcnt, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, tcp, keepcnt)
                          end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_tcp_keepcnt_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_tcp_keepcnt_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := LSock, lsa := LSA} = _State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [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 (get keepcnt(1))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, get_keepcnt)
                   end},
         #{desc => "get keepcnt",
           cmd  => fun(#{sock := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, KeepCnt} ->
                                   ?SEV_IPRINT("keepcnt: ~p", [KeepCnt]),
                                   {ok, State#{keepcnt => KeepCnt}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (get keepcnt(1))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, get_keepcnt),
                           ok
                   end},


         #{desc => "await continue (set keepcnt)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, set_keepcnt)
                   end},
         #{desc => "set keepcnt",
           cmd  => fun(#{sock    := Sock,
                         set     := Set,
                         keepcnt := KeepCnt} = State) ->
                           NewKeepCnt =
                               if
                                   (KeepCnt >= 255) ->
                                       KeepCnt - 1;
                                   true ->
                                       KeepCnt + 1
                               end,
                           case Set(Sock, NewKeepCnt) of
                               ok ->
                                   ?SEV_IPRINT("keepcnt updated (to ~p)",
                                               [NewKeepCnt]),
                                   {ok, State#{keepcnt => NewKeepCnt}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (set keepcnt)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, set_keepcnt),
                           ok
                   end},


         #{desc => "await continue (get keepcnt(2))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, get_keepcnt)
                   end},
         #{desc => "get keepcnt(2)",
           cmd  => fun(#{sock    := Sock,
                         get     := Get,
                         keepcnt := ExpKeepCnt} = _State) ->
                           case Get(Sock) of
                               {ok, KeepCnt} when (ExpKeepCnt =:= KeepCnt) ->
                                   ?SEV_IPRINT("expected keepcnt (~p)",
                                               [ExpKeepCnt]),
                                   ok;
                               {ok, KeepCnt} ->
                                   ?SEV_EPRINT("unexpected keepcnt:"
                                               "~n   Expected KeepCnt: ~p"
                                               "~n   Actual KeepCnt:   ~p",
                                               [ExpKeepCnt, KeepCnt]),
                                   {error,
                                    {unexpected_keepcnt, ExpKeepCnt, KeepCnt}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed get keepcnt: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (get keepcnt)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, get_keepcnt),
                           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(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, init),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "order server to continue (with get-keepcnt(1))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, get_keepcnt),
                           ok
                   end},
         #{desc => "await server ready (get-keepcnt(1))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, get_keepcnt)
                   end},

         #{desc => "order server to continue (with set-keepcnt)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, set_keepcnt),
                           ok
                   end},
         #{desc => "await server ready (set-keepcnt)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, set_keepcnt)
                   end},

         #{desc => "order server to continue (with get-keepcnt(2))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, get_keepcnt),
                           ok
                   end},
         #{desc => "await server ready (get-keepcnt(2))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, get_keepcnt)
                   end},


         %% *** Termination ***
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{server => Server#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    ok = ?SEV_AWAIT_FINISH([Server, Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the keepidle tcp socket option.
%%
%% "Gets or sets the number of seconds a TCP connection will remain
%%  idle before keepalive probes are sent to the remote."
%%

api_opt_tcp_keepidle_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4(), has_support_tcp_keepidle() end,
           fun() ->
                   Set  = fun(Sock, Value) when is_integer(Value) ->
                                  socket:setopt(Sock, tcp, keepidle, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, tcp, keepidle)
                          end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_tcp_keepidle_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_tcp_keepidle_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := LSock, lsa := LSA} = _State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [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 (get keepidle(1))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, get_keepidle)
                   end},
         #{desc => "get keepidle",
           cmd  => fun(#{sock := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, KeepIdle} ->
                                   ?SEV_IPRINT("keepidle: ~p", [KeepIdle]),
                                   {ok, State#{keepidle => KeepIdle}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (get keepidle(1))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, get_keepidle),
                           ok
                   end},


         #{desc => "await continue (set keepidle)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, set_keepidle)
                   end},
         #{desc => "set keepidle",
           cmd  => fun(#{sock     := Sock,
                         set      := Set,
                         keepidle := KeepIdle} = State) ->
                           NewKeepIdle = KeepIdle + 1,
                           case Set(Sock, NewKeepIdle) of
                               ok ->
                                   ?SEV_IPRINT("keepidle updated (to ~p)",
                                               [NewKeepIdle]),
                                   {ok, State#{keepidle => NewKeepIdle}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (set keepidle)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, set_keepidle),
                           ok
                   end},


         #{desc => "await continue (get keepidle(2))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, get_keepidle)
                   end},
         #{desc => "get keepidle(2)",
           cmd  => fun(#{sock     := Sock,
                         get      := Get,
                         keepidle := ExpKeepIdle} = _State) ->
                           case Get(Sock) of
                               {ok, KeepIdle} when (ExpKeepIdle =:= KeepIdle) ->
                                   ?SEV_IPRINT("expected keepidle (~p)",
                                               [ExpKeepIdle]),
                                   ok;
                               {ok, KeepIdle} ->
                                   ?SEV_EPRINT("unexpected keepidle:"
                                               "~n   Expected KeepIdle: ~p"
                                               "~n   Actual KeepIdle:   ~p",
                                               [ExpKeepIdle, KeepIdle]),
                                   {error,
                                    {unexpected_keepidle,
                                     ExpKeepIdle, KeepIdle}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed get keepidle: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (get keepidle(2))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, get_keepidle),
                           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(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, init),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "order server to continue (with get-keepidle(1))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, get_keepidle),
                           ok
                   end},
         #{desc => "await server ready (get-keepidle(1))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, get_keepidle)
                   end},

         #{desc => "order server to continue (with set-keepidle)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, set_keepidle),
                           ok
                   end},
         #{desc => "await server ready (set-keepidle)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, set_keepidle)
                   end},

         #{desc => "order server to continue (with get-keepidle(2))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, get_keepidle),
                           ok
                   end},
         #{desc => "await server ready (get-keepidle(2))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, get_keepidle)
                   end},


         %% *** Termination ***
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{server => Server#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    ok = ?SEV_AWAIT_FINISH([Server, Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the keepintvl tcp socket option.
%%
%% "Gets or sets the number of seconds a TCP connection will wait for
%%  a keepalive response before sending another keepalive probe."
%%

api_opt_tcp_keepintvl_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4(), has_support_tcp_keepintvl() end,
           fun() ->
                   Set  = fun(Sock, Value) when is_integer(Value) ->
                                  socket:setopt(Sock, tcp, keepintvl, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, tcp, keepintvl)
                          end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_tcp_keepintvl_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_tcp_keepintvl_tcp(InitState) ->
    process_flag(trap_exit, true),
    ServerSeq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := LSock, lsa := LSA} = _State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [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 (get keepintvl(1))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, get_keepintvl)
                   end},
         #{desc => "get keepintvl",
           cmd  => fun(#{sock := Sock, get := Get} = State) ->
                           case Get(Sock) of
                               {ok, KeepIntVl} ->
                                   ?SEV_IPRINT("keepintvl: ~p", [KeepIntVl]),
                                   {ok, State#{keepintvl => KeepIntVl}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (get keepintvl(1))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, get_keepintvl),
                           ok
                   end},


         #{desc => "await continue (set keepintvl)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, set_keepintvl)
                   end},
         #{desc => "set keepintvl",
           cmd  => fun(#{sock     := Sock,
                         set      := Set,
                         keepintvl := KeepIntVl} = State) ->
                           NewKeepIntVl = KeepIntVl + 1,
                           case Set(Sock, NewKeepIntVl) of
                               ok ->
                                   ?SEV_IPRINT("keepIntVl updated (to ~p)",
                                               [NewKeepIntVl]),
                                   {ok, State#{keepintvl => NewKeepIntVl}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (set keepintvl)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, set_keepintvl),
                           ok
                   end},


         #{desc => "await continue (get keepintvl(2))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, get_keepintvl)
                   end},
         #{desc => "get keepintvl(2)",
           cmd  => fun(#{sock      := Sock,
                         get       := Get,
                         keepintvl := ExpKeepIntVl} = _State) ->
                           case Get(Sock) of
                               {ok, KeepIntVl}
                                 when (ExpKeepIntVl =:= KeepIntVl) ->
                                   ?SEV_IPRINT("expected keepintvl (~p)",
                                               [ExpKeepIntVl]),
                                   ok;
                               {ok, KeepIntVl} ->
                                   ?SEV_EPRINT("unexpected keepintvl:"
                                               "~n   Expected KeepIntVl: ~p"
                                               "~n   Actual KeepIntVl:   ~p",
                                               [ExpKeepIntVl, KeepIntVl]),
                                   {error,
                                    {unexpected_keepintvl,
                                     ExpKeepIntVl, KeepIntVl}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed get keepintvl: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (get keepintvl(2))",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, get_keepintvl),
                           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(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{server := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, server, init),
                           ok
                   end},

         %% *** The actual test ***
         #{desc => "order server to continue (with get-keepintvl(1))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, get_keepintvl),
                           ok
                   end},
         #{desc => "await server ready (get-keepintvl(1))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, get_keepintvl)
                   end},

         #{desc => "order server to continue (with set-keepintvl)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, set_keepintvl),
                           ok
                   end},
         #{desc => "await server ready (set-keepintvl)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, set_keepintvl)
                   end},

         #{desc => "order server to continue (with get-keepintvl(2))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, get_keepintvl),
                           ok
                   end},
         #{desc => "await server ready (get-keepintvl(2))",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, get_keepintvl)
                   end},


         %% *** Termination ***
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    i("start server evaluator"),
    Server = ?SEV_START("server", ServerSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{server => Server#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    ok = ?SEV_AWAIT_FINISH([Server, Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tests that the cork udp socket option.
%%
%% This is a very simple test. We simple set and get the value.
%% To test that it has an effect is just "to much work"...
%%

api_opt_udp_cork_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(api_opt_udp_cork_udp4,
           fun() -> has_support_ipv4(), has_support_udp_cork() end,
           fun() ->
                   Set  = fun(Sock, Value) when is_boolean(Value) ->
                                  socket:setopt(Sock, udp, cork, Value)
                          end,
                   Get  = fun(Sock) ->
                                  socket:getopt(Sock, udp, cork)
                          end,
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 set    => Set,
                                 get    => Get},
                   ok = api_opt_udp_cork_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

api_opt_udp_cork_udp(InitState) ->
    process_flag(trap_exit, true),
    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, dgram, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, lsa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% The actual test
         #{desc => "get (default) cork (= false)",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, false = Value} ->
                                   ?SEV_IPRINT("cork default: ~p", [Value]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "enable cork (=> true)",
           cmd  => fun(#{sock := Sock, set := Set} = _State) ->
                           case Set(Sock, true) of
                               ok ->
                                   ?SEV_IPRINT("cork enabled"),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "get cork (= true)",
           cmd  => fun(#{sock := Sock, get := Get} = _State) ->
                           case Get(Sock) of
                               {ok, true = Value} ->
                                   ?SEV_IPRINT("cork: ~p", [Value]),
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},


         %% *** Termination ***
         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start tester evaluator"),
    Tester = ?SEV_START("tester", TesterSeq, InitState),

    ok = ?SEV_AWAIT_FINISH([Tester]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                  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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    Cond = fun() -> has_support_ipv4(), api_to_connect_cond() end,
    tc_try(api_to_connect_tcp4,
           Cond,
           fun() ->
                   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 at least 4.15
api_to_connect_cond({unix, linux}, {Maj, Min, _Rev}) ->
    if
        (Maj > 4) ->
            ok;
        ((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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_connect_tcp6,
           fun() -> has_support_ipv6(), api_to_connect_cond() end,
           fun() ->
                   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 response 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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, local_sa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create node",
           cmd  => fun(#{host := _Host} = State) ->
                           {Peer, Node} = ?START_NODE("client"),
                           {ok, State#{node => Node, peer => Peer}}
                   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(#{peer := Peer} = State) ->
                           {ok,
                            try peer:stop(Peer) of
                                ok ->
                                    State#{node_stop => ok};
                                {error, Reason} ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   ~p", [Reason]),
                                    State#{node_stop => error}
                            catch
                                C:E:S ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   Class: ~p"
                                                "~n   Error: ~p"
                                                "~n   Stack: ~p",[C, E, S]),
                                    State#{node_stop => error}
                            end}
                   end},
         #{desc => "await client node termination",
           cmd  => fun(#{node := Node, node_stop := ok} = State) ->
                           ?SEV_IPRINT("Success node stop - await nodedown"),
                           receive
                               {nodedown, Node} ->
                                   ?SEV_IPRINT("nodedown received - cleanup"),
                                   State1 = maps:remove(peer, State),
                                   State2 = maps:remove(node, State1),
                                   {ok, State2}
                           end;
                      (#{node_stop := error} = State) ->
                           ?SEV_IPRINT("Failed node stop - cleanup"),
                           State1 = maps:remove(peer, State),
                           State2 = maps:remove(node, State1),
                           {ok, State2}
                   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) ->
                           LSA = which_local_socket_addr(Domain),
                           {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) ->
    LSA = which_local_socket_addr(Domain),
    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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_accept_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_accept_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create (listen) socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, lsa := LSA} = _State) ->
                           case sock_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(_Config) when is_list(_Config) ->
    ?TT(?SECS(20)),
    tc_try(api_to_maccept_tcp4,
           fun() -> has_support_ipv4() end,
           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(_Config) when is_list(_Config) ->
    ?TT(?SECS(20)),
    tc_try(api_to_maccept_tcp6,
           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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create (listen) socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, lsa := LSA} = _State) ->
                           case sock_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(_Config) when is_list(_Config) ->
    tc_try(api_to_send_tcp4,
           fun() -> has_support_ipv4() end,
           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(_Config) when is_list(_Config) ->
    tc_try(api_to_send_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   not_yet_implemented()%% ,
                   %% ok = api_to_send_tcp(inet6)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% This test case is intended to test the sendto timeout option
%% on an IPv4 UDP (dgram) socket.
api_to_sendto_udp4(_Config) when is_list(_Config) ->
    tc_try(api_to_sendto_udp4,
           fun () -> has_support_ipv4() end,
           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(_Config) when is_list(_Config) ->
    tc_try(api_to_sendto_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   not_yet_implemented()%% ,
                   %% ok = api_to_sendto_to_udp(inet6)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% This test case is intended to test the sendmsg timeout option
%% on an IPv4 TCP (stream) socket.
api_to_sendmsg_tcp4(_Config) when is_list(_Config) ->
    tc_try(api_to_sendmsg_tcp4,
           fun() -> has_support_ipv4() end,
           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(_Config) when is_list(_Config) ->
    tc_try(api_to_sendmsg_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   not_yet_implemented()%% ,
                   %% ok = api_to_sendmsg_tcp(inet6)
           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(_Config) when is_list(_Config) ->
    tc_try(api_to_recv_udp4,
           fun() -> has_support_ipv4() end,
           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(_Config) when is_list(_Config) ->
    tc_try(api_to_recv_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   not_yet_implemented()%% ,
                   %% ok = api_to_recv_udp(inet6)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% This test case is intended to test the recv timeout option
%% on an IPv4 TCP (stream) socket.
api_to_recv_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_recv_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_recv_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   case socket:is_supported(ipv6) of
                       true ->
                           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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{lsock := LSock, local_sa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {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) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain} = State) ->
                           case socket:open(Domain, stream, tcp) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_recvfrom_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_recvfrom_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   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) ->
                           LSA = which_local_socket_addr(Domain),
                           {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 sock_bind(Sock, LSA) of
                               ok ->
                                   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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_recvmsg_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_recvmsg_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_recvmsg_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(api_to_recvmsg_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end,
                   InitState = #{domain  => inet6,
                                 recv    => Recv,
                                 timeout => 2000},
                   ok = api_to_receive_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                             REGISTRY                                %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% We create a bunch of different sockets and ensure that the registry
%% has the correct info.

reg_s_single_open_and_close_and_count(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(reg_s_single_open_and_close_and_count,
           fun() ->
                   ok = reg_s_single_open_and_close_and_count()
           end).


reg_s_single_open_and_close_and_count() ->
    socket:use_registry(true),

    {OS, _} = os:type(),

    %% We may have some sockets already existing.
    %% Make sure we dont count them when we test.
    Existing = socket:which_sockets(),
    N = length(Existing),
    SupportsIPV6 =
        case (catch has_support_ipv6()) of
            ok ->
                true;
            _ ->
                false
        end,
    SupportsLOCAL =
        case (catch has_support_unix_domain_socket()) of
            ok ->
                true;
            _ ->
                false
        end,
    SupportsSCTP =
        case (catch has_support_sctp()) of
            ok ->
                true;
            _ ->
                false
        end,
    InitSockInfos =
        [
         {inet, stream, tcp},
         {inet, dgram,  udp}
        ] ++
        case SupportsIPV6 of
            true ->
                [
                 {inet6, stream, tcp},
                 {inet6, dgram,  udp}
                ];
            false ->
                []
        end ++
        case SupportsLOCAL of
            true when (OS =/= win32) ->
                [
                 {local, stream, default},
                 {local, dgram,  default}
                ];
            true ->
                [
                 {local, stream, default}
                ];
            false ->
                []
        end ++
        [
         {inet, stream, tcp},
         {inet, dgram,  udp}
        ] ++
        case SupportsSCTP of
            true ->
                %% On some platforms this is not enough,
                %% we need to actually check this "by doing it"...
                ?P("test open sctp socket"),
                case socket:open(inet,
                                 seqpacket,
                                 sctp,
                                 #{use_registry => false}) of
                    {ok, S} ->
                        ?P("test open sctp socket: success"),
                        (catch socket:close(S)),
                        [
                         {inet, seqpacket, sctp},
                         {inet, seqpacket, sctp}
                        ];
                    {error, _} ->
                        ?P("test open sctp socket: failed"),
                        []
                end;
            false ->
                []
        end ++
        [
         {inet, stream, tcp},
         {inet, dgram,  udp}
        ] ++
        case SupportsSCTP andalso SupportsIPV6 of
            true ->
                %% On some platforms this is not enough,
                %% we need to actually check this "by doing it"...
                ?P("test open sctp socket"),
                case socket:open(inet6,
                                 seqpacket,
                                 sctp,
                                 #{use_registry => false}) of
                    {ok, S6} ->
                        ?P("test open sctp socket: success"),
                        (catch socket:close(S6)),
                        [
                         {inet6, seqpacket, sctp},
                         {inet6, seqpacket, sctp}
                        ];
                    {error, _} ->
                        ?P("test open sctp socket: failed"),
                        []
                end;
            false ->
                []
        end,

    i("open sockets"),
    Socks =
        [fun({Domain, Type, Proto}) ->
                 i("open socket: ~w, ~w, ~w", [Domain, Type, Proto]),
                 {ok, Sock} = socket:open(Domain, Type, Proto),
                 Sock
         end(InitSockInfo) || InitSockInfo <- InitSockInfos],

    ?SLEEP(1000),


    %% **** Total Number Of Sockets ****

    NumSocks1 = N + length(Socks),
    NumberOf1 = socket:number_of(),

    i("verify (total) number of sockets(1): ~w, ~w",
      [NumSocks1, NumberOf1]),
    case (NumSocks1 =:= NumberOf1) of
        true ->
            ok;
        false ->
            reg_si_fail(wrong_number_of_sockets1, {NumSocks1, NumberOf1})
    end,


    %% **** Number Of IPv4 TCP Sockets ****

    %% inet, stream, tcp
    SiNumTCP = reg_si_num(InitSockInfos, inet, stream, tcp),
    SrNumTCP = reg_sr_num(Existing, inet, stream, tcp),

    i("verify number of IPv4 TCP sockets: ~w, ~w", [SiNumTCP, SrNumTCP]),
    case (SiNumTCP =:= SrNumTCP) of
        true ->
            ok;
        false ->
            reg_si_fail(wrong_number_of_ipv4_tcp_sockets, {SiNumTCP, SrNumTCP})
    end,


    %% **** Number Of IPv4 UDP Sockets ****

    %% inet, dgram, udp
    SiNumUDP = reg_si_num(InitSockInfos, inet, dgram, udp),
    SrNumUDP = reg_sr_num(Existing, inet, dgram, udp),

    i("verify number of IPv4 UDP sockets: ~w, ~w", [SiNumUDP, SrNumUDP]),
    case (SiNumUDP =:= SrNumUDP) of
        true ->
            ok;
        false ->
            exit({wrong_number_of_ipv4_udp_sockets, SiNumUDP, SrNumUDP})
    end,


    %% **** Number Of IPv4 SCTP Sockets ****

    %% inet, seqpacket, sctp
    SiNumSCTP = reg_si_num(InitSockInfos, inet, seqpacket, sctp),
    SrNumSCTP = reg_sr_num(Existing, inet, seqpacket, sctp),

    i("verify number of IPv4 SCTP sockets: ~w, ~w", [SiNumSCTP, SrNumSCTP]),
    case (SiNumSCTP =:= SrNumSCTP) of
        true ->
            ok;
        false ->
            reg_si_fail(wrong_number_of_sctp_sockets, {SiNumSCTP, SrNumSCTP})
    end,


    %% **** Number Of IPv4 Sockets ****

    %% inet
    SiNumINET = reg_si_num(InitSockInfos, inet),
    SrNumINET = reg_sr_num(Existing, inet),

    i("verify number of IPv4 sockets: ~w, ~w", [SiNumINET, SrNumINET]),
    case (SiNumINET =:= SrNumINET) of
        true ->
            ok;
        false ->
            reg_si_fail(wrong_number_of_ipv4_sockets, {SiNumINET, SrNumINET})
    end,


    %% **** Number Of IPv6 Sockets ****

    %% inet6
    SiNumINET6 = reg_si_num(InitSockInfos, inet6),
    SrNumINET6 = reg_sr_num(Existing, inet6),

    i("verify number of IPv6 sockets: ~w, ~w", [SiNumINET6, SrNumINET6]),
    case (SiNumINET6 =:= SrNumINET6) of
        true ->
            ok;
        false ->
            reg_si_fail(wrong_number_of_ipv6_sockets, {SiNumINET6, SrNumINET6})
    end,


    %% **** Number Of Unix Domain Sockets Sockets ****

    %% local
    SiNumLOCAL = reg_si_num(InitSockInfos, local),
    SrNumLOCAL = reg_sr_num(Existing, local),

    i("verify number of Unix Domain Sockets sockets: ~w, ~w",
      [SiNumLOCAL, SrNumLOCAL]),
    case (SiNumLOCAL =:= SrNumLOCAL) of
        true ->
            ok;
        false ->
            reg_si_fail(wrong_number_of_local_sockets, {SiNumLOCAL, SrNumLOCAL})
    end,


    %% **** Close *all* Sockets then verify Number Of Sockets ****

    i("close sockets"),
    lists:foreach(fun(S) ->
                          i("close socket"),                          
                          ok = socket:close(S)
                  end, Socks),

    ?SLEEP(1000),

    NumberOf2 = socket:number_of(),

    i("verify number of sockets(2): ~w, ~w", [N, NumberOf2]),
    case (N =:= NumberOf2) of
        true ->
            ok;
        false ->
            reg_si_fail(wrong_number_of_sockets2, {N, NumberOf2})
    end,


    i("verify owner sockets (none)", []),
    Expected1 = [],
    case socket:which_sockets(self()) of
        Expected1 ->
            ok;
        Unexpected1 ->
            reg_si_fail(wrong_sockets_own1, {Expected1, Unexpected1})
    end,

    i("create some sockets", []),
    OwnSockets = lists:sort(
                   [fun({D, T})->
                            i("create ~w:~w socket", [D, T]),
                            {ok, OSocks} = socket:open(D, T, default),
                            OSocks
                    end(SockInfo) || SockInfo <-
                                         [{inet, dgram},
                                          {inet, dgram},
                                          {inet, stream},
                                          {inet, stream}]]),

    i("verify owner sockets (~w)", [length(OwnSockets)]),
    case lists:sort(socket:which_sockets(self())) of
        OwnSockets ->
            ok;
        Unexpected2 ->
            reg_si_fail(wrong_sockets_own2, {OwnSockets, Unexpected2})
    end,

    i("close (own) sockets"),
    lists:foreach(fun(S) ->
                          i("close socket"),                          
                          ok = socket:close(S)
                  end, OwnSockets),
    ?SLEEP(1000),

    i("verify number of sockets(2) (again)"),
    NumberOf2 = socket:number_of(),

    i("verify pre-existing sockets(2)", []),
    case socket:which_sockets() of
        Existing ->
            ok;
        OtherSockets ->
            reg_si_fail(wrong_sockets2, {Existing, OtherSockets})
    end,

    socket:use_registry(false),
    ok.


reg_si_fail(Reason, Extra) ->
    socket:use_registry(false),
    exit({Reason, Extra}).

reg_si_num(SocksInfo, Domain)
  when ((Domain =:= inet) orelse (Domain =:= inet6) orelse (Domain =:= local)) ->
    reg_si_num(SocksInfo, Domain, undefined, undefined);
reg_si_num(SocksInfo, Type)
  when ((Type =:= stream) orelse (Type =:= dgram) orelse (Type =:= seqpacket)) ->
    reg_si_num(SocksInfo, undefined, Type, undefined);
reg_si_num(SocksInfo, Proto)
  when ((Proto =:= sctp) orelse (Proto =:= tcp) orelse (Proto =:= udp)) ->
    reg_si_num(SocksInfo, undefined, undefined, Proto).

reg_si_num(SocksInfo, Domain, undefined, undefined) ->
    F = fun({D, _T, _P}) when (D =:= Domain) -> true;
           (_) -> false
        end,
    reg_si_num2(F, SocksInfo);
reg_si_num(SocksInfo, undefined, Type, undefined) ->
    F = fun({_D, T, _P}) when (T =:= Type) -> true;
           (_) -> false
        end,
    reg_si_num2(F, SocksInfo);
reg_si_num(SocksInfo, undefined, undefined, Proto) ->
    F = fun({_D, _T, P}) when (P =:= Proto) -> true;
           (_) -> false
        end,
    reg_si_num2(F, SocksInfo);
reg_si_num(SocksInfo, Domain, Type, Proto) ->
    F = fun({D, T, P}) when (D =:= Domain) andalso
                            (T =:= Type) andalso
                            (P =:= Proto) ->
                true;
           (_) ->
                false
        end,
    reg_si_num2(F, SocksInfo).

reg_si_num2(F, SocksInfo) ->
    length(lists:filter(F, SocksInfo)).


reg_sr_num(Existing, Domain)
  when ((Domain =:= inet) orelse (Domain =:= inet6)) ->
    length(socket:which_sockets(Domain) -- Existing);
reg_sr_num(Existing, Domain)
  when (Domain =:= local) ->
    reg_sr_num(Existing, Domain, undefined, undefined);
reg_sr_num(Existing, Type)
  when ((Type =:= stream) orelse (Type =:= dgram) orelse (Type =:= seqpacket)) ->
    length(socket:which_sockets(Type) -- Existing);
reg_sr_num(Existing, Proto)
  when ((Proto =:= sctp) orelse (Proto =:= tcp) orelse (Proto =:= udp)) ->
    length(socket:which_sockets(Proto) -- Existing).

reg_sr_num(Existing, Domain, undefined, undefined) ->
    F = fun(#{domain := D}) when (D =:= Domain) ->
                true;
           (_X) ->
                false
        end,
    reg_sr_num2(Existing, F);
reg_sr_num(Existing, Domain, Type, Proto) ->
    F = fun(#{domain   := D,
              type     := T,
              protocol := P}) when (D =:= Domain) andalso
                                   (T =:= Type) andalso
                                   (P =:= Proto) ->
                true;
           (_X) ->
                %% i("reg_sr_num -> not counting: "
                %%   "~n   ~p", [_X]),
                false
        end,
    reg_sr_num2(Existing, F).

reg_sr_num2(Existing, F) ->
    length(socket:which_sockets(F) -- Existing).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% We create a bunch of different sockets and ensure that the registry
%% has the correct info.

reg_s_optional_open_and_close_and_count(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(reg_s_optional_open_and_close_and_count,
           fun() ->
                   ok = reg_s_optional_open_and_close_and_count()
           end).


reg_s_optional_open_and_close_and_count() ->
    i("Make sure use of socket registry is enabled (regardless of default)"),
    socket:use_registry(true),
    #{use_registry := true} = socket:info(),

    i("get current socket base count"),
    Base = socket:number_of(),

    i("create a socket and ensure its counted"),
    {ok, S1} = socket:open(inet, dgram, udp),
    Base1 = Base + 1,
    case socket:number_of() of
        Base1 ->
            ok;
        Invalid1 -> 
            exit({wrong_number_of_sockets1, Invalid1, Base + 1})
    end,
    i("close the socket and ensure its counted (back to base)"),
    ok = socket:close(S1),
    case socket:number_of() of
        Base ->
            ok;
        Invalid2 -> 
            exit({wrong_number_of_sockets2, Invalid2, Base})
    end,

    i("create a socket with use_registry explicitly off "
      "and ensure its not counted"),
    {ok, S2} = socket:open(inet, dgram, udp, #{use_registry => false}),
    case socket:number_of() of
        Base ->
            ok;
        Invalid3 -> 
            exit({wrong_number_of_sockets3, Invalid3, Base})
    end,
    i("close the socket and ensure its not counted"),
    ok = socket:close(S2),
    case socket:number_of() of
        Base ->
            ok;
        Invalid4 -> 
            exit({wrong_number_of_sockets4, Invalid4, Base})
    end,

    i("Globally disable use of registry"),
    socket:use_registry(false),
    #{use_registry := false} = socket:info(),
    i("create a socket and ensure its not counted"),
    {ok, S3} = socket:open(inet, dgram, udp),
    case socket:number_of() of
        Base ->
            ok;
        Invalid5 -> 
            exit({wrong_number_of_sockets5, Invalid5, Base})
    end,
    i("close the socket and ensure its not counted"),
    ok = socket:close(S3),
    case socket:number_of() of
        Base ->
            ok;
        Invalid6 -> 
            exit({wrong_number_of_sockets6, Invalid6, Base})
    end,

    i("create a socket with use_registry explicitly on "
      "and ensure its counted"),
    {ok, S4} = socket:open(inet, dgram, udp, #{use_registry => true}),
    case socket:number_of() of
        Base1 ->
            ok;
        Invalid7 -> 
            exit({wrong_number_of_sockets7, Invalid7, Base + 1})
    end,
    i("close the socket and ensure counted (back to base)"),
    ok = socket:close(S4),
    case socket:number_of() of
        Base ->
            ok;
        Invalid8 -> 
            exit({wrong_number_of_sockets8, Invalid8, Base})
    end,

    socket:use_registry(false),
    i("done"),
    ok.



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                       SOCKET MONITOR                                %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create one socket, monitor from a different process then close socket.
%% The process that did the monitor shall receive a socket DOWN.

monitor_simple_open_and_close(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(monitor_simple_open_and_close,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_simple_open_and_close(InitState)
           end).


mon_simple_open_and_close(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
         #{desc => "await continue (close)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close)
                   end},

         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock, State)}
                   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},

         %% *** 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 total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "monitor socket - create two",
           cmd  => fun(#{sock := Sock} = State) ->
                           MRef1 = socket:monitor(Sock),
                           MRef2 = socket:monitor(Sock),
			   ?SEV_IPRINT("Monitor(s): "
				       "~n   1: ~p"
				       "~n   2: ~p", [MRef1, MRef2]),
			   {ok, State#{mon1 => MRef1, mon2 => MRef2}}
                   end},
         #{desc => "verify total number of monitors (=2)",
           cmd  => fun(_State) ->
			   2 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=2)",
           cmd  => fun(_State) ->
			   2 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "verify which monitors - (self) two",
           cmd  => fun(#{mon1 := MRef1,
			 mon2 := MRef2} = _State) ->
			   Mons = lists:sort([MRef1, MRef2]),
			   case lists:sort(socket:which_monitors(self())) of
			       Mons ->
				   ok;
			       SMons ->
				   ?SEV_EPRINT("Unexpected (self) monitors: "
					       "~n   Expected: ~p"
					       "~n   Actual:   ~p",
					       [Mons, SMons]),
				   {error, unexpected_monitors}
			   end
                   end},
         #{desc => "verify which monitors - (sock) two",
           cmd  => fun(#{sock := Sock,
			 mon1 := MRef1,
			 mon2 := MRef2} = _State) ->
			   Mons = lists:sort([MRef1, MRef2]),
			   case lists:sort(socket:which_monitors(Sock)) of
			       Mons ->
				   ok;
			       SMons ->
				   ?SEV_EPRINT("Unexpected (sock) monitors: "
					       "~n   Expected: ~p"
					       "~n   Actual:   ~p",
					       [Mons, SMons]),
				   {error, unexpected_monitors}
			   end
                   end},
         #{desc => "verify monitored by - only us",
           cmd  => fun(#{sock := Sock} = _State) ->
			   Self   = self(),
			   [Self] = socket:monitored_by(Sock),
			   ok
                   end},

         %% The actual test
         #{desc => "order owner to close",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close),
                           ok
                   end},
         #{desc => "await socket down 1",
           cmd  => fun(#{sock := Sock,
			 mon1 := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~s"
					       "~n      Info:   ~p",
					       [MRef,
						socket:to_list(Sock),
						Info]),
				   {ok, maps:remove(mon1, State)}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "await socket down 2",
           cmd  => fun(#{sock := Sock,
			 mon2  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~s"
					       "~n      Info:   ~p",
					       [MRef,
						socket:to_list(Sock),
						Info]),
				   {ok, maps:remove(mon2, State)}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "verify which monitors - only one",
           cmd  => fun(#{sock := Sock} = _State) ->
			   [] = socket:which_monitors(self()),
			   [] = socket:which_monitors(Sock),
			   ok
                   end},
         #{desc => "verify monitored by - none",
           cmd  => fun(#{sock := Sock} = _State) ->
			   [] = socket:monitored_by(Sock),
			   ok
                   end},

         %% Cleanup
         #{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},

         %% *** 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]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create one socket, monitor from a different process then stop the
%% owner process.
%% The process that did the monitor shall receive a socket DOWN.

monitor_simple_open_and_exit(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(monitor_simple_open_and_exit,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_simple_open_and_exit(InitState)
           end).


mon_simple_open_and_exit(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
         #{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 total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "monitor socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           MRef = socket:monitor(Sock),
			   ?SEV_IPRINT("Monitor: ~p", [MRef]),
			   {ok, State#{mon => MRef}}
                   end},
         #{desc => "verify total number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "verify which monitors - only one",
           cmd  => fun(#{sock := Sock,
			 mon  := MRef} = _State) ->
			   [MRef] = socket:which_monitors(self()),
			   [MRef] = socket:which_monitors(Sock),
			   ok
                   end},
         #{desc => "verify monitored by - only us",
           cmd  => fun(#{sock := Sock} = _State) ->
			   Self   = self(),
			   [Self] = socket:monitored_by(Sock),
			   ok
                   end},

         %% The actual test
         #{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},

         #{desc => "await socket down",
           cmd  => fun(#{sock := Sock,
			 mon  := MRef} = _State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   ok
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "verify which monitors - only one",
           cmd  => fun(#{sock := Sock} = _State) ->
			   [] = socket:which_monitors(self()),
			   [] = socket:which_monitors(Sock),
			   ok
                   end},
         #{desc => "verify monitored by - none",
           cmd  => fun(#{sock := Sock} = _State) ->
			   [] = socket:monitored_by(Sock),
			   ok
                   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]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create one socket, monitor from a different process, cancel_montor
%% (demonitor) and then close socket.
%% The process that did the monitor shall *not* receive a socket DOWN.

monitor_simple_open_and_demon_and_close(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(monitor_simple_open_and_demon_and_close,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_simple_open_and_demon_and_close(InitState)
           end).


mon_simple_open_and_demon_and_close(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
         #{desc => "await continue (close)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close)
                   end},

         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock, State)}
                   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},

         %% *** 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 total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "monitor socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           MRef = socket:monitor(Sock),
			   ?SEV_IPRINT("Monitor: ~p", [MRef]),
			   {ok, State#{mon => MRef}}
                   end},
         #{desc => "verify total number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(self()),
			   ok
                   end},

         %% The actual test
         #{desc => "demonitor socket",
           cmd  => fun(#{mon := MRef} = State) ->
                           true = socket:cancel_monitor(MRef),
			   {ok, maps:remove(mon, State)}
                   end},

	 #{desc => "verify total number of monitors (=0)",
	   cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
		   end},
	 #{desc => "verify own number of monitors (=0)",
	   cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
		   end},

         #{desc => "order owner to close",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close),
                           ok
                   end},

         #{desc => "await socket down",
           cmd  => fun(#{sock := Sock} = _State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_EPRINT("received UNEXPECTED down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   {error, unexpected_down}
			   after 5000 ->
				   ?SEV_IPRINT("expected socket down timeout"),
				   ok
			   end
                   end},

         %% Cleanup
         #{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},

         %% *** 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]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create several sockets, monitor from a different process then close
%% socket. The process that did the monitor shall receive a socket DOWN.

monitor_open_and_close_multi_socks(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(monitor_open_and_close_multi_socks,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_open_and_close_multi_socks(InitState)
           end).


mon_open_and_close_multi_socks(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 1",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock1 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 2",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock2 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 3",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock3 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 4",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock4 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 5",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock5 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester,
			 sock1  := Sock1,
			 sock2  := Sock2,
			 sock3  := Sock3,
			 sock4  := Sock4,
			 sock5  := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_READY(Tester, init, Socks),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (close1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close1)
                   end},
         #{desc => "close socket 1",
           cmd  => fun(#{sock1 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock1, State)}
                   end},

         #{desc => "await continue (close2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close2)
                   end},
         #{desc => "close socket 2",
           cmd  => fun(#{sock2 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock2, State)}
                   end},

         #{desc => "await continue (close3)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close3)
                   end},
         #{desc => "close socket 3",
           cmd  => fun(#{sock3 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock3, State)}
                   end},

         #{desc => "await continue (close4)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close4)
                   end},
         #{desc => "close socket 4",
           cmd  => fun(#{sock4 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock4, State)}
                   end},

         #{desc => "await continue (close5)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close5)
                   end},
         #{desc => "close socket 5",
           cmd  => fun(#{sock5 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock5, State)}
                   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},

         %% *** 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, [Sock1, Sock2, Sock3, Sock4, Sock5]} =
			       ?SEV_AWAIT_READY(Pid, owner, init),
                           {ok, State#{sock1 => Sock1,
				       sock2 => Sock2,
				       sock3 => Sock3,
				       sock4 => Sock4,
				       sock5 => Sock5}}
                   end},
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "monitor socket",
           cmd  => fun(#{sock1 := Sock1,
			 sock2 := Sock2,
			 sock3 := Sock3,
			 sock4 := Sock4,
			 sock5 := Sock5} = State) ->
                           MRef1 = socket:monitor(Sock1),
                           MRef2 = socket:monitor(Sock2),
                           MRef3 = socket:monitor(Sock3),
                           MRef4 = socket:monitor(Sock4),
                           MRef5 = socket:monitor(Sock5),
			   ?SEV_IPRINT("Monitors:"
				       "~n   1: ~p"
				       "~n   2: ~p"
				       "~n   3: ~p"
				       "~n   4: ~p"
				       "~n   5: ~p",
				       [MRef1, MRef2, MRef3, MRef4, MRef5]),
			   {ok, State#{mon1 => MRef1,
				       mon2 => MRef2,
				       mon3 => MRef3,
				       mon4 => MRef4,
				       mon5 => MRef5}}
                   end},
         #{desc => "verify total number of monitors (=5)",
           cmd  => fun(_State) ->
			   5 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=5)",
           cmd  => fun(_State) ->
			   5 = socket:number_of_monitors(self()),
			   ok
                   end},

         %% The actual test
         #{desc => "order owner to close socket 1",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close1),
                           ok
                   end},
         #{desc => "await socket 1 down",
           cmd  => fun(#{sock1 := Sock,
			 mon1  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~s"
					       "~n      Info:   ~p",
					       [MRef,
						socket:to_list(Sock),
						Info]),
				   State2 = maps:remove(sock1, State),
				   State3 = maps:remove(mon1,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=4)",
           cmd  => fun(_State) ->
			   4 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=4)",
           cmd  => fun(_State) ->
			   4 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "order owner to close socket 2",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close2),
                           ok
                   end},
         #{desc => "await socket 2 down",
           cmd  => fun(#{sock2 := Sock,
			 mon2  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~s"
					       "~n      Info:   ~p",
					       [MRef,
						socket:to_list(Sock),
						Info]),
				   State2 = maps:remove(sock2, State),
				   State3 = maps:remove(mon2,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=3)",
           cmd  => fun(_State) ->
			   3 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=3)",
           cmd  => fun(_State) ->
			   3 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "order owner to close socket 3",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close3),
                           ok
                   end},
         #{desc => "await socket 3 down",
           cmd  => fun(#{sock3 := Sock,
			 mon3  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~s"
					       "~n      Info:   ~p",
					       [MRef,
						socket:to_list(Sock),
						Info]),
				   State2 = maps:remove(sock3, State),
				   State3 = maps:remove(mon3,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=2)",
           cmd  => fun(_State) ->
			   2 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=2)",
           cmd  => fun(_State) ->
			   2 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "order owner to close socket 4",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close4),
                           ok
                   end},
         #{desc => "await socket 4 down",
           cmd  => fun(#{sock4 := Sock,
			 mon4  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(sock4, State),
				   State3 = maps:remove(mon4,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "order owner to close socket 5",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close5),
                           ok
                   end},
         #{desc => "await socket 5 down",
           cmd  => fun(#{sock5 := Sock,
			 mon5  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(sock5, State),
				   State3 = maps:remove(mon5,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},

         %% Cleanup
         #{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},

         %% *** 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]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create several sockets, monitor from a different process then exit
%% the owner process.
%% The process that did the monitor shall receive a socket DOWN.

monitor_open_and_exit_multi_socks(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(monitor_open_and_exit_multi_socks,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_open_and_exit_multi_socks(InitState)
           end).


mon_open_and_exit_multi_socks(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 1",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock1 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 2",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock2 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 3",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock3 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 4",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock4 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 5",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock5 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester,
			 sock1  := Sock1,
			 sock2  := Sock2,
			 sock3  := Sock3,
			 sock4  := Sock4,
			 sock5  := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_READY(Tester, init, Socks),
                           ok
                   end},


         %% The actual test
         #{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, [Sock1, Sock2, Sock3, Sock4, Sock5]} =
			       ?SEV_AWAIT_READY(Pid, owner, init),
                           {ok, State#{sock1 => Sock1,
				       sock2 => Sock2,
				       sock3 => Sock3,
				       sock4 => Sock4,
				       sock5 => Sock5}}
                   end},
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "monitor socket",
           cmd  => fun(#{sock1 := Sock1,
			 sock2 := Sock2,
			 sock3 := Sock3,
			 sock4 := Sock4,
			 sock5 := Sock5} = State) ->
                           MRef1 = socket:monitor(Sock1),
                           MRef2 = socket:monitor(Sock2),
                           MRef3 = socket:monitor(Sock3),
                           MRef4 = socket:monitor(Sock4),
                           MRef5 = socket:monitor(Sock5),
			   ?SEV_IPRINT("Monitors:"
				       "~n   1: ~p"
				       "~n   2: ~p"
				       "~n   3: ~p"
				       "~n   4: ~p"
				       "~n   5: ~p",
				       [MRef1, MRef2, MRef3, MRef4, MRef5]),
			   {ok, State#{mon1 => MRef1,
				       mon2 => MRef2,
				       mon3 => MRef3,
				       mon4 => MRef4,
				       mon5 => MRef5}}
                   end},
         #{desc => "verify total number of monitors (=5)",
           cmd  => fun(_State) ->
			   5 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=5)",
           cmd  => fun(_State) ->
			   5 = socket:number_of_monitors(self()),
			   ok
                   end},

         %% The actual test
         #{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},

         #{desc => "await socket 1 down",
           cmd  => fun(#{sock1 := Sock,
			 mon1  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(sock1, State),
				   State3 = maps:remove(mon1,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "await socket 2 down",
           cmd  => fun(#{sock2 := Sock,
			 mon2  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(sock2, State),
				   State3 = maps:remove(mon2,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "await socket 3 down",
           cmd  => fun(#{sock3 := Sock,
			 mon3  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(sock3, State),
				   State3 = maps:remove(mon3,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "await socket 4 down",
           cmd  => fun(#{sock4 := Sock,
			 mon4  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(sock4, State),
				   State3 = maps:remove(mon4,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "await socket 5 down",
           cmd  => fun(#{sock5 := Sock,
			 mon5  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(sock5, State),
				   State3 = maps:remove(mon5,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   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]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create several sockets, monitor from a different process, demonitor
%% two of them then close socket.
%% The process that did the monitor shall receive a socket DOWN for
%% the sockets that are still monitored.

monitor_open_and_demon_and_close_multi_socks(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(monitor_open_and_demon_and_close_multi_socks,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_open_and_demon_and_close_multi_socks(InitState)
           end).


mon_open_and_demon_and_close_multi_socks(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 1",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock1 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 2",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock2 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 3",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock3 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 4",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock4 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 5",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock5 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester,
			 sock1  := Sock1,
			 sock2  := Sock2,
			 sock3  := Sock3,
			 sock4  := Sock4,
			 sock5  := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_READY(Tester, init, Socks),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (close1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close1)
                   end},
         #{desc => "close socket 1",
           cmd  => fun(#{sock1 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock1, State)}
                   end},

         #{desc => "await continue (close3)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close3)
                   end},
         #{desc => "close socket 3",
           cmd  => fun(#{sock3 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock3, State)}
                   end},

         #{desc => "await continue (close5)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close5)
                   end},
         #{desc => "close socket 5",
           cmd  => fun(#{sock5 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock5, State)}
                   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},

         %% *** 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, [Sock1, Sock2, Sock3, Sock4, Sock5]} =
			       ?SEV_AWAIT_READY(Pid, owner, init),
                           {ok, State#{sock1 => Sock1,
				       sock2 => Sock2,
				       sock3 => Sock3,
				       sock4 => Sock4,
				       sock5 => Sock5}}
                   end},
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "monitor socket",
           cmd  => fun(#{sock1 := Sock1,
			 sock2 := Sock2,
			 sock3 := Sock3,
			 sock4 := Sock4,
			 sock5 := Sock5} = State) ->
                           MRef1 = socket:monitor(Sock1),
                           MRef2 = socket:monitor(Sock2),
                           MRef3 = socket:monitor(Sock3),
                           MRef4 = socket:monitor(Sock4),
                           MRef5 = socket:monitor(Sock5),
			   ?SEV_IPRINT("Monitors:"
				       "~n   1: ~p"
				       "~n   2: ~p"
				       "~n   3: ~p"
				       "~n   4: ~p"
				       "~n   5: ~p",
				       [MRef1, MRef2, MRef3, MRef4, MRef5]),
			   {ok, State#{mon1 => MRef1,
				       mon2 => MRef2,
				       mon3 => MRef3,
				       mon4 => MRef4,
				       mon5 => MRef5}}
                   end},
         #{desc => "verify total number of monitors (=5)",
           cmd  => fun(_State) ->
			   5 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=5)",
           cmd  => fun(_State) ->
			   5 = socket:number_of_monitors(self()),
			   ok
                   end},

         %% The actual test
         #{desc => "demonitor socket(s)",
           cmd  => fun(#{mon2 := MRef2,
			 mon4 := MRef4} = State) ->
                           true = socket:cancel_monitor(MRef2),
                           true = socket:cancel_monitor(MRef4),
			   ?SEV_IPRINT("cancel socket monitor 2 and 4"),
			   State2 = maps:remove(mon2,  State),
			   State3 = maps:remove(sock2, State2),
			   State4 = maps:remove(mon4,  State3),
			   State5 = maps:remove(sock4, State4),
			   {ok, State5}
                   end},
         #{desc => "verify total number of monitors (=3)",
           cmd  => fun(_State) ->
			   3 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=3)",
           cmd  => fun(_State) ->
			   3 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "order owner to close socket 1",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close1),
                           ok
                   end},
         #{desc => "await socket 1 down",
           cmd  => fun(#{sock1 := Sock,
			 mon1  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(sock1, State),
				   State3 = maps:remove(mon1,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=2)",
           cmd  => fun(_State) ->
			   2 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=2)",
           cmd  => fun(_State) ->
			   2 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "order owner to close socket 3",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close3),
                           ok
                   end},
         #{desc => "await socket 3 down",
           cmd  => fun(#{sock3 := Sock,
			 mon3  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(sock3, State),
				   State3 = maps:remove(mon3,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "order owner to close socket 5",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close5),
                           ok
                   end},
         #{desc => "await socket 5 down",
           cmd  => fun(#{sock5 := Sock,
			 mon5  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(sock5, State),
				   State3 = maps:remove(mon5,  State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},

         %% Cleanup
         #{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},

         %% *** 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]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create one socket (by 'owner' process), monitor from several different
%% processes, then close socket (from 'owner').
%% The processes that did the monitor shall receive a socket DOWN.

monitor_open_and_close_multi_mon(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(monitor_open_and_close_multi_mon,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_open_and_close_multi_mon(InitState)
           end).


mon_open_and_close_multi_mon(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
         #{desc => "await continue (close)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close)
                   end},

         #{desc => "close socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock, State)}
                   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},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    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 => "announce ready (init)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},
         #{desc => "await continue (socket)",
           cmd  => fun(#{tester := Tester} = State) ->
                           {ok, Sock} =
			       ?SEV_AWAIT_CONTINUE(Tester, tester, socket),
			   ?SEV_IPRINT("Socket: ~p", [Sock]),
			   {ok, State#{sock => Sock}}
                   end},


         %% The actual test
         #{desc => "await continue (monitor)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, monitor)
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "monitor socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           MRef = socket:monitor(Sock),
			   ?SEV_IPRINT("Monitor: ~p", [MRef]),
			   {ok, State#{mon => MRef}}
                   end},
         #{desc => "verify own number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "announce ready (monitor)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, monitor),
                           ok
                   end},

         #{desc => "await continue (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, down)
                   end},

         #{desc => "await socket down",
           cmd  => fun(#{sock := Sock,
			 mon  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon, State),
				   State3 = maps:remove(sock, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "announce ready (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, down),
                           ok
                   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},

         %% *** 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 => "monitor 'client 1'",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 1) start",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 1) ready",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, init)
                   end},
         #{desc => "send socket to client 1",
           cmd  => fun(#{client1 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},

         #{desc => "monitor 'client 2'",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 2) start",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 2) ready",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, init)
                   end},
         #{desc => "send socket to client 2",
           cmd  => fun(#{client2 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},

         #{desc => "monitor 'client 3'",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 3) start",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 3) ready",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, init)
                   end},
         #{desc => "send socket to client 3",
           cmd  => fun(#{client3 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},

         #{desc => "monitor 'client 4'",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 4) start",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 4) ready",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, init)
                   end},
         #{desc => "send socket to client 4",
           cmd  => fun(#{client4 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},

         #{desc => "monitor 'client 5'",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 5) start",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 5) ready",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, init)
                   end},
         #{desc => "send socket to client 5",
           cmd  => fun(#{client5 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},


         %% The actual test
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 1 to monitor",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 1) ready",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, monitor)
                   end},
         #{desc => "verify total number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 2 to monitor",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 2) ready",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, monitor)
                   end},
         #{desc => "verify total number of monitors (=2)",
           cmd  => fun(_State) ->
			   2 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 3 to monitor",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 3) ready",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, monitor)
                   end},
         #{desc => "verify total number of monitors (=3)",
           cmd  => fun(_State) ->
			   3 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 4 to monitor",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 4) ready",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, monitor)
                   end},
         #{desc => "verify total number of monitors (=4)",
           cmd  => fun(_State) ->
			   4 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 5 to monitor",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 5) ready",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, monitor)
                   end},
         #{desc => "verify total number of monitors (=5)",
           cmd  => fun(_State) ->
			   5 = socket:number_of_monitors(),
			   ok
                   end},
         #{desc => "verify monitored by - none",
           cmd  => fun(#{sock    := Sock,
			 client1 := Pid1,
			 client2 := Pid2,
			 client3 := Pid3,
			 client4 := Pid4,
			 client5 := Pid5} = _State) ->
			   Clients = lists:sort([Pid1, Pid2, Pid3, Pid4, Pid5]),
			   case lists:sort(socket:monitored_by(Sock)) of
			       Clients ->
				   ok;
			       SClients ->
				   ?SEV_EPRINT("Unexpected clients: "
					       "~n   Expected: ~p"
					       "~n   Actual:   ~p",
					       [Clients, SClients]),
				   {error, unexpected_clients}
			   end
                   end},

         #{desc => "order client 1 to await down",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 2 to await down",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 3 to await down",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 4 to await down",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 5 to await down",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},

         #{desc => "order owner to close",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close),
                           ok
                   end},

         #{desc => "await (client 1) down received",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, down)
                   end},
         #{desc => "await (client 2) down received",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, down)
                   end},
         #{desc => "await (client 3) down received",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, down)
                   end},
         #{desc => "await (client 4) down received",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, down)
                   end},
         #{desc => "await (client 5) down received",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, down)
                   end},
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},

         %% Cleanup
         #{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, maps:remove(owner, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 1) 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 ->
                                   {ok, maps:remove(client1, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 2) 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 ->
                                   {ok, maps:remove(client2, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 3) 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 ->
                                   {ok, maps:remove(client3, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 4) terminate",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await (client 4) termination",
           cmd  => fun(#{client4 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   {ok, maps:remove(client4, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 5) terminate",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await (client 5) termination",
           cmd  => fun(#{client5 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   {ok, maps:remove(client5, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start (socket) owner evaluator"),
    Owner = ?SEV_START("owner", OwnerSeq, InitState),

    i("start client 1 evaluator"),
    Client1 = ?SEV_START("client-1", ClientSeq, InitState),

    i("start client 2 evaluator"),
    Client2 = ?SEV_START("client-2", ClientSeq, InitState),

    i("start client 3 evaluator"),
    Client3 = ?SEV_START("client-3", ClientSeq, InitState),

    i("start client 4 evaluator"),
    Client4 = ?SEV_START("client-4", ClientSeq, InitState),

    i("start client 5 evaluator"),
    Client5 = ?SEV_START("client-5", ClientSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{owner   => Owner#ev.pid,
			client1 => Client1#ev.pid,
			client2 => Client2#ev.pid,
			client3 => Client3#ev.pid,
			client4 => Client4#ev.pid,
			client5 => Client5#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator"),
    ok = ?SEV_AWAIT_FINISH([Owner, Tester]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create one socket (by 'owner' process), monitor from several different
%% processes, then close socket (from 'owner').
%% The processes that did the monitor shall receive a socket DOWN.

monitor_open_and_exit_multi_mon(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(monitor_open_and_exit_multi_mon,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_open_and_exit_multi_mon(InitState)
           end).


mon_open_and_exit_multi_mon(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
         #{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
        ],


    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 => "announce ready (init)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},
         #{desc => "await continue (socket)",
           cmd  => fun(#{tester := Tester} = State) ->
                           {ok, Sock} =
			       ?SEV_AWAIT_CONTINUE(Tester, tester, socket),
			   ?SEV_IPRINT("Socket: ~p", [Sock]),
			   {ok, State#{sock => Sock}}
                   end},


         %% The actual test
         #{desc => "await continue (monitor)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, monitor)
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "monitor socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           MRef = socket:monitor(Sock),
			   ?SEV_IPRINT("Monitor: ~p", [MRef]),
			   {ok, State#{mon => MRef}}
                   end},
         #{desc => "verify own number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "announce ready (monitor)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, monitor),
                           ok
                   end},

         #{desc => "await continue (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, down)
                   end},

         #{desc => "await socket down",
           cmd  => fun(#{sock := Sock,
			 mon  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon, State),
				   State3 = maps:remove(sock, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "announce ready (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, down),
                           ok
                   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},

         %% *** 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 => "monitor 'client 1'",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 1) start",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 1) ready",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, init)
                   end},
         #{desc => "send socket to client 1",
           cmd  => fun(#{client1 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},

         #{desc => "monitor 'client 2'",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 2) start",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 2) ready",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, init)
                   end},
         #{desc => "send socket to client 2",
           cmd  => fun(#{client2 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},

         #{desc => "monitor 'client 3'",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 3) start",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 3) ready",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, init)
                   end},
         #{desc => "send socket to client 3",
           cmd  => fun(#{client3 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},

         #{desc => "monitor 'client 4'",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 4) start",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 4) ready",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, init)
                   end},
         #{desc => "send socket to client 4",
           cmd  => fun(#{client4 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},

         #{desc => "monitor 'client 5'",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 5) start",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 5) ready",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, init)
                   end},
         #{desc => "send socket to client 5",
           cmd  => fun(#{client5 := Pid, sock := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},


         %% The actual test
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 1 to monitor",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 1) ready",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, monitor)
                   end},
         #{desc => "verify total number of monitors (=1)",
           cmd  => fun(_State) ->
			   1 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 2 to monitor",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 2) ready",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, monitor)
                   end},
         #{desc => "verify total number of monitors (=2)",
           cmd  => fun(_State) ->
			   2 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 3 to monitor",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 3) ready",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, monitor)
                   end},
         #{desc => "verify total number of monitors (=3)",
           cmd  => fun(_State) ->
			   3 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 4 to monitor",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 4) ready",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, monitor)
                   end},
         #{desc => "verify total number of monitors (=4)",
           cmd  => fun(_State) ->
			   4 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 5 to monitor",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 5) ready",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, monitor)
                   end},
         #{desc => "verify total number of monitors (=5)",
           cmd  => fun(_State) ->
			   5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 1 to await down",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 2 to await down",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 3 to await down",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 4 to await down",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 5 to await down",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   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, maps:remove(owner, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "await (client 1) down received",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, down)
                   end},
         #{desc => "await (client 2) down received",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, down)
                   end},
         #{desc => "await (client 3) down received",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, down)
                   end},
         #{desc => "await (client 4) down received",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, down)
                   end},
         #{desc => "await (client 5) down received",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, down)
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},

         %% Cleanup
         #{desc => "order (client 1) 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 ->
                                   {ok, maps:remove(client1, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 2) 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 ->
                                   {ok, maps:remove(client2, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 3) 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 ->
                                   {ok, maps:remove(client3, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 4) terminate",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await (client 4) termination",
           cmd  => fun(#{client4 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   {ok, maps:remove(client4, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 5) terminate",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await (client 5) termination",
           cmd  => fun(#{client5 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   {ok, maps:remove(client5, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start (socket) owner evaluator"),
    Owner = ?SEV_START("owner", OwnerSeq, InitState),

    i("start client 1 evaluator"),
    Client1 = ?SEV_START("client-1", ClientSeq, InitState),

    i("start client 2 evaluator"),
    Client2 = ?SEV_START("client-2", ClientSeq, InitState),

    i("start client 3 evaluator"),
    Client3 = ?SEV_START("client-3", ClientSeq, InitState),

    i("start client 4 evaluator"),
    Client4 = ?SEV_START("client-4", ClientSeq, InitState),

    i("start client 5 evaluator"),
    Client5 = ?SEV_START("client-5", ClientSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{owner   => Owner#ev.pid,
			client1 => Client1#ev.pid,
			client2 => Client2#ev.pid,
			client3 => Client3#ev.pid,
			client4 => Client4#ev.pid,
			client5 => Client5#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator"),
    ok = ?SEV_AWAIT_FINISH([Owner, Tester]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create several sockets (by 'owner' process), monitor each from several
%% different processes, then close each socket (from 'owner').
%% The processes that did the monitor shall receive one socket DOWN for
%% each socket.

monitor_open_and_close_multi_socks_and_mon(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(monitor_open_and_close_multi_socks_and_mon,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_open_and_close_multi_socks_and_mon(InitState)
           end).


mon_open_and_close_multi_socks_and_mon(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 1",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock1 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 2",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock2 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 3",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock3 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 4",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock4 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 5",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock5 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester,
			 sock1  := Sock1,
			 sock2  := Sock2,
			 sock3  := Sock3,
			 sock4  := Sock4,
			 sock5  := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_READY(Tester, init, Socks),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (close1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close1)
                   end},
         #{desc => "close socket 1",
           cmd  => fun(#{sock1 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock1, State)}
                   end},

         #{desc => "await continue (close2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close2)
                   end},
         #{desc => "close socket 2",
           cmd  => fun(#{sock2 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock2, State)}
                   end},

         #{desc => "await continue (close3)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close3)
                   end},
         #{desc => "close socket 3",
           cmd  => fun(#{sock3 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock3, State)}
                   end},

         #{desc => "await continue (close4)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close4)
                   end},
         #{desc => "close socket 4",
           cmd  => fun(#{sock4 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock4, State)}
                   end},

         #{desc => "await continue (close5)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, close5)
                   end},
         #{desc => "close socket 5",
           cmd  => fun(#{sock5 := Sock} = State) ->
                           ok = socket:close(Sock),
			   {ok, maps:remove(sock5, State)}
                   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},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    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 => "announce ready (init)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},
         #{desc => "await continue (socket)",
           cmd  => fun(#{tester := Tester} = State) ->
                           {ok, [Sock1, Sock2, Sock3, Sock4, Sock5]} =
			       ?SEV_AWAIT_CONTINUE(Tester, tester, socket),
			   ?SEV_IPRINT("Sockets: "
				       "~n   1: ~p"
				       "~n   2: ~p"
				       "~n   3: ~p"
				       "~n   4: ~p"
				       "~n   5: ~p",
				       [Sock1, Sock2, Sock3, Sock4, Sock5]),
			   {ok, State#{sock1 => Sock1,
				       sock2 => Sock2,
				       sock3 => Sock3,
				       sock4 => Sock4,
				       sock5 => Sock5}}
                   end},


         %% The actual test
         #{desc => "await continue (monitor)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, monitor)
                   end},

         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "monitor socket",
           cmd  => fun(#{sock1 := Sock1,
			 sock2 := Sock2,
			 sock3 := Sock3,
			 sock4 := Sock4,
			 sock5 := Sock5} = State) ->
                           MRef1 = socket:monitor(Sock1),
                           MRef2 = socket:monitor(Sock2),
                           MRef3 = socket:monitor(Sock3),
                           MRef4 = socket:monitor(Sock4),
                           MRef5 = socket:monitor(Sock5),
			   ?SEV_IPRINT("Monitors: "
				       "~n   1: ~p"
				       "~n   2: ~p"
				       "~n   3: ~p"
				       "~n   4: ~p"
				       "~n   5: ~p",
				       [MRef1, MRef2, MRef3, MRef4, MRef5]),
			   {ok, State#{mon1 => MRef1,
				       mon2 => MRef2,
				       mon3 => MRef3,
				       mon4 => MRef4,
				       mon5 => MRef5}}
                   end},
         #{desc => "verify own number of monitors (=5)",
           cmd  => fun(_State) ->
			   5 = socket:number_of_monitors(self()),
			   ok
                   end},

         #{desc => "announce ready (monitor)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, monitor),
                           ok
                   end},

         #{desc => "await continue (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, down)
                   end},

         #{desc => "await socket 1 down",
           cmd  => fun(#{sock1 := Sock,
			 mon1  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon1, State),
				   State3 = maps:remove(sock1, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "announce ready (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, down),
                           ok
                   end},

         #{desc => "await socket 2 down",
           cmd  => fun(#{sock2 := Sock,
			 mon2  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon2, State),
				   State3 = maps:remove(sock2, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "announce ready (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, down),
                           ok
                   end},

         #{desc => "await socket 3 down",
           cmd  => fun(#{sock3 := Sock,
			 mon3  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon3, State),
				   State3 = maps:remove(sock3, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "announce ready (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, down),
                           ok
                   end},

         #{desc => "await socket 4 down",
           cmd  => fun(#{sock4 := Sock,
			 mon4  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon4, State),
				   State3 = maps:remove(sock4, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "announce ready (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, down),
                           ok
                   end},

         #{desc => "await socket 5 down",
           cmd  => fun(#{sock5 := Sock,
			 mon5  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon5, State),
				   State3 = maps:remove(sock5, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "announce ready (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, down),
                           ok
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   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},

         %% *** 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, [Sock1, Sock2, Sock3, Sock4, Sock5]} =
			       ?SEV_AWAIT_READY(Pid, owner, init),
                           {ok, State#{sock1 => Sock1,
				       sock2 => Sock2,
				       sock3 => Sock3,
				       sock4 => Sock4,
				       sock5 => Sock5}}
                   end},

         #{desc => "monitor 'client 1'",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 1) start",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 1) ready",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, init)
                   end},
         #{desc => "send socket to client 1",
           cmd  => fun(#{client1 := Pid,
			 sock1   := Sock1,
			 sock2   := Sock2,
			 sock3   := Sock3,
			 sock4   := Sock4,
			 sock5   := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks),
                           ok
                   end},

         #{desc => "monitor 'client 2'",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 2) start",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 2) ready",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, init)
                   end},
         #{desc => "send socket to client 2",
           cmd  => fun(#{client2 := Pid,
			 sock1   := Sock1,
			 sock2   := Sock2,
			 sock3   := Sock3,
			 sock4   := Sock4,
			 sock5   := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks),
                           ok
                   end},

         #{desc => "monitor 'client 3'",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 3) start",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 3) ready",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, init)
                   end},
         #{desc => "send socket to client 3",
           cmd  => fun(#{client3 := Pid,
			 sock1   := Sock1,
			 sock2   := Sock2,
			 sock3   := Sock3,
			 sock4   := Sock4,
			 sock5   := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks),
                           ok
                   end},

         #{desc => "monitor 'client 4'",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 4) start",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 4) ready",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, init)
                   end},
         #{desc => "send socket to client 4",
           cmd  => fun(#{client4 := Pid,
			 sock1   := Sock1,
			 sock2   := Sock2,
			 sock3   := Sock3,
			 sock4   := Sock4,
			 sock5   := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks),
                           ok
                   end},

         #{desc => "monitor 'client 5'",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 5) start",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 5) ready",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, init)
                   end},
         #{desc => "send socket to client 5",
           cmd  => fun(#{client5 := Pid,
			 sock1   := Sock1,
			 sock2   := Sock2,
			 sock3   := Sock3,
			 sock4   := Sock4,
			 sock5   := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks),
                           ok
                   end},


         %% The actual test
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 1 to monitor",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 1) ready",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, monitor)
                   end},
         #{desc => "verify total number of monitors (=1*5)",
           cmd  => fun(_State) ->
			   1*5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 2 to monitor",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 2) ready",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, monitor)
                   end},
         #{desc => "verify total number of monitors (=2*5)",
           cmd  => fun(_State) ->
			   2*5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 3 to monitor",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 3) ready",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, monitor)
                   end},
         #{desc => "verify total number of monitors (=3*5)",
           cmd  => fun(_State) ->
			   3*5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 4 to monitor",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 4) ready",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, monitor)
                   end},
         #{desc => "verify total number of monitors (=4*5)",
           cmd  => fun(_State) ->
			   4*5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 5 to monitor",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 5) ready",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, monitor)
                   end},
         #{desc => "verify total number of monitors (=5*5)",
           cmd  => fun(_State) ->
			   5*5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 1 to await down",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 2 to await down",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 3 to await down",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 4 to await down",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 5 to await down",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         #{desc => "order owner to close socket 1",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close1),
                           ok
                   end},
         #{desc => "await (client 1) down received",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, down)
                   end},
         #{desc => "await (client 2) down received",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, down)
                   end},
         #{desc => "await (client 3) down received",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, down)
                   end},
         #{desc => "await (client 4) down received",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, down)
                   end},
         #{desc => "await (client 5) down received",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, down)
                   end},
         #{desc => "verify total number of monitors (=5*4)",
           cmd  => fun(_State) ->
			   5*4 = socket:number_of_monitors(),
			   ok
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         #{desc => "order owner to close socket 2",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close2),
                           ok
                   end},
         #{desc => "await (client 1) down received",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, down)
                   end},
         #{desc => "await (client 2) down received",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, down)
                   end},
         #{desc => "await (client 3) down received",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, down)
                   end},
         #{desc => "await (client 4) down received",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, down)
                   end},
         #{desc => "await (client 5) down received",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, down)
                   end},
         #{desc => "verify total number of monitors (=5*3)",
           cmd  => fun(_State) ->
			   5*3 = socket:number_of_monitors(),
			   ok
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         #{desc => "order owner to close socket 3",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close3),
                           ok
                   end},
         #{desc => "await (client 1) down received",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, down)
                   end},
         #{desc => "await (client 2) down received",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, down)
                   end},
         #{desc => "await (client 3) down received",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, down)
                   end},
         #{desc => "await (client 4) down received",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, down)
                   end},
         #{desc => "await (client 5) down received",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, down)
                   end},
         #{desc => "verify total number of monitors (=5*2)",
           cmd  => fun(_State) ->
			   5*2 = socket:number_of_monitors(),
			   ok
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         #{desc => "order owner to close socket 4",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close4),
                           ok
                   end},
         #{desc => "await (client 1) down received",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, down)
                   end},
         #{desc => "await (client 2) down received",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, down)
                   end},
         #{desc => "await (client 3) down received",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, down)
                   end},
         #{desc => "await (client 4) down received",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, down)
                   end},
         #{desc => "await (client 5) down received",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, down)
                   end},
         #{desc => "verify total number of monitors (=5*1)",
           cmd  => fun(_State) ->
			   5*1 = socket:number_of_monitors(),
			   ok
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         #{desc => "order owner to close socket 5",
           cmd  => fun(#{owner := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, close5),
                           ok
                   end},
         #{desc => "await (client 1) down received",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, down)
                   end},
         #{desc => "await (client 2) down received",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, down)
                   end},
         #{desc => "await (client 3) down received",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, down)
                   end},
         #{desc => "await (client 4) down received",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, down)
                   end},
         #{desc => "await (client 5) down received",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, down)
                   end},
         #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         %% Cleanup
         #{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, maps:remove(owner, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 1) 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 ->
                                   {ok, maps:remove(client1, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 2) 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 ->
                                   {ok, maps:remove(client2, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 3) 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 ->
                                   {ok, maps:remove(client3, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 4) terminate",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await (client 4) termination",
           cmd  => fun(#{client4 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   {ok, maps:remove(client4, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 5) terminate",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await (client 5) termination",
           cmd  => fun(#{client5 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   {ok, maps:remove(client5, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start (socket) owner evaluator"),
    Owner = ?SEV_START("owner", OwnerSeq, InitState),

    i("start client 1 evaluator"),
    Client1 = ?SEV_START("client-1", ClientSeq, InitState),

    i("start client 2 evaluator"),
    Client2 = ?SEV_START("client-2", ClientSeq, InitState),

    i("start client 3 evaluator"),
    Client3 = ?SEV_START("client-3", ClientSeq, InitState),

    i("start client 4 evaluator"),
    Client4 = ?SEV_START("client-4", ClientSeq, InitState),

    i("start client 5 evaluator"),
    Client5 = ?SEV_START("client-5", ClientSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{owner   => Owner#ev.pid,
			client1 => Client1#ev.pid,
			client2 => Client2#ev.pid,
			client3 => Client3#ev.pid,
			client4 => Client4#ev.pid,
			client5 => Client5#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator"),
    ok = ?SEV_AWAIT_FINISH([Owner, Tester]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create several sockets (by 'owner' process), monitor each from several
%% different processes, then exit the owner.
%% The processes that did the monitor shall receive one socket DOWN for
%% each socket.

monitor_open_and_exit_multi_socks_and_mon(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(monitor_open_and_exit_multi_socks_and_mon,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_open_and_exit_multi_socks_and_mon(InitState)
           end).


mon_open_and_exit_multi_socks_and_mon(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 1",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock1 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 2",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock2 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 3",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock3 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 4",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock4 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "create socket 5",
           cmd  => fun(#{domain   := Domain, 
                         type     := Type, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, Type, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock5 => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester,
			 sock1  := Sock1,
			 sock2  := Sock2,
			 sock3  := Sock3,
			 sock4  := Sock4,
			 sock5  := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_READY(Tester, init, Socks),
                           ok
                   end},

         %% The actual test
         #{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
        ],


    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 => "announce ready (init)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},
         #{desc => "await continue (socket)",
           cmd  => fun(#{tester := Tester} = State) ->
                           {ok, [Sock1, Sock2, Sock3, Sock4, Sock5]} =
			       ?SEV_AWAIT_CONTINUE(Tester, tester, socket),
			   ?SEV_IPRINT("Sockets: "
				       "~n   1: ~p"
				       "~n   2: ~p"
				       "~n   3: ~p"
				       "~n   4: ~p"
				       "~n   5: ~p",
				       [Sock1, Sock2, Sock3, Sock4, Sock5]),
			   {ok, State#{sock1 => Sock1,
				       sock2 => Sock2,
				       sock3 => Sock3,
				       sock4 => Sock4,
				       sock5 => Sock5}}
                   end},


         %% The actual test
         #{desc => "await continue (monitor)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, monitor)
                   end},

         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "monitor socket",
           cmd  => fun(#{sock1 := Sock1,
			 sock2 := Sock2,
			 sock3 := Sock3,
			 sock4 := Sock4,
			 sock5 := Sock5} = State) ->
                           MRef1 = socket:monitor(Sock1),
                           MRef2 = socket:monitor(Sock2),
                           MRef3 = socket:monitor(Sock3),
                           MRef4 = socket:monitor(Sock4),
                           MRef5 = socket:monitor(Sock5),
			   ?SEV_IPRINT("Monitors: "
				       "~n   1: ~p"
				       "~n   2: ~p"
				       "~n   3: ~p"
				       "~n   4: ~p"
				       "~n   5: ~p",
				       [MRef1, MRef2, MRef3, MRef4, MRef5]),
			   {ok, State#{mon1 => MRef1,
				       mon2 => MRef2,
				       mon3 => MRef3,
				       mon4 => MRef4,
				       mon5 => MRef5}}
                   end},
         #{desc => "verify own number of monitors (=5)",
           cmd  => fun(_State) ->
			   5 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "announce ready (monitor)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, monitor),
                           ok
                   end},

         #{desc => "await continue (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, down)
                   end},

         #{desc => "await socket 1 down",
           cmd  => fun(#{sock1 := Sock,
			 mon1  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon1, State),
				   State3 = maps:remove(sock1, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},

         #{desc => "await socket 2 down",
           cmd  => fun(#{sock2 := Sock,
			 mon2  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon2, State),
				   State3 = maps:remove(sock2, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},

         #{desc => "await socket 3 down",
           cmd  => fun(#{sock3 := Sock,
			 mon3  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon3, State),
				   State3 = maps:remove(sock3, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},

         #{desc => "await socket 4 down",
           cmd  => fun(#{sock4 := Sock,
			 mon4  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon4, State),
				   State3 = maps:remove(sock4, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},

         #{desc => "await socket 5 down",
           cmd  => fun(#{sock5 := Sock,
			 mon5  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon5, State),
				   State3 = maps:remove(sock5, State2),
				   {ok, State3}
			   after 5000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},
         #{desc => "verify own number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(self()),
			   ok
                   end},
         #{desc => "announce ready (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, down),
                           ok
                   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},

         %% *** 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, [Sock1, Sock2, Sock3, Sock4, Sock5]} =
			       ?SEV_AWAIT_READY(Pid, owner, init),
                           {ok, State#{sock1 => Sock1,
				       sock2 => Sock2,
				       sock3 => Sock3,
				       sock4 => Sock4,
				       sock5 => Sock5}}
                   end},

         #{desc => "monitor 'client 1'",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 1) start",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 1) ready",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, init)
                   end},
         #{desc => "send socket to client 1",
           cmd  => fun(#{client1 := Pid,
			 sock1   := Sock1,
			 sock2   := Sock2,
			 sock3   := Sock3,
			 sock4   := Sock4,
			 sock5   := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks),
                           ok
                   end},

         #{desc => "monitor 'client 2'",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 2) start",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 2) ready",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, init)
                   end},
         #{desc => "send socket to client 2",
           cmd  => fun(#{client2 := Pid,
			 sock1   := Sock1,
			 sock2   := Sock2,
			 sock3   := Sock3,
			 sock4   := Sock4,
			 sock5   := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks),
                           ok
                   end},

         #{desc => "monitor 'client 3'",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 3) start",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 3) ready",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, init)
                   end},
         #{desc => "send socket to client 3",
           cmd  => fun(#{client3 := Pid,
			 sock1   := Sock1,
			 sock2   := Sock2,
			 sock3   := Sock3,
			 sock4   := Sock4,
			 sock5   := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks),
                           ok
                   end},

         #{desc => "monitor 'client 4'",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 4) start",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 4) ready",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, init)
                   end},
         #{desc => "send socket to client 4",
           cmd  => fun(#{client4 := Pid,
			 sock1   := Sock1,
			 sock2   := Sock2,
			 sock3   := Sock3,
			 sock4   := Sock4,
			 sock5   := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks),
                           ok
                   end},

         #{desc => "monitor 'client 5'",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order (client 5) start",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await (client 5) ready",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, init)
                   end},
         #{desc => "send socket to client 5",
           cmd  => fun(#{client5 := Pid,
			 sock1   := Sock1,
			 sock2   := Sock2,
			 sock3   := Sock3,
			 sock4   := Sock4,
			 sock5   := Sock5} = _State) ->
			   Socks = [Sock1, Sock2, Sock3, Sock4, Sock5],
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Socks),
                           ok
                   end},


         %% The actual test
	 #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 1 to monitor",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 1) ready",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, monitor)
                   end},
	 #{desc => "verify total number of monitors (=1*5)",
           cmd  => fun(_State) ->
			   1*5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 2 to monitor",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 2) ready",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, monitor)
                   end},
	 #{desc => "verify total number of monitors (=2*5)",
           cmd  => fun(_State) ->
			   2*5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 3 to monitor",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 3) ready",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, monitor)
                   end},
	 #{desc => "verify total number of monitors (=3*5)",
           cmd  => fun(_State) ->
			   3*5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 4 to monitor",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 4) ready",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, monitor)
                   end},
	 #{desc => "verify total number of monitors (=4*5)",
           cmd  => fun(_State) ->
			   4*5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 5 to monitor",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await (client 5) ready",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, monitor)
                   end},
	 #{desc => "verify total number of monitors (=5*5)",
           cmd  => fun(_State) ->
			   5*5 = socket:number_of_monitors(),
			   ok
                   end},

         #{desc => "order client 1 to await down",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 2 to await down",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 3 to await down",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 4 to await down",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},
         #{desc => "order client 5 to await down",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         #{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, maps:remove(owner, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "await (client 1) ready",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client1, down)
                   end},
         #{desc => "await (client 2) ready",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client2, down)
                   end},
         #{desc => "await (client 3) ready",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client3, down)
                   end},
         #{desc => "await (client 4) ready",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client4, down)
                   end},
         #{desc => "await (client 5) ready",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client5, down)
                   end},

	 #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         %% Cleanup
         #{desc => "order (client 1) 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 ->
                                   {ok, maps:remove(client1, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 2) 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 ->
                                   {ok, maps:remove(client2, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 3) 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 ->
                                   {ok, maps:remove(client3, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 4) terminate",
           cmd  => fun(#{client4 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await (client 4) termination",
           cmd  => fun(#{client4 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   {ok, maps:remove(client4, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "order (client 5) terminate",
           cmd  => fun(#{client5 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Pid),
                           ok
                   end},
         #{desc => "await (client 5) termination",
           cmd  => fun(#{client5 := Pid} = State) ->
                           case ?SEV_AWAIT_TERMINATION(Pid) of
                               ok ->
                                   {ok, maps:remove(client5, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start (socket) owner evaluator"),
    Owner = ?SEV_START("owner", OwnerSeq, InitState),

    i("start client 1 evaluator"),
    Client1 = ?SEV_START("client-1", ClientSeq, InitState),

    i("start client 2 evaluator"),
    Client2 = ?SEV_START("client-2", ClientSeq, InitState),

    i("start client 3 evaluator"),
    Client3 = ?SEV_START("client-3", ClientSeq, InitState),

    i("start client 4 evaluator"),
    Client4 = ?SEV_START("client-4", ClientSeq, InitState),

    i("start client 5 evaluator"),
    Client5 = ?SEV_START("client-5", ClientSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{owner   => Owner#ev.pid,
			client1 => Client1#ev.pid,
			client2 => Client2#ev.pid,
			client3 => Client3#ev.pid,
			client4 => Client4#ev.pid,
			client5 => Client5#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator"),
    ok = ?SEV_AWAIT_FINISH([Owner, Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create several sockets (by 'owner' process), monitor each from several
%% different processes, then exit the owner.
%% The processes that did the monitor shall receive one socket DOWN for
%% each socket.

monitor_closed_socket(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(monitor_closed_socket,
           fun() ->
		   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => tcp},
                   ok = mon_closed_socket(InitState)
           end).


mon_closed_socket(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),
                           (catch socket:close(Sock)),
                           {ok, maps:remove(sock, State)}
                   end},

         %% The actual test
         #{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
        ],


    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 => "announce ready (init)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},
         #{desc => "await continue (socket)",
           cmd  => fun(#{tester := Tester} = State) ->
                           {ok, Sock} =
                               ?SEV_AWAIT_CONTINUE(Tester, tester, socket),
			   ?SEV_IPRINT("Socket: "
				       "~n      ~p", [Sock]),
			   {ok, State#{sock => Sock}}
                   end},


         %% The actual test
         #{desc => "await continue (monitor)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, monitor)
                   end},

         #{desc => "monitor socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           MRef = socket:monitor(Sock),
			   ?SEV_IPRINT("Monitors: "
				       "~n      ~p", [MRef]),
			   {ok, State#{mon => MRef}}
                   end},
         
         #{desc => "announce ready (monitor)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, monitor),
                           ok
                   end},

         #{desc => "await continue (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, down)
                   end},

         #{desc => "await socket down",
           cmd  => fun(#{sock := Sock,
			 mon  := MRef} = State) ->
			   receive
			       {'DOWN', MRef, socket, Sock, Info} ->
				   ?SEV_IPRINT("received expected down: "
					       "~n      MRef:   ~p"
					       "~n      Socket: ~p"
					       "~n      Info:   ~p",
					       [MRef, Sock, Info]),
				   State2 = maps:remove(mon,  State),
				   State3 = maps:remove(sock, State2),
				   {ok, State3}
			   after 1000 ->
				   ?SEV_EPRINT("socket down timeout"),
				   {error, timeout}
			   end
                   end},

         #{desc => "announce ready (down)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, down),
                           ok
                   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},

         %% *** 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 => "monitor client",
           cmd  => fun(#{client := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "order client start",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await client ready",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, init)
                   end},
         #{desc => "send socket to client",
           cmd  => fun(#{client := Pid,
			 sock   := Sock} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, socket, Sock),
                           ok
                   end},

         %% The actual test
         %% There is no way we can actually know that the "system"
         %% does not create *any* sockets...
         #{desc => "order client to monitor",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, monitor),
                           ok
                   end},
         #{desc => "await client ready",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, monitor)
                   end},

         #{desc => "order client to await down",
           cmd  => fun(#{client := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, down),
                           ok
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         #{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, maps:remove(owner, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},

         #{desc => "await client ready",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, down)
                   end},

	 #{desc => "verify total number of monitors (=0)",
           cmd  => fun(_State) ->
			   0 = socket:number_of_monitors(),
			   ok
                   end},

	 ?SEV_SLEEP(?SECS(1)),

         %% Cleanup
         #{desc => "order client 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},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start (socket) owner evaluator"),
    Owner = ?SEV_START("owner", OwnerSeq, InitState),

    i("start client  evaluator"),
    Client = ?SEV_START("client", ClientSeq, InitState),

    i("start tester evaluator"),
    TesterInitState = #{owner  => Owner#ev.pid,
			client => Client#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator"),
    ok = ?SEV_AWAIT_FINISH([Owner, Client, Tester]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                         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(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(sc_cpe_socket_cleanup_tcp4,
           fun() ->
                   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(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(sc_cpe_socket_cleanup_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   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 Unix Domain (stream) socket (TCP).

sc_cpe_socket_cleanup_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(sc_cpe_socket_cleanup_tcpL,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   InitState = #{domain   => local,
                                 type     => stream,
                                 protocol => default},
                   ok = sc_cpe_socket_cleanup(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sockets are cleaned up
%% ("removed") when the controlling process terminates (without explicitly 
%% calling the close function). For a IPv4 UDP (dgram) socket.

sc_cpe_socket_cleanup_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(sc_cpe_socket_cleanup_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   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(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(sc_cpe_socket_cleanup_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 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 Unix Domain (dgram) socket (UDP).

sc_cpe_socket_cleanup_udpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(sc_cpe_socket_cleanup_udpL,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   InitState = #{domain   => local,
                                 type     => dgram,
                                 protocol => default},
                   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 *intentionally* 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},

         ?SEV_SLEEP(?SECS(5)),

         %% The reason we get closed, is that as long as there is a ref to 
         %% the resource (socket), then it will not be garbage collected.
         %% Note that its still a race that the nif has processed that the
         %% "controlling process" has terminated. There really is no 
         %% proper timeout for this, but the 5 seconds "should" be enough...
         %% We should really have some way to subscribe to socket events...
         #{desc => "verify no socket (closed)",
           cmd  => fun(#{owner := Pid, sock := Sock} = _State) ->
                           case socket:getopt(Sock, otp, controlling_process) of
                               {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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(sc_lc_recv_response_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recv(Sock) end,
                   InitState = #{domain   => inet,
                                 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(sc_lc_recv_response_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recv(Sock) end,
                   InitState = #{domain   => inet6,
                                 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 Unix Domain (stream) socket.

sc_lc_recv_response_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(sc_lc_recv_response_tcpL,
           fun() ->
		   has_support_unix_domain_socket()
	   end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recv(Sock) end,
                   InitState = #{domain   => local,
                                 protocol => default,
                                 recv     => Recv},
                   ok = sc_lc_receive_response_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

sc_lc_receive_response_tcp(InitState) ->
    %% This (acceptor) is the server that accepts connections.
    %% 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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create (listen) socket",
           cmd  => fun(#{domain   := Domain, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain := local,
                         lsock  := LSock,
                         lsa    := LSA} = _State) ->
                           ?SEV_IPRINT("bind to LSA: "
                                       "~n   ~p", [LSA]),
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{lsock := LSock,
                         lsa   := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain := local,
                         tester := Tester,
                         lsa    := #{path := Path}}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Path),
                           ok;
                      (#{tester := Tester, lport := Port}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},
                           
         %% The actual test
         #{desc => "await continue (accept)",
           cmd  => fun(#{tester := Tester} = State) ->
                           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: "
                                               "~n   ~p", [socket:sockname(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 => "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 connection socket",
           cmd  => fun(#{csock := Sock} = State) ->
                           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 listen socket",
           cmd  => fun(#{domain := local,
                         lsock  := Sock,
                         lsa    := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->maps:remove(lsa, State) end,
                                           fun() -> State end),
                           State2 = maps:remove(lsock, State1),
                           State3 = maps:remove(lport, State2),
                           {ok, State3};
                      (#{lsock := Sock} = State) ->
                           case socket:close(Sock) of
                               ok ->
                                   State1 = maps:remove(lsock, State),
                                   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 response.
    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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain   := Domain, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, stream, 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) ->
                           ?SEV_IPRINT("bind to LSA: "
                                       "~n   ~p", [LSA]),
                           case sock_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(#{domain := local = Domain,
                         tester := Tester} = State) ->
                           case ?SEV_AWAIT_CONTINUE(Tester, tester, connect) of
                               {ok, ServerPath} ->
                                   ?SEV_IPRINT("Server Path: "
                                               "~n   ~s", [ServerPath]),
                                   ServerSA = #{family => Domain,
                                                path   => ServerPath},
                                   {ok, State#{server_sa => ServerSA}};
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{tester := Tester, local_sa := LSA} = State) ->
                           case ?SEV_AWAIT_CONTINUE(Tester, tester, connect) of
                               {ok, Port} ->
                                   ServerSA = LSA#{port => Port},
                                   {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(#{domain   := local,
                         sock     := Sock,
                         local_sa := #{path := Path}} = State) ->
                           sock_close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(sock, State1)};
                       (#{sock := Sock} = State) ->
                           sock_close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** 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, PortOrPath} ->
                                   {ok, State#{server_info => PortOrPath}};
                               {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, server_info := Info} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, connect, Info),
                           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(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Recv      = fun(Sock, To) ->
                                       socket:recvfrom(Sock, [], To)
                               end,
                   InitState = #{domain   => inet,
                                 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv      = fun(Sock, To) ->
                                       socket:recvfrom(Sock, [], To)
                               end,
                   InitState = #{domain   => inet6,
                                 protocol => udp,
                                 recv     => Recv},
                   ok = sc_lc_receive_response_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test what happens when a socket is 
%% locally closed while the process is calling the recv function.
%% Socket is Unix Domainm (dgram) socket.

sc_lc_recvfrom_response_udpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(?FUNCTION_NAME,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   Recv      = fun(Sock, To) ->
                                       socket:recvfrom(Sock, [], To)
                               end,
                   InitState = #{domain   => local,
                                 protocol => default,
                                 recv     => Recv},
                   ok = sc_lc_receive_response_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "open socket",
           cmd  => fun(#{domain := Domain, protocol := Proto} = State) ->
                           Sock = sock_open(Domain, dgram, Proto),
                           {ok, State#{sock => Sock}}
                   end},
         #{desc => "bind socket",
           cmd  => fun(#{sock := Sock, local_sa := LSA}) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ?SEV_IPRINT("src bound"),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("src bind failed: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "socket name",
           cmd  => fun(#{sock := Sock} = State) ->
                           case socket:sockname(Sock) of
                               {ok, SA} ->
                                   {ok, State#{sa => SA}};
                               {error, eafnosupport = Reason} ->
                                   ?SEV_IPRINT("Failed get socket name: "
                                               "~n   ~p", [Reason]),
                                   (catch socket:close(Sock)),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Failed get socket name: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester, sock := Sock}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Sock),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (recv, 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(#{domain   := local,
                         sock     := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(sock, State1)};
                      (#{sock := Sock} = State) ->
                           case socket:close(Sock) of
                               ok ->
                                   {ok, maps:remove(sock, State)};
                               {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 infinite 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   is_not_windows(), % recvmsg does not work on Windows
                   has_support_ipv4()
           end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recvmsg(Sock) end,
                   InitState = #{domain   => inet,
                                 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   is_not_windows(), % recvmsg does not work on Windows
                   has_support_ipv6()
           end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recvmsg(Sock) end,
                   InitState = #{domain   => inet6,
                                 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 Unix Domain (stream) socket.

sc_lc_recvmsg_response_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   is_not_windows(), % recvmsg does not work on Windows
                   has_support_unix_domain_socket()
           end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recvmsg(Sock) end,
                   InitState = #{domain   => local,
                                 protocol => default,
                                 recv     => Recv},
                   ok = sc_lc_receive_response_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test what happens when a socket is 
%% locally closed while the process is calling the recvmsg function.
%% Socket is IPv4.

sc_lc_recvmsg_response_udp4(_Config) when is_list(_Config) ->
    tc_try(sc_lc_recvmsg_response_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   ?TT(?SECS(10)),
                   Recv      = fun(Sock, To) -> socket:recvmsg(Sock, To) end,
                   InitState = #{domain   => inet,
                                 protocol => udp,
                                 recv     => Recv},
                   ok = sc_lc_receive_response_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test what happens when a socket is 
%% locally closed while the process is calling the recvmsg function.
%% Socket is IPv6.

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,
                                 protocol => udp,
                                 recv     => Recv},
                   ok = sc_lc_receive_response_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test what happens when a socket is 
%% locally closed while the process is calling the recvmsg function.
%% Socket is Unix Domain (dgram) socket.

sc_lc_recvmsg_response_udpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(sc_recvmsg_response_udpL,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   Recv      = fun(Sock, To) -> socket:recvmsg(Sock, To) end,
                   InitState = #{domain   => local,
                                 protocol => default,
                                 recv     => Recv},
                   ok = sc_lc_receive_response_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(sc_lc_acceptor_response_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(sc_lc_acceptor_response_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 protocol => tcp},
                   ok = sc_lc_acceptor_response_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test what happens when a socket is 
%% locally closed while the process is calling the accept function.
%% We test what happens with a non-controlling_process also, since we 
%% git the setup anyway.
%% Socket is Unix Domain (stream) socket.

sc_lc_acceptor_response_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(sc_lc_acceptor_response_tcpL,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   InitState = #{domain   => local,
                                 protocol => default},
                   ok = sc_lc_acceptor_response_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

sc_lc_acceptor_response_tcp(InitState) ->
    PrimAcceptorSeq =
        [
         %% *** 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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create (listen) socket",
           cmd  => fun(#{domain   := Domain, 
                         protocol := Proto} = State) ->
                           case socket:open(Domain, stream, 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 sock_bind(Sock, LSA) of
                               ok ->
                                   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 := LSock, timeout := Timeout} = _State) ->
                           case socket:accept(LSock, 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(#{domain := local,
                         sock   := Sock,
                         lsa    := #{path := Path}} = State) ->
                           case socket:close(Sock) of
                               ok ->
                                   State1 =
                                       unlink_path(Path,
                                                   fun() ->
                                                           maps:remove(lsa, State)
                                                   end,
                                                   fun() ->
                                                           State
                                                   end),
                                   {ok, maps:remove(sock, State1)};
                               {error, _} = ERROR ->
                                   unlink_path(Path),
                                   ERROR
                           end;
                      (#{sock := Sock} = State) ->
                           case socket:close(Sock) of
                               ok ->
                                   {ok, maps:remove(sock, State)};
                               {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 infinite 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recv(Sock) end,
                   InitState = #{domain   => inet,
                                 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recv(Sock) end,
                   InitState = #{domain   => inet6,
                                 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 Unix Domain (stream) socket.

sc_rc_recv_response_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recv(Sock) end,
                   InitState = #{domain   => local,
                                 protocol => default,
                                 recv     => Recv},
                   ok = sc_rc_receive_response_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

sc_rc_receive_response_tcp(InitState) ->
    %% Each connection are handled by handler processes.
    %% 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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain, protocol := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain := local,
                         lsock  := LSock,
                         lsa    := LSA} = _State) ->
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{lsock    := LSock,
                         local_sa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain   := local,
                         tester   := Tester,
                         local_sa := LSA}) ->
                           %% Actually we only need to send the path,
                           %% but to keep it simple, we send the "same"
                           %% as for non-local.
                           ?SEV_ANNOUNCE_READY(Tester, init, LSA),
                           ok;
                      (#{tester := Tester, local_sa := LSA, lport := Port}) ->
                           ServerSA = LSA#{port => Port},
                           ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
                           ok
                   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 := Pid1,
                         handler2 := Pid2,
                         handler3 := Pid3} = _State) ->
                           case ?SEV_AWAIT_READY(Pid1, handler1, recv, 
                                                 [{tester, Tester},
                                                  {handler2, Pid2},
                                                  {handler3, Pid3}]) of
                               {ok, Result} ->
                                   Result;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("Unexpected failure: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "await ready from handler 2 (recv)",
           cmd  => fun(#{tester := Tester,
                         handler1 := Pid1,
                         handler2 := Pid2,
                         handler3 := Pid3} = _State) ->
                           case ?SEV_AWAIT_READY(Pid2, handler2, recv, 
                                                 [{tester, Tester},
                                                  {handler1, Pid1},
                                                  {handler3, Pid3}]) of
                               {ok, Result} ->
                                   Result;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "await ready from handler 3 (recv)",
           cmd  => fun(#{tester := Tester,
                         handler1 := Pid1,
                         handler2 := Pid2,
                         handler3 := Pid3} = _State) ->
                           case ?SEV_AWAIT_READY(Pid3, handler3, recv, 
                                                 [{tester, Tester},
                                                  {handler1, Pid1},
                                                  {handler2, Pid2}]) 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(#{domain := local,
                         lsock  := LSock,
                         lsa    := #{path := Path}} = State) ->
                           case socket:close(LSock) of
                               ok ->
                                   State1 =
                                       unlink_path(Path,
                                                   fun() ->
                                                           maps:remove(lsa, State)
                                                   end,
                                                   fun() ->
                                                           State
                                                   end),
                                   {ok, maps:remove(lsock, State1)};
                               {error, _} = ERROR ->
                                   unlink_path(Path),
                                   ERROR
                           end;
                      (#{lsock := LSock} = State) ->
                           case socket:close(LSock) of
                               ok ->
                                   {ok, maps:remove(lsock, State)};
                               {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(#{node_id := NodeID} = State) ->
                           {Peer, Node} =
                               ?START_NODE(?CT_PEER_NAME(f("client_~w",
                                                           [NodeID]))),
                           {ok, State#{node => Node, peer => Peer}}
                   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,
                         protocol  := Proto}) ->
                           ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}),
                           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,
                                {unexpected_exit, rclient, noconnection}} ->
                                   %% Global might have disconnected => SKIP
                                   ?SEV_IPRINT("lost connection "
                                               "to remote client node => SKIP"),
                                   {skip, {rclient, noconnection}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "kill remote client",
           cmd  => fun(#{rclient := RClient}) ->
                           ?SEV_IPRINT("try kill remote client ~p", [RClient]),
                           ?SEV_ANNOUNCE_TERMINATE(RClient),
                           ok
                   end},
         #{desc => "await remote client termination",
           cmd  => fun(#{rclient := RClient} = State) ->
                           ?SEV_AWAIT_TERMINATION(RClient),
                           ?SEV_IPRINT("remote client ~p terminated",
                                       [RClient]),
                           State1 = maps:remove(rclient, State),
                           {ok, State1}
                   end},
         #{desc => "stop client node",
           cmd  => fun(#{peer := Peer} = State) ->
                           {ok,
                            try peer:stop(Peer) of
                                ok ->
                                    State#{node_stop => ok};
                                {error, Reason} ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   ~p", [Reason]),
                                    State#{node_stop => error}
                            catch
                                C:E:S ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   Class: ~p"
                                                "~n   Error: ~p"
                                                "~n   Stack: ~p",[C, E, S]),
                                    State#{node_stop => error}
                            end}
                   end},
         #{desc => "await client node termination",
           cmd  => fun(#{node := Node, node_stop := ok} = State) ->
                           ?SEV_IPRINT("Success node stop - await nodedown: "
                                       "~n      ~p", [Node]),
                           receive
                               {nodedown, Node} ->
                                   ?SEV_IPRINT("nodedown received - cleanup"),
                                   State1 = maps:remove(peer, State),
                                   State2 = maps:remove(node, State1),
                                   {ok, State2}
                           end;
                      (#{node_stop := error} = State) ->
                           ?SEV_IPRINT("Failed node stop - cleanup"),
                           State1 = maps:remove(peer, State),
                           State2 = maps:remove(node, State1),
                           {ok, State2}
                   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, Proto} = sc_rc_tcp_client_await_start(Parent),
    Domain   = maps:get(family, ServerSA),
    Sock     = sc_rc_tcp_client_create(Domain, Proto),
    Path     = sc_rc_tcp_client_bind(Sock, Domain),
    sc_rc_tcp_client_announce_ready(Parent, init),
    sc_rc_tcp_client_await_continue(Parent, connect),
    sc_rc_tcp_client_connect(Sock, ServerSA),
    sc_rc_tcp_client_announce_ready(Parent, connect),
    sc_rc_tcp_client_await_continue(Parent, close),
    sc_rc_tcp_client_close(Sock, Path),
    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, Proto) ->
    i("sc_rc_tcp_client_create -> entry"),
    case socket:open(Domain, stream, Proto) 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"),
    LSA = which_local_socket_addr(Domain),
    case socket:bind(Sock, LSA) of
        ok ->
            case socket:sockname(Sock) of
                {ok, #{family := local, path := Path}} ->
                    Path;
                {ok, _} ->
                    undefined;
                {error, Reason1} ->
                    exit({sockname, Reason1})
            end;
        {error, Reason} ->
            exit({bind, Reason})
    end.

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, Path) ->
    i("sc_rc_tcp_client_close -> entry"),
    case socket:close(Sock) of
        ok ->
            unlink_path(Path),
            ok;
        {error, Reason} ->
            ?SEV_EPRINT("failed closing: "
                        "~n   Reason: ~p", [Reason]),
            unlink_path(Path),
            {error, {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, Data} ->
            ?SEV_IPRINT("unexpected success: "
                        "~n   (Unexp) Data: ~p"
                        "~n   Socket Info:  ~p", [Data, socket:info(Sock)]),
            {error, unexpected_success};
        {error, Reason} = ERROR ->
            ?SEV_IPRINT("receive error: "
                        "~n   ~p", [Reason]),
            ERROR
    catch
        error:notsup = Error:Stack ->
            ?SEV_IPRINT("receive ~w error: skip"
                        "~n   Stack: ~p", [Error, Stack]),
            exit({skip, Error});
        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(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(sc_rc_recvmsg_response_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recvmsg(Sock) end,
                   InitState = #{domain   => inet,
                                 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(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(sc_rc_recvmsg_response_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recvmsg(Sock) end,
                   InitState = #{domain   => inet6,
                                 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 Unix Domain (stream) socket.

sc_rc_recvmsg_response_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(sc_rc_recvmsg_response_tcpL,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   Recv      = fun(Sock) -> socket:recvmsg(Sock) end,
                   InitState = #{domain   => local,
                                 protocol => default,
                                 recv     => Recv},
                   ok = sc_rc_receive_response_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test what happens when a socket is
%% remotely closed while the process is calling the recv function.
%% The remote client sends data, then shutdown(write) and then the
%% 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.
%% This would of course not work for Unix Domain sockets.
%%

sc_rs_recv_send_shutdown_receive_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   MsgData   = ?DATA,
                   Recv      = fun(Sock) ->
                                       socket:recv(Sock)
                               end,
                   Send      = fun(Sock, Data) ->
                                       socket:send(Sock, Data)
                               end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 recv   => Recv,
                                 send   => Send,
                                 data   => MsgData},
                   ok = sc_rs_send_shutdown_receive_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test what happens when a socket is
%% remotely closed while the process is calling the recv function.
%% The remote client sends data, then shutdown(write) and then the
%% reader attempts a recv.
%% Socket is IPv6.

sc_rs_recv_send_shutdown_receive_tcp6(_Config) when is_list(_Config) ->
    tc_try(?FUNCTION_NAME,
           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,
                                 proto  => tcp,
                                 recv   => Recv,
                                 send   => Send,
                                 data   => MsgData},
                   ok = sc_rs_send_shutdown_receive_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test what happens when a socket is
%% remotely closed while the process is calling the recv function.
%% The remote client sends data, then shutdown(write) and then the
%% reader attempts a recv.
%% Socket is Unix Domain (stream) socket.

sc_rs_recv_send_shutdown_receive_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   MsgData   = ?DATA,
                   Recv      = fun(Sock) ->
                                       socket:recv(Sock)
                               end,
                   Send      = fun(Sock, Data) ->
                                       socket:send(Sock, Data)
                               end,
                   InitState = #{domain => local,
                                 proto  => default,
                                 recv   => Recv,
                                 send   => Send,
                                 data   => MsgData},
                   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]),
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain, proto := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain   := local,
                         lsock    := LSock,
                         local_sa := LSA} = _State) ->
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{lsock := LSock, local_sa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [Port]),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain := local,
                         tester := Tester, local_sa := LSA}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, LSA),
                           ok;
                      (#{tester := Tester, local_sa := LSA, lport := Port}) ->
                           ServerSA = LSA#{port => Port},
                           ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
                           ok
                   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(#{domain   := local,
                         lsock    := Sock,
                         local_sa := #{path := Path}} = State) ->
                           socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)};
                      (#{lsock := LSock} = State) ->
                           case socket:close(LSock) of
                               ok ->
                                   {ok, maps:remove(lsock, State)};
                               {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(State) ->
                           {Peer, Node} = ?START_NODE("client"),
                           {ok, State#{peer => Peer, node => Node}}
                   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,
                         proto     := Proto,
                         server_sa := ServerSA}) ->
                           ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}),
                           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(#{peer := Peer} = State) ->
                           {ok,
                            try peer:stop(Peer) of
                                ok ->
                                    State#{node_stop => ok};
                                {error, Reason} ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   ~p", [Reason]),
                                    State#{node_stop => error}
                            catch
                                C:E:S ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   Class: ~p"
                                                "~n   Error: ~p"
                                                "~n   Stack: ~p",[C, E, S]),
                                    State#{node_stop => error}
                            end}
                   end},
         #{desc => "await client node termination",
           cmd  => fun(#{node := Node, node_stop := ok} = State) ->
                           ?SEV_IPRINT("Success node stop - await nodedown"),
                           receive
                               {nodedown, Node} ->
                                   ?SEV_IPRINT("nodedown received - cleanup"),
                                   State1 = maps:remove(peer, State),
                                   State2 = maps:remove(node, State1),
                                   {ok, State2}
                           end;
                      (#{node_stop := error} = State) ->
                           ?SEV_IPRINT("Failed node stop - cleanup"),
                           State1 = maps:remove(peer, State),
                           State2 = maps:remove(node, State1),
                           {ok, State2}
                   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),
                        proto  => maps:get(proto,  InitState),
                        recv   => maps:get(recv,   InitState)},
    Server = ?SEV_START("server", ServerSeq, ServerInitState),

    i("start client evaluator"),
    ClientInitState = #{host   => local_host(),
                        domain => maps:get(domain, InitState),
                        proto  => maps:get(proto,  InitState),
                        send   => maps:get(send, InitState)},
    Client = ?SEV_START("client", ClientSeq, ClientInitState),

    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, Proto} = sc_rs_tcp_client_await_start(Parent),
    Domain   = maps:get(family, ServerSA),
    Sock     = sc_rs_tcp_client_create(Domain, Proto),
    Path     = sc_rs_tcp_client_bind(Sock, Domain),
    sc_rs_tcp_client_announce_ready(Parent, init),
    sc_rs_tcp_client_await_continue(Parent, connect),
    sc_rs_tcp_client_connect(Sock, ServerSA),
    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, Path),
    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, Proto) ->
    i("sc_rs_tcp_client_create -> entry"),
    case socket:open(Domain, stream, Proto) 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"),
    LSA = which_local_socket_addr(Domain),
    case socket:bind(Sock, LSA) of
        ok ->
            case socket:sockname(Sock) of
                {ok, #{family := local, path := Path}} ->
                    Path;
                {ok, _} ->
                    undefined;
                {error, Reason1} ->
                    exit({sockname, Reason1})
            end;
        {error, Reason} ->
            exit({bind, Reason})
    end.

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"),
    try Send(Sock, Data) of
        ok ->
            ok;
        {error, Reason} ->
            exit({send, Reason})
    catch
        error : notsup = Reason : _ ->
            exit({skip, 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, Path) ->
    i("sc_rs_tcp_client_close -> entry"),
    case socket:close(Sock) of
        ok ->
            unlink_path(Path),
            ok;
        {error, Reason} ->
            ?SEV_EPRINT("failed closing: "
                        "~n   Reason: ~p", [Reason]),
            unlink_path(Path),
            {error, {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 should 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
        error : notsup = Reason : _ ->
            exit({skip, Reason});
        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(_Config) when is_list(_Config) ->
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   ?TT(?SECS(30)),
                   MsgData   = ?DATA,
                   Recv      = fun(Sock) ->
                                       case socket:recvmsg(Sock) of
                                           {ok, #{iov   := [Data]}} ->
                                               {ok, Data};
                                           {ok, #{addr  := _} = Msg} ->
                                               {error, {msg, Msg}};
                                           {error, _} = ERROR ->
                                               ERROR
                                       end
                               end,
                   Send      = fun(Sock, Data) when is_binary(Data) ->
                                  Msg = #{iov => [Data]},
                                  socket:sendmsg(Sock, Msg)
                               end,
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 recv   => Recv,
                                 send   => Send,
                                 data   => MsgData},
                   ok = sc_rs_send_shutdown_receive_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test what happens when a socket is
%% remotely closed while the process is calling the recvmsg function.
%% The remote client sends data, then shutdown(write) and then the
%% reader attempts a recv.
%% Socket is IPv6.

sc_rs_recvmsg_send_shutdown_receive_tcp6(_Config) when is_list(_Config) ->
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   ?TT(?SECS(10)),
                   MsgData   = ?DATA,
                   Recv      = fun(Sock) ->
                                       case socket:recvmsg(Sock) of
                                           {ok, #{iov   := [Data]}} ->
                                               {ok, Data};
                                           {ok, #{addr  := _} = Msg} ->
                                               {error, {msg, Msg}};
                                           {error, _} = ERROR ->
                                               ERROR
                                       end
                               end,
                   Send      = fun(Sock, Data) when is_binary(Data) ->
                                       Msg = #{iov => [Data]},
                                       socket:sendmsg(Sock, Msg)
                               end,
                   InitState = #{domain => inet6,
                                 proto  => tcp,
                                 recv   => Recv,
                                 send   => Send,
                                 data   => MsgData},
                   ok = sc_rs_send_shutdown_receive_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test what happens when a socket is
%% remotely closed while the process is calling the recvmsg function.
%% The remote client sends data, then shutdown(write) and then the
%% reader attempts a recv.
%% Socket is UNix Domain (stream) socket.

sc_rs_recvmsg_send_shutdown_receive_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   {ok, CWD} = file:get_cwd(),
                   ?SEV_IPRINT("CWD: ~s", [CWD]),
                   MsgData   = ?DATA,
                   Recv      = fun(Sock) ->
                                       case socket:recvmsg(Sock) of
                                           %% On some platforms, the address
                                           %% *is* provided (e.g. linux)
                                           {ok, #{addr  := #{family := local},
                                                  iov   := [Data]}} ->
                                               {ok, Data};
                                           {ok, #{addr := _} = Msg} ->
                                               {error, {msg, Msg}};
                                           %% On some platforms, the address
                                           %% is *not* provided (e.g. FreeBSD)
                                           {ok, #{iov   := [Data]}} ->
                                               {ok, Data};
                                           {ok, Msg} ->
                                               {error, {msg, Msg}};
                                           {error, _} = ERROR ->
                                               ERROR
                                       end
                               end,
                   Send      = fun(Sock, Data) when is_binary(Data) ->
                                       Msg = #{iov => [Data]},
                                       socket:sendmsg(Sock, Msg)
                               end,
                   InitState = #{domain => local,
                                 proto  => default,
                                 recv   => Recv,
                                 send   => Send,
                                 data   => MsgData},
                   ok = sc_rs_send_shutdown_receive_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (*really* simply) test
%% "some" ioctl features.
%%

ioctl_simple1(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_ioctl_requests()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_simple1(InitState)
           end).


do_ioctl_simple1(_State) ->
    Requests = socket:supports(ioctl_requests),
    ?P("Requests: ~p", [Requests]),
    ok.



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test "some" ioctl features.
%%

ioctl_simple2(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_ioctl_requests(),
                   has_support_ioctl_gifconf()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_simple2(InitState)
           end).


do_ioctl_simple2(_State) ->
    i("create dummy dgram:UDP socket"),
    {ok, Sock1} = socket:open(inet, dgram, udp),
    i("perform simple ioctl (expect success)"),
    case socket:ioctl(Sock1, gifconf) of
        {ok, IfConf1} when is_list(IfConf1) ->
            i("=> success"),
            ok;
        {error, Reason1} ->
            i("error: unexpected failure: "
              "~n      ~p", [Reason1]),
            exit({unexpected_ioctl_failure, dgram, Reason1});
        Else11 ->
            i("error: unexpected result: "
              "~n      ~p", [Else11]),
            exit({unexpected_ioctl_result, dgram, Else11})
    end,
    i("close dummy dgram:UDP socket"),
    ok = socket:close(Sock1),
    i("perform simple ioctl (expect failure)"),
    case socket:ioctl(Sock1, gifconf) of
        {error, closed} ->
            i("=> success"),
            ok;
        Else12 ->
            i("error: unexpected result: "
              "~n      ~p", [Else12]),
            exit({unexpected_ioctl_result, dgram, Else12})
    end,

    i("create dummy stream:TCP socket"),
    {ok, Sock2} = socket:open(inet, stream, tcp),
    i("perform simple ioctl (expect success)"),
    case socket:ioctl(Sock2, gifconf) of
        {ok, IfConf2} when is_list(IfConf2) ->
            i("=> success"),
            ok;
        {error, Reason2} ->
            i("error: unexpected result: "
              "~n      ~p", [Reason2]),
            exit({unexpected_ioctl_failure, stream, Reason2});
        Else21 ->
            i("error: unexpected result: "
              "~n      ~p", [Else21]),
            exit({unexpected_ioctl_result, stream, Else21})
    end,
    i("close dummy stream:TCP socket"),
    ok = socket:close(Sock2),
    i("perform simple ioctl (expect failure)"),
    case socket:ioctl(Sock2, gifconf) of
        {error, closed} ->
            i("=> success"),
            ok;
        Else22 ->
            i("error: unexpected result: "
              "~n      ~p", [Else22]),
            exit({unexpected_ioctl_result, stream, Else22})
    end,

    i("done"),
    ok.



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test the ioctl nread feature.
%%

ioctl_nread(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_ioctl_requests(),
                   has_support_ioctl_nread()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_nread(InitState)
           end).

do_ioctl_nread(_) ->
    i("Get local socket address"),
    LSA = which_local_socket_addr(inet),
    i("Use LSA: ~p", [LSA]),

    i("Create dgram:UDP socket 1"),
    {ok, S1} = socket:open(inet, dgram, udp),

    i("Bind socket 1"),
    ok = socket:bind(S1, LSA),

    i("Create dgram:UDP socket 2"),
    {ok, S2} = socket:open(inet, dgram, udp),

    i("Bind socket 2"),
    ok = socket:bind(S2, LSA),

    i("Check data to read - expect 0 bytes"),
    {ok, 0} = socket:ioctl(S1, nread),
    
    i("Get socket 1 port number"),
    {ok, #{port := Port1}} = socket:sockname(S1),
    
    i("Send data to socket 1 (from socket 2)"),
    Data   = <<0,1,2,3,4,5,6,7,8,9>>,
    DataSz = byte_size(Data),
    ok = socket:sendto(S2, Data, LSA#{port => Port1}),
    
    i("Give it some time to arrive"),
    ?SLEEP(?SECS(1)),
    
    i("Verify that the correct amount of data (atleast ~p) is available", [DataSz]),
    case socket:ioctl(S1, nread) of
        {ok, DataSize} when (DataSize >= DataSz) ->
            i("Success: "
	      "~n   Min Size:    ~p"
	      "~n   Actual Size: ~p", [DataSz, DataSize]),
	    ok;
	{ok, DataSize} ->
            i("Unexpected data size: "
	      "~n   Expected (min) Size: ~p"
	      "~n   Actual Size:                ~p", [DataSz, DataSize]),
	    ct:fail({invalid_data_size, DataSz, DataSize})
    end,

    i("Read the data"),
    {ok, {_, Data}} = socket:recvfrom(S1),
    
    i("Verify that the data has been read (no more data is available)"),
    {ok, 0} = socket:ioctl(S1, nread),

    i("Cleanup"),
    socket:close(S1),
    socket:close(S2),

    i("Done"),
    ok.

    
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% These test case(s) are intended to (simply) test "some" ioctl get
%% request(s).
%%

ioctl_get_gifname(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_gifname()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_gifname(InitState)
           end).


do_ioctl_get_gifname(_State) ->
    i("create dummy dgram:UDP socket"),
    {ok, Sock} = socket:open(inet, dgram, udp),

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    i("try ioctl all if names: "
      "~n      ~p", [IfNames]),
    _ = [case socket:ioctl(Sock, gifname, IfIdx) of
             {ok, IfName} ->
                 i("got expected interface name ~p for index ~w", [IfName, IfIdx]),
                 ok;
             {ok, OtherName} ->
                 %% Oups?!
                 i("<ERROR> got unexpected interface name for index ~w"
                   "~n      Expected: ~p"
                   "~n      Received: ~p", [IfIdx, IfName, OtherName]),
                 socket:close(Sock),
                 ?FAIL({unexpected_interface_name, IfIdx, OtherName, IfName});
             {error, Reason} ->
                 i("<ERROR> got unexpected error for interface index ~w"
                   "~n      Reason: ~p", [IfIdx, Reason]),
                 socket:close(Sock),
                 ?FAIL({unexpected_failure, IfName, Reason})
         end || {IfIdx, IfName} <- IfNames],

    i("close dummy dgram:UDP socket"),
    ok = socket:close(Sock),

    i("done"),
    ok.


%% --- gifindex ---

ioctl_get_gifindex(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_gifindex()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_gifindex(InitState)
           end).


do_ioctl_get_gifindex(_State) ->
    i("create dummy dgram:UDP socket"),
    {ok, Sock} = socket:open(inet, dgram, udp),

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    i("try ioctl all if indexes: "
      "~n      ~p", [IfNames]),
    _ = [case socket:ioctl(Sock, gifindex, IfName) of
             {ok, IfIdx} ->
                 i("got expected interface index ~w for interface ~p",
                   [IfIdx, IfName]),
                 ok;
             {ok, OtherIdx} ->
                 %% Oups?!
                 i("<ERROR> got unexpected interface index for interface ~p"
                   "~n      Expected: ~p"
                   "~n      Received: ~p", [IfName, IfIdx, OtherIdx]),
                 socket:close(Sock),
                 ?FAIL({unexpected_interface_index, IfName, OtherIdx, IfIdx});
             {error, Reason} ->
                 i("<ERROR> got unexpected error for interface ~p"
                   "~n      Reason: ~p", [IfName, Reason]),
                 socket:close(Sock),
                 ?FAIL({unexpected_failure, IfIdx, Reason})
         end || {IfIdx, IfName} <- IfNames],

    i("close dummy dgram:UDP socket"),
    ok = socket:close(Sock),

    i("done"),
    ok.



%% --- gifaddr ---

ioctl_get_gifaddr(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_gifaddr()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_gifaddr(InitState)
           end).


do_ioctl_get_gifaddr(_State) ->
    i("create dummy dgram:UDP socket"),
    {ok, Sock} = socket:open(inet, dgram, udp),

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    i("try ioctl all if indexes: "
      "~n      ~p", [IfNames]),
    %% This a *very* simple test...
    %% ...just to check that we actually get an socket address
    _ = [case socket:ioctl(Sock, gifaddr, IfName) of
             {ok, #{family := Fam,
                    addr   := Addr}} ->
                 i("got (expected) socket address for interface ~p (~w): "
                   "~n      (~w) ~p", [IfName, IfIdx, Fam, Addr]),
                 ok;
             {ok, Crap} ->
                 %% Oups?!
                 i("<ERROR> got unexpected result for interface ~p (~w)"
                   "~n      ~p", [IfName, IfIdx, Crap]),
                 socket:close(Sock),
                 ?FAIL({unexpected_addr, IfName, IfIdx, Crap});
             {error, eaddrnotavail = Reason} ->
                 i("got unexpected error for interface ~p (~w) => "
		   "SKIP interface"
                   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
                 ignore;
             {error, eperm = Reason} ->
                 i("got unexpected error for interface ~p (~w) => "
		   "SKIP interface"
                   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
                 ignore;
             {error, Reason} ->
                 i("<ERROR> got unexpected error for interface ~p (~w)"
                   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
                 socket:close(Sock),
                 ?FAIL({unexpected_failure, IfName, IfIdx, Reason})
         end || {IfIdx, IfName} <- IfNames],

    i("close dummy dgram:UDP socket"),
    ok = socket:close(Sock),

    i("done"),
    ok.



%% --- gifdstaddr ---

ioctl_get_gifdstaddr(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_gifdstaddr()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_gifdstaddr(InitState)
           end).


do_ioctl_get_gifdstaddr(_State) ->
    Domain = inet_or_inet6(),
    LSA    = which_local_socket_addr(Domain),

    i("create and init listen stream:TCP socket"),
    {ok, LSock} = socket:open(Domain, stream, tcp),
    ok = socket:bind(LSock, LSA#{port => 0}),
    ok = socket:listen(LSock),
    {ok, #{port := LPort}} = socket:sockname(LSock),
    
    i("create and init connection stream:TCP socket"),
    {ok, CSock} = socket:open(Domain, stream, tcp),

    i("attempt connect (nowait)"),
    {ok, ASock} =
        case socket:connect(CSock, LSA#{port => LPort}, nowait) of
            ok ->
                i("connected - accept connection"),
                socket:accept(LSock);
            {select, {select_info, _Tag, SelectHandle} = _SelectInfo} ->
                i("selected - attempt accept"),
                {ok, AS} = socket:accept(LSock),

                i("await connection ready"),
                receive
                    {'$socket', CSock, select, SelectHandle} ->
                        i("select info - attempt complete connection"),
                        ok = socket:connect(CSock),
                        {ok, AS}
                end
        end,

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    i("try ioctl all if indexes: "
      "~n      ~p", [IfNames]),
    %% This a *very* simple test...
    %% ...just to check that we actually get an socket address
    verify_gifdstaddr(ASock, "accept",  IfNames),
    verify_gifdstaddr(CSock, "connect", IfNames),
    verify_gifdstaddr(LSock, "listen",  IfNames),

    i("close socket(s)"),
    _ = socket:close(CSock),
    _ = socket:close(ASock),
    _ = socket:close(LSock),

    i("done"),
    ok.



verify_gifdstaddr(_Sock, _Prefix, []) ->
    ok;
verify_gifdstaddr(Sock, Prefix, [{IfIdx, IfName} | IfNames]) ->
    i("[~s] attempt verify gifdstaddr for interface ~s (~w)",
      [Prefix, IfName, IfIdx]),
    verify_gifdstaddr(Sock, Prefix, IfIdx, IfName),
    verify_gifdstaddr(Sock, Prefix, IfNames).

verify_gifdstaddr(Sock, Prefix, IfIdx, IfName) ->
    {OsFam, OsName} = os:type(),
    case socket:ioctl(Sock, gifdstaddr, IfName) of
        {ok, #{family := Fam,
               addr   := Addr}} ->
            i("[~s] got (expected) (dest) socket address "
              "for interface ~p (~w): "
              "~n      (~w) ~p", [Prefix, IfName, IfIdx, Fam, Addr]),
            ok;
        {ok, Crap} ->
        %% Oups?!
            i("<ERROR> [~s] got unexpected result for interface ~p (~w)"
              "~n      ~p", [Prefix, IfName, IfIdx, Crap]),
            socket:close(Sock),
            ?FAIL({unexpected_addr, Prefix, IfName, IfIdx, Crap});
        {error, IgnoredReason} when IgnoredReason =:= eaddrnotavail;
                                    IgnoredReason =:= eperm;
                                    IgnoredReason =:= enotty ->
            i("[~s] got unexpected error for interface ~p (~w) => "
              "SKIP interface"
              "~n      Reason: ~p", [Prefix, IfName, IfIdx, IgnoredReason]),
            ignore;
	{error, einval = Reason} when (OsFam =:= unix) andalso
                                      ((OsName =:= darwin) orelse 
                                       (OsName =:= freebsd) orelse 
                                       (OsName =:= netbsd) orelse 
                                       (OsName =:= openbsd)) ->
	    i("[~s] got unexpected error for interface ~p (~w) => "
	      "SKIP interface"
	      "~n      Reason: ~p", [Prefix, IfName, IfIdx, Reason]),
	    ignore;
        {error, Reason} ->
            i("<ERROR> got unexpected error for interface ~p (~w)"
              "~n      Reason: ~p", [IfName, IfIdx, Reason]),
            socket:close(Sock),
            ?FAIL({unexpected_failure, Prefix, IfName, IfIdx, Reason})
    end.
    



%% --- gifbrdaddr ---

ioctl_get_gifbrdaddr(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_gifbrdaddr()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_gifbrdaddr(InitState)
           end).


do_ioctl_get_gifbrdaddr(_State) ->
    Domain = inet_or_inet6(),
    LSA    = which_local_socket_addr(Domain),

    i("create and init listen stream:TCP socket"),
    {ok, LSock} = socket:open(Domain, stream, tcp),
    ok = socket:bind(LSock, LSA#{port => 0}),
    ok = socket:listen(LSock),
    {ok, #{port := LPort}} = socket:sockname(LSock),
    
    i("create and init connection stream:TCP socket"),
    {ok, CSock} = socket:open(Domain, stream, tcp),

    i("attempt connect (nowait)"),
    {ok, ASock} =
        case socket:connect(CSock, LSA#{port => LPort}, nowait) of
            ok ->
                i("connected - accept connection"),
                socket:accept(LSock);
            {select, {select_info, _Tag, SelectHandle} = _SelectInfo} ->
                i("selected - attempt accept"),
                {ok, AS} = socket:accept(LSock),

                i("await connection ready"),
                receive
                    {'$socket', CSock, select, SelectHandle} ->
                        i("select info - attempt complete connection"),
                        ok = socket:connect(CSock),
                        {ok, AS}
                end
        end,

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    i("try ioctl all if indexes: "
      "~n      ~p", [IfNames]),
    %% This a *very* simple test...
    %% ...just to check that we actually get an socket address
    verify_gifbrdaddr(ASock, "accept",  IfNames),
    verify_gifbrdaddr(CSock, "connect", IfNames),
    verify_gifbrdaddr(LSock, "listen",  IfNames),

    i("close socket(s)"),
    _ = socket:close(CSock),
    _ = socket:close(ASock),
    _ = socket:close(LSock),

    i("done"),
    ok.


verify_gifbrdaddr(_Sock, _Prefix, []) ->
    ok;
verify_gifbrdaddr(Sock, Prefix, [{IfIdx, IfName} | IfNames]) ->
    i("[~s] attempt verify gifbrdaddr for interface ~s (~w)",
      [Prefix, IfName, IfIdx]),
    verify_gifbrdaddr(Sock, Prefix, IfIdx, IfName),
    verify_gifbrdaddr(Sock, Prefix, IfNames).

verify_gifbrdaddr(Sock, Prefix, IfIdx, IfName) ->
    {OsFam, OsName} = os:type(),
    case socket:ioctl(Sock, gifbrdaddr, IfName) of
        {ok, #{family := Fam,
               addr   := Addr}} ->
            i("[~s] got (expected) (broadcast) socket address for "
              "interface ~p (~w): "
              "~n      (~w) ~p", [Prefix, IfName, IfIdx, Fam, Addr]),
            ok;
        {ok, Crap} ->
            %% Oups?!
            i("<ERROR> [~s] got unexpected result for interface ~p (~w)"
              "~n      ~p", [Prefix, IfName, IfIdx, Crap]),
            socket:close(Sock),
            ?FAIL({unexpected_addr, IfName, IfIdx, Crap});
        {error, IgnoredReason} when IgnoredReason =:= eaddrnotavail;
                                    IgnoredReason =:= eperm;
                                    IgnoredReason =:= enotty ->
            i("[~s] got unexpected error for interface ~p (~w) => "
             "SKIP interface"
              "~n      Reason: ~p", [Prefix, IfName, IfIdx, IgnoredReason]),
            ignore;
	{error, einval = Reason} when (OsFam =:= unix) andalso
                                      ((OsName =:= darwin) orelse
                                       (OsName =:= freebsd) orelse
                                       (OsName =:= netbsd) orelse
                                       (OsName =:= openbsd)) ->
	    i("[~s] got unexpected error for interface ~p (~w) => "
	      "SKIP interface"
	      "~n      Reason: ~p", [Prefix, IfName, IfIdx, Reason]),
	    ignore;
        {error, Reason} ->
            i("<ERROR> [~s] got unexpected error for interface ~p (~w)"
              "~n      Reason: ~p", [Prefix, IfName, IfIdx, Reason]),
            socket:close(Sock),
            ?FAIL({unexpected_failure, IfName, IfIdx, Reason})
    end.



%% --- gifnetmask ---

ioctl_get_gifnetmask(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_gifnetmask()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_gifnetmask(InitState)
           end).


do_ioctl_get_gifnetmask(_State) ->
    i("create dummy stream:TCP socket"),
    {ok, Sock} = socket:open(inet, stream, tcp),

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    i("try ioctl all if indexes: "
      "~n      ~p", [IfNames]),
    %% This a *very* simple test...
    %% ...just to check that we actually get an socket address
    _ = [case socket:ioctl(Sock, gifnetmask, IfName) of
             {ok, #{family := Fam,
                    addr   := Addr}} ->
                 i("got (expected) (netmask) socket address for interface ~p (~w): "
                   "~n      (~w) ~p", [IfName, IfIdx, Fam, Addr]),
                 ok;
             {ok, Crap} ->
                 %% Oups?!
                 i("<ERROR> got unexpected result for interface ~p (~w)"
                   "~n      ~p", [IfName, IfIdx, Crap]),
                 socket:close(Sock),
                 ?FAIL({unexpected_addr, IfName, IfIdx, Crap});
             {error, eaddrnotavail = Reason} ->
                 i("got unexpected error for interface ~p (~w) => SKIP interface"
                   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
                 ignore;
	     {error, eperm = Reason} ->
		 i("got unexpected error for interface ~p (~w) => "
		   "SKIP interface"
		   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
		 ignore;
             {error, Reason} ->
                 i("<ERROR> got unexpected error for interface ~p (~w)"
                   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
                 socket:close(Sock),
                 ?FAIL({unexpected_failure, IfName, IfIdx, Reason})
         end || {IfIdx, IfName} <- IfNames],

    i("close dummy stream:TCP socket"),
    ok = socket:close(Sock),

    i("done"),
    ok.



%% --- gifmtu ---

ioctl_get_gifmtu(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_gifmtu()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_gifmtu(InitState)
           end).


do_ioctl_get_gifmtu(_State) ->
    i("create dummy stream:TCP socket"),
    {ok, Sock} = socket:open(inet, stream, tcp),

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    i("try ioctl all if indexes: "
      "~n      ~p", [IfNames]),
    %% This a *very* simple test...
    %% ...just to check that we actually get an socket address
    _ = [case socket:ioctl(Sock, gifmtu, IfName) of
             {ok, MTU} when is_integer(MTU) ->
                 i("got (expected) MTU for interface ~p (~w): "
                   "~n      ~p", [IfName, IfIdx, MTU]),
                 ok;
             {ok, Crap} ->
                 %% Oups?!
                 i("<ERROR> got unexpected result for interface ~p (~w)"
                   "~n      ~p", [IfName, IfIdx, Crap]),
                 socket:close(Sock),
                 ?FAIL({unexpected_mtu, IfName, IfIdx, Crap});
             {error, Reason} ->
                 i("<ERROR> got unexpected error for interface ~p (~w)"
                   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
                 socket:close(Sock),
                 ?FAIL({unexpected_failure, IfName, IfIdx, Reason})
         end || {IfIdx, IfName} <- IfNames],

    i("close dummy stream:TCP socket"),
    ok = socket:close(Sock),

    i("done"),
    ok.



%% --- gifhwaddr ---

ioctl_get_gifhwaddr(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_gifhwaddr()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_gifhwaddr(InitState)
           end).


do_ioctl_get_gifhwaddr(_State) ->
    i("create dummy dgram:UDP socket"),
    {ok, Sock} = socket:open(inet, dgram, udp),

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    i("try ioctl all if indexes: "
      "~n      ~p", [IfNames]),
    %% This a *very* simple test...
    %% ...just to check that we actually get an socket address
    _ = [case socket:ioctl(Sock, gifhwaddr, IfName) of
             {ok, #{family := Fam,
                    addr   := Addr}} when is_atom(Fam) orelse is_integer(Fam) ->
                 i("got (expected) socket address for interface ~p (~w): "
                   "~n      (~w) ~p", [IfName, IfIdx, Fam, Addr]),
                 ok;
             {ok, Crap} ->
                 %% Oups?!
                         i("<ERROR> got unexpected result for interface ~p (~w)"
                           "~n      ~p", [IfName, IfIdx, Crap]),
                         socket:close(Sock),
                         ?FAIL({unexpected_addr, IfName, IfIdx, Crap});
             {error, Reason} ->
                 i("<ERROR> got unexpected error for interface ~p (~w)"
                   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
                 socket:close(Sock),
                 ?FAIL({unexpected_failure, IfName, IfIdx, Reason})
         end || {IfIdx, IfName} <- IfNames],

    i("close dummy dgram:UDP socket"),
    ok = socket:close(Sock),

    i("done"),
    ok.



%% --- giftxqlen ---

ioctl_get_giftxqlen(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_giftxqlen()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_giftxqlen(InitState)
           end).


do_ioctl_get_giftxqlen(_State) ->
    i("create dummy stream:TCP socket"),
    {ok, Sock} = socket:open(inet, stream, tcp),

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    i("try ioctl all if indexes: "
      "~n      ~p", [IfNames]),
    %% This a *very* simple test...
    %% ...just to check that we actually get an socket address
    _ = [case socket:ioctl(Sock, giftxqlen, IfName) of
             {ok, QLen} when is_integer(QLen) ->
                 i("got (expected) TX QLen for interface ~p (~w): "
                   "~n      ~p", [IfName, IfIdx, QLen]),
                 ok;
             {ok, Crap} ->
                 %% Oups?!
                 i("<ERROR> got unexpected result for interface ~p (~w)"
                   "~n      ~p", [IfName, IfIdx, Crap]),
                 socket:close(Sock),
                 ?FAIL({unexpected_mtu, IfName, IfIdx, Crap});
             {error, Reason} ->
                 i("<ERROR> got unexpected error for interface ~p (~w)"
                   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
                 socket:close(Sock),
                 ?FAIL({unexpected_failure, IfName, IfIdx, Reason})
         end || {IfIdx, IfName} <- IfNames],

    i("close dummy stream:TCP socket"),
    ok = socket:close(Sock),

    i("done"),
    ok.



%% --- gifflags ---

ioctl_get_gifflags(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_gifflags()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_gifflags(InitState)
           end).


do_ioctl_get_gifflags(_State) ->
    i("create dummy stream:TCP socket"),
    {ok, Sock} = socket:open(inet, stream, tcp),

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    AllFlags = [Flag || {Flag, Supported} <-
                            socket:supports(ioctl_flags), Supported =:= true],

    i("try ioctl all if indexes: "
      "~n      ~p", [IfNames]),
    %% This a *very* simple test...
    %% ...just to check that we actually get an socket address
    _ = [case socket:ioctl(Sock, gifflags, IfName) of
             {ok, Flags} when is_list(Flags) ->
                 i("got  flags for interface ~p (~w): "
                   "~n      ~p", [IfName, IfIdx, Flags]),
                 case Flags -- AllFlags of
                     [] ->
                         i("flags accounted for"),
                         ok;
                     ExtraFlags ->
                         i("<ERROR> got superfluous flags for interface ~p (~w)"
                           "~n      Received Flags:      ~p"
                           "~n      Superfluous Flags:   ~p"
                           "~n      All Supported Flags: ~p"
                           "~n", [IfName, IfIdx, Flags, ExtraFlags, AllFlags]),
                         socket:close(Sock),
                         ?FAIL({unexpected_superfluous_flags,
                                IfName, IfIdx, Flags, ExtraFlags, AllFlags})
                 end;
             {ok, Crap} ->
                 %% Oups?!
                 i("<ERROR> got unexpected result for interface ~p (~w)"
                   "~n      ~p", [IfName, IfIdx, Crap]),
                 socket:close(Sock),
                 ?FAIL({unexpected_mtu, IfName, IfIdx, Crap});
             {error, Reason} ->
                 i("<ERROR> got unexpected error for interface ~p (~w)"
                   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
                 socket:close(Sock),
                 ?FAIL({unexpected_failure, IfName, IfIdx, Reason})
         end || {IfIdx, IfName} <- IfNames],

    i("close dummy stream:TCP socket"),
    ok = socket:close(Sock),

    i("done"),
    ok.



%% --- gifmap ---

ioctl_get_gifmap(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_net_if_names(),
                   has_support_ioctl_gifmap()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_ioctl_get_gifmap(InitState)
           end).


do_ioctl_get_gifmap(_State) ->
    i("create dummy stream:TCP socket"),
    {ok, Sock} = socket:open(inet, stream, tcp),

    i("get if names"),
    {ok, IfNames} = net:if_names(),

    i("try ioctl all if indexes: "
      "~n      ~p", [IfNames]),
    %% This a *very* simple test...
    %% ...just to check that we actually get an socket address
    _ = [case socket:ioctl(Sock, gifmap, IfName) of
             {ok, Map} when is_map(Map) ->
                 i("got (expected) map for interface ~p (~w): "
                   "~n      ~p", [IfName, IfIdx, Map]),
                 ok;
             {ok, Crap} ->
                 %% Oups?!
                 i("<ERROR> got unexpected result for interface ~p (~w)"
                   "~n      ~p", [IfName, IfIdx, Crap]),
                 socket:close(Sock),
                 ?FAIL({unexpected_mtu, IfName, IfIdx, Crap});
             {error, Reason} ->
                 i("<ERROR> got unexpected error for interface ~p (~w)"
                   "~n      Reason: ~p", [IfName, IfIdx, Reason]),
                 socket:close(Sock),
                 ?FAIL({unexpected_failure, IfName, IfIdx, Reason})
         end || {IfIdx, IfName} <- IfNames],

    i("close dummy stream:TCP socket"),
    ok = socket:close(Sock),

    i("done"),
    ok.




%% --- tcp_info ---

ioctl_tcp_info(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    Cond = fun() ->
		   has_support_ioctl_tcp_info()
	   end,
    TC   = fun() ->
		   Domain = inet,
		   case which_local_addr(Domain) of
		       {ok, Addr} ->
			   State = #{domain => Domain,
				     laddr  => Addr},
			   do_ioctl_tcp_info(State);
		       {error, Reason} ->
			   skip({no_local_addr, Reason})
		   end
	   end,
    tc_try(?FUNCTION_NAME, Cond, TC).


do_ioctl_tcp_info(#{domain := Domain,
		    laddr  := LAddr} = _State) ->
    LSA = #{family => Domain, addr => LAddr},

    i("[server] create stream:TCP server listen socket"),
    {ok, L} = socket:open(Domain, stream, tcp),

    i("[server] bind to ~p", [LSA]),
    ok = socket:bind(L, LSA),

    i("[server] make listen socket"),
    ok = socket:listen(L),

    i("[server] get sockname"),
    {ok, SSA} = socket:sockname(L),


    i("[client] create stream:TCP socket"),
    {ok, C} = socket:open(Domain, stream, tcp),

    i("[client] bind to ~p", [LSA]),
    ok = socket:bind(C, LSA),

    i("[client] connect to server: "
      "~n   ~p", [SSA]),
    ok = socket:connect(C, SSA),

    
    i("[server] accept connection"),
    {ok, A} = socket:accept(L),

    
    i("[client] try get tcp info"),
    case socket:ioctl(C, tcp_info) of
	{ok, CTcpInfo0} ->
	    i("[client] tcp info: "
	      "~n   ~p"
	      "~n", [CTcpInfo0]),
	    ok;
	{error, CReason0} ->
	    i("[client] failed get TCP info: "
	      "~n   ~p"
	      "~n", [CReason0]),
	    skip({client_tcp_info, 0, CReason0})
    end,

    i("[server] try get tcp info"),
    case socket:ioctl(A, tcp_info) of
	{ok, ATcpInfo0} ->
	    i("[server] tcp info: "
	      "~n   ~p"
	      "~n", [ATcpInfo0]),
	    ok;
	{error, AReason0} ->
	    i("[server] failed get TCP info: "
	      "~n   ~p"
	      "~n", [AReason0]),
	    skip({server_tcp_info, 0, AReason0})
    end,


    Data = <<0,1,2,3,4,5,6,7,8,9,
	     0,1,2,3,4,5,6,7,8,9>>,
    DSz  = byte_size(Data),
    i("[client] send some data"),
    ok = socket:send(C, Data),

    i("[client] try get tcp info (verify bytes-out)"),
    case socket:ioctl(C, tcp_info) of
	{ok, #{bytes_out := BytesOut} = CTcpInfo1} when (BytesOut =:= DSz) ->
	    i("[client] tcp info: "
	      "~n   ~p"
	      "~n", [CTcpInfo1]),
	    ok;
	{error, CReason1} ->
	    i("[client] failed get TCP info: "
	      "~n   ~p"
	      "~n", [CReason1]),
	    skip({client_tcp_info, 1, CReason1})
    end,

    i("[server] try get tcp info (verify bytes-in)"),
    case socket:ioctl(A, tcp_info) of
	{ok, #{bytes_in := BytesIn} = ATcpInfo1} when (BytesIn =:= DSz) ->
	    i("[server] tcp info: "
	      "~n   ~p"
	      "~n", [ATcpInfo1]),
	    ok;
	{error, AReason1} ->
	    i("[server] failed get TCP info: "
	      "~n   ~p"
	      "~n", [AReason1]),
	    skip({server_tcp_info, 1, AReason1})
    end,


    i("[server] recv some data"),
    {ok, _} = socket:recv(A),

    i("[client] try get tcp info"),
    {ok, CConnTime2} =
	case socket:ioctl(C, tcp_info) of
	    {ok, #{connection_time := CCT2} = CTcpInfo2} ->
		i("[client] tcp info: "
		  "~n   ~p"
		  "~n", [CTcpInfo2]),
		{ok, CCT2};
	    {error, CReason2} ->
		i("[client] failed get TCP info: "
		  "~n   ~p"
		  "~n", [CReason2]),
		skip({client_tcp_info, 2, CReason2})
	end,

    i("[server] try get tcp info"),
    {ok, AConnTime2} =
	case socket:ioctl(A, tcp_info) of
	    {ok, #{connection_time := ACT2} = ATcpInfo2} ->
		i("[server] tcp info: "
		  "~n   ~p"
		  "~n", [ATcpInfo2]),
		{ok, ACT2};
	    {error, AReason2} ->
		i("[server] failed get TCP info: "
		  "~n   ~p"
		  "~n", [AReason2]),
		skip({server_tcp_info, 2, AReason2})
	end,

    SLEEP = ?SECS(5),
    ?SLEEP(SLEEP),

    i("[client] try get tcp info (verify connection time)"),
    case socket:ioctl(C, tcp_info) of
	{ok, #{connection_time := CCT3} = CTcpInfo3}
	  when (CCT3 >= (SLEEP+CConnTime2)) ->
	    i("[client] tcp info: "
	      "~n   ~p"
	      "~n", [CTcpInfo3]),
	    ok;
	{error, CReason3} ->
	    i("[client] failed get TCP info: "
	      "~n   ~p"
	      "~n", [CReason3]),
	    skip({client_tcp_info, 3, CReason3})
    end,

    i("[server] try get tcp info (verify connection time)"),
    case socket:ioctl(A, tcp_info) of
	{ok, #{connection_time := ACT3} = ATcpInfo3} 
	  when (ACT3 >= (SLEEP+AConnTime2)) ->
	    i("[server] tcp info: "
	      "~n   ~p"
	      "~n", [ATcpInfo3]),
	    ok;
	{error, AReason3} ->
	    i("[server] failed get TCP info: "
	      "~n   ~p"
	      "~n", [AReason3]),
	    skip({server_tcp_info, 3, AReason3})
    end,


    i("cleanup"),
    ok = socket:close(L),
    ok = socket:close(A),
    ok = socket:close(C),

    i("done"),
    ok.




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test that the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use TCP on IPv4.

traffic_send_and_recv_counters_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_send_and_recv_counters_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 recv   => fun(S)    -> socket:recv(S)    end,
                                 send   => fun(S, D) -> socket:send(S, D) end},
                   ok = traffic_send_and_recv_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test that the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use TCP on IPv6.

traffic_send_and_recv_counters_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_send_and_recv_counters_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain => inet6,
                                 proto  => tcp,
                                 recv   => fun(S)    -> socket:recv(S)    end,
                                 send   => fun(S, D) -> socket:send(S, D) end},
                   ok = traffic_send_and_recv_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test that the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use default (TCP) on local.

traffic_send_and_recv_counters_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_send_and_recv_counters_tcpL,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   InitState = #{domain => local,
                                 proto  => default,
                                 recv   => fun(S)    -> socket:recv(S)    end,
                                 send   => fun(S, D) -> socket:send(S, D) end},
                   ok = traffic_send_and_recv_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test that the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use TCP on IPv4.

traffic_sendmsg_and_recvmsg_counters_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_sendmsg_and_recvmsg_counters_tcp4,
           fun() ->
                   is_not_windows(),
                   has_support_ipv4()
           end,
           fun() ->
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 recv   => fun(S) ->
                                                   case socket:recvmsg(S) of
                                                       {ok, #{iov  := [Data]}} ->
                                                           {ok, Data};
                                                       {error, _} = ERROR ->
                                                           ERROR
                                                   end
                                           end,
                                 send   => fun(S, Data) ->
                                                   Msg = #{iov => [Data]},
                                                   socket:sendmsg(S, Msg)
                                           end},
                   ok = traffic_send_and_recv_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test that the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use TCP on IPv6.

traffic_sendmsg_and_recvmsg_counters_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_sendmsg_and_recvmsg_counters_tcp6,
           fun() ->
                   is_not_windows(),
                   has_support_ipv6()
           end,
           fun() ->
                   InitState = #{domain => inet6,
                                 proto  => tcp,
                                 recv   => fun(S) ->
                                                   case socket:recvmsg(S) of
                                                       {ok, #{iov  := [Data]}} ->
                                                           {ok, Data};
                                                       {error, _} = ERROR ->
                                                           ERROR
                                                   end
                                           end,
                                 send   => fun(S, Data) ->
                                                   Msg = #{iov => [Data]},
                                                   socket:sendmsg(S, Msg)
                                           end},
                   ok = traffic_send_and_recv_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test that the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use default (TCP) on local.

traffic_sendmsg_and_recvmsg_counters_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_sendmsg_and_recvmsg_counters_tcpL,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   InitState = #{domain => local,
                                 proto  => default,
                                 recv   => fun(S) ->
                                                   case socket:recvmsg(S) of
                                                       {ok, #{iov  := [Data]}} ->
                                                           {ok, Data};
                                                       {error, _} = ERROR ->
                                                           ERROR
                                                   end
                                           end,
                                 send   => fun(S, Data) ->
                                                   Msg = #{iov => [Data]},
                                                   socket:sendmsg(S, Msg)
                                           end},
                   ok = traffic_send_and_recv_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

traffic_send_and_recv_tcp(InitState) ->
    ServerSeq =
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain, proto := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, eafnosupport = Reason} ->
                                   {skip, Reason};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain   := local,
                         lsock    := LSock,
                         local_sa := LSA} = _State) ->
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{lsock    := LSock,
                         local_sa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "initial (listen socket) counter validation (=zero)",
           cmd  => fun(#{lsock := LSock} = _State) ->
                           try socket:info(LSock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("Validate initial listen socket counters: "
                                               "~s", [format_counters(listen,
                                                                      Counters)]),
                                   traffic_sar_counters_validation(Counters)
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain   := local,
                         tester   := Tester,
                         local_sa := #{path := Path}}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Path),
                           ok;
                      (#{tester   := Tester,
                         lport    := Port}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (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, CSock} ->
                                   #{counters := LCnts} = socket:info(LSock),
                                   ?SEV_IPRINT("Validate listen counters: "
                                               "~s", [format_counters(listen,
                                                                      LCnts)]),
                                   traffic_sar_counters_validation(
                                     LCnts,
                                     [{acc_success, 1},
                                      {acc_fails,   0},
                                      {acc_tries,   1},
                                      {acc_waits,   0}]),
                                   #{counters := CCnts} = socket:info(CSock),
                                   ?SEV_IPRINT("Validate initial accept counters: "
                                               "~s", [format_counters(CCnts)]),
                                   traffic_sar_counters_validation(CCnts),
                                   {ok, State#{csock => CSock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "initial counter validation (=zero)",
           cmd  => fun(#{csock := Sock} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("Validate initial counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(Counters)
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (accept)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, accept),
                           ok
                   end},

         #{desc => "await continue (recv_and_validate 1)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate)
                   end},
         #{desc => "recv (1)",
           cmd  => fun(#{csock := Sock,
                         recv  := Recv} = State) ->
                           case Recv(Sock) of
                               {ok, Data} ->
                                   ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
                                   {ok, State#{read_pkg  => 1,
                                               read_byte => size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (recv 1)",
           cmd  => fun(#{csock     := Sock,
                         read_pkg  := Pkg,
                         read_byte := Byte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,     Pkg},
                                      {read_byte,    Byte},
                                      {read_tries,   any},
                                      {read_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (recv_and_validate 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_and_validate),
                           ok
                   end},

         #{desc => "await continue (send_and_validate 1)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate)
                   end},
         #{desc => "send (1)",
           cmd  => fun(#{csock := Sock,
                         send  := Send} = State) ->
                           Data = ?DATA,
                           case Send(Sock, Data) of
                               ok ->
                                   ?SEV_IPRINT("sent ~p bytes", [size(Data)]),
                                   {ok, State#{write_pkg  => 1,
                                               write_byte => size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (send 1)",
           cmd  => fun(#{csock      := Sock,
                         read_pkg   := RPkg,
                         read_byte  := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,      RPkg},
                                      {read_byte,     RByte},
                                      {write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {read_tries,    any},
                                      {write_tries,   any},
                                      {read_pkg_max,  any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (send_and_validate 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_and_validate),
                           ok
                   end},

         #{desc => "await continue (recv_and_validate 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate)
                   end},
         #{desc => "recv (2)",
           cmd  => fun(#{csock     := Sock,
                         recv      := Recv,
                         read_pkg  := Pkg,
                         read_byte := Byte} = State) ->
                           case Recv(Sock) of
                               {ok, Data} ->
                                   ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
                                   {ok, State#{read_pkg  => Pkg + 1,
                                               read_byte => Byte + size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (recv 2)",
           cmd  => fun(#{csock      := Sock,
                         read_pkg   := RPkg,
                         read_byte  := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,      RPkg},
                                      {read_byte,     RByte},
                                      {write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {read_tries,    any},
                                      {write_tries,   any},
                                      {read_pkg_max,  any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (recv_and_validate 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_and_validate),
                           ok
                   end},

         #{desc => "await continue (send_and_validate 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate)
                   end},
         #{desc => "send (2)",
           cmd  => fun(#{csock      := Sock,
                         send       := Send,
                         write_pkg  := Pkg,
                         write_byte := Byte} = State) ->
                           Data = ?DATA,
                           case Send(Sock, Data) of
                               ok ->
                                   ?SEV_IPRINT("sent ~p bytes", [size(Data)]),
                                   {ok, State#{write_pkg  => Pkg + 1,
                                               write_byte => Byte + size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (send 2)",
           cmd  => fun(#{csock      := Sock,
                         read_pkg   := RPkg,
                         read_byte  := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,      RPkg},
                                      {read_byte,     RByte},
                                      {write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {read_tries,    any},
                                      {write_tries,   any},
                                      {read_pkg_max,  any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (send_and_validate 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_and_validate),
                           ok
                   end},


         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close connection socket (just in case)",
           cmd  => fun(#{csock := Sock} = State) ->
                           (catch socket:close(Sock)),
                           {ok, maps:remove(csock, State)}
                   end},
         #{desc => "close listen socket",
           cmd  => fun(#{domain   := local,
                         lsock    := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)};
                      (#{lsock := Sock} = State) ->
                           (catch socket:close(Sock)),
                           {ok, maps:remove(lsock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    ClientSeq =
        [
         %% *** Wait for start order part ***
         #{desc => "await start (from tester)",
           cmd  => fun(#{domain := local} = State) ->
                           {Tester, Path} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_path => Path}};
                      (State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain      := local = Domain,
                         server_path := Path} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = #{family => Domain, path => Path},
                           {ok, State#{local_sa => LSA, server_sa => SSA}};
                      (#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, eafnosupport = Reason} ->
                                   {skip, Reason};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (connect)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, connect),
                           ok
                   end},
         #{desc => "connect to server",
           cmd  => fun(#{sock := Sock, server_sa := SSA}) ->
                           socket:connect(Sock, SSA)
                   end},
         #{desc => "announce ready (connect)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, connect),
                           ok
                   end},

         #{desc => "await continue (send_and_validate 1)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate)
                   end},
         #{desc => "send (1)",
           cmd  => fun(#{sock := Sock,
                         send := Send} = State) ->
                           Data = ?DATA,
                           case Send(Sock, Data) of
                               ok ->
                                   ?SEV_IPRINT("sent ~p bytes", [size(Data)]),
                                   {ok, State#{write_pkg  => 1,
                                               write_byte => size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (send 1)",
           cmd  => fun(#{sock      := Sock,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {write_tries,   any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (send_and_validate 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_and_validate),
                           ok
                   end},

         #{desc => "await continue (recv_and_validate 1)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate)
                   end},
         #{desc => "recv (1)",
           cmd  => fun(#{sock := Sock,
                         recv := Recv} = State) ->
                           case Recv(Sock) of
                               {ok, Data} ->
                                   ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
                                   {ok, State#{read_pkg  => 1,
                                               read_byte => size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (recv 1)",
           cmd  => fun(#{sock       := Sock,
                         read_pkg   := RPkg,
                         read_byte  := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,      RPkg},
                                      {read_byte,     RByte},
                                      {write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {read_tries,    any},
                                      {write_tries,   any},
                                      {read_pkg_max,  any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (recv_and_validate 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_and_validate),
                           ok
                   end},

         #{desc => "await continue (send_and_validate 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate)
                   end},
         #{desc => "send (2)",
           cmd  => fun(#{sock       := Sock,
                         send       := Send,
                         write_pkg  := SPkg,
                         write_byte := SByte} = State) ->
                           Data = ?DATA,
                           case Send(Sock, Data) of
                               ok ->
                                   ?SEV_IPRINT("sent ~p bytes", [size(Data)]),
                                   {ok, State#{write_pkg  => SPkg + 1,
                                               write_byte => SByte + size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (send 2)",
           cmd  => fun(#{sock      := Sock,
                         read_pkg  := RPkg,
                         read_byte := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,      RPkg},
                                      {read_byte,     RByte},
                                      {write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {read_tries,    any},
                                      {write_tries,   any},
                                      {read_pkg_max,  any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (send_and_validate 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_and_validate),
                           ok
                   end},

         #{desc => "await continue (recv_and_validate 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate)
                   end},
         #{desc => "recv (2)",
           cmd  => fun(#{sock      := Sock,
                         recv      := Recv,
                         read_pkg  := RPkg,
                         read_byte := RByte} = State) ->
                           case Recv(Sock) of
                               {ok, Data} ->
                                   ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
                                   {ok, State#{read_pkg  => RPkg + 1,
                                               read_byte => RByte + size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (recv 2)",
           cmd  => fun(#{sock      := Sock,
                         read_pkg  := RPkg,
                         read_byte := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,      RPkg},
                                      {read_byte,     RByte},
                                      {write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {read_tries,    any},
                                      {write_tries,   any},
                                      {read_pkg_max,  any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (recv_and_validate 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_and_validate),
                           ok
                   end},

         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close connection socket",
           cmd  => fun(#{domain   := local,
                         sock     := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(sock, State1)};
                      (#{sock := Sock} = State) ->
                           socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor client",
           cmd  => fun(#{client := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{domain := local,
                         server := Pid} = State) ->
                           {ok, Path} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{path => Path}};
                      (#{server := Pid} = State) ->
                           {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{port => Port}}
                   end},

         %% Start the client
         #{desc => "order client start",
           cmd  => fun(#{domain := local,
                         client := Pid,
                         path   := Path} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Path),
                           ok;
                      (#{client := Pid,
                         port   := Port} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Port),
                           ok
                   end},
         #{desc => "await client ready (init)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, init)
                   end},

         %% *** The actual test ***

         #{desc => "order server to continue (with accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, accept),
                           ok
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "order client to continue (with connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, connect),
                           ok
                   end},
         #{desc => "await client ready (connect)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, connect)
                   end},
         #{desc => "await server ready (accept)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, accept)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "order server to continue (recv_and_validate 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, recv_and_validate),
                           ok
                   end},
         #{desc => "order client to continue (send_and_validate 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_and_validate),
                           ok
                   end},
         #{desc => "await client ready (send_and_validate 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_and_validate)
                   end},
         #{desc => "await server ready (recv_and_validate 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_and_validate)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "order client to continue (recv_and_validate 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, recv_and_validate),
                           ok
                   end},
         #{desc => "order server to continue (send_and_validate 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_and_validate),
                           ok
                   end},
         #{desc => "await server ready (send_and_validate 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_and_validate)
                   end},
         #{desc => "await client ready (recv_and_validate 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, recv_and_validate)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "order server to continue (recv_and_validate 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, recv_and_validate),
                           ok
                   end},
         #{desc => "order client to continue (send_and_validate 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_and_validate),
                           ok
                   end},
         #{desc => "await client ready (send_and_validate 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_and_validate)
                   end},
         #{desc => "await server ready (recv_and_validate 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_and_validate)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "order client to continue (recv_and_validate 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, recv_and_validate),
                           ok
                   end},
         #{desc => "order server to continue (send_and_validate 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_and_validate),
                           ok
                   end},
         #{desc => "await server ready (send_and_validate 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_and_validate)
                   end},
         #{desc => "await client ready (recv_and_validate 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, recv_and_validate)
                   end},

         %% *** Termination ***
         #{desc => "order client to terminate",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Client),
                           ok
                   end},
         #{desc => "await client termination",
           cmd  => fun(#{client := Client} = State) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client, State),
                           {ok, State1}
                   end},
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    ServerInitState = InitState#{host => local_host()},
    Server = ?SEV_START("server", ServerSeq, ServerInitState),

    i("start client evaluator(s)"),
    ClientInitState = InitState#{host => local_host()},
    Client = ?SEV_START("client", ClientSeq, ClientInitState),

    i("start 'tester' evaluator"),
    TesterInitState = #{server => Server#ev.pid,
                        client => Client#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator"),
    ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).


format_counters(Counters) ->
    format_counters(traffic, Counters).

format_counters(Type, Counters) when (Type =:= listen) orelse (Type =:= traffic) ->
    format_counters("   ", Type, Counters).

format_counters(Prefix, traffic, Counters) ->
    ReadByte    = maps:get(read_byte,     Counters, -1),
    ReadFails   = maps:get(read_fails,    Counters, -1),
    ReadPkg     = maps:get(read_pkg,      Counters, -1),
    ReadPkgMax  = maps:get(read_pkg_max,  Counters, -1),
    ReadTries   = maps:get(read_tries,    Counters, -1),
    ReadWaits   = maps:get(read_waits,    Counters, -1),
    WriteByte   = maps:get(write_byte,    Counters, -1),
    WriteFails  = maps:get(write_fails,   Counters, -1),
    WritePkg    = maps:get(write_pkg,     Counters, -1),
    WritePkgMax = maps:get(write_pkg_max, Counters, -1),
    WriteTries  = maps:get(write_tries,   Counters, -1),
    WriteWaits  = maps:get(write_waits,   Counters, -1),
    f("~n~sNumber Of Read Bytes:     ~p"
      "~n~sNumber Of Read Fails:     ~p"
      "~n~sNumber Of Read Packages:  ~p"
      "~n~sNumber Of Read Tries:     ~p"
      "~n~sNumber Of Read Waits:     ~p"
      "~n~sMax Read Package Size:    ~p"
      "~n~sNumber Of Write Bytes:    ~p"
      "~n~sNumber Of Write Fails:    ~p"
      "~n~sNumber Of Write Packages: ~p"
      "~n~sNumber Of Write Tries:    ~p"
      "~n~sNumber Of Write Waits:    ~p"
      "~n~sMax Write Package Size:   ~p",
      [Prefix, ReadByte,
       Prefix, ReadFails,
       Prefix, ReadPkg,
       Prefix, ReadTries,
       Prefix, ReadWaits,
       Prefix, ReadPkgMax,
       Prefix, WriteByte,
       Prefix, WriteFails,
       Prefix, WritePkg,
       Prefix, WriteTries,
       Prefix, WriteWaits,
       Prefix, WritePkgMax]);

format_counters(Prefix, listen, Counters) ->
    AccSuccess = maps:get(acc_success, Counters, -1),
    AccFails   = maps:get(acc_fails,   Counters, -1),
    AccTries   = maps:get(acc_tries,   Counters, -1),
    AccWaits   = maps:get(acc_waits,   Counters, -1),
    f("~n~sNumber Of Successful Accepts: ~p"
      "~n~sNumber Of Failed Accepts:     ~p"
      "~n~sNumber Of Accept Attempts:    ~p"
      "~n~sNumber Of Accept Waits:       ~p",
      [Prefix, AccSuccess,
       Prefix, AccFails,
       Prefix, AccTries,
       Prefix, AccWaits]).

all_counters() ->
    [
     read_byte,
     read_fails,
     read_pkg,
     read_pkg_max,
     read_tries,
     read_waits,
     write_byte,
     write_fails,
     write_pkg,
     write_pkg_max,
     write_tries,
     write_waits,
     acc_success,
     acc_fails,
     acc_tries,
     acc_waits
    ].

zero_counters() ->
    [{Cnt, 0} || Cnt <- all_counters()].

any_counters() ->
    [{Cnt, any} || Cnt <- all_counters()].


%% This function ensures that we have a list of "validate counters"
%% that have an entry for each existing counter.

ensure_counters(Counters) ->
    ensure_counters(any_counters(), Counters, []).

ensure_counters([], [], Acc) ->
    lists:reverse(Acc);
ensure_counters([{Cnt, Val}|DefCounters], Counters, Acc) ->
    case lists:keysearch(Cnt, 1, Counters) of
        {value, {Cnt, _} = T} ->
            Counters2 = lists:keydelete(Cnt, 1, Counters),
            ensure_counters(DefCounters, Counters2, [T|Acc]);
        false ->
            ensure_counters(DefCounters, Counters, [{Cnt, Val}|Acc])
    end.

traffic_sar_counters_validation(Counters) ->
    %% ?SEV_IPRINT("traffic_sar_counters_validation -> entry with"
    %%             "~n   Counters: ~p", [Counters]),
    traffic_sar_counters_validation2(maps:to_list(Counters),
                                     zero_counters()).

traffic_sar_counters_validation(Counters, ValidateCounters) ->
    %% ?SEV_IPRINT("traffic_sar_counters_validation -> entry with"
    %%             "~n   Counters:          ~p"
    %%             "~n   Validate Counters: ~p", [Counters, ValidateCounters]),
    traffic_sar_counters_validation2(maps:to_list(Counters),
                                     ensure_counters(ValidateCounters)).

traffic_sar_counters_validation2(Counters, []) ->
    %% ?SEV_IPRINT("traffic_sar_counters_validation2 -> Remaining Counters: "
    %%             "~n   ~p", [Counters]),
    (catch lists:foreach(
             fun({_Cnt, 0})   -> ok;
                ({Cnt,  Val}) ->
                     throw({error, {invalid_counter, Cnt, Val}})
             end,
             Counters));
traffic_sar_counters_validation2(Counters, [{Cnt, Val}|ValidateCounters]) ->
    %% ?SEV_IPRINT("traffic_sar_counters_validation2 -> try validate ~w when"
    %%             "~n   Counters:         ~p"
    %%             "~n   ValidateCounters: ~p", [Cnt, Counters, ValidateCounters]),
    case lists:keysearch(Cnt, 1, Counters) of
        {value, {Cnt, Val}} ->
            %% ?SEV_IPRINT("traffic_sar_counters_validation2 -> ~w validated", [Cnt]),
            Counters2 = lists:keydelete(Cnt, 1, Counters),
            traffic_sar_counters_validation2(Counters2, ValidateCounters);
        {value, {Cnt, _Val}} when (Val =:= any) ->
            %% ?SEV_IPRINT("traffic_sar_counters_validation2 -> "
            %%             "~w validated (any) when"
            %%             "~n   Counters: ~p", [Cnt, Counters]),
            Counters2 = lists:keydelete(Cnt, 1, Counters),
            traffic_sar_counters_validation2(Counters2, ValidateCounters);
        {value, {Cnt, InvVal}} ->
            ?SEV_EPRINT("traffic_sar_counters_validation2 -> "
                        "~w validation failed: "
                        "~n   Expected Value: ~p"
                        "~n   Actual Value:   ~p", [Cnt, Val, InvVal]),
            {error, {invalid_counter, Cnt, InvVal, Val}};
        false ->
            ?SEV_EPRINT("traffic_sar_counters_validation2 -> "
                        "~w validation failed: Unknown", [Cnt]),
            {error, {unknown_counter, Cnt, Counters}}
    end.

                          

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use UDP on IPv4.

traffic_sendto_and_recvfrom_counters_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_sendto_and_recvfrom_counters_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 recv   => fun(S) ->
                                                   socket:recvfrom(S)
                                           end,
                                 send   => fun(S, Data, Dest) ->
                                                   socket:sendto(S, Data, Dest)
                                           end},
                   ok = traffic_send_and_recv_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use UDP on IPv6.

traffic_sendto_and_recvfrom_counters_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_sendto_and_recvfrom_counters_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain => inet6,
                                 proto  => udp,
                                 recv   => fun(S) ->
                                                   socket:recvfrom(S)
                                           end,
                                 send   => fun(S, Data, Dest) ->
                                                   socket:sendto(S, Data, Dest)
                                           end},
                   ok = traffic_send_and_recv_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use default (UDP) on local.

traffic_sendto_and_recvfrom_counters_udpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_sendto_and_recvfrom_counters_udp4,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   InitState = #{domain => local,
                                 proto  => default,
                                 recv   => fun(S) ->
                                                   socket:recvfrom(S)
                                           end,
                                 send   => fun(S, Data, Dest) ->
                                                   socket:sendto(S, Data, Dest)
                                           end},
                   ok = traffic_send_and_recv_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use UDP on IPv4.

traffic_sendmsg_and_recvmsg_counters_udp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_sendmsg_and_recvmsg_counters_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 recv   => fun(S) ->
                                                   case socket:recvmsg(S) of
                                                       {ok, #{addr  := Source,
                                                              iov   := [Data]}} ->
                                                           {ok, {Source, Data}};
                                                       {error, _} = ERROR ->
                                                           ERROR
                                                   end
                                           end,
                                 send   => fun(S, Data, Dest) ->
                                                   Msg = #{addr => Dest,
                                                              iov  => [Data]},
                                                   socket:sendmsg(S, Msg)
                                           end},
                   ok = traffic_send_and_recv_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use UDP on IPv6.

traffic_sendmsg_and_recvmsg_counters_udp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_sendmsg_and_recvmsg_counters_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain => inet6,
                                 proto  => udp,
                                 recv   => fun(S) ->
                                                   case socket:recvmsg(S) of
                                                       {ok, #{addr  := Source,
                                                              iov   := [Data]}} ->
                                                           {ok, {Source, Data}};
                                                       {error, _} = ERROR ->
                                                           ERROR
                                                   end
                                           end,
                                 send   => fun(S, Data, Dest) ->
                                                   Msg = #{addr => Dest,
                                                              iov  => [Data]},
                                                   socket:sendmsg(S, Msg)
                                           end},
                   ok = traffic_send_and_recv_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to (simply) test the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
%% We use default (UDP) on local.

traffic_sendmsg_and_recvmsg_counters_udpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(15)),
    tc_try(traffic_sendmsg_and_recvmsg_counters_udpL,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   InitState = #{domain => local,
                                 proto  => default,
                                 recv   => fun(S) ->
                                                   case socket:recvmsg(S) of
                                                       {ok, #{addr  := Source,
                                                              iov   := [Data]}} ->
                                                           {ok, {Source, Data}};
                                                       {error, _} = ERROR ->
                                                           ERROR
                                                   end
                                           end,
                                 send   => fun(S, Data, Dest) ->
                                                   Msg = #{addr => Dest,
                                                              iov  => [Data]},
                                                   socket:sendmsg(S, Msg)
                                           end},
                   ok = traffic_send_and_recv_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

traffic_send_and_recv_udp(InitState) ->
    ServerSeq =
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(State) ->
                           Tester = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which local address",
           cmd  => fun(#{domain := Domain} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain, proto := Proto} = State) ->
                           case socket:open(Domain, dgram, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain   := local,
                         sock     := Sock,
                         local_sa := LSA} = _State) ->
                           case socket:bind(Sock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{sock     := LSock,
                         local_sa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "initial counter validation (=zero)",
           cmd  => fun(#{sock := Sock} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("Validate initial counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(Counters)
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain   := local,
                         tester   := Tester,
                         local_sa := #{path := Path}}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Path),
                           ok;
                      (#{tester   := Tester,
                         lport    := Port}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Port),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (recv_and_validate 1)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate)
                   end},
         #{desc => "recv (1)",
           cmd  => fun(#{sock := Sock,
                         recv := Recv} = State) ->
                           case Recv(Sock) of
                               {ok, {ClientSA, Data}} ->
                                   ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
                                   {ok, State#{client_sa => ClientSA,
                                               read_pkg  => 1,
                                               read_byte => size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (recv 1)",
           cmd  => fun(#{sock      := Sock,
                         read_pkg  := Pkg,
                         read_byte := Byte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,   Pkg},
                                      {read_byte,  Byte},
                                      {read_tries, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (recv_and_validate 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_and_validate),
                           ok
                   end},

         #{desc => "await continue (send_and_validate 1)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate)
                   end},
         #{desc => "send (1)",
           cmd  => fun(#{sock      := Sock,
                         send      := Send,
                         client_sa := ClientSA} = State) ->
                           Data = ?DATA,
                           case Send(Sock, Data, ClientSA) of
                               ok ->
                                   ?SEV_IPRINT("sent ~p bytes", [size(Data)]),
                                   {ok, State#{write_pkg  => 1,
                                               write_byte => size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (send 1)",
           cmd  => fun(#{sock       := Sock,
                         read_pkg   := RPkg,
                         read_byte  := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,    RPkg},
                                      {read_byte,   RByte},
                                      {write_pkg,   SPkg},
                                      {write_byte,  SByte},
                                      {read_tries,  any},
                                      {write_tries, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (send_and_validate 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_and_validate),
                           ok
                   end},

         #{desc => "await continue (recv_and_validate 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate)
                   end},
         #{desc => "recv (2)",
           cmd  => fun(#{sock      := Sock,
                         recv      := Recv,
                         read_pkg  := Pkg,
                         read_byte := Byte} = State) ->
                           case Recv(Sock) of
                               {ok, {Source, Data}} ->
                                   ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
                                   {ok, State#{client_sa => Source,
                                               read_pkg  => Pkg + 1,
                                               read_byte => Byte + size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (recv 2)",
           cmd  => fun(#{sock       := Sock,
                         read_pkg   := RPkg,
                         read_byte  := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,    RPkg},
                                      {read_byte,   RByte},
                                      {write_pkg,   SPkg},
                                      {write_byte,  SByte},
                                      {read_tries,  any},
                                      {write_tries, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (recv_and_validate 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_and_validate),
                           ok
                   end},

         #{desc => "await continue (send_and_validate 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate)
                   end},
         #{desc => "send (2)",
           cmd  => fun(#{sock       := Sock,
                         client_sa  := ClientSA,
                         send       := Send,
                         write_pkg  := Pkg,
                         write_byte := Byte} = State) ->
                           Data = ?DATA,
                           case Send(Sock, Data, ClientSA) of
                               ok ->
                                   ?SEV_IPRINT("sent ~p bytes", [size(Data)]),
                                   {ok, State#{write_pkg  => Pkg + 1,
                                               write_byte => Byte + size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (send 2)",
           cmd  => fun(#{sock       := Sock,
                         read_pkg   := RPkg,
                         read_byte  := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,    RPkg},
                                      {read_byte,   RByte},
                                      {write_pkg,   SPkg},
                                      {write_byte,  SByte},
                                      {read_tries,  any},
                                      {write_tries, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (send_and_validate 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_and_validate),
                           ok
                   end},


         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket (just in case)",
           cmd  => fun(#{domain   := local,
                         sock     := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)};
                      (#{sock := Sock} = State) ->
                           (catch socket:close(Sock)),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    ClientSeq =
        [
         %% *** Wait for start order part ***
         #{desc => "await start (from tester)",
           cmd  => fun(#{domain := local} = State) ->
                           {Tester, Path} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_path => Path}};
                      (State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester} = _State) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** Init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain      := local = Domain,
                         server_path := Path} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = #{family => Domain, path => Path},
                           {ok, State#{local_sa => LSA, server_sa => SSA}};
                      (#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{local_sa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain,
                         proto  := Proto} = State) ->
                           case socket:open(Domain, dgram, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{sock := Sock, local_sa := LSA} = _State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "initial counter validation (=zero)",
           cmd  => fun(#{sock := Sock} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("Validate initial counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(Counters)
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init),
                           ok
                   end},

         %% The actual test
         #{desc => "await continue (send_and_validate 1)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate)
                   end},
         #{desc => "send (1)",
           cmd  => fun(#{sock      := Sock,
                         send      := Send,
                         server_sa := ServerSA} = State) ->
                           Data = ?DATA,
                           case Send(Sock, Data, ServerSA) of
                               ok ->
                                   ?SEV_IPRINT("sent ~p bytes", [size(Data)]),
                                   {ok, State#{write_pkg  => 1,
                                               write_byte => size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (send 1)",
           cmd  => fun(#{sock       := Sock,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {write_tries,   any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (send_and_validate 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_and_validate),
                           ok
                   end},

         #{desc => "await continue (recv_and_validate 1)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate)
                   end},
         #{desc => "recv (1)",
           cmd  => fun(#{sock      := Sock,
                         recv      := Recv,
                         server_sa := #{family := local} = ServerSA} = State) ->
                           case Recv(Sock) of
                               {ok, {ServerSA, Data}} ->
                                   ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
                                   {ok, State#{read_pkg  => 1,
                                               read_byte => size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{sock      := Sock,
                         recv      := Recv,
                         server_sa := #{addr := Addr, port := Port}} = State) ->
                           case Recv(Sock) of
                               {ok, {#{addr := Addr, port := Port}, Data}} ->
                                   ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
                                   {ok, State#{read_pkg  => 1,
                                               read_byte => size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (recv 1)",
           cmd  => fun(#{sock      := Sock,
                         read_pkg  := RPkg,
                         read_byte := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,      RPkg},
                                      {read_byte,     RByte},
                                      {write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {read_tries,    any},
                                      {write_tries,   any},
                                      {read_pkg_max,  any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (recv_and_validate 1)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_and_validate),
                           ok
                   end},

         #{desc => "await continue (send_and_validate 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, send_and_validate)
                   end},
         #{desc => "send (2)",
           cmd  => fun(#{sock       := Sock,
                         send       := Send,
                         server_sa  := ServerSA,
                         write_pkg  := SPkg,
                         write_byte := SByte} = State) ->
                           Data = ?DATA,
                           case Send(Sock, Data, ServerSA) of
                               ok ->
                                   ?SEV_IPRINT("sent ~p bytes", [size(Data)]),
                                   {ok, State#{write_pkg  => SPkg + 1,
                                               write_byte => SByte + size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (send 2)",
           cmd  => fun(#{sock       := Sock,
                         read_pkg   := RPkg,
                         read_byte  := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,      RPkg},
                                      {read_byte,     RByte},
                                      {write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {read_tries,    any},
                                      {write_tries,   any},
                                      {read_pkg_max,  any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (send_and_validate 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, send_and_validate),
                           ok
                   end},

         #{desc => "await continue (recv_and_validate 2)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_CONTINUE(Tester, tester, recv_and_validate)
                   end},
         #{desc => "recv (2)",
           cmd  => fun(#{sock      := Sock,
                         server_sa := #{family := local} = ServerSA,
                         recv      := Recv,
                         read_pkg  := RPkg,
                         read_byte := RByte} = State) ->
                           case Recv(Sock) of
                               {ok, {ServerSA, Data}} ->
                                   ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
                                   {ok, State#{read_pkg  => RPkg + 1,
                                               read_byte => RByte + size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end;
		      (#{sock      := Sock,
                         server_sa := #{addr := Addr, port := Port},
                         recv      := Recv,
                         read_pkg  := RPkg,
                         read_byte := RByte} = State) ->
                           case Recv(Sock) of
                               {ok, {#{addr := Addr, port := Port}, Data}} ->
                                   ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
                                   {ok, State#{read_pkg  => RPkg + 1,
                                               read_byte => RByte + size(Data)}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "validate (recv 2)",
           cmd  => fun(#{sock      := Sock,
                         read_pkg  := RPkg,
                         read_byte := RByte,
                         write_pkg  := SPkg,
                         write_byte := SByte} = _State) ->
                           try socket:info(Sock) of
                               #{counters := Counters} ->
                                   ?SEV_IPRINT("validate counters: "
                                               "~s", [format_counters(Counters)]),
                                   traffic_sar_counters_validation(
                                     Counters,
                                     [{read_pkg,      RPkg},
                                      {read_byte,     RByte},
                                      {write_pkg,     SPkg},
                                      {write_byte,    SByte},
                                      {read_tries,    any},
                                      {write_tries,   any},
                                      {read_pkg_max,  any},
                                      {write_pkg_max, any}])
                           catch
                               C:E:S ->
                                   ?SEV_EPRINT("Failed get socket info: "
                                               "~n   Class: ~p"
                                               "~n   Error: ~p"
                                               "~n   Stack: ~p", [C, E, S]),
                                   {error, {socket_info_failed, {C, E, S}}}
                           end
                   end},
         #{desc => "announce ready (recv_and_validate 2)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, recv_and_validate),
                           ok
                   end},

         %% Termination
         #{desc => "await terminate (from tester)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close connection socket",
           cmd  => fun(#{domain   := local,
                         sock     := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(sock, State1)};
                      (#{sock := Sock} = State) ->
                           socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],


    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor client",
           cmd  => fun(#{client := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{domain := local,
                         server := Pid} = State) ->
                           {ok, Path} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{path => Path}};
                      (#{server := Pid} = State) ->
                           {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{port => Port}}
                   end},

         %% Start the client
         #{desc => "order client start",
           cmd  => fun(#{domain := local,
                         client := Pid,
                         path   := Path} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Path),
                           ok;
                      (#{client := Pid,
                         port   := Port} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Port),
                           ok
                   end},
         #{desc => "await client ready (init)",
           cmd  => fun(#{client := Pid} = _State) ->
                           ok = ?SEV_AWAIT_READY(Pid, client, init)
                   end},

         %% *** The actual test ***

         #{desc => "order server to continue (recv_and_validate 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, recv_and_validate),
                           ok
                   end},
         #{desc => "order client to continue (send_and_validate 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_and_validate),
                           ok
                   end},
         #{desc => "await client ready (send_and_validate 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_and_validate)
                   end},
         #{desc => "await server ready (recv_and_validate 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_and_validate)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "order client to continue (recv_and_validate 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, recv_and_validate),
                           ok
                   end},
         #{desc => "order server to continue (send_and_validate 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_and_validate),
                           ok
                   end},
         #{desc => "await server ready (send_and_validate 1)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_and_validate)
                   end},
         #{desc => "await client ready (recv_and_validate 1)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, recv_and_validate)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "order server to continue (recv_and_validate 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, recv_and_validate),
                           ok
                   end},
         #{desc => "order client to continue (send_and_validate 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, send_and_validate),
                           ok
                   end},
         #{desc => "await client ready (send_and_validate 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, send_and_validate)
                   end},
         #{desc => "await server ready (recv_and_validate 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, recv_and_validate)
                   end},

         ?SEV_SLEEP(?SECS(1)),

         #{desc => "order client to continue (recv_and_validate 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Client, recv_and_validate),
                           ok
                   end},
         #{desc => "order server to continue (send_and_validate 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Server, send_and_validate),
                           ok
                   end},
         #{desc => "await server ready (send_and_validate 2)",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_AWAIT_READY(Server, server, send_and_validate)
                   end},
         #{desc => "await client ready (recv_and_validate 2)",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_AWAIT_READY(Client, client, recv_and_validate)
                   end},

         %% *** Termination ***
         #{desc => "order client to terminate",
           cmd  => fun(#{client := Client} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Client),
                           ok
                   end},
         #{desc => "await client termination",
           cmd  => fun(#{client := Client} = State) ->
                           ?SEV_AWAIT_TERMINATION(Client),
                           State1 = maps:remove(client, State),
                           {ok, State1}
                   end},
         #{desc => "order server to terminate",
           cmd  => fun(#{server := Server} = _State) ->
                           ?SEV_ANNOUNCE_TERMINATE(Server),
                           ok
                   end},
         #{desc => "await server termination",
           cmd  => fun(#{server := Server} = State) ->
                           ?SEV_AWAIT_TERMINATION(Server),
                           State1 = maps:remove(server, State),
                           {ok, State1}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    i("start server evaluator"),
    ServerInitState = InitState#{host => local_host()},
    Server = ?SEV_START("server", ServerSeq, ServerInitState),

    i("start client evaluator(s)"),
    ClientInitState = InitState#{host => local_host()},
    Client = ?SEV_START("client", ClientSeq, ClientInitState),

    i("start 'tester' evaluator"),
    TesterInitState = #{server => Server#ev.pid,
                        client => Client#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator"),
    ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the send and recv functions
%% behave as expected when sending and/or reading chunks.
%% First send data in one "big" chunk, and read it in "small" chunks.
%% Second, send in a bunch of "small" chunks, and read in one "big" chunk.
%% Socket is IPv4.

traffic_send_and_recv_chunks_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(traffic_send_and_recv_chunks_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain => inet,
                                 proto  => tcp},
                   ok = traffic_send_and_recv_chunks_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the send and recv functions
%% behave as expected when sending and/or reading chunks.
%% First send data in one "big" chunk, and read it in "small" chunks.
%% Second, send in a bunch of "small" chunks, and read in one "big" chunk.
%% Socket is IPv6.

traffic_send_and_recv_chunks_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(traffic_send_and_recv_chunks_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain => inet6,
                                 proto  => tcp},
                   ok = traffic_send_and_recv_chunks_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the send and recv functions
%% behave as expected when sending and/or reading chunks.
%% First send data in one "big" chunk, and read it in "small" chunks.
%% Second, send in a bunch of "small" chunks, and read in one "big" chunk.
%% Socket is UNix Domain (Stream) socket.

traffic_send_and_recv_chunks_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(30)),
    tc_try(traffic_send_and_recv_chunks_tcp6,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   InitState = #{domain => local,
                                 proto  => default},
                   ok = traffic_send_and_recv_chunks_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain, proto := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain   := local,
                         lsock    := LSock,
                         local_sa := LSA} = _State) ->
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{lsock    := LSock,
                         local_sa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain   := local,
                         tester   := Tester,
                         local_sa := LSA}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, LSA),
                           ok;
                      (#{tester   := Tester,
                         local_sa := LSA,
                         lport    := Port}) ->
                           ServerSA = LSA#{port => Port},
                           ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
                           ok
                   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(#{domain   := local,
                         lsock    := Sock,
                         local_sa := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)};
                      (#{lsock := Sock} = State) ->
                           (catch socket:close(Sock)),
                           {ok, maps:remove(lsock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    ClientSeq =
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           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(State) ->
                           {Peer, Node} = ?START_NODE("client"),
                           {ok, State#{peer => Peer, node => Node}}
                   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,
                         proto     := Proto}) ->
                           ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}),
                           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(#{peer := Peer} = State) ->
                           {ok,
                            try peer:stop(Peer) of
                                ok ->
                                    State#{node_stop => ok};
                                {error, Reason} ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   ~p", [Reason]),
                                    State#{node_stop => error}
                            catch
                                C:E:S ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   Class: ~p"
                                                "~n   Error: ~p"
                                                "~n   Stack: ~p",[C, E, S]),
                                    State#{node_stop => error}
                            end}
                   end},
         #{desc => "await client node termination",
           cmd  => fun(#{node := Node, node_stop := ok} = State) ->
                           ?SEV_IPRINT("Success node stop - await nodedown"),
                           receive
                               {nodedown, Node} ->
                                   ?SEV_IPRINT("nodedown received - cleanup"),
                                   State1 = maps:remove(node, State),
                                   {ok, State1}
                           end;
                      (#{node_stop := error} = State) ->
                           ?SEV_IPRINT("Failed node stop - cleanup"),
                           State1 = maps:remove(node, State),
                           {ok, State1}
                   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, Path} = traffic_snr_tcp_client_init(Parent),
    traffic_snr_tcp_client_announce_ready(Parent, init),
    traffic_snr_tcp_client_await_continue(Parent, connect),
    traffic_snr_tcp_client_connect(Sock, ServerSA),
    traffic_snr_tcp_client_announce_ready(Parent, connect),
    traffic_snr_tcp_client_send_loop(Parent, Sock),
    Reason = traffic_snr_tcp_client_await_terminate(Parent),
    traffic_snr_tcp_client_close(Sock, Path),
    exit(Reason).


traffic_snr_tcp_client_send_loop(Parent, Sock) ->
    case ?SEV_AWAIT_CONTINUE(Parent, parent, send) of
        {ok, stop} -> % Breaks 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, Proto} = traffic_snr_tcp_client_await_start(Parent),
    Domain   = maps:get(family, ServerSA),
    Sock     = traffic_snr_tcp_client_create(Domain, Proto),
    Path     = traffic_snr_tcp_client_bind(Sock, Domain),
    {Sock, ServerSA, Path}.

traffic_snr_tcp_client_await_start(Parent) ->
    i("traffic_snr_tcp_client_await_start -> entry"),
    ?SEV_AWAIT_START(Parent).

traffic_snr_tcp_client_create(Domain, Proto) ->
    i("traffic_snr_tcp_client_create -> entry"),
    case socket:open(Domain, stream, Proto) 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"),
    LSA = which_local_socket_addr(Domain),
    case socket:bind(Sock, LSA) of
        ok ->
            case socket:sockname(Sock) of
                {ok, #{family := local, path := Path}} ->
                    Path;
                {ok, _} ->
                    undefined;
                {error, Reason1} ->
                    exit({sockname, Reason1})
            end;
        {error, Reason} ->
            exit({bind, Reason})
    end.

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, Path) ->
    i("traffic_snr_tcp_client_close -> entry"),
    case socket:close(Sock) of
        ok ->
            unlink_path(Path),
            ok;
        {error, Reason} ->
            ?SEV_EPRINT("failed closing: "
                        "~n   Reason: ~p", [Reason]),
            unlink_path(Path),
            {error, {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(Config) when is_list(Config) ->
    ?TT(?SECS(15)),
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_send_and_recv_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_send_and_recv_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the send and recv functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'small' message test case, for IPv6.

traffic_ping_pong_small_send_and_recv_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(15)),
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_send_and_recv_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain => inet6,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_send_and_recv_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the send and recv functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'small' message test case, for Unix Domain (stream) socket.

traffic_ping_pong_small_send_and_recv_tcpL(Config) when is_list(Config) ->
    ?TT(?SECS(15)),
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_send_and_recv_tcpL,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   InitState = #{domain => local,
                                 proto  => default,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_send_and_recv_tcp(InitState)
           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(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?TPP_MEDIUM_NUM),
    tc_try(traffic_ping_pong_medium_send_and_recv_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   ?TT(?SECS(30)),
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_send_and_recv_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the send and recv functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'medium' message test case, for IPv6.

traffic_ping_pong_medium_send_and_recv_tcp6(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?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,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_send_and_recv_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the send and recv functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'medium' message test case, for Unix Domain (stream) socket.

traffic_ping_pong_medium_send_and_recv_tcpL(Config) when is_list(Config) ->
    ?TT(?SECS(30)),
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?TPP_MEDIUM_NUM),
    tc_try(traffic_ping_pong_medium_send_and_recv_tcpL,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   InitState = #{domain => local,
                                 proto  => default,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_send_and_recv_tcp(InitState)
           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(Config) when is_list(Config) ->
    ?TT(?SECS(60)),
    Msg = l2b(?TPP_LARGE),
    Num = ?TPP_NUM(Config, ?TPP_LARGE_NUM),
    tc_try(traffic_ping_pong_large_send_and_recv_tcp4,
           fun() -> has_support_ipv4(),
                    is_old_fedora16(),
		    is_slow_ubuntu(Config) end,
           fun() ->
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_send_and_recv_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the send and recv functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'large' message test case, for IPv6.

traffic_ping_pong_large_send_and_recv_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(60)),
    Msg = l2b(?TPP_LARGE),
    Num = ?TPP_NUM(Config, ?TPP_LARGE_NUM),
    tc_try(traffic_ping_pong_large_send_and_recv_tcp6,
           fun() -> is_old_fedora16(),
                    has_support_ipv6(),
		    is_slow_ubuntu(Config) end,
           fun() ->
                   InitState = #{domain => inet6,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_send_and_recv_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the send and recv functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'large' message test case, for UNix Domain (stream) socket.

traffic_ping_pong_large_send_and_recv_tcpL(Config) when is_list(Config) ->
    ?TT(?SECS(60)),
    Msg = l2b(?TPP_LARGE),
    Num = ?TPP_NUM(Config, ?TPP_LARGE_NUM),
    tc_try(traffic_ping_pong_large_send_and_recv_tcpL,
           fun() ->
                   has_support_unix_domain_socket(),
                   traffic_ping_pong_large_host_cond()
           end,
           fun() ->
                   InitState = #{domain => local,
                                 proto  => default,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_send_and_recv_tcp(InitState)
           end).

%% This test case is a bit extreme and fails on some hosts
%% (e.g. OpenIndiana Hipster), so exclude them.
traffic_ping_pong_large_host_cond() ->
    traffic_ping_pong_large_host_cond(os:type(), os:version()).

traffic_ping_pong_large_host_cond({unix, sunos}, _) ->
    skip("TC does not work on platform");
traffic_ping_pong_large_host_cond({unix, linux}, _) ->
    traffic_ping_pong_large_host_cond2(string:trim(os:cmd("cat /etc/issue")));
traffic_ping_pong_large_host_cond(_, _) ->
    ok.

traffic_ping_pong_large_host_cond2("Welcome to SUSE Linux Enterprise Server 10 SP1 (i586)" ++ _) ->
    skip("TC does not work on platform");
traffic_ping_pong_large_host_cond2("Fedora release 16 " ++ _) ->
    skip("Very slow VM");
traffic_ping_pong_large_host_cond2(_) ->
    ok.


etc_issue() ->
    string:trim(os:cmd("cat /etc/issue")).

is_old_fedora16() ->
    is_old_fedora16( etc_issue() ).

%% We actually only have one host running this, a slow VM.
is_old_fedora16("Fedora release 16 " ++ _) ->
    skip("Very slow VM");
is_old_fedora16(_) ->
    ok.


%% This is a bit subjective, but...
%% ..we have some WMs that is not "fast enough", but the only
%% thing we can test on is 'esock factor' (other than host name).
%% This means we actually skip this on platforms where its
%% not actually needed.
%% The host in question is a Ubuntu 20.04...
is_slow_ubuntu(Config) ->
    case lookup(kernel_factor, 1, Config) of
	F when is_integer(F) andalso (F > 1) ->
	    case os:type() of
		{unix, linux} ->
		    case etc_issue() of
			"Ubuntu 20.04" ++ _ ->
			    skip("Slow Ubuntu host");
			_ ->
			    ok
		    end;
		_ ->
		    ok
	    end;
	_ ->
	    ok
    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(Config) when is_list(Config) ->
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   ?TT(?SECS(45)),
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendto and recvfrom 
%% functions by repeatedly sending a meassage between two entities.
%% The same basic test case is used for two different message sizes; 
%% small (8 bytes) and medium (8K).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'small' message test case, for IPv6.

traffic_ping_pong_small_sendto_and_recvfrom_udp6(Config) when is_list(Config) ->
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   ?TT(?SECS(45)),
                   InitState = #{domain => inet6,
                                 proto  => udp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendto and recvfrom 
%% functions by repeatedly sending a meassage between two entities.
%% The same basic test case is used for two different message sizes; 
%% small (8 bytes) and medium (8K).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'small' message test case, for Unix Domain (dgram) socket.

traffic_ping_pong_small_sendto_and_recvfrom_udpL(Config) when is_list(Config) ->
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udpL,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   ?TT(?SECS(45)),
                   InitState = #{domain => local,
                                 proto  => default,
                                 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(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?TPP_MEDIUM_NUM),
    tc_try(traffic_ping_pong_medium_sendto_and_recvfrom_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   ?TT(?SECS(45)),
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendto and recvfrom 
%% functions by repeatedly sending a meassage between two entities.
%% The same basic test case is used for two different message sizes; 
%% small (8 bytes) and medium (8K).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'medium' message test case, for IPv6.

traffic_ping_pong_medium_sendto_and_recvfrom_udp6(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?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,
                                 proto  => udp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendto and recvfrom 
%% functions by repeatedly sending a meassage between two entities.
%% The same basic test case is used for two different message sizes; 
%% small (8 bytes) and medium (8K).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'medium' message test case, for Unix Domain (dgram) socket.

traffic_ping_pong_medium_sendto_and_recvfrom_udpL(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?TPP_MEDIUM_NUM),
    tc_try(traffic_ping_pong_medium_sendto_and_recvfrom_udpL,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   ?TT(?SECS(45)),
                   InitState = #{domain => local,
                                 proto  => default,
                                 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(Config) when is_list(Config) ->
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4,
           fun() ->
                   is_not_windows(),
                   has_support_ipv4()
           end,
           fun() ->
                   ?TT(?SECS(20)),
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendmsg and recvmsg functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'small' message test case, for IPv6.

traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6(Config) when is_list(Config) ->
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6,
           fun() ->
                   is_not_windows(),
                   has_support_ipv6()
           end,
           fun() ->
                   ?TT(?SECS(20)),
                   InitState = #{domain => inet6,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendmsg and recvmsg functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'small' message test case, for Unix Domain (stream) socket.

traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL(Config) when is_list(Config) ->
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL,
           fun() ->
                   is_not_windows(),
		   has_support_unix_domain_socket()
	   end,
           fun() ->
                   ?TT(?SECS(20)),
                   InitState = #{domain => local,
                                 proto  => default,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState)
           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(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?TPP_MEDIUM_NUM),
    tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4,
           fun() ->
                   is_not_windows(),
                   has_support_ipv4()
           end,
           fun() ->
                   ?TT(?SECS(30)),
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendmsg and recvmsg functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'medium' message test case, for IPv6.

traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?TPP_MEDIUM_NUM),
    tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6,
           fun() ->
                   is_not_windows(),
                   has_support_ipv6()
           end,
           fun() ->
                   ?TT(?SECS(30)),
                   InitState = #{domain => inet6,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendmsg and recvmsg functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'medium' message test case, for Unix Domain (stream) socket.

traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?TPP_MEDIUM_NUM),
    tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL,
           fun() ->
                   is_not_windows(),
		   has_support_unix_domain_socket()
	   end,
           fun() ->
                   ?TT(?SECS(30)),
                   InitState = #{domain => local,
                                 proto  => default,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState)
           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(Config) when is_list(Config) ->
    Msg = l2b(?TPP_LARGE),
    Num = ?TPP_NUM(Config, ?TPP_LARGE_NUM),
    tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4,
           fun() ->
                   is_not_windows(),
                   has_support_ipv4(),
                   traffic_ping_pong_large_sendmsg_and_recvmsg_cond()
           end,
           fun() ->
                   ?TT(?SECS(60)),
                   InitState = #{domain => inet,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState)
           end).


traffic_ping_pong_large_sendmsg_and_recvmsg_cond() ->
    traffic_ping_pong_large_sendmsg_and_recvmsg_cond(os:type(), os:version()).

traffic_ping_pong_large_sendmsg_and_recvmsg_cond({unix, linux}, {M, _, _})
  when (M < 3) ->
    skip("TC may not work on this version");
traffic_ping_pong_large_sendmsg_and_recvmsg_cond(_, _) ->
    ok.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendmsg and recvmsg functions
%% by repeatedly sending a meassage between two entities.
%% 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(Config) when is_list(Config) ->
    Msg = l2b(?TPP_LARGE),
    Num = ?TPP_NUM(Config, ?TPP_LARGE_NUM),
    tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6,
           fun() ->
                   is_not_windows(),
                   has_support_ipv6(),
                   traffic_ping_pong_large_sendmsg_and_recvmsg_cond()
           end,
           fun() ->
                   ?TT(?SECS(60)),
                   InitState = #{domain => inet6,
                                 proto  => tcp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendmsg and recvmsg functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes), medium (8K) and large (8M).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'large' message test case, for Unix Domain (stream) socket.

traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(Config) when is_list(Config) ->
    Msg = l2b(?TPP_LARGE),
    Num = ?TPP_NUM(Config, ?TPP_LARGE_NUM),
    tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL,
           fun() ->
                   is_not_windows(),
		   has_support_unix_domain_socket()
	   end,
           fun() ->
                   ?TT(?SECS(60)),
                   InitState = #{domain => local,
                                 proto  => default,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState)
           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(Config) when is_list(Config) ->
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   ?TT(?SECS(60)),
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendmsg and recvmsg functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes) and medium (8K).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'small' message test case, for IPv6.

traffic_ping_pong_small_sendmsg_and_recvmsg_udp6(Config) when is_list(Config) ->
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   ?TT(?SECS(60)),
                   InitState = #{domain => inet6,
                                 proto  => udp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendmsg and recvmsg functions
%% by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes) and medium (8K).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'small' message test case, for Unix Domain (dgram) socket.

traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(Config) when is_list(Config) ->
    Msg = l2b(?TPP_SMALL),
    Num = ?TPP_NUM(Config, ?TPP_SMALL_NUM),
    tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udpL,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   ?TT(?SECS(60)),
                   InitState = #{domain => local,
                                 proto  => default,
                                 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(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?TPP_MEDIUM_NUM),
    tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   ?TT(?SECS(60)),
                   InitState = #{domain => inet,
                                 proto  => udp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendmsg and recvmsg 
%% functions by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes) and medium (8K).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'medium' message test case, for IPv6.

traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?TPP_MEDIUM_NUM),
    tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   ?TT(?SECS(60)),
                   InitState = #{domain => inet6,
                                 proto  => udp,
                                 msg    => Msg,
                                 num    => Num},
                   ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState)
           end).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendmsg and recvmsg 
%% functions by repeatedly sending a meassage between two entities.
%% The same basic test case is used for three different message sizes; 
%% small (8 bytes) and medium (8K).
%% The message is sent from A to B and then back again. This is 
%% repeated a set number of times (more times the small the message).
%% This is the 'medium' message test case, for Unix Domain (dgram) socket.

traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(Config) when is_list(Config) ->
    Msg = l2b(?TPP_MEDIUM),
    Num = ?TPP_NUM(Config, ?TPP_MEDIUM_NUM),
    tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL,
           fun() ->
		   has_support_unix_domain_socket(),
		   is_not_windows()
	   end,
           fun() ->
                   ?TT(?SECS(60)),
                   InitState = #{domain => local,
                                 proto  => default,
                                 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(#{domain := local} = InitState) ->
    Recv = fun(Sock, Sz)   -> 
                   case socket:recvmsg(Sock, Sz, 0) of
                       %% On some platforms, the address
                       %% *is* provided (e.g. linux)
                       {ok, #{addr  := #{family := local},
                              iov   := [Data]}} ->
                           {ok, Data};
                       {ok, #{addr := _} = Msg} ->
                           {error, {msg, Msg}};
                       %% On some platforms, the address
                       %% is *not* provided (e.g. FreeBSD)
                       {ok, #{iov   := [Data]}} ->
                           {ok, Data};
                       {ok, Msg} ->
                           {error, {msg, Msg}};
                       {error, _} = ERROR ->
                           ERROR
                   end
           end,
    InitState2 = InitState#{recv => Recv},  % Receive function
    traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState2);
traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) ->
    Recv = fun(Sock, Sz)   -> 
                   case socket:recvmsg(Sock, Sz, 0) of
                       {ok, #{iov   := [Data]}} ->
                           {ok, Data};
                       {ok, Msg} ->
                           {error, {msg, Msg}};
                       {error, _} = ERROR ->
                           ERROR
                   end
           end,
    InitState2 = InitState#{recv => Recv},  % Receive function
    traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState2).

traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState) ->
    Send = fun(Sock, Data) when is_binary(Data) ->
                   Msg = #{iov => [Data]},
                   socket:sendmsg(Sock, Msg);
              (Sock, Data) when is_list(Data) -> %% We assume iovec...
                   Msg = #{iov => Data},
                   socket:sendmsg(Sock, Msg)
           end,
    InitState2 = InitState#{send => Send}, % Send 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 at least ~p)", 
			      [RcvSz, 16+size(Msg)]),
                  if (RcvSz < size(Msg)) ->
                          NewRcvSz = 1024+size(Msg),
                          case socket:setopt(Sock, socket, rcvbuf, NewRcvSz) of
			      ok ->
				  ok;
			      {error, enobufs} ->
				  skip(?F("Change ~w buffer size (to ~w) "
                                          "not allowed", 
                                          [rcvbuf, NewRcvSz]));
			      {error, Reason1} ->
				  ?FAIL({rcvbuf, Reason1})
			  end;
                     true ->
                          ok
                  end,
                  {ok, SndSz} = socket:getopt(Sock, socket, sndbuf),
		  ?SEV_IPRINT("SndBuf is ~p (needs at least ~p)", 
			      [SndSz, 16+size(Msg)]),
                  if (SndSz < size(Msg)) ->
                          NewSndSz = 1024+size(Msg),
                          case socket:setopt(Sock, socket, sndbuf, NewSndSz) of
			      ok ->
				  ok;
			      {error, enobufs} ->
				  skip(?F("Change ~w buffer size (to ~w) "
                                          "not allowed", 
                                          [sndbuf, NewSndSz]));
			      {error, Reason2} ->
				  ?FAIL({sndbuf, Reason2})
			  end;
                     true ->
                          ok
                  end,
                  case os:type() of
                      {win32, nt} ->
                          ok = socket:setopt(Sock, otp, rcvbuf, 12*1024);
                      _ ->
                          ok = socket:setopt(Sock, otp, rcvbuf, {12, 1024})
                  end
          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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create listen socket",
           cmd  => fun(#{domain := Domain, proto := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain := local,
                         lsock  := LSock,
                         lsa    := LSA} = _State) ->
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{lsock := LSock, local_sa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   ?SEV_IPRINT("bound to port: ~w", [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(#{domain := local,
                         tester := Tester, local_sa := LSA}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, LSA),
                           ok;
                      (#{tester := Tester, local_sa := LSA, lport := Port}) ->
                           ServerSA = LSA#{port => Port},
                           ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
                           ok
                   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(#{domain   := local,
                         lsock    := Sock,
                         local_sa := #{path := Path}} = State) ->
                           (catch socket:close(Sock)),
                           State1 =
                               unlink_path(Path,
                                           fun() -> 
                                                   maps:remove(local_sa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)};
                      (#{lsock := Sock} = State) ->
                           (catch socket:close(Sock)),
                           {ok, maps:remove(lsock, State)}
                   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(State) ->
                           {Peer, Node} = ?START_NODE("client"),
                           {ok, State#{peer => Peer, node => Node}}
                   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,
                         proto     := Proto,
                         server_sa := ServerSA,
                         buf_init  := BufInit,
                         send      := Send,
                         recv      := Recv}) ->
                           ?SEV_ANNOUNCE_START(RClient, 
                                               {ServerSA, Proto, BufInit,
                                                Send, Recv}),
                           ok
                   end},
         #{desc => "await remote client ready",
           cmd  => fun(#{tester  := Tester,
                         rclient := RClient} = _State) ->
                           case ?SEV_AWAIT_READY(RClient, rclient, init, 
                                                 [{tester, Tester}]) of
                               ok ->
                                   ?SEV_IPRINT("remote client started"),
                                   ok;
                               {error, {unexpected_exit, _, {bind, eaddrnotavail = Reason}}} ->
                                   ?SEV_IPRINT("remote client bind failure:"
                                               "~n   ~p", [Reason]),
                                   {skip, Reason};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("remote client failure:"
                                               "~n   ~p", [Reason]),
                                   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,
                         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} ->
                                   {ok, State#{result => Result}};
                               {error,
                                {unexpected_exit, rclient, noconnection}} ->
                                   %% One guess is that the message is so
                                   %% "large" that the client node died on us.
                                   %% Or so "large" that the connection (to
                                   %% the node) fails/dies.
                                   %% Either way, we assume this is not actually
                                   %% related to what we are testing => skip
                                   ?SEV_IPRINT("lost connection "
                                               "to remote client node => SKIP"),
                                   {skip, {rclient, noconnection}};
                               {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(#{peer := Peer} = State) ->
                           {ok,
                            try peer:stop(Peer) of
                                ok ->
                                    State#{node_stop => ok};
                                {error, Reason} ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   ~p", [Reason]),
                                    State#{node_stop => error}
                            catch
                                C:E:S ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   Class: ~p"
                                                "~n   Error: ~p"
                                                "~n   Stack: ~p",[C, E, S]),
                                    State#{node_stop => error}
                            end}
                   end},
         #{desc => "await client node termination",
           cmd  => fun(#{node := Node, node_stop := ok} = State) ->
                           ?SEV_IPRINT("Success node stop - await nodedown"),
                           receive
                               {nodedown, Node} ->
                                   ?SEV_IPRINT("nodedown received - cleanup"),
                                   State1 = maps:remove(node, State),
                                   {ok, State1}
                           end;
                      (#{node_stop := error} = State) ->
                           ?SEV_IPRINT("Failed node stop - cleanup"),
                           State1 = maps:remove(node, State),
                           {ok, State1}
                   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),
                           ?SEV_IPRINT("process result data:"
                                       "~n   Num:          ~p"
                                       "~n   Server Sent:  ~p"
                                       "~n   Server Recv:  ~p"
                                       "~n   Server Start: ~p"
                                       "~n   Server Stop:  ~p"
                                       "~n   Server Time:  ~p"
                                       "~n   Client Sent:  ~p"
                                       "~n   Client Recv:  ~p"
                                       "~n   Client Start: ~p"
                                       "~n   Client Stop:  ~p"
                                       "~n   Client Time:  ~p",
                                       [Num,
                                        SSent, SReceived, SStart, SStop,
                                        STime,
                                        CSent, CReceived, CStart, CStop,
                                        CTime]),
                           if
                               (STime =:= 0) orelse
                               (CTime =:= 0) ->
                                   {skip,
                                    ?F("Invalid exec time(s): ~w , ~w",
                                       [STime, CTime])};
                               true ->
                                   %% 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 course 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
                   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),
                        proto    => maps:get(proto,    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, 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, Proto, BufInit, Send, Recv} = tpp_tcp_client_await_start(Parent),
    Domain   = maps:get(family, ServerSA),
    Sock     = tpp_tcp_client_sock_open(Domain, Proto, BufInit),
    Path     = tpp_tcp_client_sock_bind(Sock, Domain),
    tpp_tcp_client_announce_ready(Parent, init),
    tpp_tcp_client_await_continue(Parent, connect),
    tpp_tcp_client_sock_connect(Sock, ServerSA),
    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, Path),
    ?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 ->
            ?SEV_IPRINT("termination received: normal"),
            normal;
        {error, Reason} ->
            ?SEV_IPRINT("termination received: ~w", [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(),
    Info = socket:info(Sock),
    case socket:close(Sock) of
        ok ->
            {Sent, Received, Info, 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, Proto, BufInit) ->
    case socket:open(Domain, stream, Proto) of
        {ok, Sock} ->
            ok = BufInit(Sock),
            Sock;
        {error, Reason} ->
            exit({open_failed, Reason})
    end.

tpp_tcp_client_sock_bind(Sock, Domain) ->
    LSA = which_local_socket_addr(Domain),
    case socket:bind(Sock, LSA) of
        ok ->
            case socket:sockname(Sock) of
                {ok, #{family := local, path := Path}} ->
                    Path;
                {ok, _} ->
                    undefined;
                {error, Reason1} ->
                    exit({sockname, Reason1})
            end;
        {error, Reason2} ->
            exit({bind, Reason2})
    end.

tpp_tcp_client_sock_connect(Sock, ServerSA) ->
    case socket:connect(Sock, ServerSA) of
        ok ->
            ok;
        {error, Reason} ->
            exit({connect, Reason})
    end.

tpp_tcp_client_sock_close(Sock, Path) ->
    case socket:close(Sock) of
        ok ->
            unlink_path(Path),
            ok;
        {error, closed} ->
            ok;
        {error, Reason} ->
            ?SEV_EPRINT("failed closing: "
                        "~n   Reason: ~p", [Reason]),
            unlink_path(Path),
            {error, {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)).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% 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) ->
                   Msg = #{addr => Dest, iov => [Data]},
                   socket:sendmsg(Sock, Msg);
              (Sock, Data, Dest) when is_list(Data) -> %% We assume iovec...
                   Msg = #{addr => Dest, iov => Data},
                   socket:sendmsg(Sock, Msg)
           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) ->
                           LSA = which_local_socket_addr(Domain),
                           {ok, State#{local_sa => LSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain := Domain, proto := Proto} = State) ->
                           case socket:open(Domain, dgram, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain := local,
                         sock := Sock, local_sa := LSA} = _State) ->
                           case socket:bind(Sock, LSA) of
                               ok ->
                                   ok;
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{sock := Sock, local_sa := LSA} = State) ->
                           case sock_bind(Sock, LSA) of
                               ok ->
                                   Port = sock_port(Sock),
                                   {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(#{domain := local,
                         tester := Tester, local_sa := LSA}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, LSA),
                           ok;
                      (#{tester := Tester, local_sa := LSA, port := Port}) ->
                           ServerSA = LSA#{port => Port},
                           ?SEV_ANNOUNCE_READY(Tester, init, ServerSA),
                           ok
                   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 => "(maybe) unlink socket",
           cmd  => fun(#{domain   := local,
                         local_sa := #{path := Path}} = State) ->
                           unlink_path(Path,
                                       fun() ->
                                               {ok, maps:remove(local_sa, State)}
                                       end,
                                       fun() ->
                                               ok
                                       end);
                      (_) ->
                           ok
                   end},
         #{desc => "announce ready (close)",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_ANNOUNCE_READY(Tester, close),
                           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(State) ->
                           {Peer, Node} = ?START_NODE("client"),
                           {ok, State#{peer => Peer, node => Node}}
                   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,
                         proto     := Proto,
                         buf_init  := BufInit,
                         send      := Send,
                         recv      := Recv}) ->
                           ?SEV_ANNOUNCE_START(Handler, 
                                               {ServerSA, Proto, 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(#{peer := Peer} = State) ->
                           {ok,
                            try peer:stop(Peer) of
                                ok ->
                                    State#{node_stop => ok};
                                {error, Reason} ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   ~p", [Reason]),
                                    State#{node_stop => error}
                            catch
                                C:E:S ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   Class: ~p"
                                                "~n   Error: ~p"
                                                "~n   Stack: ~p",[C, E, S]),
                                    State#{node_stop => error}
                            end}
                   end},
         #{desc => "await client node termination",
           cmd  => fun(#{node := Node} = State) ->
                           ?SEV_IPRINT("Success node stop - await nodedown"),
                           receive
                               {nodedown, Node} ->
                                   ?SEV_IPRINT("nodedown received - cleanup"),
                                   State1 = maps:remove(node, State),
                                   {ok, State1}
                           end;
                      (#{node_stop := error} = State) ->
                           ?SEV_IPRINT("Failed node stop - cleanup"),
                           State1 = maps:remove(node, State),
                           {ok, State1}
                   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),
                           if
                               (CTime =:= 0) ->
                                   {skip,
                                    ?F("Invalid exec time: ~w ", [CTime])};
                               true ->
                                   %% 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 course 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
                   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),
                        proto    => maps:get(proto,    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, Proto, BufInit, Send, Recv} = tpp_udp_handler_await_start(Parent),
    ?SEV_IPRINT("start command with"
                "~n   ServerSA: ~p", [ServerSA]),
    Domain   = maps:get(family, ServerSA),
    Sock     = tpp_udp_sock_open(Domain, Proto, BufInit),
    Path     = tpp_udp_sock_bind(Sock, Domain),
    ?SEV_IPRINT("announce ready", []),
    tpp_udp_handler_announce_ready(Parent, init),
    {InitMsg, Num} = tpp_udp_handler_await_continue(Parent, send),
    ?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, Path),
    ?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,
					 #{family := local} = 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_client_handler_msg_exchange_loop(Sock,
					 #{addr := Addr, port := Port} = Dest0,
					 Send, Recv, Data,
                                         Num, N, Sent, Received, Start) ->
    case tpp_udp_send_req(Sock, Send, Data, Dest0) of
        {ok, SendSz} ->
            case tpp_udp_recv_rep(Sock, Recv) of
                {ok, NewData, RecvSz, #{addr := Addr, port := Port} = Dest1} ->
                    tpp_udp_client_handler_msg_exchange_loop(Sock, Dest1,
                                                             Send, Recv,
                                                             NewData, Num, N+1,
                                                             Sent+SendSz, 
                                                             Received+RecvSz, 
                                                             Start);
                {error, RReason} ->
                    ?SEV_EPRINT("recv (~w of ~w): ~p", [N, Num, RReason]),
                    exit({recv, RReason, N})
            end;
        {error, SReason} ->
            ?SEV_EPRINT("send (~w of ~w): ~p", [N, Num, SReason]),
            exit({send, SReason, N})
    end.


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, {caught, 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, Proto, BufInit) ->
    case socket:open(Domain, dgram, Proto) of
        {ok, Sock} ->
            ok = BufInit(Sock),
            Sock;
        {error, Reason} ->
            exit({open_failed, Reason})
    end.

tpp_udp_sock_bind(Sock, Domain) ->
    LSA = which_local_socket_addr(Domain),
    case socket:bind(Sock, LSA) of
        ok ->
            ok;
        {error, Reason} ->
            exit({bind, Reason})
    end.

tpp_udp_sock_close(Sock, Path) ->
    case socket:close(Sock) of
        ok ->
            unlink_path(Path),
            ok;
        {error, Reason} ->
            ?SEV_EPRINT("Failed closing socket: "
                        "~n   ~p", [Reason]),
            unlink_path(Path),
            {error, {close, Reason}}
    end.



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                           TIME TEST                                 %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime        = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgenf_small_tcp4,
              Runtime,
              inet,
              gen, false,
              gen, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgenf_small_tcp6,
              Runtime,
              inet6,
              gen, false,
              gen, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgenf_medium_tcp4,
              Runtime,
              inet,
              gen, false,
              gen, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgenf_medium_tcp6,
              Runtime,
              inet6,
              gen, false,
              gen, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgenf_large_tcp4,
              Runtime,
              inet,
              gen, false,
              gen, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgenf_large_tcp6,
              Runtime,
              inet6,
              gen, false,
              gen, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgeno_small_tcp4,
              Runtime,
              inet,
              gen, false,
              gen, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgeno_small_tcp6,
              Runtime,
              inet6,
              gen, false,
              gen, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgeno_medium_tcp4,
              Runtime,
              inet,
              gen, false,
              gen, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgeno_medium_tcp6,
              Runtime,
              inet6,
              gen, false,
              gen, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgeno_large_tcp4,
              Runtime,
              inet,
              gen, false,
              gen, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgeno_large_tcp6,
              Runtime,
              inet6,
              gen, false,
              gen, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgent_small_tcp4,
              Runtime,
              inet,
              gen, false,
              gen, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgeno_small_tcp6,
              Runtime,
              inet6,
              gen, false,
              gen, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgent_medium_tcp4,
              Runtime,
              inet,
              gen, false,
              gen, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgent_medium_tcp6,
              Runtime,
              inet6,
              gen, false,
              gen, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgent_large_tcp4,
              Runtime,
              inet,
              gen, false,
              gen, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_cgent_large_tcp6,
              Runtime,
              inet6,
              gen, false,
              gen, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockf_small_tcp4,
              Runtime,
              inet,
              gen, false,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockf_small_tcp6,
              Runtime,
              inet6,
              gen, false,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockf_medium_tcp4,
              Runtime,
              inet,
              gen, false,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockf_medium_tcp6,
              Runtime,
              inet6,
              gen, false,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockf_large_tcp4,
              Runtime,
              inet,
              gen, false,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockf_large_tcp6,
              Runtime,
              inet6,
              gen, false,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = gen_tcp(socket), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       inet
%%

ttest_sgsf_csockf_small_tcp4(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet,
              gs, false,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = gen_tcp(socket), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       inet6
%% 

ttest_sgsf_csockf_small_tcp6(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet6,
              gs, false,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = gen_tcp(socket), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       inet
%%

ttest_sgsf_csockf_medium_tcp4(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet,
              gs, false,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = gen_tcp(socket), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

ttest_sgsf_csockf_medium_tcp6(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet6,
              gs, false,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = gen_tcp(socket), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       inet
%%

ttest_sgsf_csockf_large_tcp4(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet,
              gs, false,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = gen_tcp(socket), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       inet6
%% 

ttest_sgsf_csockf_large_tcp6(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet6,
              gs, false,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csocko_small_tcp4,
              Runtime,
              inet,
              gen, false,
              sock, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csocko_small_tcp6,
              Runtime,
              inet6,
              gen, false,
              sock, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csocko_medium_tcp4,
              Runtime,
              inet,
              gen, false,
              sock, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csocko_medium_tcp6,
              Runtime,
              inet6,
              gen, false,
              sock, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csocko_large_tcp4,
              Runtime,
              inet,
              gen, false,
              sock, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csocko_large_tcp6,
              Runtime,
              inet6,
              gen, false,
              sock, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockt_small_tcp4,
              Runtime,
              inet,
              gen, false,
              sock, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csocko_small_tcp6,
              Runtime,
              inet6,
              gen, false,
              sock, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockt_medium_tcp4,
              Runtime,
              inet,
              gen, false,
              sock, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockt_medium_tcp6,
              Runtime,
              inet6,
              gen, false,
              sock, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockt_large_tcp4,
              Runtime,
              inet,
              gen, false,
              sock, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgenf_csockt_large_tcp6,
              Runtime,
              inet6,
              gen, false,
              sock, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgenf_small_tcp4,
              Runtime,
              inet,
              gen, once,
              gen, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgenf_small_tcp6,
              Runtime,
              inet6,
              gen, once,
              gen, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgenf_medium_tcp4,
              Runtime,
              inet,
              gen, once,
              gen, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgenf_medium_tcp6,
              Runtime,
              inet6,
              gen, once,
              gen, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgenf_large_tcp4,
              Runtime,
              inet,
              gen, once,
              gen, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgenf_large_tcp6,
              Runtime,
              inet6,
              gen, once,
              gen, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgeno_small_tcp4,
              Runtime,
              inet,
              gen, once,
              gen, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgeno_small_tcp6,
              Runtime,
              inet6,
              gen, once,
              gen, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgeno_medium_tcp4,
              Runtime,
              inet,
              gen, once,
              gen, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgeno_medium_tcp6,
              Runtime,
              inet6,
              gen, once,
              gen, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgeno_large_tcp4,
              Runtime,
              inet,
              gen, once,
              gen, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgeno_large_tcp6,
              Runtime,
              inet6,
              gen, once,
              gen, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgent_small_tcp4,
              Runtime,
              inet,
              gen, once,
              gen, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgeno_small_tcp6,
              Runtime,
              inet6,
              gen, once,
              gen, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgent_medium_tcp4,
              Runtime,
              inet,
              gen, once,
              gen, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgent_medium_tcp6,
              Runtime,
              inet6,
              gen, once,
              gen, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgent_large_tcp4,
              Runtime,
              inet,
              gen, once,
              gen, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_cgent_large_tcp6,
              Runtime,
              inet6,
              gen, once,
              gen, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockf_small_tcp4,
              Runtime,
              inet,
              gen, once,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockf_small_tcp6,
              Runtime,
              inet6,
              gen, once,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockf_medium_tcp4,
              Runtime,
              inet,
              gen, once,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockf_medium_tcp6,
              Runtime,
              inet6,
              gen, once,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockf_large_tcp4,
              Runtime,
              inet,
              gen, once,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockf_large_tcp6,
              Runtime,
              inet6,
              gen, once,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csocko_small_tcp4,
              Runtime,
              inet,
              gen, once,
              sock, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csocko_small_tcp6,
              Runtime,
              inet6,
              gen, once,
              sock, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csocko_medium_tcp4,
              Runtime,
              inet,
              gen, once,
              sock, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csocko_medium_tcp6,
              Runtime,
              inet6,
              gen, once,
              sock, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csocko_large_tcp4,
              Runtime,
              inet,
              gen, once,
              sock, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csocko_large_tcp6,
              Runtime,
              inet6,
              gen, once,
              sock, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockt_small_tcp4,
              Runtime,
              inet,
              gen, once,
              sock, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csocko_small_tcp6,
              Runtime,
              inet6,
              gen, once,
              sock, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockt_medium_tcp4,
              Runtime,
              inet,
              gen, once,
              sock, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockt_medium_tcp6,
              Runtime,
              inet6,
              gen, once,
              sock, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockt_large_tcp4,
              Runtime,
              inet,
              gen, once,
              sock, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgeno_csockt_large_tcp6,
              Runtime,
              inet6,
              gen, once,
              sock, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgenf_small_tcp4,
              Runtime,
              inet,
              gen, true,
              gen, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgenf_small_tcp6,
              Runtime,
              inet6,
              gen, true,
              gen, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgenf_medium_tcp4,
              Runtime,
              inet,
              gen, true,
              gen, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgenf_medium_tcp6,
              Runtime,
              inet6,
              gen, true,
              gen, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgenf_large_tcp4,
              Runtime,
              inet,
              gen, true,
              gen, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgenf_large_tcp6,
              Runtime,
              inet6,
              gen, true,
              gen, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgeno_small_tcp4,
              Runtime,
              inet,
              gen, true,
              gen, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgeno_small_tcp6,
              Runtime,
              inet6,
              gen, true,
              gen, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgeno_medium_tcp4,
              Runtime,
              inet,
              gen, true,
              gen, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgeno_medium_tcp6,
              Runtime,
              inet6,
              gen, true,
              gen, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgeno_large_tcp4,
              Runtime,
              inet,
              gen, true,
              gen, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgeno_large_tcp6,
              Runtime,
              inet6,
              gen, true,
              gen, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgent_small_tcp4,
              Runtime,
              inet,
              gen, true,
              gen, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_cgeno_small_tcp6,
              Runtime,
              inet6,
              gen, true,
              gen, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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() ->
    [{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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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() ->
    [{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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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() ->
    [{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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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() ->
    [{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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockf_small_tcp4,
              Runtime,
              inet,
              gen, true,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockf_small_tcp6,
              Runtime,
              inet6,
              gen, true,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockf_medium_tcp4,
              Runtime,
              inet,
              gen, true,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockf_medium_tcp6,
              Runtime,
              inet6,
              gen, true,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockf_large_tcp4,
              Runtime,
              inet,
              gen, true,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockf_large_tcp6,
              Runtime,
              inet6,
              gen, true,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csocko_small_tcp4,
              Runtime,
              inet,
              gen, true,
              sock, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csocko_small_tcp6,
              Runtime,
              inet6,
              gen, true,
              sock, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csocko_medium_tcp4,
              Runtime,
              inet,
              gen, true,
              sock, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csocko_medium_tcp6,
              Runtime,
              inet6,
              gen, true,
              sock, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csocko_large_tcp4,
              Runtime,
              inet,
              gen, true,
              sock, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csocko_large_tcp6,
              Runtime,
              inet6,
              gen, true,
              sock, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockt_small_tcp4,
              Runtime,
              inet,
              gen, true,
              sock, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csocko_small_tcp6,
              Runtime,
              inet6,
              gen, true,
              sock, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockt_medium_tcp4,
              Runtime,
              inet,
              gen, true,
              sock, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockt_medium_tcp6,
              Runtime,
              inet6,
              gen, true,
              sock, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockt_large_tcp4,
              Runtime,
              inet,
              gen, true,
              sock, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_sgent_csockt_large_tcp6,
              Runtime,
              inet6,
              gen, true,
              sock, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport =  socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp(socket), Active = false
%% Message Size: small (=1)
%% Domain:       inet
%%

ttest_ssockf_cgsf_small_tcp4(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet,
              sock, false,
              gs, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp(socket), Active = false
%% Message Size: small (=1)
%% Domain:       inet6
%% 

ttest_ssockf_cgsf_small_tcp6(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet6,
              sock, false,
              gs, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp(socket), Active = false
%% Message Size: medium (=2)
%% Domain:       inet
%%

ttest_ssockf_cgsf_medium_tcp4(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet,
              sock, false,
              gs, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp(socket), Active = false
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

ttest_ssockf_cgsf_medium_tcp6(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet6,
              sock, false,
              gs, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp(socket), Active = false
%% Message Size: large (=3)
%% Domain:       inet
%%

ttest_ssockf_cgsf_large_tcp4(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet,
              sock, false,
              gs, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp(socket), Active = false
%% Message Size: large (=3)
%% Domain:       inet6
%% 

ttest_ssockf_cgsf_large_tcp6(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet6,
              sock, false,
              gs, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       local
%% 

ttest_ssockf_csockf_small_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockf_csockf_small_tcpL,
              Runtime,
              local,
              sock, false,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       local
%% 

ttest_ssockf_csockf_medium_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockf_csockf_medium_tcpL,
              Runtime,
              local,
              sock, false,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       local
%% 

ttest_ssockf_csockf_large_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockf_csockf_large_tcpL,
              Runtime,
              local,
              sock, false,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: small (=1)
%% Domain:       local
%% 

ttest_ssockf_csocko_small_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockf_csocko_small_tcpL,
              Runtime,
              local,
              sock, false,
              sock, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: medium (=2)
%% Domain:       local
%% 

ttest_ssockf_csocko_medium_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockf_csocko_medium_tcpL,
              Runtime,
              local,
              sock, false,
              sock, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: large (=3)
%% Domain:       local
%% 

ttest_ssockf_csocko_large_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockf_csocko_large_tcpL,
              Runtime,
              local,
              sock, false,
              sock, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport =  socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport =  socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: small (=1)
%% Domain:       local
%% 

ttest_ssockf_csockt_small_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockf_csocko_small_tcpL,
              Runtime,
              local,
              sock, false,
              sock, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: medium (=2)
%% Domain:       local
%% 

ttest_ssockf_csockt_medium_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockf_csockt_medium_tcpL,
              Runtime,
              local,
              sock, false,
              sock, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: large (=3)
%% Domain:       local
%% 

ttest_ssockf_csockt_large_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockf_csockt_large_tcpL,
              Runtime,
              local,
              sock, false,
              sock, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport =  socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = false
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       local
%% 

ttest_ssocko_csockf_small_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssocko_csockf_small_tcpL,
              Runtime,
              local,
              sock, once,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       local
%% 

ttest_ssocko_csockf_medium_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssocko_csockf_medium_tcpL,
              Runtime,
              local,
              sock, once,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       local
%% 

ttest_ssocko_csockf_large_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssocko_csockf_large_tcpL,
              Runtime,
              local,
              sock, once,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: small (=1)
%% Domain:       local
%% 

ttest_ssocko_csocko_small_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssocko_csocko_small_tcpL,
              Runtime,
              local,
              sock, once,
              sock, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: medium (=2)
%% Domain:       local
%% 

ttest_ssocko_csocko_medium_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssocko_csocko_medium_tcpL,
              Runtime,
              local,
              sock, once,
              sock, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: large (=3)
%% Domain:       local
%% 

ttest_ssocko_csocko_large_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssocko_csocko_large_tcpL,
              Runtime,
              local,
              sock, once,
              sock, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport =  socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport =  socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: small (=1)
%% Domain:       local
%% 

ttest_ssocko_csockt_small_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssocko_csocko_small_tcpL,
              Runtime,
              local,
              sock, once,
              sock, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: medium (=2)
%% Domain:       local
%% 

ttest_ssocko_csockt_medium_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssocko_csockt_medium_tcpL,
              Runtime,
              local,
              sock, once,
              sock, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = once
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: large (=3)
%% Domain:       local
%% 

ttest_ssocko_csockt_large_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssocko_csockt_large_tcpL,
              Runtime,
              local,
              sock, once,
              sock, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = false
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = once
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport =  socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = gen_tcp, Active = true
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: small (=1)
%% Domain:       local
%% 

ttest_ssockt_csockf_small_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockt_csockf_small_tcpL,
              Runtime,
              local,
              sock, true,
              sock, false,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: medium (=2)
%% Domain:       local
%% 

ttest_ssockt_csockf_medium_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockt_csockf_medium_tcpL,
              Runtime,
              local,
              sock, true,
              sock, false,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = false
%% Message Size: large (=3)
%% Domain:       local
%% 

ttest_ssockt_csockf_large_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockt_csockf_large_tcpL,
              Runtime,
              local,
              sock, true,
              sock, false,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: small (=1)
%% Domain:       local
%% 

ttest_ssockt_csocko_small_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockt_csocko_small_tcpL,
              Runtime,
              local,
              sock, true,
              sock, once,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: medium (=2)
%% Domain:       local
%% 

ttest_ssockt_csocko_medium_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockt_csocko_medium_tcpL,
              Runtime,
              local,
              sock, true,
              sock, once,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: large (=3)
%% Domain:       local
%% 

ttest_ssockt_csocko_large_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockt_csocko_large_tcpL,
              Runtime,
              local,
              sock, true,
              sock, once,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: small (=1)
%% Domain:       inet
%%

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport =  socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: small (=1)
%% Domain:       inet6
%% 

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, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport =  socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: small (=1)
%% Domain:       local
%% 

ttest_ssockt_csockt_small_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockt_csocko_small_tcpL,
              Runtime,
              local,
              sock, true,
              sock, true,
              1, ttest_small_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: medium (=2)
%% Domain:       inet
%%

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: medium (=2)
%% Domain:       inet6
%% 

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, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: medium (=2)
%% Domain:       local
%% 

ttest_ssockt_csockt_medium_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockt_csockt_medium_tcpL,
              Runtime,
              local,
              sock, true,
              sock, true,
              2, ttest_medium_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: large (=3)
%% Domain:       inet
%%

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: large (=3)
%% Domain:       inet6
%% 

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, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = true
%% Message Size: large (=3)
%% Domain:       local
%% 

ttest_ssockt_csockt_large_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(ttest_ssockt_csockt_large_tcpL,
              Runtime,
              local,
              sock, true,
              sock, true,
              3, ttest_large_max_outstanding(Config)).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case uses the time test (ttest) utility to implement a 
%% ping-pong like test case.
%% Server:       Transport = socket(tcp), Active = true
%% Client:       Transport = socket(tcp), Active = once
%% Message Size: small (=1)
%% Domain:       inet
%% Remote:       false (run everything on the local node)
%%

ttest_simple_ssockt_csocko_small_tcp4(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet,
              sock, true,
              sock, once,
              1, ttest_small_max_outstanding(Config),
	      false).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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_simple_ssockt_csocko_small_tcp6(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              inet6,
              sock, true,
              sock, once,
              1, ttest_small_max_outstanding(Config),
	      false).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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:       local
%% 

ttest_simple_ssockt_csocko_small_tcpL(Config) when is_list(Config) ->
    Runtime = which_ttest_runtime(Config),
    ttest_tcp(?FUNCTION_NAME,
              Runtime,
              local,
              sock, true,
              sock, once,
              1, ttest_small_max_outstanding(Config),
	      false).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

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([$s, $m | MS]) when (length(MS) > 0) ->
    convert_time(MS, fun(X) -> X end);
which_ttest_runtime_env2([$m | M]) when (length(M) > 0) ->
    convert_time(M, fun(X) -> ?MINS(X) end);
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) ->
    ttest_tcp(TC,
	      Runtime,
	      Domain,
	      ServerMod, ServerActive,
	      ClientMod, ClientActive,
	      MsgID, MaxOutstanding, true).

ttest_tcp(TC,
          Runtime,
          Domain,
          ServerMod, ServerActive,
          ClientMod, ClientActive,
          MsgID, MaxOutstanding,
	  Remote) ->
    tc_try(TC,
           fun() ->
                   if
 
                       (Domain =:= local) ->
                           %% On darwin we seem to hit the system limit(s)
                           %% much earlier.
                           %% The tests "mostly" work, but random cases fail
                           %% (even on reasonably powerful machines),
                           %% so its much simpler to just skip on darwin...
                           has_support_unix_domain_socket(),
                           is_not_darwin();
                       (Domain =:= inet) ->
                           has_support_ipv4();
                       (Domain =:= inet6) ->
                           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)),
                   ?P("Parameters: "
                      "~n   Domain:          ~p"
                      "~n   Message ID:      ~p"
                      "~n   Max Outstanding: ~p"
                      "~n   Running Time:    ~p"
                      "~n   Server Module:   ~p"
                      "~n   Server Active:   ~p"
                      "~n   Client Module:   ~p"
                      "~n   Client Active:   ~p"
                      "~n   Remote:          ~p",
                      [Domain, MsgID, MaxOutstanding, Runtime,
                       ServerMod, ServerActive, ClientMod, ClientActive,
		       Remote]),
                   InitState = #{domain          => Domain,
                                 msg_id          => MsgID,
                                 max_outstanding => MaxOutstanding,
                                 runtime         => Runtime,
                                 server_mod      => ServerMod,
                                 server_active   => ServerActive,
                                 client_mod      => ClientMod,
                                 client_active   => ClientActive,
				 remote          => Remote},
                   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 => "(maybe) create node",
           cmd  => fun(#{remote := true} = State) ->
                           {Peer, Node} = ?START_NODE("server"),
			   ?SEV_IPRINT("server node created:"
				       "~n   Peer: ~p"
				       "~n   Node: ~p", [Peer, Node]),
                           {ok, State#{peer => Peer, node => Node}};
		      (State) ->
			   ?SEV_IPRINT("use local node for server"),
			   {ok, State#{peer => undefined, node => node()}}
                   end},
         #{desc => "(maybe) monitor server node",
           cmd  => fun(#{node := Node} = _State) when (Node =/= node) ->
                           true = erlang:monitor_node(Node, true),
			   ?SEV_IPRINT("~p monitored", [Node]),
                           ok;
		      (_State) ->
			   ?SEV_IPRINT("nothing"),
			   ok
                   end},
         #{desc => "start ttest (remote) server",
           cmd  => fun(#{domain := local = Domain,
                         mod    := Mod,
                         active := Active,
                         node   := Node} = State) ->
                           case ttest_tcp_server_start(Node,
                                                       Domain, Mod, Active) of
                               {ok, {{Pid, _}, Path}} ->
                                   ?SEV_IPRINT("server started: "
                                               "~n   Pid:  ~p"
                                               "~n   Path: ~p", [Pid, Path]),
                                   {ok, State#{rserver => Pid,
                                               path    => Path}};
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{domain := Domain,
                         mod    := Mod,
                         active := Active,
                         node   := Node} = State) ->
                           case ttest_tcp_server_start(Node,
                                                       Domain, Mod, Active) of
                               {ok, {{Pid, _}, {Addr, Port}}} ->
                                   ?SEV_IPRINT("server started: "
                                               "~n   Pid:  ~p"
                                               "~n   Addr: ~p"
                                               "~n   Port: ~p",
                                               [Pid, Addr, Port]),
                                   {ok, State#{rserver => Pid,
                                               addr    => Addr,
                                               port    => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain := local,
                         tester := Tester,
                         path   := Path}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, Path),
                           ok;
                      (#{tester := Tester,
                         addr   := Addr,
                         port   := Port}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, {Addr, Port}),
                           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 ->
                                   ?SEV_IPRINT("received termination request"),
                                   {ok, maps:remove(tester, State)};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("received unexpected error: "
                                               "~n   ~p", [Reason]),
                                   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 => "(maybe) stop (server) node",
           cmd  => fun(#{peer := Peer} = State)  when (Peer =/= undefined) ->
                           {ok,
                            try peer:stop(Peer) of
                                ok ->
                                    State#{node_stop => ok};
                                {error, Reason} ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   ~p", [Reason]),
                                    State#{node_stop => error}
                            catch
                                C:E:S ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   Class: ~p"
                                                "~n   Error: ~p"
                                                "~n   Stack: ~p",[C, E, S]),
                                    State#{node_stop => error}
                            end};
		      (_) ->
                           ?SEV_IPRINT("nothing"),
			   ok
                   end},
         #{desc => "(maybe) await (server) node termination",
           cmd  => fun(#{node := Node, node_stop := ok} = State)
			 when (Node =/= node()) ->
                           ?SEV_IPRINT("Success node stop - await nodedown"),
                           receive
                               {nodedown, Node} ->
                                   ?SEV_IPRINT("nodedown received - cleanup"),
                                   State1 = maps:remove(node, State),
                                   {ok, State1}
                           end;
                      (#{node_stop := error} = State) ->
                           ?SEV_IPRINT("Failed node stop - cleanup"),
                           State1 = maps:remove(node, State),
                           {ok, State1};
		      (State) ->
                           ?SEV_IPRINT("nothing"),
                           State1 = maps:remove(node, State),
                           {ok, State1}
                   end},


         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    ClientSeq =
        [
         %% *** Wait for start order part ***
         #{desc => "await start",
           cmd  => fun(#{domain := local} = State) ->
                           {Tester, ServerPath} = 
                               ?SEV_AWAIT_START(),
                           ?SEV_IPRINT("started with server info: "
                                       "~n   Path: ~p", [ServerPath]),
                           {ok, State#{tester      => Tester,
                                       server_path => ServerPath}};
                      (State) ->
                           {Tester, {ServerAddr, ServerPort}} = 
                               ?SEV_AWAIT_START(),
                           ?SEV_IPRINT("started with server info: "
                                       "~n   Addr: ~p"
                                       "~n   Port: ~p",
                                       [ServerAddr, ServerPort]),
                           {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(#{remote := true, host := _Host} = State) ->
                           %% Because peer does not accept a host argument,
                           %% we can no longer start "remote" nodes...
                           %% Not that we actually did that. We always
                           %% used local-host.
                           {Peer, Node} = ?START_NODE("client"),
                           {ok, State#{peer => Peer, node => Node}};
		      (State) ->
			   {ok, State#{peer => undefined, node => node()}}
                   end},
         #{desc => "(maybe) monitor client node",
           cmd  => fun(#{node := Node} = _State) when (Node =/= node()) ->
                           true = erlang:monitor_node(Node, true),
			   ?SEV_IPRINT("~p monitored", [Node]),
                           ok;
		      (_) ->
			   ?SEV_IPRINT("nothing"),
			   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(#{domain          := local = Domain,
                         node            := Node,
                         mod             := Mod,
                         active          := Active,
                         msg_id          := MsgID,
                         max_outstanding := MaxOutstanding,
                         runtime         := RunTime,
                         server_path     := Path} = State) ->
                           Self   = self(),
                           Notify =
                               fun(Result) ->
                                       ?SEV_ANNOUNCE_READY(Self, ttest, Result)
                               end,                           
                           case ttest_tcp_client_start(Node, Notify,
                                                       Domain, Mod,
                                                       Path,
                                                       Active,
                                                       MsgID, MaxOutstanding,
                                                       RunTime) of
                               {ok, {Pid, _MRef}} ->
                                   {ok, State#{rclient => Pid}};
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{domain          := Domain,
                         node            := Node,
                         mod             := Mod,
                         active          := Active,
                         msg_id          := MsgID,
                         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,
                                                       Domain, Mod,
                                                       {Addr, Port},
                                                       Active,
                                                       MsgID, MaxOutstanding,
                                                       RunTime) of
                               {ok, {Pid, _MRef}} ->
                                   {ok, State#{rclient => Pid}};
                               {error, {connect, Reason, ServerInfo} = EI} ->
                                   ?SEV_EPRINT("Failed connecting to server: "
                                               "~n   Reason: ~p"
                                               "~n   Server: ~p",
                                               [Reason, ServerInfo]),
                                   case Reason of
                                       eaddrnotavail ->
                                           {skip, Reason};
                                       network_unreachable ->
                                           {skip, Reason};
                                       _ ->
                                           {error, EI}
                                   end;
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "await ttest ready",
           cmd  => fun(#{tester  := Tester,
                         rclient := RClient} = State) ->
                           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 => "(maybe) stop (client) node",
           cmd  => fun(#{peer := Peer} = State) when (Peer =/= undefined) ->
                           {ok,
                            try peer:stop(Peer) of
                                ok ->
                                    State#{node_stop => ok};
                                {error, Reason} ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   ~p", [Reason]),
                                    State#{node_stop => error}
                            catch
                                C:E:S ->
                                    ?SEV_EPRINT("Unexpected node stop result: "
                                                "~n   Class: ~p"
                                                "~n   Error: ~p"
                                                "~n   Stack: ~p",[C, E, S]),
                                    State#{node_stop => error}
                            end};
		      (_) ->
			   ?SEV_IPRINT("nothing"),
			   ok
                   end},
         #{desc => "(maybe) await (client) node termination",
           cmd  => fun(#{node := Node, node_stop := ok} = State)
			 when (Node =/= node()) ->
                           ?SEV_IPRINT("Success node stop - await nodedown"),
                           receive
                               {nodedown, Node} ->
                                   ?SEV_IPRINT("nodedown received - cleanup"),
                                   State1 = maps:remove(node, State),
                                   {ok, State1}
                           end;
                      (#{node_stop := error} = State) ->
                           ?SEV_IPRINT("Failed node stop - cleanup"),
                           State1 = maps:remove(node, State),
                           {ok, State1};
		      (_) ->
			   ?SEV_IPRINT("nothing"),
			   ok
                   end},


         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    TesterSeq =
        [
         %% *** Init part ***
         #{desc => "monitor server",
           cmd  => fun(#{server := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},
         #{desc => "monitor client",
           cmd  => fun(#{client := Pid} = _State) ->
                           _MRef = erlang:monitor(process, Pid),
                           ok
                   end},

         %% Start the server
         #{desc => "order server start",
           cmd  => fun(#{server := Pid} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid),
                           ok
                   end},
         #{desc => "await server ready (init)",
           cmd  => fun(#{domain := local,
                         server := Pid} = State) ->
                           {ok, Path} = ?SEV_AWAIT_READY(Pid, server, init),
                           {ok, State#{server_path => Path}};
                      (#{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(#{domain      := local,
                         client      := Pid,
                         server_path := Path} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, Path),
                           ok;
                      (#{client      := Pid,
                         server_addr := Addr,
                         server_port := Port} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, {Addr, Port}),
                           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,
                         domain        := Domain,
                         server_mod    := ServerTrans,
                         server_active := ServerActive,
                         client_mod    := ClientTrans,
                         client_active := ClientActive,
                         msg_id        := MsgID} = State) ->
                           case Result of
                               #{status  := ok,
                                 runtime := RunTime,
                                 cnt     := Cnt,
                                 bcnt    := BCnt} ->
                                   ttest_report(Domain,
                                                ServerTrans, ServerActive,
                                                ClientTrans, ClientActive,
                                                MsgID,
                                                RunTime, BCnt, Cnt),
                                   ?SEV_IPRINT(
                                      "TTest results: "
                                      "~n   Run Time:                    ~s"
                                      "~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
        ],

    Domain = maps:get(domain, InitState),
    LHost  = local_host(),
    LAddr  = which_local_addr(Domain),

    i("start server evaluator"),
    ServerInitState = #{host   => LHost,
			addr   => LAddr,
                        domain => Domain,
                        mod    => maps:get(server_mod,    InitState),
                        active => maps:get(server_active, InitState)},
    Server          = ?SEV_START("server", ServerSeq, ServerInitState),

    i("start client evaluator"),
    ClientInitState = #{host            => LHost,
			addr            => LAddr,
                        domain          => Domain,
                        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 = #{domain        => Domain,
			msg_id        => maps:get(msg_id,        InitState),
                        client        => Client#ev.pid,
			client_mod    => maps:get(client_mod,    InitState),
			client_active => maps:get(client_active, InitState),
                        server        => Server#ev.pid,
                        server_mod    => maps:get(server_mod,    InitState),
                        server_active => maps:get(server_active, InitState)},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).



ttest_tcp_server_start(Node, Domain, gen, Active) ->
    TransportMod = socket_test_ttest_tcp_gen,
    Transport    = {TransportMod, #{domain => Domain}},
    socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active);
ttest_tcp_server_start(Node, Domain, gs, Active) ->
    TransportMod = socket_test_ttest_tcp_gs,
    Transport    = {TransportMod, #{domain => Domain}},
    socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active);
ttest_tcp_server_start(Node, Domain, sock, Active) ->
    TransportMod = socket_test_ttest_tcp_socket,
    Transport    = {TransportMod, #{domain => Domain,
                                    async  => true,
                                    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,
                       Domain, gen,
                       ServerInfo, Active, MsgID, MaxOutstanding, RunTime) ->
    TransportMod = socket_test_ttest_tcp_gen,
    Transport    = {TransportMod, #{domain => Domain}},    
    socket_test_ttest_tcp_client:start_monitor(Node,
                                               Notify,
                                               Transport,
                                               ServerInfo,
                                               Active,
                                               MsgID, MaxOutstanding, RunTime);
ttest_tcp_client_start(Node,
                       Notify,
                       Domain, gs,
                       ServerInfo, Active, MsgID, MaxOutstanding, RunTime) ->
    TransportMod = socket_test_ttest_tcp_gs,
    Transport    = {TransportMod, #{domain => Domain}},    
    socket_test_ttest_tcp_client:start_monitor(Node,
                                               Notify,
                                               Transport,
                                               ServerInfo,
                                               Active,
                                               MsgID, MaxOutstanding, RunTime);
ttest_tcp_client_start(Node,
                       Notify,
                       Domain, sock,
                       ServerInfo, Active, MsgID, MaxOutstanding, RunTime) ->
    TransportMod = socket_test_ttest_tcp_socket,
    Transport    = {TransportMod, #{domain => Domain,
                                    async  => true,
                                    method => plain}},
    socket_test_ttest_tcp_client:start_monitor(Node,
                                               Notify,
                                               Transport,
                                               ServerInfo,
                                               Active,
                                               MsgID, MaxOutstanding, RunTime).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-define(TTEST_MANAGER, esock_ttest_manager).

-record(ttest_report_id,
        {domain        :: socket:domain(),
         serv_trans    :: gen | sock,
         serv_active   :: once | boolean(),
         client_trans  :: gen | sock,
         client_active :: once | boolean(),
         msg_id        :: small | medium | large}).

-record(ttest_report, {id    :: #ttest_report_id{},
                       time  :: non_neg_integer(),
                       bytes :: non_neg_integer(),
                       msgs  :: non_neg_integer()}).

-spec ttest_report(Domain      :: socket:domain(),
                   ServTrans   :: gen | sock, ServActive   :: once | boolean(),
                   ClientTrans :: gen | sock, ClientActive :: once | boolean(),
                   MsgID       :: 1 | 2 | 3,
                   RunTime     :: non_neg_integer(),
                   NumBytes    :: non_neg_integer(),
                   NumMsgs     :: non_neg_integer()) -> ok.

ttest_report(Domain,
             ServTrans,   ServActive,
             ClientTrans, ClientActive,
             MsgID,
             RunTime,
             NumBytes,
             NumMsgs) ->
    ID = #ttest_report_id{domain        = Domain,
                          serv_trans    = ServTrans,
                          serv_active   = ServActive,
                          client_trans  = ClientTrans,
                          client_active = ClientActive,
                          msg_id        = ttest_msg_id_num_to_name(MsgID)},
    Report = #ttest_report{id    = ID,
                           time  = RunTime,
                           bytes = NumBytes,
                           msgs  = NumMsgs},
    %% If we run just one test case, the group init has never been run
    %% and therefore the ttest manager is not running (we also don't actually
    %% care about collecting reports in that case).
    (catch global:send(?TTEST_MANAGER, Report)),
    ok.

ttest_msg_id_num_to_name(1) ->
    small;
ttest_msg_id_num_to_name(2) ->
    medium;
ttest_msg_id_num_to_name(3) ->
    large.
    
ttest_manager_start() ->
    Self = self(),
    {Pid, MRef} = spawn_monitor(fun() -> ttest_manager_init(Self) end),
    receive
        {ttest_manager_started, Pid} ->
            erlang:demonitor(MRef, [flush]),
            ok;
        {'DOWN', MRef, process, Pid, Reason} ->
            exit({failed_starting, ttest_manager, Reason})
    after 5000 ->
            exit(Pid, kill),
            exit({failed_starting, ttest_manager, timeout})
    end.

ttest_manager_stop() ->
    case global:whereis_name(?TTEST_MANAGER) of
        Pid when is_pid(Pid) ->
            erlang:monitor(process, Pid),
            global:send(?TTEST_MANAGER, stop),
            receive
                {'DOWN', _MRef, process, Pid, _} ->
                    ok
            after 10000 ->
                    exit(Pid, kill),
                    ok
            end;
        _ ->
            ok
    end.

ttest_manager_init(Parent) ->
    yes = global:register_name(?TTEST_MANAGER, self()),
    ets:new(?TTEST_MANAGER, 
            [{keypos, #ttest_report.id}, named_table, protected, ordered_set]),
    Parent ! {ttest_manager_started, self()},
    ttest_manager_loop().

ttest_manager_loop() ->
    receive
        stop ->
            ?LOGGER:format("manager stopping~n", []),
            ttest_manager_done();

        #ttest_report{id    = _ID,
                      time  = _RunTime,
                      bytes = _NumBytes,
                      msgs  = _NumMsgs} = Report ->
            true = ets:insert_new(?TTEST_MANAGER, Report),
            ttest_manager_loop()
    end.

%% We are supposed to pretty print the result here...
ttest_manager_done() ->
    format_reports(inet),
    %% format_reports(inet6),
    ets:delete(?TTEST_MANAGER),
    exit(normal).

format_reports(Domain) ->
    ?LOGGER:format("Domain ~w reports:~n~n", [Domain]),
    format_reports(Domain, small),
    format_reports(Domain, medium),
    format_reports(Domain, large).
    
format_reports(Domain, MsgID) when is_atom(MsgID) ->
    case which_ttest_reports(Domain, MsgID) of
        [] ->
            ?LOGGER:format("   No ~w reports~n~n", [MsgID]);
        Reports ->
            ?LOGGER:format("   ~w reports: ~n", [MsgID]),
            lists:foreach(fun(R) -> format_report(R) end, Reports)
    end.

%% This should really be a table like this:
%%
%%             client
%% server      gen(false)  gen(once)  gen(true)  sock(false)  sock(once)  sock(true)
%% gen(false)  nnn
%% gen(once)   nnn
%% gen(true)   nnn
%% sock(false) nnn
%% sock(once)  nnn
%% sock(true)  nnn
%%
format_report(#ttest_report{id    = #ttest_report_id{serv_trans    = STrans,
                                                     serv_active   = SActive,
                                                     client_trans  = CTrans,
                                                     client_active = CActive},
                            time  = RunTime,
                            bytes = BCnt,
                            msgs  = MCnt}) ->
    ?LOGGER:format("      server ~w[~w] - client ~w[~w] => "
                   "~n         Run Time: ~s"
                   "~n         Bytes:    ~s"
                   "~n         Messages: ~s"
                   "~n", [STrans, SActive, CTrans, CActive,
                          ?TTEST_LIB:format_time(RunTime),
                          if ((BCnt =:= 0) orelse (RunTime =:= 0)) ->
                                  ?TTEST_LIB:format("~w, ~w",
                                                    [BCnt, RunTime]);
                             true ->
                                  ?TTEST_LIB:format("~p => ~p byte / ms",
                                                    [BCnt, BCnt div RunTime])
                          end,
                          if (RunTime =:= 0) ->
                                  "-";
                             true ->
                                  ?TTEST_LIB:format("~p => ~p iterations / ms",
                                                    [MCnt, MCnt div RunTime])
                          end]),
    ok.


which_ttest_reports(Domain, all) ->
    [R || R = #ttest_report{id = #ttest_report_id{domain = D}} <- 
              ets:tab2list(?TTEST_MANAGER), Domain =:= D];
which_ttest_reports(Domain, MsgID) ->
    [R || R = #ttest_report{id = #ttest_report_id{domain = D, msg_id = MID}} <- 
              ets:tab2list(?TTEST_MANAGER), (Domain =:= D) andalso (MsgID =:= MID)].



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                                                     %%
%%                            TICKETS                                  %%
%%                                                                     %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Create several acceptor processes (processes that calls socket:accept/1)
%% and then a couple of clients connects to them without any problems.
%% TCP, IPv4.
otp16359_maccept_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(otp16359_maccept_tcp4,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 protocol => tcp},
                   ok = otp16359_maccept_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Create several acceptor processes (processes that calls socket:accept/1)
%% and then a couple of clients connects to them without any problems.
%% TCP, IPv6.
otp16359_maccept_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(otp16359_maccept_tcp6,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 protocol => tcp},
                   ok = otp16359_maccept_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Create several acceptor processes (processes that calls socket:accept/1)
%% and then a couple of clients connects to them without any problems.
%% TCP, UNix Domain Sockets.
otp16359_maccept_tcpL(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(otp16359_maccept_tcpL,
           fun() -> has_support_unix_domain_socket() end,
           fun() ->
                   InitState = #{domain   => local,
                                 protocol => default},
                   ok = otp16359_maccept_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

otp16359_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) ->
                           LSA = which_local_socket_addr(Domain),
                           ?SEV_IPRINT("LSA: ~p", [LSA]),
                           {ok, State#{lsa => LSA}}
                   end},
         #{desc => "create (listen) socket",
           cmd  => fun(#{domain   := Domain,
                         protocol := Proto} = State) ->
                           case socket:open(Domain, stream, Proto) of
                               {ok, Sock} ->
                                   {ok, State#{lsock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "bind to local address",
           cmd  => fun(#{domain := local,
                         lsock  := LSock,
                         lsa    := LSA} = _State) ->
                           case socket:bind(LSock, LSA) of
                               ok ->
                                   ok; % We do not care about the port for local
                               {error, _} = ERROR ->
                                   ERROR
                           end;
                      (#{lsock := LSock, lsa := LSA} = State) ->
                           case sock_bind(LSock, LSA) of
                               ok ->
                                   Port = sock_port(LSock),
                                   {ok, State#{lport => Port}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "make listen socket",
           cmd  => fun(#{lsock := LSock}) ->
                           socket:listen(LSock)
                   end},
         #{desc => "announce ready (init)",
           cmd  => fun(#{domain := local,
                         tester := Tester,
                         lsock  := LSock,
                         lsa    := #{path := Path}}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, {LSock, Path}),
                           ok;
                      (#{lsock := LSock, lport := LPort, tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, init, {LSock, LPort}),
                           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 (with success)",
           cmd  => fun(#{lsock := LSock} = State) ->
                           case socket:accept(LSock) of
                               {ok, Sock} ->
                                   ?SEV_IPRINT("Expected (accept) success: "
                                               "~n   ~p", [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},

         %% *** Terminate ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = _State) ->
                           ?SEV_AWAIT_TERMINATE(Tester, tester),
                           ok
                   end},
         %% *** Close (connect) socket ***
         #{desc => "close (connect) socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           sock_close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},
         %% *** Close (listen) socket ***
         #{desc => "close (listen) socket",
           cmd  => fun(#{domain := local,
                         lsock  := Sock,
                         lsa    := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(lsa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(lsock, State1)};
                      (#{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",
           cmd  => fun(#{lsock := LSock} = State) ->
                           case socket:accept(LSock) of
                               {ok, Sock} ->
                                   ?SEV_IPRINT("Expected (accept) success: "
                                               "~n   ~p", [Sock]),
                                   {ok, State#{sock => Sock}};
                               {error, _} = ERROR ->
                                   ERROR
                           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},
         %% *** Close (connect) socket ***
         #{desc => "close (connect) socket",
           cmd  => fun(#{sock := Sock} = State) ->
                           sock_close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?SEV_FINISH_NORMAL
        ],

    ClientSeq =
        [
         %% *** Wait for start order ***
         #{desc => "await start (from tester)",
           cmd  => fun(#{domain := local} = State) ->
                           {Tester, Path} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_path => Path}};
                      (State) ->
                           {Tester, Port} = ?SEV_AWAIT_START(),
                           {ok, State#{tester => Tester, server_port => Port}}
                   end},
         #{desc => "monitor tester",
           cmd  => fun(#{tester := Tester}) ->
                           _MRef = erlang:monitor(process, Tester),
                           ok
                   end},

         %% *** The init part ***
         #{desc => "which server (local) address",
           cmd  => fun(#{domain      := local = Domain,
                         server_path := Path} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = #{family => Domain, path => Path},
                           {ok, State#{lsa => LSA, server_sa => SSA}};
                      (#{domain := Domain, server_port := Port} = State) ->
                           LSA = which_local_socket_addr(Domain),
                           SSA = LSA#{port => Port},
                           {ok, State#{lsa => LSA, server_sa => SSA}}
                   end},
         #{desc => "create socket",
           cmd  => fun(#{domain   := Domain,
                         protocol := Proto} = State) ->
                           case socket:open(Domain, stream, 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 sock_bind(Sock, LSA) of
                               ok ->
                                   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}) ->
                           case socket:connect(Sock, SSA) of
                               ok ->
                                   ?SEV_IPRINT("connected", []),
                                   ok;
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed connect: "
                                               "~n   ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (connect)",
           cmd  => fun(#{tester := Tester}) ->
                           ?SEV_ANNOUNCE_READY(Tester, connect),
                           ok
                   end},

         %% *** Termination ***
         #{desc => "await terminate",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_TERMINATE(Tester, tester) of
                               ok ->
                                   {ok, maps:remove(tester, State)};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "close socket",
           cmd  => fun(#{domain := local,
                         sock   := Sock,
                         lsa    := #{path := Path}} = State) ->
                           ok = socket:close(Sock),
                           State1 =
                               unlink_path(Path,
                                           fun() ->
                                                   maps:remove(lsa, State)
                                           end,
                                           fun() -> State end),
                           {ok, maps:remove(sock, State1)};
                      (#{sock := Sock} = State) ->
                           ok = socket:close(Sock),
                           {ok, maps:remove(sock, State)}
                   end},

         %% *** We are done ***
         ?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},
         #{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 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, Port}} =
                               ?SEV_AWAIT_READY(Pid, prim_acceptor, init),
                           {ok, State#{lsock => Sock, lport => Port}}
                   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},

         %% Start client-1
         #{desc => "start client 1",
           cmd  => fun(#{client1 := Pid, lport := LPort} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, LPort),
                           ok
                   end},
         #{desc => "await client 1 ready (init)",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client1, init)
                   end},

         %% Start client-2
         #{desc => "start client 2",
           cmd  => fun(#{client2 := Pid, lport := LPort} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, LPort),
                           ok
                   end},
         #{desc => "await client 2 ready (init)",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client2, init)
                   end},

         %% Start client-3
         #{desc => "start client 3",
           cmd  => fun(#{client3 := Pid, lport := LPort} = _State) ->
                           ?SEV_ANNOUNCE_START(Pid, LPort),
                           ok
                   end},
         #{desc => "await client 3 ready (init)",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client3, 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},

         ?SEV_SLEEP(?SECS(1)),

         %% Activate the client(s)
         #{desc => "active client 1",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
                           ok
                   end},

         ?SEV_SLEEP(100),

         #{desc => "active client 2",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
                           ok
                   end},

         ?SEV_SLEEP(100),

         #{desc => "active client 3",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_ANNOUNCE_CONTINUE(Pid, connect),
                           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},
         #{desc => "await client 1 ready (connect)",
           cmd  => fun(#{client1 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client1, connect)
                   end},
         #{desc => "await client 2 ready (connect)",
           cmd  => fun(#{client2 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client2, connect)
                   end},
         #{desc => "await client 3 ready (connect)",
           cmd  => fun(#{client3 := Pid} = _State) ->
                           ?SEV_AWAIT_READY(Pid, client3, connect)
                   end},

         %% Terminate
         #{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 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 client 1 evaluator"),
    ClientInitState1 = InitState,
    Client1 = ?SEV_START("client-1", ClientSeq, ClientInitState1),

    i("create client 2 evaluator"),
    ClientInitState2 = InitState,
    Client2 = ?SEV_START("client-2", ClientSeq, ClientInitState2),

    i("create client 3 evaluator"),
    ClientInitState3 = InitState,
    Client3 = ?SEV_START("client-3", ClientSeq, ClientInitState3),

    i("create tester evaluator"),
    TesterInitState = #{prim_acceptor => PrimAcceptor#ev.pid,
                        sec_acceptor1 => SecAcceptor1#ev.pid,
                        sec_acceptor2 => SecAcceptor2#ev.pid,
                        client1       => Client1#ev.pid,
                        client2       => Client2#ev.pid,
                        client3       => Client3#ev.pid},
    Tester = ?SEV_START("tester", TesterSeq, TesterInitState),

    i("await evaluator(s)"),
    ok = ?SEV_AWAIT_FINISH([PrimAcceptor, SecAcceptor1, SecAcceptor2,
                            Client1, Client2, Client3,
                            Tester]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% This test case is to verify that we do not leak monitors.
otp18240_accept_mon_leak_tcp4(Config) when is_list(Config) ->
    ?TT(?SECS(30)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   InitState = #{domain    => inet,
                                 protocol  => tcp,
                                 num_socks => 10},
                   ok = otp18240_accept_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% This test case is to verify that we do not leak monitors.
otp18240_accept_mon_leak_tcp6(Config) when is_list(Config) ->
    ?TT(?SECS(30)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv6() end,
           fun() ->
                   InitState = #{domain    => inet6,
                                 protocol  => tcp,
                                 num_socks => 10},
                   ok = otp18240_accept_tcp(InitState)
           end).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

otp18240_accept_tcp(#{domain    := Domain,
                      protocol  := Proto,
                      num_socks := NumSocks}) ->
    Self = self(),
    {Pid, Mon} = spawn_monitor(fun() ->
                                       otp18240_acceptor(Self,
                                                         Domain, Proto,
                                                         NumSocks)
                               end),
    otp18240_await_acceptor(Pid, Mon).

otp18240_await_acceptor(Pid, Mon) ->
    receive
	{'DOWN', Mon, process, Pid, ok} ->
	    i("acceptor successfully terminated"),
            ok;
	{'DOWN', Mon, process, Pid, {skip, _} = SKIP} ->
	    i("acceptor successfully terminated"),
            exit(SKIP);
        {'DOWN', Mon, process, Pid, Info} ->
            i("acceptor unexpected termination: "
              "~n   ~p", [Info]),
            exit({unexpected_result, Info})
    after 5000 ->
	    i("acceptor process (~p) info"
	      "~n   Refs: ~p"
	      "~n   Info: ~p",
	      [Pid, monitored_by(Pid), erlang:process_info(Pid)]),
	    otp18240_await_acceptor(Pid, Mon)
    end.

otp18240_acceptor(Parent, Domain, Proto, NumSocks) ->
    i("[acceptor] begin with: "
      "~n   Parent:   ~p"
      "~n   Domain:   ~p"
      "~n   Protocol: ~p", [Parent, Domain, Proto]),
    MonitoredBy0 = monitored_by(),
    ?SLEEP(?SECS(5)),
    Addr = case ?LIB:which_local_host_info(Domain) of
               {ok, #{addr := A}} ->
                   A;
               {error, Reason} ->
                   exit({skip, Reason})
           end,
    {ok, LSock} = socket:open(Domain, stream, Proto,
                              #{use_registry => false}),
    ok = socket:bind(LSock, #{family => Domain, addr => Addr, port => 0}),
    ok = socket:listen(LSock, NumSocks),
    ?SLEEP(?SECS(5)),
    MonitoredBy1 = monitored_by(),
    i("[acceptor]: listen socket created"
      "~n   'Montored By' before listen socket: ~p"
      "~n   'Montored By' after listen socket:  ~p",
      [MonitoredBy0, MonitoredBy1]),

    [LSockMon] = MonitoredBy1 -- MonitoredBy0,

    i("[acceptor]: "
      "~n   Listen Socket Monitor: ~p"
      "~n   Listen Socket info:    ~p",
      [LSockMon, socket:info(LSock)]),

    {ok, #{port := Port}} = socket:sockname(LSock),

    i("[acceptor] create ~w clients (connectors)", [NumSocks]),
    _Clients = [spawn_link(fun() ->
                                   otp18240_client(CID,
                                                   Domain, Proto,
                                                   Addr, Port)
                           end) || CID <- lists:seq(1, NumSocks)],

    i("[acceptor] accept ~w connections", [NumSocks]),
    ServSocks = [otp18240_do_accept(AID, LSock) ||
                    AID <- lists:seq(1, NumSocks)],

    i("[acceptor] close accepted connections when: "
      "~n   Listen Socket info: ~p", [socket:info(LSock)]),
    _ = [otp18240_do_close(S) || S <- ServSocks],

    %% at this point in time there should be no monitors from NIFs,
    %% because we're not accepting anything
    i("[acceptor] check monitor status"),
    MonitoredBy2 = monitored_by(),
    MonitoredBy3 = MonitoredBy2 -- [Parent, LSockMon],
    i("[acceptor] monitor status: "
      "~n   UnRefs:       ~p"
      "~n   MonitoredBy2: ~p"
      "~n   MonitoredBy3: ~p",
      [[Parent, LSockMon], MonitoredBy2, MonitoredBy3]),
    if
        ([] =:= MonitoredBy3) ->
            i("[acceptor] done"),
            socket:close(LSock),
            exit(ok);
        true ->
            socket:close(LSock),
            i("[acceptor] Unexpected monitors: "
              "~n   ~p", [MonitoredBy2]),
            exit({unexpected_monitors, MonitoredBy2})
    end.


otp18240_client(ID, Domain, Proto, Addr, PortNo) ->
    i("[connector ~w] try create connector socket", [ID]),
    {ok, Sock} = socket:open(Domain, stream, Proto, #{use_registry => false}),
    ok = socket:bind(Sock, #{family => Domain, addr => Addr, port => 0}),
    %% ok = socket:setopt(Sock, otp, debug, true),
    i("[connector ~w] try connect", [ID]),
    case socket:connect(Sock,
                        #{family => Domain, addr => Addr, port => PortNo}) of
        ok ->
            i("[connector ~w] connected - now try recv", [ID]),
            case socket:recv(Sock) of
                {ok, Data} ->
                    i("[connector ~w] received unexpected data: "
                      "~n   ~p", [ID, Data]),
                    (catch socket:close(Sock)),
                    exit('unexpected data');
                {error, closed} ->
                    i("[connector ~w] expected socket close", [ID]);
                {error, Reason} ->
                    i("[connector ~w] unexpected error when reading: "
                      "~n   ~p", [ID, Reason]),
                    (catch socket:close(Sock))
            end;
        {error, {completion_status, #{info := invalid_netname = R} = Reason}} ->
            i("[connector ~w] failed connecting: "
              "~n   ~p", [ID, Reason]),
            (catch socket:close(Sock)),
            exit({skip, R});
        {error, {completion_status, invalid_netname = Reason}} ->
            i("[connector ~w] failed connecting: "
              "~n   ~p", [ID, Reason]),
            (catch socket:close(Sock)),
            exit({skip, Reason});
        {error, enetunreach = Reason} ->
            i("[connector ~w] failed connecting: "
              "~n   ~p", [ID, Reason]),
            (catch socket:close(Sock)),
            exit({skip, Reason});

        {error, Reason} ->
            i("[connector ~w] failed connecting: "
              "~n   ~p", [ID, Reason]),
            (catch socket:close(Sock))
    end,
    i("[connector ~w] done", [ID]),
    ok.


otp18240_do_accept(ID, LSock) ->
    i("[acceptor ~w] try accept", [ID]),
    {ok, Sock} = socket:accept(LSock),
    i("[acceptor ~w] accepted: ~p", [ID, Sock]),
    {ID, Sock}.


otp18240_do_close({ID, Sock}) ->
    i("[acceptor ~w] try close ~p", [ID, Sock]),
    case socket:close(Sock) of
	ok ->
	    i("[acceptor ~w] socket closed", [ID]),
	    ok;
	{error, Reason} ->
	    i("[acceptor ~w] failed close socket: "
	      "~n   ~p", [ID, Reason]),
	    error
    end.



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% This test case is to verify that we do not leak monitors.
otp18635(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   is_not_windows(),
                   has_support_ipv4()
           end,
           fun() ->
                   InitState = #{},
                   ok = do_otp18635(InitState)
           end).


do_otp18635(_) ->
    Parent = self(),

    ?P("try create (listen) socket when"
       "~n   (gen socket) info: ~p"
       "~n   Sockets:           ~p",
       [socket:info(), socket:which_sockets()]),

    ?P("Get \"proper\" local socket address"),
    LSA = which_local_socket_addr(inet),

    {ok, LSock} = socket:open(inet, stream, #{use_registry => true}),

    ?P("bind (listen) socket to: "
       "~n   ~p", [LSA]),
    ok = socket:bind(LSock, LSA),

    ?P("make listen socket"),
    ok = socket:listen(LSock),

    ?P("get sockname for listen socket"),
    {ok, SA} = socket:sockname(LSock),

    %% ok = socket:setopt(LSock, otp, debug, true),

    % show handle returned from nowait accept
    ?P("try accept with timeout = nowait - expect select when"
       "~n   (gen socket) info: ~p"
       "~n   Sockets:           ~p",
       [socket:info(), socket:which_sockets()]),
    {select, {select_info, _, Handle}} = socket:accept(LSock, nowait),
    ?P("expected select result: "
       "~n   Select Handle:     ~p"
       "~n   (gen socket) info: ~p"
       "~n   Sockets:           ~p",
       [Handle, socket:info(), socket:which_sockets()]),

    ?SLEEP(?SECS(1)),

    %% perform a blocking accept that will fail (timeout)
    ?P("attempt accept with timeout = 500 - expect failure (timeout)"),
    {error, timeout} = socket:accept(LSock, 500),

    ?P("await abort message for the first accept call: "
       "~n   Select Handle:     ~p"
       "~n   (gen socket) info: ~p"
       "~n   Sockets:           ~p",
       [Handle, socket:info(), socket:which_sockets()]),
    receive
        {'$socket', LSock, abort, {Handle, cancelled}} ->
            ?P("received expected abort message"),
            ok
    end,

    %% spawn a client to connect
    ?P("spawn connector when"
       "~n   (gen socket) info:  ~p"
       "~n   Listen Socket info: ~p"
       "~n   Sockets:            ~p",
       [socket:info(), socket:info(LSock), socket:which_sockets()]),
    {Connector, MRef} =
        spawn_monitor(
          fun() ->
                  ?P("[connector] try create socket"),
                  {ok, CSock} = socket:open(inet, stream),
                  ?P("[connector] try connect: "
                       "~n   (server) ~p", [SA]),
                  ok = socket:connect(CSock, SA),
                  ?P("[connector] connected - inform parent"),
                  Parent ! {self(), connected},
                  ?P("[connector] await termination command"),
                  receive
                      {Parent, terminate} ->
                          ?P("[connector] terminate - close socket"),
                          (catch socket:close(CSock)),
                          exit(normal)
                  end
              end),

    ?P("await (connection-) confirmation from connector (~p)", [Connector]),
    receive
        {Connector, connected} ->
            ?P("connector connected"),
            ok
    end,

    %% We should *not* get *any* select messages;
    %% since the second call (that *replaced* the current active request)
    %% timeout out
    ?P("wait for a select message that should never come"),
    Result =
        receive
            {'$socket', LSock, select, AnyHandle} ->
                ?P("received unexpected select message when"
                   "~n   Unexpected Handle:  ~p"
                   "~n   (gen socket) info:  ~p"
                   "~n   Listen Socket info: ~p"
                   "~n   Sockets:            ~p",
                   [AnyHandle,
                    socket:info(), socket:info(LSock), socket:which_sockets()]),
                error
        after 5000 ->
                ?P("expected timeout"),
                ok
        end,

    ?P("try accept the waiting connection when"
       "~n   (gen socket) info:  ~p"
       "~n   Listen Socket info: ~p"
       "~n   Sockets:            ~p",
       [socket:info(), socket:info(LSock), socket:which_sockets()]),

    {ok, ASock} = socket:accept(LSock),

    ?P("connection accepted"
       "~n   (gen socket) info:    ~p"
       "~n   Accepted socket:      ~p"
       "~n   Accepted Socket info: ~p"
       "~n   Listen Socket info:   ~p"
       "~n   Sockets:              ~p",
       [socket:info(),
        ASock,
        socket:info(ASock),
        socket:info(LSock),
        socket:which_sockets()]),

    ?P("cleanup"),
    socket:close(LSock),
    socket:close(ASock),
    Connector ! {self(), terminate},
    receive
        {'DOWN', MRef, process, Connector, _} ->
            ?P("connector terminated"),
            ok
    end,

    ?SLEEP(?SECS(1)),

    ?P("done when"
       "~n   (gen socket) info: ~p"
       "~n   Sockets:           ~p", [socket:info(), socket:which_sockets()]),

    Result.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

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, LSA) ->
    try socket:bind(Sock, LSA) of
        ok = OK ->
            OK;
        {error, eaddrnotavail = Reason} ->
            ?SEV_IPRINT("Address not available"),
            throw({skip, Reason});
        {error, _} = ERROR ->
            ERROR
    catch
        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_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.


%% The point of this is to "ensure" that paths from different test runs
%% don't clash.

mk_unique_path() ->
    ?LIB:mk_unique_path().


which_local_socket_addr(local = Domain) ->
    #{family => Domain,
      path   => mk_unique_path()};

%% This gets the local socket address (not 127.0...)
%% We should really implement this using the (new) net module,
%% but until that gets the necessary functionality...
which_local_socket_addr(Domain) ->
    case ?KLIB:which_local_host_info(Domain) of
        {ok, [#{addr := Addr}|_]} ->
            #{family => Domain,
              addr   => Addr};
        {error, Reason} ->
            ?FAIL(Reason)
    end.

which_local_host_info(Domain) ->
    case ?KLIB:which_local_host_info(Domain) of
        {ok, [Info|_]} ->
            {ok, Info#{family => Domain}};
        {error, Reason} ->
            ?FAIL(Reason)
    end.



which_local_addr(local = _Domain) ->
    mk_unique_path();

%% This gets the local address (not 127.0...)
%% We should really implement this using the (new) net module,
%% but until that gets the necessary functionality...
which_local_addr(Domain) ->
    ?KLIB:which_local_addr(Domain).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

monitored_by() ->
    monitored_by(self()).
monitored_by(Pid) ->	
    {monitored_by, Refs} = erlang:process_info(Pid, monitored_by),
    Refs.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Here are all the *general* test case condition functions.

%% We also need to (be able to) figure out the multicast address,
%% which we only support for some platforms (linux and sunos).
%% We don't do that here, but since we can only do that (find a
%% multicast address) for specific platforms, we check that we are
%% on of those platforms here.
has_support_ip_multicast() ->
    case os:type() of
        {unix, OsName} when (OsName =:= linux) orelse
                            (OsName =:= sunos) ->
            case ?LIB:which_local_host_info(inet) of
                {ok, #{flags := Flags}} ->
                    case lists:member(multicast, Flags) of
                        true ->
                            ok;
                        false ->
                            not_supported(multicast)
                    end;
                {error, Reason} ->
                    not_supported({multicast, Reason})
            end;
        {unix, OsName} ->
            skip(?F("Not Supported: platform ~w", [OsName]));
        Type ->
            skip(?F("Not Supported: platform ~p", [Type]))
    end.


%% --- SOCK socket option test functions ---

has_support_sock_acceptconn() ->
    has_support_socket_option_sock(acceptconn).

has_support_sock_bindtodevice() ->
    has_support_socket_option_sock(bindtodevice).

has_support_sock_broadcast() ->
    has_support_ipv4(),
    has_support_socket_option_sock(broadcast),
    case ?LIB:which_local_host_info(inet) of
        {ok, #{flags := Flags}} ->
            case lists:member(broadcast, Flags) of
                true ->
                    ok;
                false ->
                    not_supported({broadcast, Flags})
            end;
        {error, Reason} ->
            not_supported({broadcast, Reason})
    end.

has_support_sock_debug() ->
    has_support_socket_option_sock(debug).

has_support_sock_domain() ->
    has_support_socket_option_sock(domain).

has_support_sock_dontroute() ->
    has_support_socket_option_sock(dontroute).

has_support_sock_keepalive() ->
    has_support_socket_option_sock(keepalive).

has_support_sock_reuseaddr() ->
    has_support_socket_option_sock(reuseaddr).

has_support_sock_bsp_state() ->
    has_support_socket_option_sock(bsp_state).

has_support_sock_exclusiveaddruse() ->
    has_support_socket_option_sock(exclusiveaddruse).

has_support_sock_maxdg() ->
    has_support_socket_option_sock(maxdg).

has_support_sock_max_msg_size() ->
    has_support_socket_option_sock(max_msg_size).

has_support_sock_oobinline() ->
    has_support_socket_option_sock(oobinline).

has_support_sock_passcred() ->
    has_support_socket_option_sock(passcred).

has_support_sock_peek_off() ->
    has_support_socket_option_sock(peek_off).

has_support_sock_peercred() ->
    has_support_socket_option_sock(peercred).

has_support_sock_priority() ->
    has_support_socket_option_sock(priority).

has_support_sock_rcvbuf() ->
    has_support_socket_option_sock(rcvbuf).

has_support_sock_rcvlowat() ->
    has_support_socket_option_sock(rcvlowat).

has_support_sock_rcvtimeo() ->
    has_support_socket_option_sock(rcvtimeo).

has_support_sock_sndbuf() ->
    has_support_socket_option_sock(sndbuf).

has_support_sock_sndlowat() ->
    has_support_socket_option_sock(sndlowat).

has_support_sock_sndtimeo() ->
    has_support_socket_option_sock(sndtimeo).

has_support_sock_timestamp() ->
    has_support_socket_option_sock(timestamp).


%% --- IP socket option test functions ---

has_support_ip_add_membership() ->
    has_support_socket_option_ip(add_membership).

has_support_ip_drop_membership() ->
    has_support_socket_option_ip(drop_membership).

has_support_ip_pktinfo() ->
    has_support_socket_option_ip(pktinfo).

has_support_ip_recvopts() ->
    has_support_socket_option_ip(recvopts).

has_support_ip_recvorigdstaddr() ->
    has_support_socket_option_ip(recvorigdstaddr).

has_support_ip_recvtos() ->
    has_support_socket_option_ip(recvtos).

has_support_ip_recvtos_and_or_sock_timestamp() ->
    case (socket:is_supported(options, ip, recvtos) orelse 
          socket:is_supported(options, socket, timestamp)) of
        true ->
            ok;
        false ->
            skip(?F("Neither needed opts "
                    "ip:recvtos or socket:timestamp supported", []))
    end.

has_support_ip_recvttl() ->
    has_support_socket_option_ip(recvttl).

has_support_ip_tos() ->
    has_support_socket_option_ip(tos).

has_support_ip_recverr() ->
    has_support_socket_option_ip(recverr).


%% --- IPv6 socket option test functions ---

has_support_ipv6_flowinfo() ->
    has_support_socket_option_ipv6(flowinfo).

has_support_ipv6_hoplimit_or_recvhoplimit() ->
    %% case (socket:is_supported(options, ipv6, recvhoplimit) orelse
    %%       socket:is_supported(options, ipv6, hoplimit)) of
    case is_any_options_supported([{ipv6, recvhoplimit}, {ipv6, hoplimit}]) of
	true ->
	    ok;
	false ->
	    skip(?F("Neither recvhoplimit or hoplimit supported", []))
    end.

has_support_ipv6_recvpktinfo() ->
    has_support_socket_option_ipv6(recvpktinfo).


has_support_ipv6_tclass_or_recvtclass() ->
    case is_any_options_supported([{ipv6, recvtclass}, {ipv6, tclass}]) of
	true ->
	    ok;
	false ->
	    skip(?F("Neither recvtclass or tclass supported", []))
    end.


has_support_ipv6_recverr() ->
    has_support_socket_option_ipv6(recverr).


%% --- TCP socket option test functions ---

has_support_tcp_congestion() ->
    has_support_socket_option_tcp(congestion).

has_support_tcp_cork() ->
    has_support_socket_option_tcp(cork).

has_support_tcp_maxseg() ->
    has_support_socket_option_tcp(maxseg).

has_support_tcp_nodelay() ->
    has_support_socket_option_tcp(nodelay).

has_support_tcp_keepcnt() ->
    has_support_socket_option_tcp(keepcnt).

has_support_tcp_keepidle() ->
    has_support_socket_option_tcp(keepidle).

has_support_tcp_keepintvl() ->
    has_support_socket_option_tcp(keepintvl).


%% --- UDP socket option test functions ---

has_support_udp_cork() ->
    has_support_socket_option_udp(cork).


%% --- General purpose socket option test functions ---

has_support_socket_option_sock(Opt) ->
    has_support_socket_option(socket, Opt).

has_support_socket_option_ip(Opt) ->
    has_support_socket_option(ip, Opt).

has_support_socket_option_ipv6(Opt) ->
    has_support_socket_option(ipv6, Opt).

has_support_socket_option_tcp(Opt) ->
    has_support_socket_option(tcp, Opt).

has_support_socket_option_udp(Opt) ->
    has_support_socket_option(udp, Opt).


has_support_socket_option(Level, Option) ->
    case socket:is_supported(options, Level, Option) of
        true ->
            ok;
        false ->
            skip(?F("Not Supported: ~w option ~w", [Level, Option]))
    end.

is_any_options_supported(Options) ->
    Pred = fun({Level, Option}) -> socket:is_supported(options, Level, Option) end,
    lists:any(Pred, Options).


%% --- Send flag test functions ---

has_support_msg_flag(Flag) ->
    case socket:is_supported(msg_flags, Flag) of
        true ->
            ok;
        false ->
            skip(?F("Message flag ~w *Not* Supported", [Flag]))
    end.


%% Checks that the version is "good enough" (of the specified platform).

is_good_enough_linux(CondVsn) ->
    is_good_enough_platform(unix, linux, CondVsn).

is_good_enough_darwin(CondVsn) ->
    is_good_enough_platform(unix, darwin, CondVsn).

is_good_enough_montavista(_Vsn) ->
    %% We have *one* old M, which have kernel version 2.6.10.
    %% So if its that kernel version, we only need to check
    %% if its M (no need to figure out the version of M).
    case os:type() of
        {unix, linux} ->
            case os:version() of
                {2,6,10} ->
                    case string:trim(os:cmd("cat /etc/issue")) of
                        "MontaVista" ++ _ = V -> % Stone age MontaVista => Skip
                            skip(V);
                        _ ->
                            ok
                    end;
                _ ->
                    ok
            end;
        _ ->
            ok
    end.
                    

is_good_enough_platform(Family, Name, CondVsn) ->
    case os:type() of
	{Family, Name} ->
	    ID = fun() -> ?F("~w:~w", [Family, Name]) end,
	    is_good_enough_platform2(os:version(), CondVsn, ID);
	_ ->
	    ok
    end.

is_good_enough_platform2(Vsn, CondVsn, _) when (Vsn > CondVsn) ->
    ok;
is_good_enough_platform2(Vsn, CondVsn, ID) ->
    skip(?F("Not 'good enough' ~s (~p <= ~p)", [ID(), Vsn, CondVsn])).

is_not_freebsd() ->
    is_not_platform(freebsd, "FreeBSD").

is_not_openbsd() ->
    is_not_platform(openbsd, "OpenBSD").

is_not_netbsd() ->
    is_not_platform(netbsd, "NetBSD").

is_not_darwin() ->
    is_not_platform(darwin, "Darwin").

is_not_windows() ->
    case os:type() of
        {win32, nt} ->
            skip("This does not work on Windows");
        _ ->
            ok
    end.

is_not_platform(Platform, PlatformStr)
  when is_atom(Platform) andalso is_list(PlatformStr) ->
      case os:type() of
          {unix, Platform} ->
              skip("This does not work on " ++ PlatformStr);
        _ ->
            ok
    end.
  

unix_domain_socket_host_cond() ->
    unix_domain_socket_host_cond(os:type(), os:version()).

unix_domain_socket_host_cond({unix, linux}, {M, _, _}) when (M < 3) ->
    skip("TC may not work on this version");
unix_domain_socket_host_cond(_, _) ->
    ok.

has_support_unix_domain_socket() ->
    case socket:is_supported(local) of
	true ->
	    ok;
	false ->
	    skip("Not supported")
    end.

has_support_sctp() ->
    case os:type() of
        {win32, _} ->
            skip("Not supported");
        {unix, netbsd} ->
            %% XXX We will have to investigate this later...
            skip("Not supported");
        _ ->
            case socket:is_supported(sctp) of
                true ->
                    ok;
                false ->
                    skip("Not supported")
            end
    end.


%% The idea is that this function shall test if the test host has 
%% support for IPv4 or IPv6. If not, there is no point in running corresponding tests.
%% Currently we just skip.
has_support_ipv4() ->
    ?KLIB:has_support_ipv4().

has_support_ipv6() ->
    ?KLIB:has_support_ipv6().

inet_or_inet6() ->
    try
        has_support_ipv4(),
        inet
    catch
        throw:{skip, _Reason} ->
            has_support_ipv6(),
            inet6
    end.

has_support_sendfile() ->
    try socket:is_supported(sendfile) of
        true ->
            ok;
        false ->
            skip("Not supported: sendfile")
    catch
        error : notsup ->
            skip("Not supported: socket")
    end.


has_support_net_if_names() ->
    try net:if_names() of
        {ok, N} when is_list(N) ->
            ok;
        _ ->
            skip("Not supported: net:if_names()")
    catch
        error : notsup ->
            skip("Not supported: net")
    end.

has_support_ioctl_requests() ->
    try socket:supports(ioctl_requests) of
        Reqs when is_list(Reqs) ->
            ok;
        _ ->
            skip("Not supported: ioctl_requests")
    catch
        error : notsup ->
            skip("Not supported: socket")
    end.

has_support_ioctl_gifconf() ->
    has_support_ioctl_request(gifconf).

has_support_ioctl_nread() ->
    has_support_ioctl_request(nread).

has_support_ioctl_gifname() ->
    has_support_ioctl_request(gifname).

has_support_ioctl_gifindex() ->
    has_support_ioctl_request(gifindex).

has_support_ioctl_gifaddr() ->
    has_support_ioctl_request(gifaddr).

has_support_ioctl_gifdstaddr() ->
    has_support_ioctl_request(gifdstaddr).

has_support_ioctl_gifbrdaddr() ->
    has_support_ioctl_request(gifbrdaddr).

has_support_ioctl_gifnetmask() ->
    has_support_ioctl_request(gifnetmask).

has_support_ioctl_gifmtu() ->
    has_support_ioctl_request(gifmtu).

has_support_ioctl_gifhwaddr() ->
    has_support_ioctl_request(gifhwaddr).

has_support_ioctl_giftxqlen() ->
    has_support_ioctl_request(giftxqlen).

has_support_ioctl_gifflags() ->
    has_support_ioctl_request(gifflags).

has_support_ioctl_gifmap() ->
    has_support_ioctl_request(gifmap).

has_support_ioctl_tcp_info() ->
    has_support_ioctl_request(tcp_info).

has_support_ioctl_request(Req) when is_atom(Req) ->
    try socket:is_supported(ioctl_requests, Req) of
        true ->
            ok;
        false ->
            skip(f("Not supported: ioctl_request: ~w", [Req]))
    catch
        error : notsup ->
            skip("Not supported: socket")
    end.


%% has_support_ioctl_flags() ->
%%     try socket:supports(ioctl_flags) of
%%         Reqs when is_list(Reqs) ->
%%             ok;
%%         _ ->
%%             skip("Not supported: ioctl_flags")
%%     catch
%%         error : notsup ->
%%             skip("Not supported: socket")
%%     end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

unlink_path(Path) ->
    unlink_path(Path, fun() -> ok end, fun() -> ok end).

unlink_path(Path, Success, Failure)
  when is_function(Success, 0), is_function(Failure, 0) ->
    case Path of
        undefined ->
            ?SEV_IPRINT("not a path to unlink"),
                    Success();
        _ ->
            ?SEV_IPRINT("try unlink path: "
                        "~n   ~s", [Path]),
            case file:delete(Path) of
                ok ->
                    ?SEV_IPRINT("path unlinked: "
                                "~n   Path: ~s", [Path]),
                    Success();
                Error ->
                    ?SEV_EPRINT("unlink failed: "
                                "~n   Path: ~s"
                                "~n   Res:  ~p", [Path, Error]),
                    Failure()
            end
    end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

not_supported(What) ->
    skip({not_supported, What}).

not_yet_implemented() ->
    skip("not yet implemented").

skip(Reason) ->
    throw({skip, Reason}).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

lookup(Key, Default, Config) ->
    case lists:keysearch(Key, 1, Config) of
        {value, {Key, Value}} ->
            Value;
        _ ->
            Default
    end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

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("caught[tc] (skip): "
                    %%   "~n   C:    ~p"
                    %%   "~n   SKIP: ~p"
                    %%   "~n", [C, SKIP]),
                    tc_end( f("skipping(caught,~w,tc)", [C]) ),
                    SKIP;
                C:E:S ->
                    %% i("caught[tc]: "
                    %%   "~n   C: ~p"
                    %%   "~n   E: ~p"
                    %%   "~n   S: ~p"
                    %%    "~n", [C, E, S]),
                    tc_end( f("failed(caught,~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("caught[cond] (skip): "
            %%   "~n   C:    ~p"
            %%   "~n   SKIP: ~p"
            %%   "~n", [C, SKIP]),
            tc_end( f("skipping(caught,~w,cond)", [C]) ),
            SKIP;
        C:E:S ->
            %% i("caught[cond]: "
            %%   "~n   C: ~p"
            %%   "~n   E: ~p"
            %%   "~n   S: ~p"
            %%   "~n", [C, E, S]),
            tc_end( f("failed(caught,~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.
    
   

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% start_node(Name) ->
%%     start_node(Name, 5000).

start_node(Name, Timeout) when is_integer(Timeout) andalso (Timeout > 0) ->
    try ?CT_PEER(#{name => Name, wait_boot => Timeout}) of
        {ok, Peer, Node} ->
            ?SEV_IPRINT("Started node ~p", [Name]),
            {Peer, Node};
        {error, Reason} ->
            ?SEV_EPRINT("failed starting node ~p (=> SKIP):"
                        "~n   ~p", [Name, Reason]),
            skip(Reason)
    catch
        Class:Reason:Stack ->
            ?SEV_EPRINT("Failed starting node: "
                        "~n   Class:  ~p"
                        "~n   Reason: ~p"
                        "~n   Stack:  ~p",
                        [Class, Reason, Stack]),
            skip({node_start, Class, Reason})
    end.

            
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

mq() ->
    mq(self()).

mq(Pid) when is_pid(Pid) ->
    {messages, MQ} = process_info(Pid, messages),
    MQ.

             
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

nowait(Config) ->
    case lists:member({select_handle, true}, Config) of
        true ->
            make_ref();
        false ->
            nowait
    end.

sock_port(S) ->
    case socket:sockname(S) of
        {ok, #{port := Port}} -> Port;
        {ok, #{}}             -> undefined
    end.

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, []).

