summaryrefslogtreecommitdiff
path: root/lib/kernel/test/erpc_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/test/erpc_SUITE.erl')
-rw-r--r--lib/kernel/test/erpc_SUITE.erl1282
1 files changed, 1282 insertions, 0 deletions
diff --git a/lib/kernel/test/erpc_SUITE.erl b/lib/kernel/test/erpc_SUITE.erl
new file mode 100644
index 0000000000..1c969ad183
--- /dev/null
+++ b/lib/kernel/test/erpc_SUITE.erl
@@ -0,0 +1,1282 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. 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(erpc_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([call/1, call_reqtmo/1, call_against_old_node/1, cast/1,
+ send_request/1, send_request_fun/1,
+ send_request_receive_reqtmo/1,
+ send_request_wait_reqtmo/1,
+ send_request_check_reqtmo/1,
+ send_request_against_old_node/1,
+ multicall/1, multicall_reqtmo/1,
+ multicast/1,
+ timeout_limit/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+-export([call_func1/1, call_func2/1, call_func4/4]).
+
+-export([f/0, f2/0]).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,2}}].
+
+all() ->
+ [call,
+ call_reqtmo,
+ call_against_old_node,
+ cast,
+ send_request,
+ send_request_fun,
+ send_request_receive_reqtmo,
+ send_request_wait_reqtmo,
+ send_request_check_reqtmo,
+ send_request_against_old_node,
+ multicall,
+ multicall_reqtmo,
+ multicast,
+ timeout_limit].
+
+groups() ->
+ [].
+
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ [{testcase, Func}|Config].
+
+end_per_testcase(_Func, _Config) ->
+ ok.
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+call(Config) when is_list(Config) ->
+ call_test(Config).
+
+call_test(Config) ->
+ call_test(node(), 10000),
+ call_test(node(), infinity),
+ try
+ erpc:call(node(), timer, sleep, [100], 10),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} ->
+ ok
+ end,
+ try
+ erpc:call(node(), fun () -> timer:sleep(100) end, 10),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} ->
+ ok
+ end,
+ {ok, Node} = start_node(Config),
+ call_test(Node, 10000),
+ call_test(Node, infinity),
+ try
+ erpc:call(Node, timer, sleep, [100], 10),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} ->
+ ok
+ end,
+ try
+ erpc:call(Node, fun () -> timer:sleep(100) end, 10),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, halt, []),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} ->
+ ok
+ end,
+ try
+ erpc:call(Node, fun () -> erlang:node() end),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, node, []),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} ->
+ ok
+ end,
+ try
+ erpc:call(badnodename, erlang, node, []),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} ->
+ ok
+ end,
+
+ receive after 1000 -> ok end,
+ [] = flush([]),
+ ok.
+
+call_test(Node, Timeout) ->
+ io:format("call_test(~p, ~p)~n", [Node, Timeout]),
+ Node = erpc:call(Node, erlang, node, [], Timeout),
+ try
+ erpc:call(Node, erlang, error, [oops|invalid], Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{exception, badarg, [{erlang,apply,[erlang,error,[oops|invalid]],_}]} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, error, [oops], Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{exception, oops, [{erlang,error,[oops],_}]} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, exit, [oops], Timeout),
+ ct:fail(unexpected)
+ catch
+ exit:{exception, oops} ->
+ ok
+ end,
+ try
+ erpc:call(Node, fun () -> erlang:exit(oops) end, Timeout),
+ ct:fail(unexpected)
+ catch
+ exit:{exception, oops} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, throw, [oops], Timeout),
+ ct:fail(unexpected)
+ catch
+ throw:oops ->
+ ok
+ end,
+ try
+ erpc:call(Node, fun () -> erlang:throw(oops) end, Timeout),
+ ct:fail(unexpected)
+ catch
+ throw:oops ->
+ ok
+ end,
+ case {node() == Node, Timeout == infinity} of
+ {true, true} ->
+ %% This would kill the test since local calls
+ %% without timeout is optimized to execute in
+ %% calling process itself...
+ ok;
+ _ ->
+ ExitSignal = fun () ->
+ exit(self(), oops),
+ receive after infinity -> ok end
+ end,
+ try
+ erpc:call(Node, ExitSignal, Timeout),
+ ct:fail(unexpected)
+ catch
+ exit:{signal, oops} ->
+ ok
+ end,
+ try
+ erpc:call(Node, erlang, apply, [ExitSignal, []], Timeout),
+ ct:fail(unexpected)
+ catch
+ exit:{signal, oops} ->
+ ok
+ end
+ end,
+ try
+ erpc:call(Node, ?MODULE, call_func1, [boom], Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{exception,
+ {exception,
+ boom,
+ [{?MODULE, call_func3, A2, _},
+ {?MODULE, call_func2, 1, _}]},
+ [{erpc, call, A1, _},
+ {?MODULE, call_func1, 1, _}]}
+ when ((A1 == 5)
+ orelse (A1 == [Node, ?MODULE, call_func2, [boom]]))
+ andalso ((A2 == 1)
+ orelse (A2 == [boom])) ->
+ ok
+ end,
+ try
+ erpc:call(Node, fun () -> ?MODULE:call_func1(boom) end, Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{exception,
+ {exception,
+ boom,
+ [{?MODULE, call_func3, A4, _},
+ {?MODULE, call_func2, 1, _}]},
+ [{erpc, call, A3, _},
+ {?MODULE, call_func1, 1, _},
+ {erlang, apply, 2, _}]}
+ when ((A3 == 5)
+ orelse (A3 == [Node, ?MODULE, call_func2, [boom]]))
+ andalso ((A4 == 1)
+ orelse (A4 == [boom])) ->
+ ok
+ end,
+ try
+ call_func4(Node, node(), 10, Timeout),
+ ct:fail(unexpected)
+ catch
+ error:Error1 ->
+%%% io:format("Error1=~p~n", [Error1]),
+ check_call_func4_error(Error1, 10)
+ end,
+ try
+ call_func4(node(), Node, 5, Timeout),
+ ct:fail(unexpected)
+ catch
+ error:Error2 ->
+%%% io:format("Error2=~p~n", [Error2]),
+ check_call_func4_error(Error2, 5)
+ end,
+ ok.
+
+check_call_func4_error({exception,
+ badarg,
+ [{?MODULE, call_func5, _, _},
+ {?MODULE, call_func4, _, _}]},
+ 0) ->
+ ok;
+check_call_func4_error({exception,
+ Exception,
+ [{erpc, call, _, _},
+ {?MODULE, call_func5, _, _},
+ {?MODULE, call_func4, _, _}]},
+ N) ->
+ check_call_func4_error(Exception, N-1).
+
+call_func1(X) ->
+ erpc:call(node(), ?MODULE, call_func2, [X]),
+ ok.
+
+call_func2(X) ->
+ call_func3(X),
+ ok.
+
+call_func3(X) ->
+ erlang:error(X, [X]).
+
+call_func4(A, B, N, T) ->
+ call_func5(A, B, N, T),
+ ok.
+
+call_func5(A, B, N, T) when N >= 0 ->
+ erpc:call(A, ?MODULE, call_func4, [B, A, N-1, T], T),
+ ok;
+call_func5(_A, _B, _N, _T) ->
+ erlang:error(badarg).
+
+call_against_old_node(Config) ->
+ case start_22_node(Config) of
+ {ok, Node22} ->
+ try
+ erpc:call(Node22, erlang, node, []),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, notsup} ->
+ ok
+ end,
+ stop_node(Node22),
+ ok;
+ _ ->
+ {skipped, "No OTP 22 available"}
+ end.
+
+call_reqtmo(Config) when is_list(Config) ->
+ Fun = fun (Node, SendMe, Timeout) ->
+ try
+ erpc:call(Node, erlang, send,
+ [self(), SendMe], Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} -> ok
+ end
+ end,
+ reqtmo_test(Config, Fun).
+
+reqtmo_test(Config, Test) ->
+ %% Tests that we time out in time also when the request itself
+ %% does not get through. A typical issue we have had
+ %% in the past, is that the timeout has not triggered until
+ %% the request has gotten through...
+
+ Timeout = 500,
+ WaitBlock = 100,
+ BlockTime = 1000,
+
+ {ok, Node} = start_node(Config),
+
+ erpc:call(Node, erts_debug, set_internal_state, [available_internal_state,
+ true]),
+
+ SendMe = make_ref(),
+
+ erpc:cast(Node, erts_debug, set_internal_state, [block, BlockTime]),
+ receive after WaitBlock -> ok end,
+
+ Start = erlang:monotonic_time(),
+
+ Test(Node, SendMe, Timeout),
+
+ Stop = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(Stop-Start, native, millisecond),
+ io:format("Actual time: ~p ms~n", [Time]),
+ true = Time >= Timeout,
+ true = Time =< Timeout + 200,
+
+ receive SendMe -> ok end,
+
+ receive UnexpectedMsg -> ct:fail({unexpected_message, UnexpectedMsg})
+ after 0 -> ok
+ end,
+
+ stop_node(Node),
+
+ {comment,
+ "Timeout = " ++ integer_to_list(Timeout)
+ ++ " Actual = " ++ integer_to_list(Time)}.
+
+cast(Config) when is_list(Config) ->
+ %% silently fail
+ ok = erpc:cast(badnodename, erlang, send, [hej]),
+
+ try
+ erpc:cast(<<"node">>, erlang, send, [hej]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ try
+ erpc:cast(node(), erlang, send, hej),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ try
+ erpc:cast(node(), "erlang", send, [hej]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ try
+ erpc:cast(node(), erlang, make_ref(), [hej]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+
+ erpc:cast(node(), erlang, send, [self()|blupp]), %% silent remote error...
+
+ Me = self(),
+ Ok1 = make_ref(),
+ ok = erpc:cast(node(), erlang, send, [Me, {mfa, Ok1}]),
+ receive
+ {mfa, Ok1} -> ok
+ end,
+ ok = erpc:cast(node(), fun () -> Me ! {a_fun, Ok1} end),
+ receive
+ {a_fun, Ok1} -> ok
+ end,
+ {ok, Node} = start_node(Config),
+ Ok2 = make_ref(),
+ ok = erpc:cast(Node, erlang, send, [Me, {mfa, Ok2}]),
+ receive
+ {mfa, Ok2} -> ok
+ end,
+ ok = erpc:cast(Node, fun () -> Me ! {a_fun, Ok2} end),
+ receive
+ {a_fun, Ok2} -> ok
+ end,
+
+ ok = erpc:cast(Node, erlang, halt, []),
+
+ monitor_node(Node, true),
+ receive {nodedown, Node} -> ok end,
+
+ ok = erpc:cast(Node, erlang, send, [Me, wont_reach_me]),
+
+ receive after 1000 -> ok end,
+ [] = flush([]),
+
+ case start_22_node(Config) of
+ {ok, Node22} ->
+ ok = erpc:cast(Node, erlang, send, [Me, wont_reach_me]),
+ ok = erpc:cast(Node, fun () -> Me ! wont_reach_me end),
+ receive
+ Msg -> ct:fail({unexpected_message, Msg})
+ after
+ 2000 -> ok
+ end,
+ stop_node(Node22),
+ {comment, "Tested against OTP 22 as well"};
+ _ ->
+ {comment, "No tested against OTP 22"}
+ end.
+
+send_request(Config) when is_list(Config) ->
+ %% Note: First part of nodename sets response delay in seconds.
+ PA = filename:dirname(code:which(?MODULE)),
+ NodeArgs = [{args,"-pa "++ PA}],
+ {ok,Node1} = test_server:start_node('1_erpc_SUITE_call', slave, NodeArgs),
+ {ok,Node2} = test_server:start_node('10_erpc_SUITE_call', slave, NodeArgs),
+ {ok,Node3} = test_server:start_node('20_erpc_SUITE_call', slave, NodeArgs),
+ ReqId1 = erpc:send_request(Node1, ?MODULE, f, []),
+ ReqId2 = erpc:send_request(Node2, ?MODULE, f, []),
+ ReqId3 = erpc:send_request(Node3, ?MODULE, f, []),
+ ReqId4 = erpc:send_request(Node1, erlang, error, [bang]),
+ ReqId5 = erpc:send_request(Node1, ?MODULE, f, []),
+
+ try
+ erpc:receive_response(ReqId4, 1000)
+ catch
+ error:{exception, bang, [{erlang,error,[bang],_}]} ->
+ ok
+ end,
+ try
+ erpc:receive_response(ReqId5, 10)
+ catch
+ error:{erpc, timeout} ->
+ ok
+ end,
+
+ %% Test fast timeouts.
+ no_response = erpc:wait_response(ReqId2),
+ no_response = erpc:wait_response(ReqId2, 10),
+
+ %% Let Node1 finish its work before yielding.
+ ct:sleep({seconds,2}),
+ {hej,_,Node1} = erpc:receive_response(ReqId1),
+
+ %% Wait for the Node2 and Node3.
+ {response,{hej,_,Node2}} = erpc:wait_response(ReqId2, infinity),
+ {hej,_,Node3} = erpc:receive_response(ReqId3),
+
+ try
+ erpc:receive_response(ReqId5, 10),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+
+ receive
+ Msg0 -> ct:fail(Msg0)
+ after 0 -> ok
+ end,
+
+ stop_node(Node1),
+ stop_node(Node2),
+ stop_node(Node3),
+
+ [] = flush([]),
+
+ {ok, Node4} = start_node(Config),
+
+ ReqId6 = erpc:send_request(Node4, erlang, node, []),
+
+ no_response = erpc:check_response({response, Node1}, ReqId6),
+ no_response = erpc:check_response(ReqId6, ReqId6),
+ receive
+ Msg1 ->
+ {response, Node4} = erpc:check_response(Msg1, ReqId6)
+ end,
+
+ ReqId7 = erpc:send_request(Node4, erlang, halt, []),
+ try
+ erpc:receive_response(ReqId7),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} -> ok
+ end,
+
+ ReqId8 = erpc:send_request(Node4, erlang, node, []),
+ receive
+ Msg2 ->
+ try
+ erpc:check_response(Msg2, ReqId8),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} ->
+ ok
+ end
+ end,
+
+ [] = flush([]),
+
+ case start_22_node(Config) of
+ {ok, Node5} ->
+ ReqId9 = erpc:send_request(Node5, erlang, node, []),
+ try
+ erpc:receive_response(ReqId9),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, notsup} -> ok
+ end,
+
+ stop_node(Node5),
+
+ [] = flush([]),
+
+ ok;
+ _ ->
+ {comment, "No test against OTP 22 node performed"}
+ end.
+
+send_request_fun(Config) when is_list(Config) ->
+ %% Note: First part of nodename sets response delay in seconds.
+ PA = filename:dirname(code:which(?MODULE)),
+ NodeArgs = [{args,"-pa "++ PA}],
+ {ok,Node1} = test_server:start_node('1_erpc_SUITE_call', slave, NodeArgs),
+ {ok,Node2} = test_server:start_node('10_erpc_SUITE_call', slave, NodeArgs),
+ {ok,Node3} = test_server:start_node('20_erpc_SUITE_call', slave, NodeArgs),
+ ReqId1 = erpc:send_request(Node1, fun () -> ?MODULE:f() end),
+ ReqId2 = erpc:send_request(Node2, fun () -> ?MODULE:f() end),
+ ReqId3 = erpc:send_request(Node3, fun () -> ?MODULE:f() end),
+ ReqId4 = erpc:send_request(Node1, fun () -> erlang:error(bang) end),
+ ReqId5 = erpc:send_request(Node1, fun () -> ?MODULE:f() end),
+
+ try
+ erpc:receive_response(ReqId4, 1000)
+ catch
+ error:{exception, bang, [{?MODULE, _, _, _},
+ {erlang,apply,2,_}]} ->
+ ok
+ end,
+ try
+ erpc:receive_response(ReqId5, 10)
+ catch
+ error:{erpc, timeout} ->
+ ok
+ end,
+
+ %% Test fast timeouts.
+ no_response = erpc:wait_response(ReqId2),
+ no_response = erpc:wait_response(ReqId2, 10),
+
+ %% Let Node1 finish its work before yielding.
+ ct:sleep({seconds,2}),
+ {hej,_,Node1} = erpc:receive_response(ReqId1),
+
+ %% Wait for the Node2 and Node3.
+ {response,{hej,_,Node2}} = erpc:wait_response(ReqId2, infinity),
+ {hej,_,Node3} = erpc:receive_response(ReqId3),
+
+ try
+ erpc:receive_response(ReqId5, 10),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+
+ receive
+ Msg0 -> ct:fail(Msg0)
+ after 0 -> ok
+ end,
+
+ stop_node(Node1),
+ stop_node(Node2),
+ stop_node(Node3),
+
+ {ok, Node4} = start_node(Config),
+
+ ReqId6 = erpc:send_request(Node4, fun () -> erlang:node() end),
+
+ no_response = erpc:check_response({response, Node1}, ReqId6),
+ no_response = erpc:check_response(ReqId6, ReqId6),
+ receive
+ Msg1 ->
+ {response, Node4} = erpc:check_response(Msg1, ReqId6)
+ end,
+
+ ReqId7 = erpc:send_request(Node4, erlang, halt, []),
+ try
+ erpc:receive_response(ReqId7),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} -> ok
+ end,
+
+ ReqId8 = erpc:send_request(Node4, erlang, node, []),
+ receive
+ Msg2 ->
+ try
+ erpc:check_response(Msg2, ReqId8),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, noconnection} ->
+ ok
+ end
+ end,
+
+ [] = flush([]),
+
+ case start_22_node(Config) of
+ {ok, Node5} ->
+ ReqId9 = erpc:send_request(Node5, fun () -> erlang:node() end),
+ try
+ erpc:receive_response(ReqId9),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, notsup} -> ok
+ end,
+
+ stop_node(Node5),
+
+ [] = flush([]),
+
+ ok;
+ _ ->
+ {comment, "No test against OTP 22 node performed"}
+ end.
+
+
+send_request_receive_reqtmo(Config) when is_list(Config) ->
+ Fun = fun (Node, SendMe, Timeout) ->
+ RID = erpc:send_request(Node, erlang, send,
+ [self(), SendMe]),
+ try
+ erpc:receive_response(RID, Timeout),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} -> ok
+ end
+ end,
+ reqtmo_test(Config, Fun).
+
+send_request_wait_reqtmo(Config) when is_list(Config) ->
+ Fun = fun (Node, SendMe, Timeout) ->
+ RID = erpc:send_request(Node, erlang, send,
+ [self(), SendMe]),
+ no_response = erpc:wait_response(RID, 0),
+ no_response = erpc:wait_response(RID, Timeout),
+ %% Cleanup...
+ try
+ erpc:receive_response(RID, 0),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} -> ok
+ end
+ end,
+ reqtmo_test(Config, Fun).
+
+send_request_check_reqtmo(Config) when is_list(Config) ->
+ Fun = fun (Node, SendMe, Timeout) ->
+ RID = erpc:send_request(Node, erlang, send,
+ [self(), SendMe]),
+ receive Msg -> erpc:check_response(Msg, RID)
+ after Timeout -> ok
+ end,
+ %% Cleanup...
+ try
+ erpc:receive_response(RID, 0),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, timeout} -> ok
+ end
+ end,
+ reqtmo_test(Config, Fun).
+
+send_request_against_old_node(Config) when is_list(Config) ->
+ case start_22_node(Config) of
+ {ok, Node22} ->
+ RID1 = erpc:send_request(Node22, erlang, node, []),
+ RID2 = erpc:send_request(Node22, erlang, node, []),
+ RID3 = erpc:send_request(Node22, erlang, node, []),
+ try
+ erpc:receive_response(RID1),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, notsup} ->
+ ok
+ end,
+ try
+ erpc:wait_response(RID2, infinity),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, notsup} ->
+ ok
+ end,
+ try
+ receive
+ Msg ->
+ erpc:check_response(Msg, RID3),
+ ct:fail(unexpected)
+ end
+ catch
+ error:{erpc, notsup} ->
+ ok
+ end,
+ stop_node(Node22),
+ ok;
+ _ ->
+ {skipped, "No OTP 22 available"}
+ end.
+
+multicall(Config) ->
+ {ok, Node1} = start_node(Config),
+ {ok, Node2} = start_node(Config),
+ {Node3, Node3Res} = case start_22_node(Config) of
+ {ok, N3} ->
+ {N3, {error, {erpc, notsup}}};
+ _ ->
+ {ok, N3} = start_node(Config),
+ stop_node(N3),
+ {N3, {error, {erpc, noconnection}}}
+ end,
+ {ok, Node4} = start_node(Config),
+ {ok, Node5} = start_node(Config),
+ stop_node(Node2),
+
+ ThisNode = node(),
+ Nodes = [ThisNode, Node1, Node2, Node3, Node4, Node5],
+
+ [{ok, ThisNode},
+ {ok, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, Node4},
+ {ok, Node5},
+ {error, {erpc, noconnection}}]
+ = erpc:multicall(Nodes ++ [badnodename], erlang, node, []),
+
+ [{ok, ThisNode},
+ {ok, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, Node4},
+ {ok, Node5},
+ {error, {erpc, noconnection}}]
+ = erpc:multicall(Nodes ++ [badnodename], fun () -> erlang:node() end),
+
+ try
+ erpc:multicall(Nodes ++ [<<"badnodename">>], erlang, node, []),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+
+ try
+ erpc:multicall([Node1, Node2, Node3, Node4, Node5 | node()], erlang, node, []),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+
+
+ try
+ erpc:multicall(Nodes ++ [<<"badnodename">>], fun () -> erlang:node() end),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+
+ try
+ erpc:multicall(Nodes, fun (X) -> X end),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+
+ [{ok, ThisNode},
+ {ok, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, Node4},
+ {ok, Node5}]
+ = erpc:multicall(Nodes, erlang, node, []),
+
+ [{ok, ThisNode},
+ {ok, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, Node4},
+ {ok, Node5}]
+ = erpc:multicall(Nodes, erlang, node, [], 60000),
+
+ [{throw, ThisNode},
+ {throw, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {throw, Node4},
+ {throw, Node5}]
+ = erpc:multicall(Nodes, fun () -> throw(erlang:node()) end),
+
+ [{throw, ThisNode},
+ {throw, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {throw, Node4},
+ {throw, Node5}]
+ = erpc:multicall(Nodes, fun () -> throw(erlang:node()) end, 60000),
+
+ S0 = erlang:monotonic_time(millisecond),
+ [{error, {erpc, timeout}},
+ {error, {erpc, timeout}},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {error, {erpc, timeout}},
+ {error, {erpc, timeout}},
+ {error, {erpc, timeout}},
+ {error, {erpc, timeout}},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {error, {erpc, timeout}},
+ {error, {erpc, timeout}}]
+ = erpc:multicall(Nodes++Nodes, timer, sleep, [2000], 500),
+ E0 = erlang:monotonic_time(millisecond),
+ T0 = E0 - S0,
+ io:format("T0=~p~n", [T0]),
+ true = T0 < 1000,
+
+ S1 = erlang:monotonic_time(millisecond),
+ [{ok, ok},
+ {ok, ok},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, ok},
+ {ok, ok},
+ {ok, ok},
+ {ok, ok},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, ok},
+ {ok, ok}]
+ = erpc:multicall(Nodes++Nodes, timer, sleep, [2000]),
+ E1 = erlang:monotonic_time(millisecond),
+ T1 = E1 - S1,
+ io:format("T1=~p~n", [T1]),
+ true = T1 < 3000,
+
+ S2 = erlang:monotonic_time(millisecond),
+ [{ok, ok},
+ {ok, ok},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, ok},
+ {ok, ok},
+ {ok, ok},
+ {ok, ok},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, ok},
+ {ok, ok}]
+ = erpc:multicall(Nodes++Nodes, timer, sleep, [2000], 3000),
+ E2 = erlang:monotonic_time(millisecond),
+ T2 = E2 - S2,
+ io:format("T2=~p~n", [T2]),
+ true = T2 < 3000,
+
+ [{ok, ThisNode},
+ {ok, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, Node4},
+ {ok, Node5},
+ {ok, ThisNode},
+ {ok, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, Node4},
+ {ok, Node5}]
+ = erpc:multicall(Nodes++Nodes, erlang, node, []),
+
+ [{ok, ThisNode},
+ {ok, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, Node4},
+ {ok, Node5},
+ {ok, ThisNode},
+ {ok, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, Node4},
+ {ok, Node5}]
+ = erpc:multicall(Nodes++Nodes, erlang, node, [], 60000),
+
+ [{ok, ThisNode},
+ {ok, Node1},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {ok, Node4},
+ {ok, Node5}]
+ = erpc:multicall(Nodes, fun () -> erlang:node() end),
+
+ [BlingError,
+ BlingError,
+ {error, {erpc, noconnection}},
+ Node3Res,
+ BlingError,
+ BlingError]
+ = erpc:multicall(Nodes, ?MODULE, call_func2, [bling]),
+
+ [BlingError,
+ BlingError,
+ {error, {erpc, noconnection}},
+ Node3Res,
+ BlingError,
+ BlingError]
+ = erpc:multicall(Nodes, ?MODULE, call_func2, [bling], 60000),
+
+ {error, {exception,
+ bling,
+ [{?MODULE, call_func3, A, _},
+ {?MODULE, call_func2, 1, _}]}} = BlingError,
+ true = (A == 1) orelse (A == [bling]),
+
+ [{error, {exception, blong, [{erlang, error, [blong], _}]}},
+ {error, {exception, blong, [{erlang, error, [blong], _}]}},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {error, {exception, blong, [{erlang, error, [blong], _}]}},
+ {error, {exception, blong, [{erlang, error, [blong], _}]}}]
+ = erpc:multicall(Nodes, erlang, error, [blong]),
+
+ [{error, {exception, blong, [{erlang, error, [blong], _}]}},
+ {error, {exception, blong, [{erlang, error, [blong], _}]}},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {error, {exception, blong, [{erlang, error, [blong], _}]}},
+ {error, {exception, blong, [{erlang, error, [blong], _}]}}]
+ = erpc:multicall(Nodes, erlang, error, [blong], 60000),
+
+ SlowNode4 = fun () ->
+ case node() of
+ Node4 ->
+ receive after 1000 -> ok end,
+ slow;
+ ThisNode ->
+ throw(fast);
+ _ ->
+ fast
+ end
+ end,
+
+ Start1 = erlang:monotonic_time(),
+ [{throw, fast},
+ {ok, fast},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {error, {erpc, timeout}},
+ {ok, fast}]
+ = erpc:multicall(Nodes, erlang, apply, [SlowNode4, []], 500),
+
+ End1 = erlang:monotonic_time(),
+
+ Time1 = erlang:convert_time_unit(End1-Start1, native, millisecond),
+ io:format("Time1 = ~p~n",[Time1]),
+ true = Time1 >= 500,
+ true = Time1 =< 1000,
+
+ SlowThisNode = fun () ->
+ case node() of
+ ThisNode ->
+ receive after 1000 -> ok end,
+ slow;
+ Node4 ->
+ throw(fast);
+ Node5 ->
+ exit(fast);
+ _ ->
+ fast
+ end
+ end,
+
+ Start2 = erlang:monotonic_time(),
+ [{error, {erpc, timeout}},
+ {ok, fast},
+ {error, {erpc, noconnection}},
+ Node3Res,
+ {throw, fast},
+ {exit, {exception, fast}}] = erpc:multicall(Nodes, SlowThisNode, 500),
+
+ End2 = erlang:monotonic_time(),
+
+ Time2 = erlang:convert_time_unit(End2-Start2, native, millisecond),
+ io:format("Time2 = ~p~n",[Time2]),
+ true = Time2 >= 500,
+ true = Time2 =< 1000,
+
+ %% We should not get any stray messages due to timed out operations...
+ receive Msg -> ct:fail({unexpected, Msg})
+ after 1000 -> ok
+ end,
+
+ [{error, {erpc, noconnection}},
+ Node3Res,
+ {error, {erpc, noconnection}},
+ {error, {erpc, noconnection}}]
+ = erpc:multicall([Node2, Node3, Node4, Node5], erlang, halt, []),
+
+ [] = flush([]),
+
+ stop_node(Node3),
+ case Node3Res of
+ {error, {erpc, notsup}} ->
+ {comment, "Tested against an OTP 22 node as well"};
+ _ ->
+ {comment, "No OTP 22 node available; i.e., only testing against current release"}
+ end.
+
+multicall_reqtmo(Config) when is_list(Config) ->
+ {ok, QuickNode1} = start_node(Config),
+ {ok, QuickNode2} = start_node(Config),
+ Fun = fun (Node, SendMe, Timeout) ->
+ Me = self(),
+ SlowSend = fun () ->
+ if node() == Node ->
+ Me ! SendMe,
+ done;
+ true ->
+ done
+ end
+ end,
+ [{ok, done},{error,{erpc,timeout}},{ok, done}]
+ = erpc:multicall([QuickNode1, Node, QuickNode2],
+ erlang, apply, [SlowSend, []],
+ Timeout)
+ end,
+ Res = reqtmo_test(Config, Fun),
+ stop_node(QuickNode1),
+ stop_node(QuickNode2),
+ Res.
+
+
+multicast(Config) when is_list(Config) ->
+ {ok, Node} = start_node(Config),
+ Nodes = [node(), Node],
+ try
+ erpc:multicast(Nodes, erlang, send, hej),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ try
+ erpc:multicast(node(), erlang, send, [hej]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ try
+ erpc:multicast([<<"node">>, Node], erlang, send, [hej]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ try
+ erpc:multicast(Nodes, "erlang", send, [hej]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+ try
+ erpc:multicast(Nodes, erlang, make_ref(), [hej]),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} -> ok
+ end,
+
+ %% Silently fail...
+ erpc:multicast([badnodename], erlang, send, [self(), blupp]),
+ %% silent remote error...
+ erpc:multicast(Nodes, erlang, send, [self()|blupp]),
+
+ Me = self(),
+ Ok1 = make_ref(),
+ ok = erpc:multicast(Nodes, erlang, send, [Me, {mfa, Ok1}]),
+ receive
+ {mfa, Ok1} -> ok
+ end,
+ receive
+ {mfa, Ok1} -> ok
+ end,
+ ok = erpc:multicast(Nodes, fun () -> Me ! {a_fun, Ok1} end),
+ receive
+ {a_fun, Ok1} -> ok
+ end,
+ receive
+ {a_fun, Ok1} -> ok
+ end,
+
+ ok = erpc:multicast([Node], erlang, halt, []),
+
+ monitor_node(Node, true),
+ receive {nodedown, Node} -> ok end,
+
+ ok = erpc:multicast([Node], erlang, send, [Me, wont_reach_me]),
+
+ receive after 1000 -> ok end,
+
+ [] = flush([]),
+ case start_22_node(Config) of
+ {ok, Node22} ->
+ ok = erpc:multicast([Node], erlang, send, [Me, wont_reach_me]),
+ ok = erpc:multicast([Node], fun () -> Me ! wont_reach_me end),
+ receive
+ Msg -> ct:fail({unexpected_message, Msg})
+ after
+ 2000 -> ok
+ end,
+ stop_node(Node22),
+ {comment, "Tested against OTP 22 as well"};
+ _ ->
+ {comment, "No tested against OTP 22"}
+ end.
+
+timeout_limit(Config) when is_list(Config) ->
+ Node = node(),
+ MaxTmo = (1 bsl 32) - 1,
+ erlang:send_after(100, self(), dummy_message),
+ try
+ receive
+ M ->
+ M
+ after MaxTmo + 1 ->
+ ok
+ end,
+ ct:fail("The ?MAX_INT_TIMEOUT define in erpc.erl needs "
+ "to be updated to reflect max timeout value "
+ "in a receive/after...")
+ catch
+ error:timeout_value ->
+ ok
+ end,
+ Node = erpc:call(Node, erlang, node, [], MaxTmo),
+ try
+ erpc:call(node(), erlang, node, [], MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+ [{ok,Node}] = erpc:multicall([Node], erlang, node, [], MaxTmo),
+ try
+ erpc:multicall([Node], erlang, node, [], MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+ ReqId1 = erpc:send_request(Node, erlang, node, []),
+ Node = erpc:receive_response(ReqId1, MaxTmo),
+ ReqId2 = erpc:send_request(Node, erlang, node, []),
+ try
+ erpc:receive_response(ReqId2, MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+ ReqId3 = erpc:send_request(Node, erlang, node, []),
+ Node = erpc:receive_response(ReqId3, MaxTmo),
+ ReqId4 = erpc:send_request(Node, erlang, node, []),
+ try
+ erpc:receive_response(ReqId4, MaxTmo+1),
+ ct:fail(unexpected)
+ catch
+ error:{erpc, badarg} ->
+ ok
+ end,
+ ok.
+
+
+%%%
+%%% Utility functions.
+%%%
+
+start_node(Config) ->
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
+ ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
+ Pa = filename:dirname(code:which(?MODULE)),
+ test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]).
+
+start_22_node(Config) ->
+ Rel = "22_latest",
+ case test_server:is_release_available(Rel) of
+ false ->
+ notsup;
+ true ->
+ Cookie = atom_to_list(erlang:get_cookie()),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
+ ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
+ Pa = filename:dirname(code:which(?MODULE)),
+ test_server:start_node(Name,
+ peer,
+ [{args, "-pa " ++ Pa ++ " -setcookie "++Cookie},
+ {erl, [{release, Rel}]}])
+ end.
+
+stop_node(Node) ->
+ test_server:stop_node(Node).
+
+flush(L) ->
+ receive
+ M ->
+ flush([M|L])
+ after 0 ->
+ L
+ end.
+
+t() ->
+ [N | _] = string:tokens(atom_to_list(node()), "_"),
+ 1000*list_to_integer(N).
+
+f() ->
+ timer:sleep(T=t()),
+ spawn(?MODULE, f2, []),
+ {hej,T,node()}.
+
+f2() ->
+ timer:sleep(500),
+ halt().