%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2024-2025. 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_MISC     include
%%         ESOCK_TEST_API_BASIC    include
%%         ESOCK_TEST_API_SENDFILE include
%%         ESOCK_TEST_API_FROM_FD  include
%%         ESOCK_TEST_API_OPTS     include
%%         ESOCK_TEST_API_OPWTO    include
%%
%% Variable that controls "verbosity" of the test case(s):
%%
%%         ESOCK_TEST_QUIET: true (default) | false
%%

%% 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).
%%
%% SUITE = socket_api_SUITE.
%%
%% S = fun() -> ts:run(kernel, socket_api_SUITE, [batch]) end.
%% S = fun(SUITE) -> ts:run(kernel, SUITE, [batch]) end.
%% G = fun(GROUP) -> ts:run(kernel, socket_api_SUITE, {group, GROUP}, [batch]) end.
%% G = fun(SUITE, GROUP) -> ts:run(kernel, SUITE, {group, GROUP}, [batch]) end.
%% T = fun(TC) -> ts:run(kernel, socket_api_SUITE, TC, [batch]) end.
%%
%% S = fun() -> ct:run_test([{suite, socket_api_SUITE}]) end.
%% S = fun(SUITE) -> ct:run_test([{suite, SUITE}]) end.
%% G = fun(GROUP) -> ct:run_test([{suite, socket_api_SUITE}, {group, GROUP}]) end.
%% G = fun(SUITE, GROUP) -> ct:run_test([{suite, SUITE}, {group, GROUP}]) end.
%% T = fun(TC) -> ct:run_test([{suite, socket_api_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_api_SUITE).

-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
-include("socket_test_evaluator.hrl").
-include("kernel_test_lib.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_seqp_sctp4/1,
	 api_b_open_and_close_seqp_sctp6/1,
	 api_b_open_and_close_stream_sctp4/1,
	 api_b_open_and_close_stream_sctp6/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_stream_sctp4/1,
         api_b_sendv_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_stream_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_b_sendv_tcp4/1,
         api_b_sendv_tcp6/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_send_and_recv_sctp4/1,
         api_a_send_and_recv_sctp6/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
        ]).



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

-define(SLIB,       socket_test_lib).
-define(KLIB,       kernel_test_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),    ?SLIB:f(F, A)).
%% -define(P(F),       ?SLIB:print(F)).
%% -define(P(F, A),    ?SLIB:print(F, A)).

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

%% -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 = [
              {misc,                   "ESOCK_TEST_API_MISC",     include},
              {basic,                  "ESOCK_TEST_API_BASIC",    include},
              {sendfile,               "ESOCK_TEST_API_SENDFILE", include},
              {from_fd,                "ESOCK_TEST_API_FROM_FD",  include},
              {options,                "ESOCK_TEST_API_OPTS",     include},
              {operation_with_timeout, "ESOCK_TEST_API_OPWTO",    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() -> 
    [{misc,                    [], api_misc_cases()},
     {basic,                   [], api_basic_cases()},
     {sendfile,                [], api_sendfile_cases()},
     {from_fd,                 [], api_from_fd_cases()},
     {async,                   [], api_async_cases()},
     {async_ref,               [], api_async_cases()},
     {options,                 [], api_options_cases()},
     {options_otp,             [], api_options_otp_cases()},
     {options_socket,          [], api_options_socket_cases()},
     {option_sock_acceptconn,  [], api_option_sock_acceptconn_cases()},
     {option_sock_passcred,    [], api_option_sock_passcred_cases()},
     {option_sock_priority,    [], api_option_sock_priority_cases()},
     {option_sock_buf,         [], api_option_sock_buf_cases()},
     {option_sock_lowat,       [], api_option_sock_lowat_cases()},
     {option_sock_timeo,       [], api_option_sock_timeo_cases()},
     {option_sock_timestamp,   [], api_option_sock_timestamp_cases()},
     {options_ip,              [], api_options_ip_cases()},
     {options_ipv6,            [], api_options_ipv6_cases()},
     {options_tcp,             [], api_options_tcp_cases()},
     {options_udp,             [], api_options_udp_cases()},
     %% {options_sctp,            [], api_options_sctp_cases()},
     {operation_with_timeout,  [], api_op_with_timeout_cases()}
    ].
     
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_seqp_sctp4,
     api_b_open_and_close_seqp_sctp6,
     api_b_open_and_close_stream_sctp4,
     api_b_open_and_close_stream_sctp6,
     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_stream_sctp4,
     api_b_sendv_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_stream_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_b_sendv_tcp4,
     api_b_sendv_tcp6
    ].

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_send_and_recv_sctp4,
     api_a_send_and_recv_sctp6,
     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, options_otp},
     {group, options_socket},
     {group, options_ip},
     {group, options_ipv6},
     {group, options_tcp},
     {group, options_udp}
     %% {group, 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, 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, option_sock_passcred},
     api_opt_sock_peek_off_tcpL,
     api_opt_sock_peercred_tcpL,
     {group, option_sock_priority},
     {group, option_sock_buf},
     {group, option_sock_lowat},
     {group, option_sock_timeo},
     {group, 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
    ].



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

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(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(from_fd = _GroupName, Config) ->
    has_multi_scheds(),
    Config;
init_per_group(_GroupName, Config) ->
    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]),
    Config.

end_per_testcase(_TC, Config) ->
    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_seqp_sctp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_sctp() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => seqpacket,
                                 protocol => sctp},
                   ok = api_b_open_and_close(InitState)
           end).


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

%% Basically open (create) and close an IPv6 SCTP (seqpacket) socket.
%% With some extra checks...
api_b_open_and_close_seqp_sctp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_sctp(),
                   has_support_ipv6()
           end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => seqpacket,
                                 protocol => sctp},
                   ok = api_b_open_and_close(InitState)
           end).


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

%% Basically open (create) and close an IPv4 SCTP (stream) socket.
%% With some extra checks...
api_b_open_and_close_stream_sctp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_sctp() end,
           fun() ->
                   InitState = #{domain   => inet,
                                 type     => stream,
                                 protocol => sctp},
                   ok = api_b_open_and_close(InitState)
           end).


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

%% Basically open (create) and close an IPv6 SCTP (stream) socket.
%% With some extra checks...
api_b_open_and_close_stream_sctp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_sctp(),
                   has_support_ipv6()
           end,
           fun() ->
                   InitState = #{domain   => inet6,
                                 type     => stream,
                                 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}}) -> 
                           ?SEV_IPRINT("expected domain: ~p", [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}}) ->
                           ?SEV_IPRINT("expected type: ~p", [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}}) ->
                           ?SEV_IPRINT("expected protocol: ~p", [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 IPv4 SCTP (stream) socket.
api_b_send_and_recv_stream_sctp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_sctp(),
                   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  => sctp,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_send_and_recv_conn(InitState)
           end).


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

%% Basically send and receive using the sendv and recv functions
%% on an IPv4 TCP (stream) socket.
api_b_sendv_and_recv_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() -> has_support_ipv4() end,
           fun() ->
                   Send = fun(Sock, Data) ->
                                  %% Since we are just reusing this test case,
                                  %% the I/O vector is just one binary.
                                  %% _ = socket:setopt(Sock, otp, debug, true),
                                  Res = socket:sendv(Sock, [Data]),
                                  %% _ = socket:setopt(Sock, otp, debug, false),
                                  Res
                          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 = #{%% ctrl => CMsgs,
                                          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) ->
			   ?SEV_IPRINT("try accept"),
                           case socket:accept(LSock) of
                               {ok, Sock} ->
                                   ?SEV_IPRINT("accepted: ~n   ~p", [Sock]),
                                   {ok, State#{csock => Sock}};
                               {error, Reason} = ERROR ->
                                   ?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]).



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

-define(REQ_IOV_SND, [<<0:32>>,
                      <<1:32>>,
                      <<2:32>>,
                      <<3:32>>,
                      <<4:32>>,
                      <<5:32>>,
                      <<6:32>>,
                      <<7:32>>,
                      <<8:32>>,
                      <<9:32>>]).
-define(REQ_IOV_RCV, erlang:iolist_to_binary(?REQ_IOV_SND)).
-define(REP_IOV_SND, [<<10:32>>,
                      <<11:32>>,
                      <<12:32>>,
                      <<13:32>>,
                      <<14:32>>,
                      <<15:32>>,
                      <<16:32>>,
                      <<17:32>>,
                      <<18:32>>,
                      <<19:32>>]).
-define(REP_IOV_RCV, erlang:iolist_to_binary(?REP_IOV_SND)).

ensure_sufficient_iov_max() ->
    case socket:info() of
        #{iov_max := IOVMax} ->
            IOVUsed = length(?REQ_IOV_SND),
            if
                IOVMax >= IOVUsed ->
                    ok;
                true ->
                    skip({iov_max, IOVMax, IOVUsed})
            end;
        _ ->
            skip(no_info)
    end.
    

%% Basically send a I/O vector and receive using the sendv and recv
%% functions on an IPv4 TCP (stream) socket.
api_b_sendv_tcp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_ipv4(),
                   ensure_sufficient_iov_max()
           end,
           fun() ->
                   Send = fun(Sock, IOV) when is_list(IOV) ->
                                  %% Since we are just reusing this test case,
                                  %% the I/O vector is just one binary.
                                  %% _ = socket:setopt(Sock, otp, debug, true),
                                  Res = socket:sendv(Sock, IOV),
                                  %% _ = socket:setopt(Sock, otp, debug, false),
                                  Res
                          end,
                   Recv = fun(Sock) ->
                                  socket:recv(Sock)
                          end,
                   InitState = #{domain => inet,
                                 type   => stream,
                                 proto  => tcp,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_sendv(InitState)
           end).


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

%% Basically send a I/O vector and receive using the sendv and recv
%% functions on an IPv6 TCP (stream) socket.
api_b_sendv_tcp6(_Config) when is_list(_Config) ->
    ?TT(?SECS(10)),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_ipv6(),
                   ensure_sufficient_iov_max()
           end,
           fun() ->
                   Send = fun(Sock, IOV) when is_list(IOV) ->
                                  %% Since we are just reusing this test case,
                                  %% the I/O vector is just one binary.
                                  %% _ = socket:setopt(Sock, otp, debug, true),
                                  Res = socket:sendv(Sock, IOV),
                                  %% _ = socket:setopt(Sock, otp, debug, false),
                                  Res
                          end,
                   Recv = fun(Sock) ->
                                  socket:recv(Sock)
                          end,
                   InitState = #{domain => inet6,
                                 type   => stream,
                                 proto  => tcp,
                                 send   => Send,
                                 recv   => Recv},
                   ok = api_b_sendv(InitState)
           end).


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

api_b_sendv(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}) ->
                           Rep = ?REQ_IOV_RCV,
                           case Recv(Sock) of
                               {ok, Rep} ->
                                   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, ?REP_IOV_SND)
                   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, ?REQ_IOV_SND)
                   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}) ->
                           Rep = ?REP_IOV_RCV,
                           {ok, 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 (stream) socket
%% using sendmsg and recvmsg.
api_b_sendmsg_and_recvmsg_stream_sctp4(_Config) when is_list(_Config) ->
    ?TT(?SECS(5)),
    tc_try(?FUNCTION_NAME,
           fun() ->
		   has_support_sctp(),
                   has_support_ipv4()%% ,
		   %% 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) ->
                                  case socket:recvmsg(Sock) of
                                      {ok, #{flags := [eor],
                                             addr  := _,
                                             iov   := [Data]}} ->
                                          {ok, Data};
                                      {ok, Msg} ->
                                          {error, {msg, Msg}};
                                      %% {ok, #{addr  := Source,
                                      %%        iov   := [Data]}} ->
                                      %%     {ok, {Source, Data}};
                                      {error, _} = ERROR ->
                                          ERROR
                                  end
                          end,
                   InitState = #{domain => inet,
                                 type   => stream,
                                 proto  => sctp,
                                 send   => Send,
                                 recv   => Recv},
                   %% ok = api_b_send_and_recv_sctp(InitState)
                   ok = api_b_send_and_recv_conn(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 domain or not",
           cmd  => fun(#{sock1 := Sock1} = State) ->
                           case socket:getopt(Sock1, socket, domain) of
                               {ok, _} ->
                                   ?SEV_IPRINT("domain accessible"),
                                   {ok, State#{provide_domain => false}};
                               {error, Reason} ->
                                   ?SEV_IPRINT("failed get domain: "
                                               "~n   ~p", [Reason]),
                                   {ok, State#{provide_domain => true}}
                           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_domain   := PD,
                         domain           := Domain,
                         provide_protocol := PP,
                         protocol         := Protocol} = State) ->
                           Opts =
                               case {PD, PP} of
                                   {true, true} ->
                                       #{dup      => DUP,
                                         domain   => Domain,
                                         protocol => Protocol};
                                   {true, false} ->
                                       #{dup      => DUP,
                                         domain   => Domain};
                                   {false, true} ->
                                       #{dup      => DUP,
                                         protocol => Protocol};
                                   {false, false} ->
                                       #{dup      => DUP}
                               end,
                           ?SEV_IPRINT("try open socket with"
                                       "~n   FD:   ~p"
                                       "~n   Opts: ~p", [FD, Opts]),
                           case socket:open(FD, Opts) of
                               {ok, Sock2} ->
                                   ?SEV_IPRINT("socket 2 open"),
                                   {ok, State#{sock2 => Sock2}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed open socket with FD "
                                                 "(p, ~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,
                         domain := Domain} = State) ->
                           ?SEV_IPRINT("try create socket with: "
                                       "~n   FD:     ~p"
                                       "~n   DUP:    ~p"
                                       "~n   Domain: ~p", [FD, DUP, Domain]),
                           %% On some platforms (Darwin and NetBSD) domain
                           %% is needed...
                           case socket:open(FD, #{dup    => DUP,
                                                  domain => Domain}) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error,
                                {invalid,{options,domain,#{dup := DUP}}} = R} ->
                                   ?SEV_EPRINT("failed create socket:"
                                               "~n   ~p", [R]),
                                   {skip, domain};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed create socket:"
                                               "~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 (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(?FUNCTION_NAME,
           fun() ->
                   has_support_ipv4()
           end,
           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,
                         domain := Domain} = State) ->
                           ?SEV_IPRINT("try create socket with: "
                                       "~n   FD:     ~p"
                                       "~n   DUP:    ~p"
                                       "~n   Domain: ~p", [FD, DUP, Domain]),
                           case socket:open(FD, #{dup    => DUP,
                                                  domain => Domain}) of
                               {ok, Sock} ->
                                   {ok, State#{sock => Sock}};
                               {error,
                                {invalid,{options,domain,#{dup := DUP}}} = R} ->
                                   ?SEV_EPRINT("failed create socket:"
                                               "~n   ~p", [R]),
                                   {skip, domain};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("failed create socket:"
                                               "~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 (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(?FUNCTION_NAME,
           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,
                                 proto     => tcp,
                                 send      => Send,
                                 recv      => Recv,
                                 recv_sref => Nowait},
                   ok = api_a_send_and_recv_stream(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(?FUNCTION_NAME,
           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,
                                 proto     => tcp,
                                 send      => Send,
                                 recv      => Recv,
                                 recv_sref => Nowait},
                   ok = api_a_send_and_recv_stream(Config, InitState)
           end).



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

%% Basically send and receive using the "common" functions (send and recv)
%% on an IPv4 SCTP (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_sctp4(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_sctp(),
                   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,
                                 proto     => sctp,
                                 send      => Send,
                                 recv      => Recv,
                                 recv_sref => Nowait},
                   ok = api_a_send_and_recv_stream(Config, InitState)
           end).



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

%% Basically send and receive using the "common" functions (send and recv)
%% on an IPv4 SCTP (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_sctp6(Config) when is_list(Config) ->
    ?TT(?SECS(10)),
    Nowait = nowait(Config),
    tc_try(?FUNCTION_NAME,
           fun() ->
                   has_support_sctp(),
                   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,
                                 proto     => sctp,
                                 send      => Send,
                                 recv      => Recv,
                                 recv_sref => Nowait},
                   ok = api_a_send_and_recv_stream(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(?FUNCTION_NAME,
           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,
                                 proto     => tcp,
                                 send      => Send,
                                 recv      => Recv,
                                 recv_sref => Nowait},
                   ok = api_a_send_and_recv_stream(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(?FUNCTION_NAME,
           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,
                                 proto     => tcp,
                                 send      => Send,
                                 recv      => Recv,
                                 recv_sref => Nowait},
                   ok = api_a_send_and_recv_stream(Config, InitState)
           end).



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

api_a_send_and_recv_stream(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, proto := Proto} = State) ->
                           ?SEV_IPRINT("try create (open) ~w (~w) socket",
                                       [Proto, Domain]),
                           case socket:open(Domain, stream, 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(#{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, proto := Proto} = State) ->
                           ?SEV_IPRINT("try create (open) ~w (~w) socket",
                                       [Proto, Domain]),
                           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},

         #{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 = 1)",
           cmd  => fun(#{tester := Tester} = State) ->
                           case ?SEV_AWAIT_CONTINUE(Tester, tester, recv) of
                               {ok, MsgSz} ->
                                   ?SEV_IPRINT("Msg Sz to expect: ~p", [MsgSz]),
                                   {ok, State#{msg_sz => MsgSz}};
                               {error, _} = ERROR ->
                                   ERROR
                           end
                   end},
         #{desc => "attempt to recv (1)",
           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 (byte_size(Data) =:= MsgSz) ->
                                   ?SEV_IPRINT("expected recv result"),
                                   ok;
                               {ok, Data} -> 
                                  {error,
                                    {invalid_msg_sz, MsgSz, byte_size(Data)}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("unexpected recv error:"
                                               "~n   Reason: ~p", [Reason]),
                                   ERROR
                           end
                   end},
         #{desc => "announce ready (recv initial = 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 (2)",
           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 (byte_size(Data) =:= MsgSz) ->
                                   ?SEV_IPRINT("expected recv result"),
                                   ok;
                               {ok, Data} ->
                                   ?SEV_EPRINT("got unexpected amount of data:"
                                               "~n   Expected: ~p"
                                               "~n   Got:      ~p",
                                               [MsgSz, byte_size(Data)]),
                                   {error,
                                    {invalid_msg_sz, MsgSz, byte_size(Data)}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("unexpected recv error:"
                                               "~n   Reason: ~p", [Reason]),
                                   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)",
           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 (3)",
           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 (byte_size(Data) =:= MsgSz) ->
                                   ?SEV_IPRINT("expected recv result"),
                                   ok;
                               {ok, Data} ->
                                   ?SEV_EPRINT("got unexpected amount of data:"
                                               "~n   Expected: ~p"
                                               "~n   Got:      ~p",
                                               [MsgSz, byte_size(Data)]),
                                   {error,
                                    {invalid_msg_sz, MsgSz, byte_size(Data)}};
                               {error, Reason} = ERROR ->
                                   ?SEV_EPRINT("unexpected recv error:"
                                               "~n   Reason: ~p", [Reason]),
                                   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)),
    Cond =
        fun() ->
                has_support_ipv4(),
                has_support_sock_broadcast()
        end,
    Pre  =
        fun() ->
                Platform =
                    case os:type() of
                        {unix, Flavor} ->
                            Flavor;
                        _ ->
                            other
                    end,
                Domain = inet,
                Opt    = broadcast,
                case which_local_host_info(Domain) of
                    {ok, #{name      := Name,
                           addr      := {A1, A2, A3, _}}}
                      when (A1 =:= 192) andalso
                           (A2 =:= 168) andalso
                           (A3 =:= 0) andalso
                           (Platform =:= darwin) ->
                        ?SEV_IPRINT("home network (~p) on darwin - skip",
                                    [Name]),
                        skip(home_network_on_darwin);
                    {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},
                        Set = fun(S, Val) when is_boolean(Val) ->
                                      socket:setopt(S, socket, Opt, Val)
                              end,
                        Get = fun(S) ->
                                      socket:getopt(S, socket, Opt)
                              end,
                        #{domain => Domain,
                          opt    => Opt,
                          set    => Set,
                          get    => Get,
                          lsa    => LSA,
                          bsa    => BSA};
                    {ok, _} ->
                        skip(no_valid_broadcast_address);
                    {error, Reason} ->
                        skip({local_host_info, Reason})
                end
        end,
    TC   = fun(State) -> do_api_opt_sock_broadcast(State) end,
    Post = fun(_) -> ok end,
    tc_try(?FUNCTION_NAME, Cond, Pre, TC, Post).


do_api_opt_sock_broadcast(InitState) ->
    TesterSeq =
        [
         #{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(#{get   := Get,
                         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(#{set   := Set,
                         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(#{get   := Get,
                         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(#{get   := Get,
                         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 (send) 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
        ],

    i("start tester evaluator"),
    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 ?SLIB: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,
    %%                                 ?SLIB: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 ?SLIB: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 ?SLIB: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 => "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 => "extract the (expected) recvtos \"default\" value",
           cmd  => fun(#{sock_dst := Sock} = State) ->
                           {ok, DefValue} = socket:getopt(Sock, ip, tos),
                           ?SEV_IPRINT("(expected) recvtos def value: ~w",
                                       [DefValue]),
                           {ok, State#{dst_def_value => DefValue}}
                   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,
                         dst_def_value := DefValue,
                         sa_src        := Src,
                         recv          := Recv}) ->
                           ExpCHdr1 = #{level => ip,
                                        type  => tos,
                                        value => DefValue,
                                        data  => any},
                           ExpCHdr2 = #{level => ip,
                                        type  => recvtos,
                                        value => DefValue,
                                        data  => any},
                           case Recv(Sock) of
                               {ok, {Src, [#{level := ip,
                                             type  := TOS,
                                             value := DefValue}], ?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:"
                                               "~n     Alt 1:  ~p"
                                               "~n     Alt 2:  ~p"
                                               "~n   Recv CHdrs:    ~p"
                                               "~n   Expect Msg:    ~p"
                                               "~n   Recv Msg:      ~p",
                                               [Src, BadSrc,
                                                ExpCHdr1, ExpCHdr2, 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, Value} ->
                                   %% On FreeBSD 14 default value is not 0!
                                   case os:type() of
                                       {unix, freebsd} ->
                                           case os:version() of
                                               {14, _, _}
                                                 when (Value =:= mincost) ->
                                                   ok;
                                               _ ->
                                                   ?SEV_EPRINT("Unexpected "
                                                               "default "
                                                               "tos: ~p",
                                                               [Value]),
                                                   {error, {unexpected, Value}}
                                           end;
                                       _ ->
                                           ?SEV_EPRINT("Unexpected "
                                                       "default "
                                                       "tos: ~p",
                                                       [Value]),
                                           {error, {unexpected, Value}}
                                   end;
                               {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, peer := Peer} = State) ->
                           case api_toc_tcp_client_start(Node) of
                               Pid when is_pid(Pid) ->
                                   ?SEV_IPRINT("remote client ~p started",
                                               [Pid]),
                                   {ok, State#{rclient => Pid}};
                               {error, Reason} ->
                                   ?SEV_EPRINT(
                                      "Failed starting (remote) client:"
                                      "~n   Reason:     ~p"
                                      "~nwhen"
                                      "~n   Peer State: ~p",
                                      [Reason, peer:get_state(Peer)]),
                                   SkipReason =
                                       ?SLIB:f("Remote Client Start: ~p",
                                               [Reason]),
                                   {skip, SkipReason}
                           end
                   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} ->
                                   ?SEV_IPRINT("ok => success"),
                                   {ok, maps:remove(connect_limit, State)};
                               {ok, {error, {connect_limit_reached,R,L}}} ->
                                   ?SEV_IPRINT("limit reached - skip: "
                                               "~n   R: ~p"
                                               "~n   L: ~p", [R, L]),
                                   {skip,
                                    ?SLIB:f("Connect limit reached ~w: ~w",
                                           [L, R])};
                               {ok, Result} ->
                                   ?SEV_IPRINT("result: "
                                               "~n   ~p", [Result]),
                                   Result;
                               {error, _Reason} = ERROR ->
                                   ?SEV_IPRINT("error: "
                                               "~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 (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) ->
    api_toc_tcp_client_start(Node, 5000).
api_toc_tcp_client_start(Node, Timeout) ->
    case net_adm:ping(Node) of
        pong ->
            Self = self(),
            Fun  = fun() -> api_toc_tcp_client(Self) end,
            {Pid, MRef} = erlang:spawn_monitor(Node, Fun),
            receive
                {Pid, started} ->
                    Pid;
                {'DOWN', MRef, process, Pid, Reason} ->
                    {error, Reason};
                {nodedown, Node} = NODEDOWN ->
                    {error, NODEDOWN}
            after Timeout ->
                    %% This should only happen if the (remote) node is
                    %% dead or dying...
                    exit(Pid, kill),
                    {error, timeout}
            end;
        pang ->
            {error, node_down}
    end.

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("(client) connect 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),
    Parent ! {self(), started},
    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 = ?TS(),
    case socket:connect(Sock, ServerSA, To) of
        {error, timeout} ->
            Stop  = ?TS(),
            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 = ?TS(),
                           case socket:accept(LSock, To) of
                               {error, timeout} ->
                                   {ok, State#{start => Start, stop => ?TS()}};
                               {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 = ?TS(),
                           case socket:accept(LSock, To) of
                               {error, timeout} ->
                                   {ok, State#{start => Start, stop => ?TS()}};
                               {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 = ?TS(),
                           case socket:accept(LSock, To) of
                               {error, timeout} ->
                                   {ok, State#{start => Start, stop => ?TS()}};
                               {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 = ?TS(),
                           case Recv(Sock, To) of
                               {error, timeout} ->
                                   {ok, State#{start => Start, stop => ?TS()}};
                               {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 = ?TS(),
                           case Recv(Sock, To) of
                               {error, timeout} ->
                                   {ok, State#{start => Start,
                                               stop => ?TS()}};
                               {ok, _} ->
                                   {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},
         
         %% *** 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).



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

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_port(S) ->
    case sock_sockname(S) of
        #{port := Port} -> Port;
        _               -> undefined
    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() ->
    ?SLIB: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.



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

%% 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 ?SLIB: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 ?SLIB: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 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").

etc_issue() ->
    string:trim(os:cmd("cat /etc/issue")).

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_multi_scheds() ->
    case erlang:system_info(schedulers) of
        N when (N > 1) ->
            ok;
        _ ->
            skip("Num schedulers *not* > 1")
    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}).


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

%% *** 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) ->
    ?TC_TRY(Case, TCFun).

tc_try(Case, TCCondFun, TCFun) ->
    ?TC_TRY(Case, TCCondFun, TCFun).


tc_try(Case, TCCondFun, TCPre, TCFun, TCPost) ->
    ?TC_TRY(Case, TCCondFun, TCPre, TCFun, TCPost).


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

start_node(Name) ->
    start_node(Name, 5000).

start_node(Name, Timeout) when is_integer(Timeout) andalso (Timeout > 0) ->
    Pa   = filename:dirname(code:which(?MODULE)),
    Args = ["-pa", Pa,
            "-s", atom_to_list(?PROXY), "start", atom_to_list(node()),
            "-s", "global", "sync"],
    try ?CT_PEER(#{name      => Name,
                   wait_boot => Timeout,
                   args      => Args}) of
        {ok, Peer, Node} ->
            ?SEV_IPRINT("Started node ~p - now (global) sync", [Name]),
            global:sync(), % Again, just in case...
            ?SEV_IPRINT("ping proxy"),
            pong = ?PPING(Node),
            {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.

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

nowait(Config) ->
    case lists:member({select_handle, true}, Config) of
        true ->
            make_ref();
        false ->
            nowait
    end.

i(F) ->
    i(F, []).

i(F, A) ->
    FStr = ?F("[~s] " ++ F, [?FTS()|A]),
    io:format(user, FStr ++ "~n", []),
    io:format(FStr, []).

