aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/test/erl_distribution_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/test/erl_distribution_SUITE.erl')
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl1235
1 files changed, 1235 insertions, 0 deletions
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
new file mode 100644
index 0000000000..8f2e2512e0
--- /dev/null
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -0,0 +1,1235 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erl_distribution_SUITE).
+
+%-define(line_trace, 1).
+-include("test_server.hrl").
+
+-export([all/1]).
+
+-export([tick/1, tick_change/1, illegal_nodenames/1, hidden_node/1,
+ table_waste/1, net_setuptime/1,
+ monitor_nodes/1,
+ monitor_nodes_nodedown_reason/1,
+ monitor_nodes_complex_nodedown_reason/1,
+ monitor_nodes_node_type/1,
+ monitor_nodes_misc/1,
+ monitor_nodes_otp_6481/1,
+ monitor_nodes_errors/1,
+ monitor_nodes_combinations/1,
+ monitor_nodes_cleanup/1,
+ monitor_nodes_many/1]).
+
+%% Performs the test at another node.
+-export([tick_cli_test/1, tick_cli_test1/1,
+ tick_serv_test/2, tick_serv_test1/1,
+ keep_conn/1, time_ping/1]).
+
+-export([init_per_testcase/2, fin_per_testcase/2]).
+
+-export([start_node/2]).
+
+-export([pinger/1]).
+
+
+-define(DUMMY_NODE,dummy@test01).
+
+%%-----------------------------------------------------------------
+%% The distribution is mainly tested in the big old test_suite.
+%% This test only tests the net_ticktime configuration flag.
+%% Should be started in a CC view with:
+%% erl -sname master -rsh ctrsh
+%%-----------------------------------------------------------------
+
+all(suite) ->
+ [tick, tick_change, illegal_nodenames, hidden_node,
+ table_waste, net_setuptime,
+ monitor_nodes].
+
+init_per_testcase(Func, Config) when atom(Func), list(Config) ->
+ Dog=?t:timetrap(?t:minutes(4)),
+ [{watchdog, Dog}|Config].
+
+fin_per_testcase(_Func, Config) ->
+ Dog=?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog).
+
+tick(suite) -> [];
+tick(doc) -> [];
+tick(Config) when list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(120)),
+ PaDir = filename:dirname(code:which(erl_distribution_SUITE)),
+
+ %% First check that the normal case is OK!
+ ?line {ok, Node} = start_node(dist_test, "-pa " ++ PaDir),
+ rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [node()]),
+
+ erlang:monitor_node(Node, true),
+ receive
+ {nodedown, Node} ->
+ test_server:fail("nodedown from other node")
+ after 30000 ->
+ erlang:monitor_node(Node, false),
+ stop_node(Node)
+ end,
+
+ %% Now, set the net_ticktime for the other node to 12 secs.
+ %% After the sleep(2sec) and cast the other node shall destroy
+ %% the connection as it has not received anything on the connection.
+ %% The nodedown message should arrive within 8 < T < 16 secs.
+
+ %% We must have two slave nodes as the slave mechanism otherwise
+ %% halts the client node after tick timeout (the connection is down
+ %% and the slave node decides to halt !!
+
+ %% Set the ticktime on the server node to 100 secs so the server
+ %% node doesn't tick the client node within the interval ...
+
+ ?line {ok, ServNode} = start_node(dist_test_server,
+ "-kernel net_ticktime 100 "
+ "-pa " ++ PaDir),
+ rpc:call(ServNode, erl_distribution_SUITE, tick_serv_test, [Node, node()]),
+
+ ?line {ok, _} = start_node(dist_test,
+ "-kernel net_ticktime 12 "
+ "-pa " ++ PaDir),
+ rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [ServNode]),
+
+ spawn_link(erl_distribution_SUITE, keep_conn, [Node]),
+
+ {tick_serv, ServNode} ! {i_want_the_result, self()},
+
+ monitor_node(ServNode, true),
+ monitor_node(Node, true),
+
+ receive
+ {tick_test, T} when integer(T) ->
+ stop_node(ServNode),
+ stop_node(Node),
+ T;
+ {tick_test, Error} ->
+ stop_node(ServNode),
+ stop_node(Node),
+ test_server:fail(Error);
+ {nodedown, Node} ->
+ stop_node(ServNode),
+ test_server:fail("client node died");
+ {nodedown, ServNode} ->
+ stop_node(Node),
+ test_server:fail("server node died")
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+table_waste(doc) ->
+ ["Checks that pinging nonexistyent nodes does not waste space in distribution table"];
+table_waste(suite) ->
+ [];
+table_waste(Config) when list(Config) ->
+ ?line {ok, HName} = inet:gethostname(),
+ F = fun(0,_F) -> [];
+ (N,F) ->
+ ?line Name = list_to_atom("erl_distribution_"++integer_to_list(N)++
+ "@"++HName),
+ ?line pang = net_adm:ping(Name),
+ ?line F(N-1,F)
+ end,
+ ?line F(256,F),
+ ?line {ok, N} = start_node(erl_distribution_300,""),
+ ?line stop_node(N),
+ ok.
+
+
+
+illegal_nodenames(doc) ->
+ ["Test that pinging an illegal nodename does not kill the node"];
+illegal_nodenames(suite) ->
+ [];
+illegal_nodenames(Config) when list(Config) ->
+ ?line Dog=?t:timetrap(?t:minutes(2)),
+ PaDir = filename:dirname(code:which(erl_distribution_SUITE)),
+ ?line {ok, Node}=start_node(illegal_nodenames, "-pa " ++ PaDir),
+ monitor_node(Node, true),
+ ?line RPid=rpc:call(Node, erlang, spawn,
+ [?MODULE, pinger, [self()]]),
+ receive
+ {RPid, pinged} ->
+ ok;
+ {nodedown, Node} ->
+ ?t:fail("Remote node died.")
+ end,
+ stop_node(Node),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+pinger(Starter) ->
+ io:format("Starter:~p~n",[Starter]),
+ net_adm:ping(a@b@c),
+ Starter ! {self(), pinged},
+ ok.
+
+
+net_setuptime(doc) -> ["Test that you can set the net_setuptime properly"];
+net_setuptime(Config) when is_list(Config) ->
+ %% In this test case, we reluctantly accept shorter times than the given
+ %% setup time, because the connection attempt can end in a
+ %% "Host unreachable" error before the timeout fires.
+
+ Res0 = do_test_setuptime("2"),
+ io:format("Res0 = ~p", [Res0]),
+ ?line true = (Res0 =< 4000),
+ Res1 = do_test_setuptime("0.3"),
+ io:format("Res1 = ~p", [Res1]),
+ ?line true = (Res1 =< 500),
+ ok.
+
+do_test_setuptime(Setuptime) when is_list(Setuptime) ->
+ ?line PaDir = filename:dirname(code:which(?MODULE)),
+ ?line {ok, Node} = start_node(dist_setuptime_test, "-pa " ++ PaDir ++
+ " -kernel net_setuptime " ++ Setuptime),
+ ?line Res = rpc:call(Node,?MODULE,time_ping,[?DUMMY_NODE]),
+ ?line stop_node(Node),
+ Res.
+
+time_ping(Node) ->
+ T0 = erlang:now(),
+ pang = net_adm:ping(Node),
+ T1 = erlang:now(),
+ time_diff(T0,T1).
+
+
+%% Keep the connection with the client node up.
+%% This is neccessary as the client node runs with much shorter
+%% tick time !!
+keep_conn(Node) ->
+ sleep(1),
+ rpc:cast(Node, erlang, time, []),
+ keep_conn(Node).
+
+tick_serv_test(Node, MasterNode) ->
+ spawn(erl_distribution_SUITE, keep_conn, [MasterNode]),
+ spawn(erl_distribution_SUITE, tick_serv_test1, [Node]).
+
+tick_serv_test1(Node) ->
+ register(tick_serv, self()),
+ TestServer = receive {i_want_the_result, TS} -> TS end,
+ monitor_node(Node, true),
+ receive
+ {nodedown, Node} ->
+ net_adm:ping(Node), %% Set up the connection again !!
+
+ {tick_test, Node} ! {whats_the_result, self()},
+ receive
+ {tick_test, Res} ->
+ TestServer ! {tick_test, Res}
+ end
+ end.
+
+tick_cli_test(Node) ->
+ spawn(erl_distribution_SUITE, tick_cli_test1, [Node]).
+
+tick_cli_test1(Node) ->
+ register(tick_test, self()),
+ erlang:monitor_node(Node, true),
+ sleep(2),
+ rpc:call(Node, erlang, time, []), %% simulate action on the connection
+ T1 = now(),
+ receive
+ {nodedown, Node} ->
+ T2 = now(),
+ receive
+ {whats_the_result, From} ->
+ case time_diff(T1, T2) of
+ T when T > 8000, T < 16000 ->
+ From ! {tick_test, T};
+ T ->
+ From ! {tick_test,
+ {"T not in interval 8000 < T < 16000",
+ T}}
+ end
+ end
+ end.
+
+
+tick_change(doc) -> ["OTP-4255"];
+tick_change(suite) -> [];
+tick_change(Config) when list(Config) ->
+ ?line PaDir = filename:dirname(code:which(?MODULE)),
+ ?line [BN, CN] = get_nodenames(2, tick_change),
+ ?line DefaultTT = net_kernel:get_net_ticktime(),
+ ?line case DefaultTT of
+ I when integer(I) -> ?line ok;
+ _ -> ?line ?t:fail(DefaultTT)
+ end,
+
+ % In case other nodes are connected
+ case nodes(connected) of
+ [] -> ?line net_kernel:set_net_ticktime(10, 0);
+ _ -> ?line rpc:multicall(nodes([this, connected]), net_kernel,
+ set_net_ticktime, [10, 5])
+ end,
+
+ ?line wait_until(fun () -> 10 == net_kernel:get_net_ticktime() end),
+ ?line {ok, B} = start_node(BN, "-kernel net_ticktime 10 -pa " ++ PaDir),
+ ?line {ok, C} = start_node(CN, "-kernel net_ticktime 10 -hidden -pa "
+ ++ PaDir),
+
+ ?line OTE = process_flag(trap_exit, true),
+ case catch begin
+ ?line run_tick_change_test(B, C, 10, 1, PaDir),
+ ?line run_tick_change_test(B, C, 1, 10, PaDir)
+ end of
+ {'EXIT', Reason} ->
+ ?line stop_node(B),
+ ?line stop_node(C),
+ %% In case other nodes are connected
+ case nodes(connected) of
+ [] -> ?line net_kernel:set_net_ticktime(DefaultTT, 0);
+ _ -> ?line rpc:multicall(nodes([this, connected]), net_kernel,
+ set_net_ticktime, [DefaultTT, 10])
+ end,
+ ?line wait_until(fun () ->
+ DefaultTT == net_kernel:get_net_ticktime()
+ end),
+ ?line process_flag(trap_exit, OTE),
+ ?t:fail(Reason);
+ _ ->
+ ok
+ end,
+ ?line process_flag(trap_exit, OTE),
+ ?line stop_node(B),
+ ?line stop_node(C),
+
+ % In case other nodes are connected
+ case nodes(connected) of
+ [] -> ?line net_kernel:set_net_ticktime(DefaultTT, 0);
+ _ -> ?line rpc:multicall(nodes([this, connected]), net_kernel,
+ set_net_ticktime, [DefaultTT, 5])
+ end,
+
+ ?line wait_until(fun () -> DefaultTT == net_kernel:get_net_ticktime() end),
+ ?line ok.
+
+
+wait_for_nodedowns(Tester, Ref) ->
+ receive
+ {nodedown, Node} ->
+ ?t:format("~p~n", [{node(), {nodedown, Node}}]),
+ ?line Tester ! {Ref, {node(), {nodedown, Node}}}
+ end,
+ wait_for_nodedowns(Tester, Ref).
+
+run_tick_change_test(B, C, PrevTT, TT, PaDir) ->
+ ?line [DN, EN] = get_nodenames(2, tick_change),
+
+ ?line Tester = self(),
+ ?line Ref = make_ref(),
+ ?line MonitorNodes = fun (Nodes) ->
+ ?line lists:foreach(
+ fun (N) ->
+ ?line monitor_node(N,true)
+ end,
+ Nodes),
+ wait_for_nodedowns(Tester, Ref)
+ end,
+
+ ?line {ok, D} = start_node(DN, "-kernel net_ticktime "
+ ++ integer_to_list(PrevTT) ++ " -pa " ++ PaDir),
+
+ ?line NMA = spawn_link(fun () -> MonitorNodes([B, C, D]) end),
+ ?line NMB = spawn_link(B, fun () -> MonitorNodes([node(), C, D]) end),
+ ?line NMC = spawn_link(C, fun () -> MonitorNodes([node(), B, D]) end),
+
+ ?line MaxTT = case PrevTT > TT of
+ true -> ?line PrevTT;
+ false -> ?line TT
+ end,
+
+ ?line CheckResult = make_ref(),
+ ?line spawn_link(fun () ->
+ receive
+ after (25 + MaxTT)*1000 ->
+ Tester ! CheckResult
+ end
+ end),
+
+ % In case other nodes than these are connected
+ case nodes(connected) -- [B, C, D] of
+ [] -> ?line ok;
+ OtherNodes -> ?line rpc:multicall(OtherNodes, net_kernel,
+ set_net_ticktime, [TT, 20])
+ end,
+
+ ?line change_initiated = net_kernel:set_net_ticktime(TT,20),
+ ?line sleep(3),
+ ?line change_initiated = rpc:call(B,net_kernel,set_net_ticktime,[TT,15]),
+ ?line sleep(7),
+ ?line change_initiated = rpc:call(C,net_kernel,set_net_ticktime,[TT,10]),
+
+ ?line {ok, E} = start_node(EN, "-kernel net_ticktime "
+ ++ integer_to_list(TT) ++ " -pa " ++ PaDir),
+ ?line NME = spawn_link(E, fun () -> MonitorNodes([node(), B, C, D]) end),
+ ?line NMA2 = spawn_link(fun () -> MonitorNodes([E]) end),
+ ?line NMB2 = spawn_link(B, fun () -> MonitorNodes([E]) end),
+ ?line NMC2 = spawn_link(C, fun () -> MonitorNodes([E]) end),
+
+ receive CheckResult -> ?line ok end,
+
+ ?line unlink(NMA), exit(NMA, kill),
+ ?line unlink(NMB), exit(NMB, kill),
+ ?line unlink(NMC), exit(NMC, kill),
+ ?line unlink(NME), exit(NME, kill),
+ ?line unlink(NMA2), exit(NMA2, kill),
+ ?line unlink(NMB2), exit(NMB2, kill),
+ ?line unlink(NMC2), exit(NMC2, kill),
+
+ %% The node not changing ticktime should have been disconnected from the
+ %% other nodes
+ receive {Ref, {Node, {nodedown, D}}} when Node == node() -> ?line ok
+ after 0 -> ?line exit({?LINE, no_nodedown})
+ end,
+ receive {Ref, {B, {nodedown, D}}} -> ?line ok
+ after 0 -> ?line exit({?LINE, no_nodedown})
+ end,
+ receive {Ref, {C, {nodedown, D}}} -> ?line ok
+ after 0 -> ?line exit({?LINE, no_nodedown})
+ end,
+ receive {Ref, {E, {nodedown, D}}} -> ?line ok
+ after 0 -> ?line exit({?LINE, no_nodedown})
+ end,
+
+ %% No other connections should have been broken
+ receive
+ {Ref, Reason} ->
+ ?line stop_node(E),
+ ?line exit({?LINE, Reason});
+ {'EXIT', Pid, Reason} when Pid == NMA;
+ Pid == NMB;
+ Pid == NMC;
+ Pid == NME;
+ Pid == NMA2;
+ Pid == NMB2;
+ Pid == NMC2 ->
+ ?line stop_node(E),
+
+ ?line exit({?LINE, {node(Pid), Reason}})
+ after 0 ->
+ ?line TT = net_kernel:get_net_ticktime(),
+ ?line TT = rpc:call(B, net_kernel, get_net_ticktime, []),
+ ?line TT = rpc:call(C, net_kernel, get_net_ticktime, []),
+ ?line TT = rpc:call(E, net_kernel, get_net_ticktime, []),
+ ?line stop_node(E),
+ ?line ok
+ end.
+
+%%
+%% Basic tests of hidden node.
+%%
+hidden_node(doc) ->
+ ["Basic test of hidden node"];
+hidden_node(suite) ->
+ [];
+hidden_node(Config) when list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(40)),
+ PaDir = filename:dirname(code:which(?MODULE)),
+ VArgs = "-pa " ++ PaDir,
+ HArgs = "-hidden -pa " ++ PaDir,
+ ?line {ok, V} = start_node(visible_node, VArgs),
+ VMN = start_monitor_nodes_proc(V),
+ ?line {ok, H} = start_node(hidden_node, HArgs),
+ % Connect visible_node -> hidden_node
+ connect_nodes(V, H),
+ test_nodes(V, H),
+ stop_node(H),
+ sleep(5),
+ check_monitor_nodes_res(VMN, H),
+ stop_node(V),
+ ?line {ok, H} = start_node(hidden_node, HArgs),
+ HMN = start_monitor_nodes_proc(H),
+ ?line {ok, V} = start_node(visible_node, VArgs),
+ % Connect hidden_node -> visible_node
+ connect_nodes(H, V),
+ test_nodes(V, H),
+ stop_node(V),
+ sleep(5),
+ check_monitor_nodes_res(HMN, V),
+ stop_node(H),
+ ?line ?t:timetrap_cancel(Dog),
+ ok.
+
+connect_nodes(A, B) ->
+ % Check that they haven't already connected.
+ ?line false = lists:member(A, rpc:call(B, erlang, nodes, [connected])),
+ ?line false = lists:member(B, rpc:call(A, erlang, nodes, [connected])),
+ % Connect them.
+ ?line pong = rpc:call(A, net_adm, ping, [B]).
+
+
+test_nodes(V, H) ->
+ % No nodes should be visible on hidden_node
+ ?line [] = rpc:call(H, erlang, nodes, []),
+ % visible_node should be hidden on hidden_node
+ ?line true = lists:member(V, rpc:call(H, erlang, nodes, [hidden])),
+ % hidden_node node shouldn't be visible on visible_node
+ ?line false = lists:member(H, rpc:call(V, erlang, nodes, [])),
+ % hidden_node should be hidden on visible_node
+ ?line true = lists:member(H, rpc:call(V, erlang, nodes, [hidden])).
+
+mn_loop(MNs) ->
+ receive
+ {nodeup, N} ->
+ mn_loop([{nodeup, N}|MNs]);
+ {nodedown, N} ->
+ mn_loop([{nodedown, N}|MNs]);
+ {monitor_nodes_result, Ref, From} ->
+ From ! {Ref, MNs};
+ _ ->
+ mn_loop(MNs)
+ end.
+
+start_monitor_nodes_proc(Node) ->
+ Ref = make_ref(),
+ Starter = self(),
+ Pid = spawn(Node,
+ fun() ->
+ net_kernel:monitor_nodes(true),
+ Starter ! Ref,
+ mn_loop([])
+ end),
+ receive
+ Ref ->
+ ok
+ end,
+ Pid.
+
+
+check_monitor_nodes_res(Pid, Node) ->
+ Ref = make_ref(),
+ Pid ! {monitor_nodes_result, Ref, self()},
+ receive
+ {Ref, MNs} ->
+ ?line false = lists:keysearch(Node, 2, MNs)
+ end.
+
+
+monitor_nodes(doc) ->
+ [];
+monitor_nodes(suite) ->
+ [monitor_nodes_nodedown_reason,
+ monitor_nodes_complex_nodedown_reason,
+ monitor_nodes_node_type,
+ monitor_nodes_misc,
+ monitor_nodes_otp_6481,
+ monitor_nodes_errors,
+ monitor_nodes_combinations,
+ monitor_nodes_cleanup,
+ monitor_nodes_many].
+
+%%
+%% Testcase:
+%% monitor_nodes_nodedown_reason
+%%
+
+monitor_nodes_nodedown_reason(doc) -> [];
+monitor_nodes_nodedown_reason(suite) -> [];
+monitor_nodes_nodedown_reason(Config) when list(Config) ->
+ ?line MonNodeState = monitor_node_state(),
+ ?line ok = net_kernel:monitor_nodes(true),
+ ?line ok = net_kernel:monitor_nodes(true, [nodedown_reason]),
+
+ ?line Names = get_numbered_nodenames(5, node),
+ ?line [NN1, NN2, NN3, NN4, NN5] = Names,
+
+ ?line {ok, N1} = start_node(NN1),
+ ?line {ok, N2} = start_node(NN2),
+ ?line {ok, N3} = start_node(NN3),
+ ?line {ok, N4} = start_node(NN4, "-hidden"),
+
+ ?line receive {nodeup, N1} -> ok end,
+ ?line receive {nodeup, N2} -> ok end,
+ ?line receive {nodeup, N3} -> ok end,
+
+ ?line receive {nodeup, N1, []} -> ok end,
+ ?line receive {nodeup, N2, []} -> ok end,
+ ?line receive {nodeup, N3, []} -> ok end,
+
+ ?line stop_node(N1),
+ ?line stop_node(N4),
+ ?line true = net_kernel:disconnect(N2),
+ ?line TickTime = net_kernel:get_net_ticktime(),
+ ?line SleepTime = TickTime + (TickTime div 4),
+ ?line spawn(N3, fun () ->
+ block_emu(SleepTime*1000),
+ halt()
+ end),
+
+ ?line receive {nodedown, N1} -> ok end,
+ ?line receive {nodedown, N2} -> ok end,
+ ?line receive {nodedown, N3} -> ok end,
+
+ ?line receive {nodedown, N1, [{nodedown_reason, R1}]} -> connection_closed = R1 end,
+ ?line receive {nodedown, N2, [{nodedown_reason, R2}]} -> disconnect = R2 end,
+ ?line receive {nodedown, N3, [{nodedown_reason, R3}]} -> net_tick_timeout = R3 end,
+
+ ?line ok = net_kernel:monitor_nodes(false, [nodedown_reason]),
+
+ ?line {ok, N5} = start_node(NN5),
+ ?line stop_node(N5),
+
+ ?line receive {nodeup, N5} -> ok end,
+ ?line receive {nodedown, N5} -> ok end,
+ ?line print_my_messages(),
+ ?line ok = check_no_nodedown_nodeup(1000),
+ ?line ok = net_kernel:monitor_nodes(false),
+ ?line MonNodeState = monitor_node_state(),
+ ?line ok.
+
+
+monitor_nodes_complex_nodedown_reason(doc) -> [];
+monitor_nodes_complex_nodedown_reason(suite) -> [];
+monitor_nodes_complex_nodedown_reason(Config) when list(Config) ->
+ ?line MonNodeState = monitor_node_state(),
+ ?line Me = self(),
+ ?line ok = net_kernel:monitor_nodes(true, [nodedown_reason]),
+ ?line [Name] = get_nodenames(1, monitor_nodes_complex_nodedown_reason),
+ ?line {ok, Node} = start_node(Name, ""),
+ ?line Pid = spawn(Node,
+ fun() ->
+ Me ! {stuff,
+ self(),
+ [make_ref(),
+ {processes(), erlang:ports()}]}
+ end),
+ ?line receive {nodeup, Node, []} -> ok end,
+ ?line {ok, NodeInfo} = net_kernel:node_info(Node),
+ ?line {value,{owner, Owner}} = lists:keysearch(owner, 1, NodeInfo),
+ ?line ComplexTerm = receive {stuff, Pid, _} = Msg ->
+ {Msg, term_to_binary(Msg)}
+ end,
+ ?line exit(Owner, ComplexTerm),
+ ?line receive
+ {nodedown, Node, [{nodedown_reason, NodeDownReason}]} ->
+ ?line ok
+ end,
+ %% If the complex nodedown_reason messed something up garbage collections
+ %% are likely to dump core
+ ?line garbage_collect(),
+ ?line garbage_collect(),
+ ?line garbage_collect(),
+ ?line ComplexTerm = NodeDownReason,
+ ?line ok = net_kernel:monitor_nodes(false, [nodedown_reason]),
+ ?line no_msgs(),
+ ?line MonNodeState = monitor_node_state(),
+ ?line ok.
+
+
+
+
+%%
+%% Testcase:
+%% monitor_nodes_node_type
+%%
+
+monitor_nodes_node_type(doc) -> [];
+monitor_nodes_node_type(suite) -> [];
+monitor_nodes_node_type(Config) when is_list(Config) ->
+ ?line MonNodeState = monitor_node_state(),
+ ?line ok = net_kernel:monitor_nodes(true),
+ ?line ok = net_kernel:monitor_nodes(true, [{node_type, all}]),
+ ?line Names = get_numbered_nodenames(9, node),
+% ?line ?t:format("Names: ~p~n", [Names]),
+ ?line [NN1, NN2, NN3, NN4, NN5, NN6, NN7, NN8, NN9] = Names,
+
+ ?line {ok, N1} = start_node(NN1),
+ ?line {ok, N2} = start_node(NN2),
+ ?line {ok, N3} = start_node(NN3, "-hidden"),
+ ?line {ok, N4} = start_node(NN4, "-hidden"),
+
+ ?line receive {nodeup, N1} -> ok end,
+ ?line receive {nodeup, N2} -> ok end,
+
+ ?line receive {nodeup, N1, [{node_type, visible}]} -> ok end,
+ ?line receive {nodeup, N2, [{node_type, visible}]} -> ok end,
+ ?line receive {nodeup, N3, [{node_type, hidden}]} -> ok end,
+ ?line receive {nodeup, N4, [{node_type, hidden}]} -> ok end,
+
+ ?line stop_node(N1),
+ ?line stop_node(N2),
+ ?line stop_node(N3),
+ ?line stop_node(N4),
+
+ ?line receive {nodedown, N1} -> ok end,
+ ?line receive {nodedown, N2} -> ok end,
+
+ ?line receive {nodedown, N1, [{node_type, visible}]} -> ok end,
+ ?line receive {nodedown, N2, [{node_type, visible}]} -> ok end,
+ ?line receive {nodedown, N3, [{node_type, hidden}]} -> ok end,
+ ?line receive {nodedown, N4, [{node_type, hidden}]} -> ok end,
+
+ ?line ok = net_kernel:monitor_nodes(false, [{node_type, all}]),
+ ?line {ok, N5} = start_node(NN5),
+
+ ?line receive {nodeup, N5} -> ok end,
+ ?line stop_node(N5),
+ ?line receive {nodedown, N5} -> ok end,
+
+ ?line ok = net_kernel:monitor_nodes(true, [{node_type, hidden}]),
+ ?line {ok, N6} = start_node(NN6),
+ ?line {ok, N7} = start_node(NN7, "-hidden"),
+
+
+ ?line receive {nodeup, N6} -> ok end,
+ ?line receive {nodeup, N7, [{node_type, hidden}]} -> ok end,
+ ?line stop_node(N6),
+ ?line stop_node(N7),
+
+ ?line receive {nodedown, N6} -> ok end,
+ ?line receive {nodedown, N7, [{node_type, hidden}]} -> ok end,
+
+ ?line ok = net_kernel:monitor_nodes(true, [{node_type, visible}]),
+ ?line ok = net_kernel:monitor_nodes(false, [{node_type, hidden}]),
+ ?line ok = net_kernel:monitor_nodes(false),
+
+ ?line {ok, N8} = start_node(NN8),
+ ?line {ok, N9} = start_node(NN9, "-hidden"),
+
+ ?line receive {nodeup, N8, [{node_type, visible}]} -> ok end,
+ ?line stop_node(N8),
+ ?line stop_node(N9),
+
+ ?line receive {nodedown, N8, [{node_type, visible}]} -> ok end,
+ ?line print_my_messages(),
+ ?line ok = check_no_nodedown_nodeup(1000),
+ ?line ok = net_kernel:monitor_nodes(false, [{node_type, visible}]),
+ ?line MonNodeState = monitor_node_state(),
+ ?line ok.
+
+
+%%
+%% Testcase:
+%% monitor_nodes
+%%
+
+monitor_nodes_misc(doc) -> [];
+monitor_nodes_misc(suite) -> [];
+monitor_nodes_misc(Config) when is_list(Config) ->
+ ?line MonNodeState = monitor_node_state(),
+ ?line ok = net_kernel:monitor_nodes(true),
+ ?line ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]),
+ ?line ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}]),
+ ?line Names = get_numbered_nodenames(3, node),
+% ?line ?t:format("Names: ~p~n", [Names]),
+ ?line [NN1, NN2, NN3] = Names,
+
+ ?line {ok, N1} = start_node(NN1),
+ ?line {ok, N2} = start_node(NN2, "-hidden"),
+
+ ?line receive {nodeup, N1} -> ok end,
+
+ ?line receive {nodeup, N1, [{node_type, visible}]} -> ok end,
+ ?line receive {nodeup, N1, [{node_type, visible}]} -> ok end,
+ ?line receive {nodeup, N2, [{node_type, hidden}]} -> ok end,
+ ?line receive {nodeup, N2, [{node_type, hidden}]} -> ok end,
+
+ ?line stop_node(N1),
+ ?line stop_node(N2),
+
+ ?line VisbleDownInfo = lists:sort([{node_type, visible},
+ {nodedown_reason, connection_closed}]),
+ ?line HiddenDownInfo = lists:sort([{node_type, hidden},
+ {nodedown_reason, connection_closed}]),
+
+ ?line receive {nodedown, N1} -> ok end,
+
+ ?line receive {nodedown, N1, Info1A} -> VisbleDownInfo = lists:sort(Info1A) end,
+ ?line receive {nodedown, N1, Info1B} -> VisbleDownInfo = lists:sort(Info1B) end,
+ ?line receive {nodedown, N2, Info2A} -> HiddenDownInfo = lists:sort(Info2A) end,
+ ?line receive {nodedown, N2, Info2B} -> HiddenDownInfo = lists:sort(Info2B) end,
+
+ ?line ok = net_kernel:monitor_nodes(false, [{node_type, all}, nodedown_reason]),
+
+ ?line {ok, N3} = start_node(NN3),
+ ?line receive {nodeup, N3} -> ok end,
+ ?line stop_node(N3),
+ ?line receive {nodedown, N3} -> ok end,
+ ?line print_my_messages(),
+ ?line ok = check_no_nodedown_nodeup(1000),
+ ?line ok = net_kernel:monitor_nodes(false),
+ ?line MonNodeState = monitor_node_state(),
+ ?line ok.
+
+
+monitor_nodes_otp_6481(doc) ->
+ ["Tests that {nodeup, Node} messages are received before "
+ "messages from Node and that {nodedown, Node} messages are"
+ "received after messages from Node"];
+monitor_nodes_otp_6481(suite) ->
+ [];
+monitor_nodes_otp_6481(Config) when is_list(Config) ->
+ ?line ?t:format("Testing nodedown...~n"),
+ ?line monitor_nodes_otp_6481_test(Config, nodedown),
+ ?line ?t:format("ok~n"),
+ ?line ?t:format("Testing nodeup...~n"),
+ ?line monitor_nodes_otp_6481_test(Config, nodeup),
+ ?line ?t:format("ok~n"),
+ ?line ok.
+
+monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) ->
+ ?line MonNodeState = monitor_node_state(),
+ ?line NodeMsg = make_ref(),
+ ?line Me = self(),
+ ?line [Name] = get_nodenames(1, monitor_nodes_otp_6481),
+ ?line case TestType of
+ nodedown -> ?line ok = net_kernel:monitor_nodes(true);
+ nodeup -> ?line ok
+ end,
+ ?line Seq = lists:seq(1,10000),
+ ?line MN = spawn_link(
+ fun () ->
+ ?line lists:foreach(
+ fun (_) ->
+ ?line ok = net_kernel:monitor_nodes(true)
+ end,
+ Seq),
+ ?line Me ! {mon_set, self()},
+ ?line receive after infinity -> ok end
+ end),
+ ?line receive {mon_set, MN} -> ok end,
+ ?line case TestType of
+ nodedown -> ?line ok;
+ nodeup -> ?line ok = net_kernel:monitor_nodes(true)
+ end,
+
+ %% Whitebox:
+ %% nodedown test: Since this process was the first one monitoring
+ %% nodes this process will be the first one notified
+ %% on nodedown.
+ %% nodeup test: Since this process was the last one monitoring
+ %% nodes this process will be the last one notified
+ %% on nodeup
+
+ %% Verify the monitor_nodes order expected
+ ?line TestMonNodeState = monitor_node_state(),
+ %?line ?t:format("~p~n", [TestMonNodeState]),
+ ?line TestMonNodeState =
+ MonNodeState
+ ++ case TestType of
+ nodedown -> [{self(), []}];
+ nodeup -> []
+ end
+ ++ lists:map(fun (_) -> {MN, []} end, Seq)
+ ++ case TestType of
+ nodedown -> [];
+ nodeup -> [{self(), []}]
+ end,
+
+
+ ?line {ok, Node} = start_node(Name, "", this),
+ ?line receive {nodeup, Node} -> ok end,
+
+ ?line spawn(Node,
+ fun () ->
+ receive after 1000 -> ok end,
+ lists:foreach(fun (No) ->
+ Me ! {NodeMsg, No}
+ end,
+ Seq),
+ halt()
+ end),
+
+ ?line net_kernel:disconnect(Node),
+ ?line receive {nodedown, Node} -> ok end,
+
+ %% Verify that '{nodeup, Node}' comes before '{NodeMsg, 1}' (the message
+ %% bringing up the connection).
+ %%?line no_msgs(500), % Why wait? It fails test sometimes /sverker
+ ?line {nodeup, Node} = receive Msg1 -> Msg1 end,
+ ?line {NodeMsg, 1} = receive Msg2 -> Msg2 end,
+
+ %% Verify that '{nodedown, Node}' comes after the last '{NodeMsg, N}'
+ %% message.
+ ?line {nodedown, Node} = flush_node_msgs(NodeMsg, 2),
+ ?line no_msgs(500),
+
+ ?line Mon = erlang:monitor(process, MN),
+ ?line unlink(MN),
+ ?line exit(MN, bang),
+ ?line receive {'DOWN', Mon, process, MN, bang} -> ok end,
+ ?line ok = net_kernel:monitor_nodes(false),
+ ?line MonNodeState = monitor_node_state(),
+ ?line ok.
+
+flush_node_msgs(NodeMsg, No) ->
+ case receive Msg -> Msg end of
+ {NodeMsg, No} -> flush_node_msgs(NodeMsg, No+1);
+ OtherMsg -> OtherMsg
+ end.
+
+monitor_nodes_errors(doc) ->
+ [];
+monitor_nodes_errors(suite) ->
+ [];
+monitor_nodes_errors(Config) when list(Config) ->
+ ?line MonNodeState = monitor_node_state(),
+ ?line error = net_kernel:monitor_nodes(asdf),
+ ?line {error,
+ {unknown_options,
+ [gurka]}} = net_kernel:monitor_nodes(true,
+ [gurka]),
+ ?line {error,
+ {options_not_a_list,
+ gurka}} = net_kernel:monitor_nodes(true,
+ gurka),
+ ?line {error,
+ {option_value_mismatch,
+ [{node_type,visible},
+ {node_type,hidden}]}}
+ = net_kernel:monitor_nodes(true,
+ [{node_type,hidden},
+ {node_type,visible}]),
+ ?line {error,
+ {option_value_mismatch,
+ [{node_type,visible},
+ {node_type,all}]}}
+ = net_kernel:monitor_nodes(true,
+ [{node_type,all},
+ {node_type,visible}]),
+ ?line {error,
+ {bad_option_value,
+ {node_type,
+ blaha}}}
+ = net_kernel:monitor_nodes(true, [{node_type, blaha}]),
+ ?line MonNodeState = monitor_node_state(),
+ ?line ok.
+
+monitor_nodes_combinations(doc) ->
+ [];
+monitor_nodes_combinations(suite) ->
+ [];
+monitor_nodes_combinations(Config) when list(Config) ->
+ ?line MonNodeState = monitor_node_state(),
+ ?line monitor_nodes_all_comb(true),
+ ?line [VisibleName, HiddenName] = get_nodenames(2,
+ monitor_nodes_combinations),
+ ?line {ok, Visible} = start_node(VisibleName, ""),
+ ?line receive_all_comb_nodeup_msgs(visible, Visible),
+ ?line no_msgs(),
+ ?line stop_node(Visible),
+ ?line receive_all_comb_nodedown_msgs(visible, Visible, connection_closed),
+ ?line no_msgs(),
+ ?line {ok, Hidden} = start_node(HiddenName, "-hidden"),
+ ?line receive_all_comb_nodeup_msgs(hidden, Hidden),
+ ?line no_msgs(),
+ ?line stop_node(Hidden),
+ ?line receive_all_comb_nodedown_msgs(hidden, Hidden, connection_closed),
+ ?line no_msgs(),
+ ?line monitor_nodes_all_comb(false),
+ ?line MonNodeState = monitor_node_state(),
+ ?line no_msgs(),
+ ?line ok.
+
+monitor_nodes_all_comb(Flag) ->
+ ?line ok = net_kernel:monitor_nodes(Flag),
+ ?line ok = net_kernel:monitor_nodes(Flag,
+ [nodedown_reason]),
+ ?line ok = net_kernel:monitor_nodes(Flag,
+ [{node_type, hidden}]),
+ ?line ok = net_kernel:monitor_nodes(Flag,
+ [{node_type, visible}]),
+ ?line ok = net_kernel:monitor_nodes(Flag,
+ [{node_type, all}]),
+ ?line ok = net_kernel:monitor_nodes(Flag,
+ [nodedown_reason,
+ {node_type, hidden}]),
+ ?line ok = net_kernel:monitor_nodes(Flag,
+ [nodedown_reason,
+ {node_type, visible}]),
+ ?line ok = net_kernel:monitor_nodes(Flag,
+ [nodedown_reason,
+ {node_type, all}]),
+ %% There currently are 8 different combinations
+ ?line 8.
+
+
+receive_all_comb_nodeup_msgs(visible, Node) ->
+ ?t:format("Receive nodeup visible...~n"),
+ Exp = [{nodeup, Node},
+ {nodeup, Node, []}]
+ ++ mk_exp_mn_all_comb_nodeup_msgs_common(visible, Node),
+ receive_mn_msgs(Exp),
+ ?t:format("ok~n"),
+ ok;
+receive_all_comb_nodeup_msgs(hidden, Node) ->
+ ?t:format("Receive nodeup hidden...~n"),
+ Exp = mk_exp_mn_all_comb_nodeup_msgs_common(hidden, Node),
+ receive_mn_msgs(Exp),
+ ?t:format("ok~n"),
+ ok.
+
+mk_exp_mn_all_comb_nodeup_msgs_common(Type, Node) ->
+ InfoNt = [{node_type, Type}],
+ [{nodeup, Node, InfoNt},
+ {nodeup, Node, InfoNt},
+ {nodeup, Node, InfoNt},
+ {nodeup, Node, InfoNt}].
+
+receive_all_comb_nodedown_msgs(visible, Node, Reason) ->
+ ?t:format("Receive nodedown visible...~n"),
+ Exp = [{nodedown, Node},
+ {nodedown, Node, [{nodedown_reason, Reason}]}]
+ ++ mk_exp_mn_all_comb_nodedown_msgs_common(visible,
+ Node,
+ Reason),
+ receive_mn_msgs(Exp),
+ ?t:format("ok~n"),
+ ok;
+receive_all_comb_nodedown_msgs(hidden, Node, Reason) ->
+ ?t:format("Receive nodedown hidden...~n"),
+ Exp = mk_exp_mn_all_comb_nodedown_msgs_common(hidden, Node, Reason),
+ receive_mn_msgs(Exp),
+ ?t:format("ok~n"),
+ ok.
+
+mk_exp_mn_all_comb_nodedown_msgs_common(Type, Node, Reason) ->
+ InfoNt = [{node_type, Type}],
+ InfoNdrNt = lists:sort([{nodedown_reason, Reason}]++InfoNt),
+ [{nodedown, Node, InfoNt},
+ {nodedown, Node, InfoNt},
+ {nodedown, Node, InfoNdrNt},
+ {nodedown, Node, InfoNdrNt}].
+
+receive_mn_msgs([]) ->
+ ok;
+receive_mn_msgs(Msgs) ->
+ ?t:format("Expecting msgs: ~p~n", [Msgs]),
+ receive
+ {_Dir, _Node} = Msg ->
+ ?t:format("received ~p~n", [Msg]),
+ case lists:member(Msg, Msgs) of
+ true -> receive_mn_msgs(lists:delete(Msg, Msgs));
+ false -> ?t:fail({unexpected_message, Msg,
+ expected_messages, Msgs})
+ end;
+ {Dir, Node, Info} ->
+ Msg = {Dir, Node, lists:sort(Info)},
+ ?t:format("received ~p~n", [Msg]),
+ case lists:member(Msg, Msgs) of
+ true -> receive_mn_msgs(lists:delete(Msg, Msgs));
+ false -> ?t:fail({unexpected_message, Msg,
+ expected_messages, Msgs})
+ end;
+ Msg ->
+ ?t:format("received ~p~n", [Msg]),
+ ?t:fail({unexpected_message, Msg,
+ expected_messages, Msgs})
+ end.
+
+monitor_nodes_cleanup(doc) ->
+ [];
+monitor_nodes_cleanup(suite) ->
+ [];
+monitor_nodes_cleanup(Config) when list(Config) ->
+ ?line MonNodeState = monitor_node_state(),
+ ?line Me = self(),
+ ?line No = monitor_nodes_all_comb(true),
+ ?line Inf = spawn(fun () ->
+ monitor_nodes_all_comb(true),
+ Me ! {mons_set, self()},
+ receive after infinity -> ok end
+ end),
+ ?line TO = spawn(fun () ->
+ monitor_nodes_all_comb(true),
+ Me ! {mons_set, self()},
+ receive after 500 -> ok end
+ end),
+ ?line receive {mons_set, Inf} -> ok end,
+ ?line receive {mons_set, TO} -> ok end,
+ ?line MNLen = length(MonNodeState) + No*3,
+ ?line MNLen = length(monitor_node_state()),
+ ?line MonInf = erlang:monitor(process, Inf),
+ ?line MonTO = erlang:monitor(process, TO),
+ ?line exit(Inf, bang),
+ ?line No = monitor_nodes_all_comb(false),
+ ?line receive {'DOWN', MonInf, process, Inf, bang} -> ok end,
+ ?line receive {'DOWN', MonTO, process, TO, normal} -> ok end,
+ ?line MonNodeState = monitor_node_state(),
+ ?line no_msgs(),
+ ?line ok.
+
+monitor_nodes_many(doc) ->
+ [];
+monitor_nodes_many(suite) ->
+ [];
+monitor_nodes_many(Config) when list(Config) ->
+ ?line MonNodeState = monitor_node_state(),
+ ?line [Name] = get_nodenames(1, monitor_nodes_many),
+ %% We want to perform more than 2^16 net_kernel:monitor_nodes
+ %% since this will wrap an internal counter
+ ?line No = (1 bsl 16) + 17,
+ ?line repeat(fun () -> ok = net_kernel:monitor_nodes(true) end, No),
+ ?line No = length(monitor_node_state()) - length(MonNodeState),
+ ?line {ok, Node} = start_node(Name),
+ ?line repeat(fun () -> receive {nodeup, Node} -> ok end end, No),
+ ?line stop_node(Node),
+ ?line repeat(fun () -> receive {nodedown, Node} -> ok end end, No),
+ ?line ok = net_kernel:monitor_nodes(false),
+ ?line no_msgs(10),
+ ?line MonNodeState = monitor_node_state(),
+ ?line ok.
+
+%% Misc. functions
+
+monitor_node_state() ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ MonitoringNodes = erts_debug:get_internal_state(monitoring_nodes),
+ erts_debug:set_internal_state(available_internal_state, false),
+ MonitoringNodes.
+
+
+check_no_nodedown_nodeup(TimeOut) ->
+ ?line receive
+ {nodeup, _, _} = Msg -> ?line ?t:fail({unexpected_nodeup, Msg});
+ {nodeup, _} = Msg -> ?line ?t:fail({unexpected_nodeup, Msg});
+ {nodedown, _, _} = Msg -> ?line ?t:fail({unexpected_nodedown, Msg});
+ {nodedown, _} = Msg -> ?line ?t:fail({unexpected_nodedown, Msg})
+ after TimeOut ->
+ ok
+ end.
+
+print_my_messages() ->
+ ?line {messages, Messages} = process_info(self(), messages),
+ ?line ?t:format("Messages: ~p~n", [Messages]),
+ ?line ok.
+
+%% Time difference in milliseconds !!
+time_diff({TimeM, TimeS, TimeU}, {CurM, CurS, CurU}) when CurM > TimeM ->
+ ((CurM - TimeM) * 1000000000) + sec_diff({TimeS, TimeU}, {CurS, CurU});
+time_diff({_, TimeS, TimeU}, {_, CurS, CurU}) ->
+ sec_diff({TimeS, TimeU}, {CurS, CurU}).
+
+sec_diff({TimeS, TimeU}, {CurS, CurU}) when CurS > TimeS ->
+ ((CurS - TimeS) * 1000) + micro_diff(TimeU, CurU);
+sec_diff({_, TimeU}, {_, CurU}) ->
+ micro_diff(TimeU, CurU).
+
+micro_diff(TimeU, CurU) ->
+ trunc(CurU/1000) - trunc(TimeU/1000).
+
+sleep(T) -> receive after T * 1000 -> ok end.
+
+start_node(Name, Param, this) ->
+ NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
+ ?t:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]);
+start_node(Name, Param, "this") ->
+ NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
+ ?t:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]);
+start_node(Name, Param, Rel) when atom(Rel) ->
+ NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
+ ?t:start_node(Name, peer, [{args, NewParam}, {erl, [{release, atom_to_list(Rel)}]}]);
+start_node(Name, Param, Rel) when list(Rel) ->
+ NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
+ ?t:start_node(Name, peer, [{args, NewParam}, {erl, [{release, Rel}]}]).
+
+start_node(Name, Param) ->
+ NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
+ ?t:start_node(Name, slave, [{args, NewParam}]).
+% M = list_to_atom(from($@, atom_to_list(node()))),
+% slave:start_link(M, Name, Param).
+
+start_node(Name) ->
+ start_node(Name, "").
+
+stop_node(Node) ->
+ ?t:stop_node(Node).
+% erlang:monitor_node(Node, true),
+% rpc:cast(Node, init, stop, []),
+% receive
+% {nodedown, Node} ->
+% ok
+% after 10000 ->
+% test_server:fail({stop_node, Node})
+% end.
+
+% from(H, [H | T]) -> T;
+% from(H, [_ | T]) -> from(H, T);
+% from(H, []) -> [].
+
+get_nodenames(N, T) ->
+ get_nodenames(N, T, []).
+
+get_nodenames(0, _, Acc) ->
+ Acc;
+get_nodenames(N, T, Acc) ->
+ {A, B, C} = now(),
+ get_nodenames(N-1, T, [list_to_atom(atom_to_list(T)
+ ++ "-"
+ ++ atom_to_list(?MODULE)
+ ++ "-"
+ ++ integer_to_list(A)
+ ++ "-"
+ ++ integer_to_list(B)
+ ++ "-"
+ ++ integer_to_list(C)) | Acc]).
+
+get_numbered_nodenames(N, T) ->
+ get_numbered_nodenames(N, T, []).
+
+get_numbered_nodenames(0, _, Acc) ->
+ Acc;
+get_numbered_nodenames(N, T, Acc) ->
+ {A, B, C} = now(),
+ NL = [list_to_atom(atom_to_list(T) ++ integer_to_list(N)
+ ++ "-"
+ ++ atom_to_list(?MODULE)
+ ++ "-"
+ ++ integer_to_list(A)
+ ++ "-"
+ ++ integer_to_list(B)
+ ++ "-"
+ ++ integer_to_list(C)) | Acc],
+ get_numbered_nodenames(N-1, T, NL).
+
+wait_until(Fun) ->
+ case Fun() of
+ true ->
+ ok;
+ _ ->
+ receive
+ after 100 ->
+ wait_until(Fun)
+ end
+ end.
+
+repeat(Fun, 0) when function(Fun) ->
+ ok;
+repeat(Fun, N) when function(Fun), integer(N), N > 0 ->
+ Fun(),
+ repeat(Fun, N-1).
+
+no_msgs(Wait) ->
+ receive after Wait -> no_msgs() end.
+
+no_msgs() ->
+ {messages, []} = process_info(self(), messages).
+
+block_emu(Ms) ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ Res = erts_debug:set_internal_state(block, Ms),
+ erts_debug:set_internal_state(available_internal_state, false),
+ Res.