aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2018-10-26 18:07:45 +0200
committerMicael Karlberg <[email protected]>2018-10-26 18:07:45 +0200
commit944c0714c9342c4b72fc4433448058db4cb7861f (patch)
tree52841c7e4498f61aaef5704d6ce24008bcdefd8b /erts/emulator/test
parent468fd132f5b9a13af76abd2c452967132be2ffdf (diff)
downloadotp-944c0714c9342c4b72fc4433448058db4cb7861f.tar.gz
otp-944c0714c9342c4b72fc4433448058db4cb7861f.tar.bz2
otp-944c0714c9342c4b72fc4433448058db4cb7861f.zip
[socket-nif|test] Added "proper" evaluator module
Add a "proper" evaluator module and adapted a couple of test cases to use that instead. OTP-14831
Diffstat (limited to 'erts/emulator/test')
-rw-r--r--erts/emulator/test/Makefile5
-rw-r--r--erts/emulator/test/socket_SUITE.erl151
-rw-r--r--erts/emulator/test/socket_test_evaluator.erl487
-rw-r--r--erts/emulator/test/socket_test_evaluator.hrl57
-rw-r--r--erts/emulator/test/socket_test_lib.erl72
5 files changed, 695 insertions, 77 deletions
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 39a3ef2a3d..22c2f24a80 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -32,6 +32,8 @@ SOCKET_MODULES = \
socket_lib \
socket_server \
socket_client \
+ socket_test_lib \
+ socket_test_evaluator \
socket_SUITE
MODULES= \
@@ -156,6 +158,7 @@ NATIVE_MODULES= $(NATIVE:%=%_native_SUITE)
NATIVE_ERL_FILES= $(NATIVE_MODULES:%=%.erl)
ERL_FILES= $(MODULES:%=%.erl)
+HRL_FILES= socket_test_evaluator.hrl
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
SOCKET_TARGETS = $(SOCKET_MODULES:%=$(EBIN)/%.$(EMULATOR))
@@ -228,7 +231,7 @@ release_spec:
release_tests_spec: make_emakefile
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(EMAKEFILE) $(TEST_SPEC_FILES) \
- $(ERL_FILES) "$(RELSYSDIR)"
+ $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(NO_OPT_ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(NATIVE_ERL_FILES) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index 8d96065076..f4b8d94346 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -93,23 +93,26 @@
%% Tickets
]).
+
+-include("socket_test_evaluator.hrl").
+
%% Internal exports
%% -export([]).
--record(ev, {name :: string(),
- pid :: pid(),
- mref :: reference()}).
--type ev() :: #ev{}.
+%% -record(ev, {name :: string(),
+%% pid :: pid(),
+%% mref :: reference()}).
+%% -type ev() :: #ev{}.
-define(MKEV(N,P,R), #ev{name = N, pid = P, mref = R}).
--type initial_evaluator_state() :: map().
--type evaluator_state() :: term().
--type command_fun() ::
- fun((State :: evaluator_state()) -> ok) |
- fun((State :: evaluator_state()) -> {ok, evaluator_state()}) |
- fun((State :: evaluator_state()) -> {error, term()}).
+%% -type initial_evaluator_state() :: map().
+%% -type evaluator_state() :: map().
+%% -type command_fun() ::
+%% fun((State :: evaluator_state()) -> ok) |
+%% fun((State :: evaluator_state()) -> {ok, evaluator_state()}) |
+%% fun((State :: evaluator_state()) -> {error, term()}).
--type command() :: #{desc := string(),
- cmd := command_fun()}.
+%% -type command() :: #{desc := string(),
+%% cmd := command_fun()}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -126,6 +129,7 @@
-define(TT(T), ct:timetrap(T)).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
suite() ->
@@ -403,12 +407,16 @@ api_b_open_and_close(InitState) ->
end},
#{desc => "validate socket close",
cmd => fun({_, ok}) ->
- {ok, normal};
+ ok;
({_, {error, _} = ERROR}) ->
ERROR
- end}],
- Evaluator = evaluator_start("tester", Seq, InitState),
- ok = await_evaluator_finish([Evaluator]).
+ end},
+
+ %% *** We are done ***
+ ?FINISH_NORMAL
+ ],
+ Evaluator = ?SEV_START("tester", Seq, InitState),
+ ok = ?SEV_AWAIT_FINISH([Evaluator]).
@@ -543,12 +551,14 @@ api_b_send_and_recv_udp(InitState) ->
end},
#{desc => "close dst socket",
cmd => fun(#{sock_dst := Sock}) ->
- ok = socket:close(Sock),
- {ok, normal}
- end}
+ ok = socket:close(Sock)
+ end},
+
+ %% *** We are done ***
+ ?FINISH_NORMAL
],
- Evaluator = evaluator_start("tester", Seq, InitState),
- ok = await_evaluator_finish([Evaluator]).
+ Evaluator = ?SEV_START("tester", Seq, InitState),
+ ok = ?SEV_AWAIT_FINISH([Evaluator]).
@@ -618,7 +628,7 @@ api_b_send_and_recv_tcp(InitState) ->
%% *** Wait for start order ***
#{desc => "await start (from tester)",
cmd => fun(State) ->
- Tester = ev_await_start(),
+ Tester = ?SEV_AWAIT_START(),
{ok, State#{tester => Tester}}
end},
#{desc => "monitor tester",
@@ -658,14 +668,14 @@ api_b_send_and_recv_tcp(InitState) ->
end},
#{desc => "announce ready (init)",
cmd => fun(#{tester := Tester, lport := Port}) ->
- ev_ready(Tester, init, Port),
+ ?SEV_ANNOUNCE_READY(Tester, init, Port),
ok
end},
%% The actual test
#{desc => "await continue (accept)",
cmd => fun(#{tester := Tester}) ->
- ev_await_continue(Tester, tester, accept)
+ ?SEV_AWAIT_CONTINUE(Tester, tester, accept)
end},
#{desc => "await connection",
cmd => fun(#{lsock := LSock} = State) ->
@@ -679,7 +689,7 @@ api_b_send_and_recv_tcp(InitState) ->
end},
#{desc => "announce ready (accept)",
cmd => fun(#{tester := Tester}) ->
- ev_ready(Tester, accept),
+ ?SEV_ANNOUNCE_READY(Tester, accept),
ok
end},
#{desc => "await (recv) request",
@@ -693,13 +703,12 @@ api_b_send_and_recv_tcp(InitState) ->
end},
#{desc => "announce ready (recv request)",
cmd => fun(#{tester := Tester}) ->
- %% Tester ! {ready, self(), Port},
- ev_ready(Tester, recv_req),
+ ?SEV_ANNOUNCE_READY(Tester, recv_req),
ok
end},
#{desc => "await continue (with send reply)",
cmd => fun(#{tester := Tester}) ->
- ev_await_continue(Tester, tester, send_reply)
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply)
end},
#{desc => "send reply",
cmd => fun(#{csock := Sock, send := Send}) ->
@@ -707,15 +716,14 @@ api_b_send_and_recv_tcp(InitState) ->
end},
#{desc => "announce ready (send reply)",
cmd => fun(#{tester := Tester}) ->
- %% Tester ! {ready, self(), Port},
- ev_ready(Tester, send_reply),
+ ?SEV_ANNOUNCE_READY(Tester, send_reply),
ok
end},
%% *** Termination ***
#{desc => "await terminate",
cmd => fun(#{tester := Tester} = State) ->
- case ev_await_terminate(Tester, tester) of
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
ok ->
{ok, maps:remove(tester, State)};
{error, _} = ERROR ->
@@ -732,10 +740,7 @@ api_b_send_and_recv_tcp(InitState) ->
end},
%% *** We are done ***
- #{desc => "finish",
- cmd => fun(_) ->
- {ok, normal}
- end}
+ ?FINISH_NORMAL
],
ClientSeq =
@@ -743,7 +748,7 @@ api_b_send_and_recv_tcp(InitState) ->
%% *** Wait for start order ***
#{desc => "await start (from tester)",
cmd => fun(State) ->
- {Tester, Port} = ev_await_start(),
+ {Tester, Port} = ?SEV_AWAIT_START(),
{ok, State#{tester => Tester, server_port => Port}}
end},
#{desc => "monitor tester",
@@ -781,14 +786,14 @@ api_b_send_and_recv_tcp(InitState) ->
end},
#{desc => "announce ready (init)",
cmd => fun(#{tester := Tester}) ->
- ev_ready(Tester, init),
+ ?SEV_ANNOUNCE_READY(Tester, init),
ok
end},
%% *** The actual test ***
#{desc => "await continue (connect)",
cmd => fun(#{tester := Tester} = _State) ->
- ev_await_continue(Tester, tester, connect)
+ ?SEV_AWAIT_CONTINUE(Tester, tester, connect)
end},
#{desc => "connect to server",
cmd => fun(#{sock := Sock, server_sa := SSA}) ->
@@ -796,12 +801,12 @@ api_b_send_and_recv_tcp(InitState) ->
end},
#{desc => "announce ready (connect)",
cmd => fun(#{tester := Tester}) ->
- ev_ready(Tester, connect),
+ ?SEV_ANNOUNCE_READY(Tester, connect),
ok
end},
#{desc => "await continue (send request)",
cmd => fun(#{tester := Tester} = _State) ->
- ev_await_continue(Tester, tester, send_req)
+ ?SEV_AWAIT_CONTINUE(Tester, tester, send_req)
end},
#{desc => "send request (to server)",
cmd => fun(#{sock := Sock, send := Send}) ->
@@ -809,7 +814,7 @@ api_b_send_and_recv_tcp(InitState) ->
end},
#{desc => "announce ready (send request)",
cmd => fun(#{tester := Tester}) ->
- ev_ready(Tester, send_req),
+ ?SEV_ANNOUNCE_READY(Tester, send_req),
ok
end},
#{desc => "await recv reply (from server)",
@@ -819,14 +824,14 @@ api_b_send_and_recv_tcp(InitState) ->
end},
#{desc => "announce ready (recv reply)",
cmd => fun(#{tester := Tester}) ->
- ev_ready(Tester, recv_reply),
+ ?SEV_ANNOUNCE_READY(Tester, recv_reply),
ok
end},
%% *** Termination ***
#{desc => "await terminate",
cmd => fun(#{tester := Tester} = State) ->
- case ev_await_terminate(Tester, tester) of
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
ok ->
{ok, maps:remove(tester, State)};
{error, _} = ERROR ->
@@ -839,10 +844,7 @@ api_b_send_and_recv_tcp(InitState) ->
end},
%% *** We are done ***
- #{desc => "finish",
- cmd => fun(_) ->
- {ok, normal}
- end}
+ ?FINISH_NORMAL
],
TesterSeq =
@@ -862,31 +864,31 @@ api_b_send_and_recv_tcp(InitState) ->
%% Start the server
#{desc => "order server start",
cmd => fun(#{server := Pid} = _State) ->
- ev_start(Pid),
+ ?SEV_ANNOUNCE_START(Pid),
ok
end},
#{desc => "await server ready (init)",
cmd => fun(#{server := Pid} = State) ->
- {ok, Port} = ev_await_ready(Pid, server, init),
+ {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) ->
- ev_start(Pid, Port),
+ ?SEV_ANNOUNCE_START(Pid, Port),
ok
end},
#{desc => "await client ready (init)",
cmd => fun(#{client := Pid} = _State) ->
- ev_await_ready(Pid, client, init),
+ ?SEV_AWAIT_READY(Pid, client, init),
ok
end},
%% *** The actual test ***
#{desc => "order server to continue (with accept)",
cmd => fun(#{server := Server} = _State) ->
- ev_continue(Server, accept),
+ ?SEV_ANNOUNCE_CONTINUE(Server, accept),
ok
end},
#{desc => "sleep",
@@ -896,89 +898,86 @@ api_b_send_and_recv_tcp(InitState) ->
end},
#{desc => "order client to continue (with connect)",
cmd => fun(#{client := Client} = _State) ->
- ev_continue(Client, connect),
+ ?SEV_ANNOUNCE_CONTINUE(Client, connect),
ok
end},
#{desc => "await client ready (connect)",
cmd => fun(#{client := Client} = _State) ->
- ev_await_ready(Client, client, connect)
+ ?SEV_AWAIT_READY(Client, client, connect)
end},
#{desc => "await server ready (accept)",
cmd => fun(#{server := Server} = _State) ->
- ev_await_ready(Server, server, accept)
+ ?SEV_AWAIT_READY(Server, server, accept)
end},
#{desc => "order client to continue (with send request)",
cmd => fun(#{client := Client} = _State) ->
- ev_continue(Client, send_req),
+ ?SEV_ANNOUNCE_CONTINUE(Client, send_req),
ok
end},
#{desc => "await client ready (with send request)",
cmd => fun(#{client := Client} = _State) ->
- ev_await_ready(Client, client, send_req)
+ ?SEV_AWAIT_READY(Client, client, send_req)
end},
#{desc => "await server ready (request recv)",
cmd => fun(#{server := Server} = _State) ->
- ev_await_ready(Server, server, recv_req)
+ ?SEV_AWAIT_READY(Server, server, recv_req)
end},
#{desc => "order server to continue (with send reply)",
cmd => fun(#{server := Server} = _State) ->
- ev_continue(Server, send_reply),
+ ?SEV_ANNOUNCE_CONTINUE(Server, send_reply),
ok
end},
#{desc => "await server ready (with reply sent)",
cmd => fun(#{server := Server} = _State) ->
- ev_await_ready(Server, server, send_reply)
+ ?SEV_AWAIT_READY(Server, server, send_reply)
end},
#{desc => "await client ready (reply recv)",
cmd => fun(#{client := Client} = _State) ->
- ev_await_ready(Client, client, recv_reply)
+ ?SEV_AWAIT_READY(Client, client, recv_reply)
end},
%% *** Termination ***
#{desc => "order client to terminate",
cmd => fun(#{client := Client} = _State) ->
- ev_terminate(Client),
+ ?SEV_ANNOUNCE_TERMINATE(Client),
ok
end},
#{desc => "await client termination",
cmd => fun(#{client := Client} = State) ->
- ev_await_termination(Client),
+ ?SEV_AWAIT_TERMINATION(Client),
State1 = maps:remove(client, State),
{ok, State1}
end},
#{desc => "order server to terminate",
cmd => fun(#{server := Server} = _State) ->
- ev_terminate(Server),
+ ?SEV_ANNOUNCE_TERMINATE(Server),
ok
end},
#{desc => "await server termination",
cmd => fun(#{server := Server} = State) ->
- ev_await_termination(Server),
+ ?SEV_AWAIT_TERMINATION(Server),
State1 = maps:remove(server, State),
{ok, State1}
end},
%% *** We are done ***
- #{desc => "finish",
- cmd => fun(_) ->
- {ok, normal}
- end}
+ ?FINISH_NORMAL
],
i("start server evaluator"),
- Server = evaluator_start("server", ServerSeq, InitState),
+ Server = ?SEV_START("server", ServerSeq, InitState),
i("start client evaluator"),
- Client = evaluator_start("client", ClientSeq, InitState),
+ Client = ?SEV_START("client", ClientSeq, InitState),
i("await evaluator(s)"),
i("start tester evaluator"),
TesterInitState = #{server => Server#ev.pid,
client => Client#ev.pid},
- Tester = evaluator_start("tester", TesterSeq, TesterInitState),
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
- ok = await_evaluator_finish([Server, Client, Tester]).
+ ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]).
@@ -5891,10 +5890,10 @@ which_addr2(Domain, [_|IFO]) ->
%% will be used as exit reason.
%% A successful command shall evaluate to ok | {ok, NewState}
--spec evaluator_start(Name, Seq, Init) -> ev() when
+-spec evaluator_start(Name, Seq, Init) -> socket_test_evaluator:ev() when
Name :: string(),
- Seq :: [command()],
- Init :: initial_evaluator_state().
+ Seq :: [socket_test_evaluator:command()],
+ Init :: socket_test_evaluator:initial_evaluator_state().
evaluator_start(Name, Seq, Init)
when is_list(Name) andalso is_list(Seq) andalso (Seq =/= []) ->
diff --git a/erts/emulator/test/socket_test_evaluator.erl b/erts/emulator/test/socket_test_evaluator.erl
new file mode 100644
index 0000000000..8498ef6a0c
--- /dev/null
+++ b/erts/emulator/test/socket_test_evaluator.erl
@@ -0,0 +1,487 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_evaluator).
+
+%% Evaluator control functions
+-export([
+ start/3,
+ await_finish/1
+ ]).
+
+%% Functions used by evaluators to interact with eachother
+-export([
+ %% Announce functions
+ %% (Send an announcement from one evaluator to another)
+ announce_start/1, announce_start/2,
+ announce_continue/2, announce_continue/3,
+ announce_ready/2, announce_ready/3,
+ announce_terminate/1,
+
+ %% Await functions
+ %% (Wait for an announcement from another evaluator)
+ await_start/0,
+ await_continue/3, await_continue/4,
+ await_ready/3, await_ready/4,
+ await_terminate/2, await_terminate/3,
+ await_termination/1, await_termination/2
+ ]).
+
+-export_type([
+ ev/0,
+ initial_evaluator_state/0,
+ evaluator_state/0,
+ command_fun/0,
+ command/0
+ ]).
+
+
+-include("socket_test_evaluator.hrl").
+
+-type ev() :: #ev{}.
+-type initial_evaluator_state() :: map().
+-type evaluator_state() :: term().
+-type command_fun() ::
+ fun((State :: evaluator_state()) -> ok) |
+ fun((State :: evaluator_state()) -> {ok, evaluator_state()}) |
+ fun((State :: evaluator_state()) -> {error, term()}).
+
+-type command() :: #{desc := string(),
+ cmd := command_fun()}.
+
+
+%% ============================================================================
+
+-define(LIB, socket_test_lib).
+
+-define(EXTRA_NOTHING, '$nothing').
+-define(ANNOUNCEMENT_START, '$start').
+-define(ANNOUNCEMENT_READY, '$ready').
+-define(ANNOUNCEMENT_CONTINUE, '$continue').
+-define(ANNOUNCEMENT_TERMINATE, '$terminate').
+
+-define(START_NAME_NONE, '$no-name').
+-define(START_SLOGAN, ?ANNOUNCEMENT_START).
+-define(TERMINATE_SLOGAN, ?ANNOUNCEMENT_TERMINATE).
+
+
+%% ============================================================================
+
+-spec start(Name, Seq, Init) -> ev() when
+ Name :: string(),
+ Seq :: [command()],
+ Init :: initial_evaluator_state().
+
+start(Name, Seq, InitState)
+ when is_list(Name) andalso is_list(Seq) andalso (Seq =/= []) ->
+ %% Make sure 'parent' is not already used
+ case maps:find(parent, InitState) of
+ {ok, _} ->
+ erlang:error({already_used, parent});
+ error ->
+ InitState2 = InitState#{parent => self()},
+ {Pid, MRef} = erlang:spawn_monitor(
+ fun() -> init(Name, Seq, InitState2) end),
+ #ev{name = Name, pid = Pid, mref = MRef}
+ end.
+
+init(Name, Seq, Init) ->
+ put(sname, Name),
+ loop(1, Seq, Init).
+
+loop(_ID, [], FinalState) ->
+ exit(FinalState);
+loop(ID, [#{desc := Desc,
+ cmd := Cmd}|Cmds], State) when is_function(Cmd, 1) ->
+ i("evaluate command ~2w: ~s", [ID, Desc]),
+ try Cmd(State) of
+ ok ->
+ loop(ID + 1, Cmds, State);
+ {ok, NewState} ->
+ loop(ID + 1, Cmds, NewState);
+ {error, Reason} ->
+ e("command ~w failed: "
+ "~n Reason: ~p", [ID, Reason]),
+ exit({command_failed, ID, Reason, State})
+ catch
+ C:E:S ->
+ e("command ~w crashed: "
+ "~n Class: ~p"
+ "~n Error: ~p"
+ "~n Call Stack: ~p", [ID, C, E, S]),
+ exit({command_crashed, ID, {C,E,S}, State})
+ end.
+
+
+%% ============================================================================
+
+-spec await_finish(Evs) -> term() when
+ Evs :: [ev()].
+
+await_finish(Evs) ->
+ await_finish(Evs, []).
+
+await_finish([], []) ->
+ ok;
+await_finish([], Fails) ->
+ Fails;
+await_finish(Evs, Fails) ->
+ receive
+ {'DOWN', _MRef, process, Pid, normal} ->
+ case lists:keysearch(Pid, #ev.pid, Evs) of
+ {value, #ev{name = Name}} ->
+
+
+ i("evaluator '~s' (~p) success", [Name, Pid]),
+
+
+
+
+ NewEvs = lists:keydelete(Pid, #ev.pid, Evs),
+ await_finish(NewEvs, Fails);
+ false ->
+ i("unknown process ~p died (normal)", [Pid]),
+ await_finish(Evs, Fails)
+ end;
+ {'DOWN', _MRef, process, Pid, Reason} ->
+ case lists:keysearch(Pid, #ev.pid, Evs) of
+ {value, #ev{name = Name}} ->
+ i("evaluator '~s' (~p) failed", [Name, Pid]),
+ NewEvs = lists:keydelete(Pid, #ev.pid, Evs),
+ await_finish(NewEvs, [{Pid, Reason}|Fails]);
+ false ->
+ i("unknown process ~p died: "
+ "~n ~p", [Pid, Reason]),
+ await_finish(Evs, Fails)
+ end
+ end.
+
+
+%% ============================================================================
+
+-spec announce_start(To) -> ok when
+ To :: pid().
+
+announce_start(To) ->
+ announce(To, ?ANNOUNCEMENT_START, ?START_SLOGAN).
+
+-spec announce_start(To, Extra) -> ok when
+ To :: pid(),
+ Extra :: term().
+
+announce_start(To, Extra) ->
+ announce(To, ?ANNOUNCEMENT_START, ?START_SLOGAN, Extra).
+
+
+%% ============================================================================
+
+-spec announce_continue(To, Slogan) -> ok when
+ To :: pid(),
+ Slogan :: atom().
+
+announce_continue(To, Slogan) ->
+ announce_continue(To, Slogan, ?EXTRA_NOTHING).
+
+-spec announce_continue(To, Slogan, Extra) -> ok when
+ To :: pid(),
+ Slogan :: atom(),
+ Extra :: term().
+
+announce_continue(To, Slogan, Extra) ->
+ announce(To, ?ANNOUNCEMENT_CONTINUE, Slogan, Extra).
+
+
+%% ============================================================================
+
+-spec announce_ready(To, Slogan) -> ok when
+ To :: pid(),
+ Slogan :: atom().
+
+announce_ready(To, Slogan) ->
+ announce_ready(To, Slogan, ?EXTRA_NOTHING).
+
+-spec announce_ready(To, Slogan, Extra) -> ok when
+ To :: pid(),
+ Slogan :: atom(),
+ Extra :: term().
+
+announce_ready(To, Slogan, Extra) ->
+ announce(To, ?ANNOUNCEMENT_READY, Slogan, Extra).
+
+
+%% ============================================================================
+
+-spec announce_terminate(To) -> ok when
+ To :: pid().
+
+announce_terminate(To) ->
+ announce(To, ?ANNOUNCEMENT_TERMINATE, ?TERMINATE_SLOGAN).
+
+
+%% ============================================================================
+
+-spec announce(To, Announcement, Slogan) -> ok when
+ To :: pid(),
+ Announcement :: atom(),
+ Slogan :: atom().
+
+announce(To, Announcement, Slogan) ->
+ announce(To, Announcement, Slogan, ?EXTRA_NOTHING).
+
+-spec announce(To, Announcement, Slogan, Extra) -> ok when
+ To :: pid(),
+ Announcement :: atom(),
+ Slogan :: atom(),
+ Extra :: term().
+
+announce(To, Announcement, Slogan, Extra)
+ when is_pid(To) andalso
+ is_atom(Announcement) andalso
+ is_atom(Slogan) ->
+ To ! {Announcement, self(), Slogan, Extra},
+ ok.
+
+
+
+%% ============================================================================
+
+-spec await_start() -> Pid | {Pid, Extra} when
+ Pid :: pid(),
+ Extra :: term().
+
+await_start() ->
+ case await(any, ?START_NAME_NONE, ?ANNOUNCEMENT_START, ?START_SLOGAN, []) of
+ {ok, Pid} when is_pid(Pid) ->
+ Pid;
+ {ok, {Pid, _} = OK} when is_pid(Pid) ->
+ OK
+ end.
+
+
+
+%% ============================================================================
+
+-spec await_continue(From, Name, Slogan) -> ok | {ok, Extra} | {error, Reason} when
+ From :: pid(),
+ Name :: atom(),
+ Slogan :: atom(),
+ Extra :: term(),
+ Reason :: term().
+
+await_continue(From, Name, Slogan) ->
+ await_continue(From, Name, Slogan, []).
+
+-spec await_continue(From, Name, Slogan, OtherPids) ->
+ ok | {ok, Extra} | {error, Reason} when
+ From :: pid(),
+ Name :: atom(),
+ Slogan :: atom(),
+ OtherPids :: [{pid(), atom()}],
+ Extra :: term(),
+ Reason :: term().
+
+await_continue(From, Name, Slogan, OtherPids)
+ when is_pid(From) andalso
+ is_atom(Name) andalso
+ is_atom(Slogan) andalso
+ is_list(OtherPids) ->
+ await(From, Name, ?ANNOUNCEMENT_CONTINUE, Slogan, OtherPids).
+
+
+
+%% ============================================================================
+
+-spec await_ready(From, Name, Slogan) -> ok | {ok, Extra} | {error, Reason} when
+ From :: pid(),
+ Name :: atom(),
+ Slogan :: atom(),
+ Extra :: term(),
+ Reason :: term().
+
+await_ready(From, Name, Slogan) ->
+ await_ready(From, Name, Slogan, []).
+
+-spec await_ready(From, Name, Slogan, OtherPids) ->
+ ok | {ok, Extra} | {error, Reason} when
+ From :: pid(),
+ Name :: atom(),
+ Slogan :: atom(),
+ OtherPids :: [{pid(), atom()}],
+ Extra :: term(),
+ Reason :: term().
+
+await_ready(From, Name, Slogan, OtherPids)
+ when is_pid(From) andalso
+ is_atom(Name) andalso
+ is_atom(Slogan) andalso
+ is_list(OtherPids) ->
+ await(From, Name, ?ANNOUNCEMENT_READY, Slogan, OtherPids).
+
+
+
+%% ============================================================================
+
+-spec await_terminate(Pid, Name) -> ok | {error, Reason} when
+ Pid :: pid(),
+ Name :: atom(),
+ Reason :: term().
+
+await_terminate(Pid, Name) when is_pid(Pid) andalso is_atom(Name) ->
+ await_terminate(Pid, Name, []).
+
+-spec await_terminate(Pid, Name, OtherPids) -> ok | {error, Reason} when
+ Pid :: pid(),
+ Name :: atom(),
+ OtherPids :: [{pid(), atom()}],
+ Reason :: term().
+
+await_terminate(Pid, Name, OtherPids) ->
+ await(Pid, Name, ?ANNOUNCEMENT_TERMINATE, ?TERMINATE_SLOGAN, OtherPids).
+
+
+%% ============================================================================
+
+-spec await_termination(Pid) -> ok | {error, Reason} when
+ Pid :: pid(),
+ Reason :: term().
+
+await_termination(Pid) when is_pid(Pid) ->
+ await_termination(Pid, any).
+
+-spec await_termination(Pid, ExpReason) -> ok | {error, Reason} when
+ Pid :: pid(),
+ ExpReason :: term(),
+ Reason :: term().
+
+await_termination(Pid, ExpReason) ->
+ receive
+ {'DOWN', _, process, Pid, _} when (ExpReason =:= any) ->
+ ok;
+ {'DOWN', _, process, Pid, Reason} when (ExpReason =:= Reason) ->
+ ok;
+ {'DOWN', _, process, Pid, Reason} ->
+ {error, {unexpected_exit, ExpReason, Reason}}
+ end.
+
+
+%% ============================================================================
+
+%% We expect a message (announcement) from Pid, but we also watch for DOWN from
+%% both Pid and OtherPids, in which case the test has failed!
+
+-spec await(ExpPid, Name, Announcement, Slogan, OtherPids) ->
+ ok | {ok, Extra} | {error, Reason} when
+ ExpPid :: any | pid(),
+ Name :: atom(),
+ Announcement :: atom(),
+ Slogan :: atom(),
+ OtherPids :: [{pid(), atom()}],
+ Extra :: term(),
+ Reason :: term().
+
+await(ExpPid, Name, Announcement, Slogan, OtherPids)
+ when (is_pid(ExpPid) orelse (ExpPid =:= any)) andalso
+ is_atom(Name) andalso
+ is_atom(Announcement) andalso
+ is_atom(Slogan) andalso
+ is_list(OtherPids) ->
+ receive
+ {Announcement, Pid, Slogan, ?EXTRA_NOTHING} when (ExpPid =:= any) ->
+ {ok, Pid};
+ {Announcement, Pid, Slogan, Extra} when (ExpPid =:= any) ->
+ {ok, {Pid, Extra}};
+ {Announcement, Pid, Slogan, ?EXTRA_NOTHING} when (Pid =:= ExpPid) ->
+ ok;
+ {Announcement, Pid, Slogan, Extra} when (Pid =:= ExpPid) ->
+ {ok, Extra};
+ {'DOWN', _, process, Pid, Reason} when (Pid =:= ExpPid) ->
+ e("Unexpected DOWN regarding ~w ~p: "
+ "~n ~p", [Name, Pid, Reason]),
+ {error, {unexpected_exit, Name}};
+ {'DOWN', _, process, OtherPid, Reason} ->
+ case check_down(OtherPid, Reason, OtherPids) of
+ ok ->
+ i("DOWN from unknown process ~p: "
+ "~n ~p", [OtherPid, Reason]),
+ await(ExpPid, Name, Announcement, Slogan, OtherPids);
+ {error, _} = ERROR ->
+ ERROR
+ end
+ after infinity -> % For easy debugging, just change to some valid time (5000)
+ i("await -> timeout for msg from ~p (~w): "
+ "~n Announcement: ~p"
+ "~n Slogan: ~p"
+ "~nwhen"
+ "~n Messages: ~p",
+ [ExpPid, Name, Announcement, Slogan, pi(messages)]),
+ await(ExpPid, Name, Announcement, Slogan, OtherPids)
+ end.
+
+pi(Item) ->
+ pi(self(), Item).
+
+pi(Pid, Item) ->
+ {Item, Info} = process_info(Pid, Item),
+ Info.
+
+check_down(Pid, DownReason, Pids) ->
+ case lists:keymember(Pid, 1, Pids) of
+ {value, {_, Name}} ->
+ e("Unexpected DOWN regarding ~w ~p: "
+ "~n ~p", [Name, Pid, DownReason]),
+ {error, {unexpected_exit, Name}};
+ false ->
+ ok
+ end.
+
+
+%% ============================================================================
+
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+
+%% i(F) ->
+%% i(F, []).
+i(F, A) ->
+ print("", F, A).
+
+%% e(F) ->
+%% e(F, []).
+e(F, A) ->
+ print("<ERROR> ", F, A).
+
+print(Prefix, F, A) ->
+ %% The two prints is to get the output both in the shell (for when
+ %% "personal" testing is going on) and in the logs.
+ IDStr =
+ case get(sname) of
+ undefined ->
+ %% This means its not an evaluator,
+ %% or a named process. Instead its
+ %% most likely the test case itself,
+ %% so skip the name and the pid.
+ "";
+ SName ->
+ f("[~s][~p]", [SName, self()])
+ end,
+ FStr = f("[~s]~s ~s" ++ F, [?LIB:formated_timestamp(), IDStr, Prefix | A]),
+ io:format(user, FStr ++ "~n", []),
+ io:format(FStr, []).
diff --git a/erts/emulator/test/socket_test_evaluator.hrl b/erts/emulator/test/socket_test_evaluator.hrl
new file mode 100644
index 0000000000..1fb42a33b2
--- /dev/null
+++ b/erts/emulator/test/socket_test_evaluator.hrl
@@ -0,0 +1,57 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-ifndef(socket_test_evaluator).
+-define(socket_test_evaluator, true).
+
+-define(SEV, socket_test_evaluator).
+
+-define(SEV_START(N, S, IS), ?SEV:start(N, S, IS)).
+-define(SEV_AWAIT_FINISH(Evs), ?SEV:await_finish(Evs)).
+
+-define(SEV_ANNOUNCE_START(To), ?SEV:announce_start(To)).
+-define(SEV_ANNOUNCE_START(To, Ex), ?SEV:announce_start(To, Ex)).
+-define(SEV_ANNOUNCE_CONTINUE(To, S), ?SEV:announce_continue(To, S)).
+-define(SEV_ANNOUNCE_CONTINUE(To, S, Ex), ?SEV:announce_continue(To, S, Ex)).
+-define(SEV_ANNOUNCE_READY(To, S), ?SEV:announce_ready(To, S)).
+-define(SEV_ANNOUNCE_READY(To, S, Ex), ?SEV:announce_ready(To, S, Ex)).
+-define(SEV_ANNOUNCE_TERMINATE(To), ?SEV:announce_terminate(To)).
+
+-define(SEV_AWAIT_START(), ?SEV:await_start()).
+-define(SEV_AWAIT_CONTINUE(F, N, S), ?SEV:await_continue(F, N, S)).
+-define(SEV_AWAIT_CONTINUE(F, N, S, Ps), ?SEV:await_continue(F, N, S, Ps)).
+-define(SEV_AWAIT_READY(F, N, S), ?SEV:await_ready(F, N, S)).
+-define(SEV_AWAIT_READY(F, N, S, Ps), ?SEV:await_ready(F, N, S, Ps)).
+-define(SEV_AWAIT_TERMINATE(F, N), ?SEV:await_terminate(F, N)).
+-define(SEV_AWAIT_TERMINATE(F, N, Ps), ?SEV:await_terminate(F, N, Ps)).
+-define(SEV_AWAIT_TERMINATION(P), ?SEV:await_termination(P)).
+-define(SEV_AWAIT_TERMINATION(P, R), ?SEV:await_termination(P, R)).
+
+-record(ev, {name :: string(),
+ pid :: pid(),
+ mref :: reference()}).
+
+-define(FINISH_NORMAL, #{desc => "finish",
+ cmd => fun(_) ->
+ {ok, normal}
+ end}).
+
+-endif. % -ifdef(socket_test_evaluator).
+
diff --git a/erts/emulator/test/socket_test_lib.erl b/erts/emulator/test/socket_test_lib.erl
new file mode 100644
index 0000000000..c36cc4fbfa
--- /dev/null
+++ b/erts/emulator/test/socket_test_lib.erl
@@ -0,0 +1,72 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_test_lib).
+
+-export([
+ %% Time stuff
+ timestamp/0,
+ tdiff/2,
+ formated_timestamp/0,
+ format_timestamp/1,
+
+ %% Skipping
+ not_yet_implemented/0,
+ skip/1
+ ]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+timestamp() ->
+ os:timestamp().
+
+
+tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) ->
+ T1 = A1*1000000000+B1*1000+(C1 div 1000),
+ T2 = A2*1000000000+B2*1000+(C2 div 1000),
+ T2 - T1.
+
+
+formated_timestamp() ->
+ format_timestamp(os:timestamp()).
+
+format_timestamp({_N1, _N2, _N3} = TS) ->
+ {_Date, Time} = calendar:now_to_local_time(TS),
+ %% {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ %% FormatTS =
+ %% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w.~w",
+ %% [YYYY, MM, DD, Hour, Min, Sec, N3]),
+ FormatTS = io_lib:format("~.2.0w:~.2.0w:~.2.0w", [Hour, Min, Sec]),
+ lists:flatten(FormatTS).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+not_yet_implemented() ->
+ skip("not yet implemented").
+
+skip(Reason) ->
+ throw({skip, Reason}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%