diff options
Diffstat (limited to 'lib/kernel/test/rpc_SUITE.erl')
-rw-r--r-- | lib/kernel/test/rpc_SUITE.erl | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/lib/kernel/test/rpc_SUITE.erl b/lib/kernel/test/rpc_SUITE.erl new file mode 100644 index 0000000000..2b39e31a80 --- /dev/null +++ b/lib/kernel/test/rpc_SUITE.erl @@ -0,0 +1,518 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2000-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(rpc_SUITE). + +-export([all/1]). +-export([call/1, block_call/1, multicall/1, multicall_timeout/1, + multicall_dies/1, multicall_node_dies/1, + called_dies/1, called_node_dies/1, + called_throws/1, call_benchmark/1, async_call/1]). + +-export([suicide/2, suicide/3, f/0, f2/0]). + +-include("test_server.hrl"). + +all(suite) -> + [call, block_call, multicall, multicall_timeout, + multicall_dies, multicall_node_dies, + called_dies, called_node_dies, + called_throws, call_benchmark, async_call]. + + +call(doc) -> "Test different rpc calls"; +call(Config) when is_list(Config) -> + Timetrap = ?t:timetrap(?t:seconds(30)), + ?line PA = filename:dirname(code:which(?MODULE)), + %% Note. First part of nodename sets response delay in seconds + ?line {ok, N1} = ?t:start_node('3_rpc_SUITE_call', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N2} = ?t:start_node('1_rcp_SUITE_call', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N3} = ?t:start_node('4_rcp_SUITE_call', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N4} = ?t:start_node('8_rcp_SUITE_call', slave, + [{args, "-pa " ++ PA}]), + ?line ok = io:format("~p~n", [[N1, N2, N3]]), + ?line {hej,_,N1} = rpc:call(N1, ?MODULE, f, []), + ?line {hej,_,N2} = rpc:call(N2, ?MODULE, f, [], 2000), + ?line {badrpc,timeout} = rpc:call(N3, ?MODULE, f, [], 2000), + ?line receive after 6000 -> ok end, + ?line [] = flush([]), + ?line {hej,_,N4} = rpc:call(N4, ?MODULE, f, []), + ?line ?t:stop_node(N1), + ?line ?t:stop_node(N2), + ?line ?t:stop_node(N3), + ?line ?t:stop_node(N4), + ?t:timetrap_cancel(Timetrap), + ok. + +block_call(doc) -> "Test different rpc calls"; +block_call(Config) when is_list(Config) -> + Timetrap = ?t:timetrap(?t:seconds(30)), + ?line PA = filename:dirname(code:which(?MODULE)), + %% Note. First part of nodename sets response delay in seconds + ?line {ok, N1} = ?t:start_node('3_rpc_SUITE_block_call', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N2} = ?t:start_node('1_rcp_SUITE_block_call', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N3} = ?t:start_node('4_rcp_SUITE_block_call', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N4} = ?t:start_node('8_rcp_SUITE_block_call', slave, + [{args, "-pa " ++ PA}]), + ?line ok = io:format("~p~n", [[N1, N2, N3]]), + ?line {hej,_,N1} = rpc:block_call(N1, ?MODULE, f, []), + ?line {hej,_,N2} = rpc:block_call(N2, ?MODULE, f, [], 2000), + ?line {badrpc,timeout} = rpc:block_call(N3, ?MODULE, f, [], 2000), + ?line receive after 6000 -> ok end, + ?line [] = flush([]), + ?line {hej,_,N4} = rpc:block_call(N4, ?MODULE, f, []), + ?line ?t:stop_node(N1), + ?line ?t:stop_node(N2), + ?line ?t:stop_node(N3), + ?line ?t:stop_node(N4), + ?t:timetrap_cancel(Timetrap), + ok. + + +multicall(doc) -> + "OTP-3449"; +multicall(Config) when is_list(Config) -> + Timetrap = ?t:timetrap(?t:seconds(20)), + ?line PA = filename:dirname(code:which(?MODULE)), + %% Note. First part of nodename sets response delay in seconds + ?line {ok, N1} = ?t:start_node('3_rpc_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N2} = ?t:start_node('1_rcp_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + ?line ok = io:format("~p~n", [[N1, N2]]), + ?line {[{hej,_,N1},{hej,_,N2}],[]} = + rpc:multicall([N1, N2], ?MODULE, f, []), + ?line Msgs = flush([]), + ?line [] = Msgs, + ?line ?t:stop_node(N1), + ?line ?t:stop_node(N2), + ?t:timetrap_cancel(Timetrap), + ok. + +multicall_timeout(Config) when is_list(Config) -> + Timetrap = ?t:timetrap(?t:seconds(30)), + ?line PA = filename:dirname(code:which(?MODULE)), + %% Note. First part of nodename sets response delay in seconds + ?line {ok, N1} = ?t:start_node('11_rpc_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N2} = ?t:start_node('8_rpc_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N3} = ?t:start_node('5_rpc_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N4} = ?t:start_node('2_rcp_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + ?line ok = io:format("~p~n", [[N1, N2]]), + ?line {[{hej,_,N3},{hej,_,N4}],[N1, N2]} = + rpc:multicall([N3, N1, N2, N4], ?MODULE, f, [], ?t:seconds(6)), + ?t:sleep(?t:seconds(8)), %% Wait for late answers + ?line Msgs = flush([]), + ?line [] = Msgs, + ?line ?t:stop_node(N1), + ?line ?t:stop_node(N2), + ?line ?t:stop_node(N3), + ?line ?t:stop_node(N4), + ?t:timetrap_cancel(Timetrap), + ok. + +multicall_dies(Config) when is_list(Config) -> + Timetrap = ?t:timetrap(?t:seconds(30)), + ?line PA = filename:dirname(code:which(?MODULE)), + ?line {ok, N1} = ?t:start_node('rpc_SUITE_multicall_dies_1', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N2} = ?t:start_node('rcp_SUITE_multicall_dies_2', slave, + [{args, "-pa " ++ PA}]), + ?line Nodes = [N1, N2], + %% + ?line {[{badrpc, {'EXIT', normal}}, {badrpc, {'EXIT', normal}}], []} = + do_multicall(Nodes, erlang, exit, [normal]), + ?line {[{badrpc, {'EXIT', abnormal}}, {badrpc, {'EXIT', abnormal}}], []} = + do_multicall(Nodes, erlang, exit, [abnormal]), + ?line {[{badrpc, {'EXIT', {badarith, _}}}, + {badrpc, {'EXIT', {badarith, _}}}], + []} = + do_multicall(Nodes, erlang, 'div', [1, 0]), + ?line {[{badrpc, {'EXIT', {badarg, _}}}, + {badrpc, {'EXIT', {badarg, _}}}], + []} = + do_multicall(Nodes, erlang, atom_to_list, [1]), + ?line {[{badrpc, {'EXIT', {undef, _}}}, + {badrpc, {'EXIT', {undef, _}}}], + []} = + do_multicall(Nodes, ?MODULE, suicide, []), + ?line {[timeout, timeout], []} = + do_multicall(Nodes, ?MODULE, suicide, [link, normal]), + ?line {[{badrpc, {'EXIT', abnormal}}, {badrpc, {'EXIT', abnormal}}], []} = + do_multicall(Nodes, ?MODULE, suicide, [link, abnormal]), + ?line {[timeout, timeout], []} = + do_multicall(Nodes, ?MODULE, suicide, [exit, normal]), + ?line {[{badrpc, {'EXIT', abnormal}}, {badrpc, {'EXIT', abnormal}}], []} = + do_multicall(Nodes, ?MODULE, suicide, [exit, abnormal]), + ?line {[{badrpc, {'EXIT', killed}}, {badrpc, {'EXIT', killed}}], []} = + do_multicall(Nodes, ?MODULE, suicide, [exit, kill]), + %% + ?line ?t:stop_node(N1), + ?line ?t:stop_node(N2), + ?t:timetrap_cancel(Timetrap), + ok. + +do_multicall(Nodes, Mod, Func, Args) -> + ?line ok = io:format("~p:~p~p~n", [Mod, Func, Args]), + ?line Result = rpc:multicall(Nodes, Mod, Func, Args), + ?line Msgs = flush([]), + ?line [] = Msgs, + Result. + + + +multicall_node_dies(doc) -> + ""; +multicall_node_dies(Config) when is_list(Config) -> + Timetrap = ?t:timetrap(?t:seconds(60)), + %% + do_multicall_2_nodes_dies(?MODULE, suicide, [erlang, halt, []]), + do_multicall_2_nodes_dies(?MODULE, suicide, [init, stop, []]), + do_multicall_2_nodes_dies(?MODULE, suicide, [rpc, stop, []]), + %% + ?t:timetrap_cancel(Timetrap), + ok. + +do_multicall_2_nodes_dies(Mod, Func, Args) -> + ?line ok = io:format("~p:~p~p~n", [Mod, Func, Args]), + ?line PA = filename:dirname(code:which(?MODULE)), + ?line {ok, N1} = ?t:start_node('rpc_SUITE_multicall_node_dies_1', slave, + [{args, "-pa " ++ PA}]), + ?line {ok, N2} = ?t:start_node('rcp_SUITE_multicall_node_dies_2', slave, + [{args, "-pa " ++ PA}]), + ?line Nodes = [N1, N2], + ?line {[], Nodes} = rpc:multicall(Nodes, Mod, Func, Args), + ?line Msgs = flush([]), + ?line [] = Msgs, + ok. + + + +called_dies(doc) -> + "OTP-3766"; +called_dies(Config) when is_list(Config) -> + Timetrap = ?t:timetrap(?t:seconds(210)), + ?line PA = filename:dirname(code:which(?MODULE)), + ?line {ok, N} = ?t:start_node(rpc_SUITE_called_dies, slave, + [{args, "-pa " ++ PA}]), + %% + ?line rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',normal}}} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, exit, [normal]), + ?line rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',abnormal}}} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, exit, [abnormal]), + ?line rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',{badarith,_}}}} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, 'div', [1,0]), + ?line rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',{badarg,_}}}} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, atom_to_list, [1]), + ?line rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',{undef,_}}}} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, []), + %% + TrapExit = process_flag(trap_exit, true), + %% + ?line rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)}, + {Tag,flush,[{'EXIT',_,normal}]} = + {Tag,flush,flush([])}; + (Tag, Call, Args) -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, [link,normal]), + ?line rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)}, + {Tag,flush,[{'EXIT',_,abnormal}]} = + {Tag,flush,flush([])}; + (Tag, block_call, Args) -> + {Tag,timeout} = + {Tag,apply(rpc, block_call, Args)}; + (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',abnormal}}} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, [link,abnormal]), + ?line rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)}, + {Tag,flush,[{'EXIT',_,normal}]} = + {Tag,flush,flush([])}; + (Tag, Call, Args) -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, [exit,normal]), + ?line rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)}, + {Tag,flush,[{'EXIT',_,abnormal}]} = + {Tag,flush,flush([])}; + (Tag, block_call, Args) -> + {Tag,timeout} = + {Tag,apply(rpc, block_call, Args)}; + (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',abnormal}}} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, [exit,abnormal]), + %% + process_flag(trap_exit, TrapExit), + %% + ?line rep(fun %% A local [exit,kill] would kill the test case process + (_Tag, _Call, [Node|_]) when Node == node() -> + ok; + %% A block_call [exit,kill] would kill the rpc server + (_Tag, block_call, _Args) -> ok; + (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',killed}}} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, [exit,kill]), + %% + ?line [] = flush([]), + ?line ?t:stop_node(N), + ?t:timetrap_cancel(Timetrap), + ok. + +rep(Fun, N, M, F, A) -> + Fun(1, call, [node(), M, F, A]), + Fun(2, call, [node(), M, F, A, infinity]), + Fun(3, call, [N, M, F, A]), + Fun(4, call, [N, M, F, A, infinity]), + Fun(5, call, [N, M, F, A, 3000]), + Fun(6, block_call, [node(), M, F, A]), + Fun(7, block_call, [node(), M, F, A, infinity]), + Fun(8, block_call, [N, M, F, A]), + Fun(9, block_call, [N, M, F, A, infinity]), + Fun(10, block_call, [N, M, F, A, 3000]), + ok. + + +suicide(link, Reason) -> + spawn_link( + fun() -> + exit(Reason) + end), + receive after 2000 -> timeout end; +suicide(exit, Reason) -> + Self = self(), + spawn( + fun() -> + exit(Self, Reason) + end), + receive after 2000 -> timeout end. + +suicide(erlang, exit, [Name, Reason]) when is_atom(Name) -> + case whereis(Name) of + Pid when pid(Pid) -> suicide(erlang, exit, [Pid, Reason]) + end; +suicide(Mod, Func, Args) -> + spawn_link( + fun() -> + apply(Mod, Func, Args) + end), + receive after 10000 -> timeout end. + + + +called_node_dies(doc) -> + ""; +called_node_dies(suite) -> []; +called_node_dies(Config) when is_list(Config) -> + Timetrap = ?t:timetrap(?t:minutes(2)), + ?line PA = filename:dirname(code:which(?MODULE)), + %% + ?line node_rep( + fun (Tag, Call, Args) -> + {Tag,{badrpc,nodedown}} = + {Tag,apply(rpc, Call, Args)} + end, "rpc_SUITE_called_node_dies_1", + PA, ?MODULE, suicide, [erlang,halt,[]]), + ?line node_rep( + fun (Tag, Call, Args) -> + {Tag,{badrpc,nodedown}} = + {Tag,apply(rpc, Call, Args)} + end, "rpc_SUITE_called_node_dies_2", + PA, ?MODULE, suicide, [init,stop,[]]), + ?line node_rep( + fun (Tag, Call, Args=[_|_]) -> + {Tag,{'EXIT',{killed,_}}} = + {Tag,catch {noexit,apply(rpc, Call, Args)}} + end, "rpc_SUITE_called_node_dies_3", + PA, ?MODULE, suicide, [erlang,exit,[rex,kill]]), + ?line node_rep( + fun %% Cannot block call rpc - will hang + (_Tag, block_call, _Args) -> ok; + (Tag, Call, Args=[_|_]) -> + {Tag,{'EXIT',{normal,_}}} = + {Tag,catch {noexit,apply(rpc, Call, Args)}} + end, "rpc_SUITE_called_node_dies_4", + PA, ?MODULE, suicide, [rpc,stop,[]]), + %% + ?t:timetrap_cancel(Timetrap), + ok. + +node_rep(Fun, Name, PA, M, F, A) -> + {ok, Na} = ?t:start_node(list_to_atom(Name++"_a"), slave, + [{args, "-pa " ++ PA}]), + Fun(a, call, [Na, M, F, A]), + catch ?t:stop_node(Na), + {ok, Nb} = ?t:start_node(list_to_atom(Name++"_b"), slave, + [{args, "-pa " ++ PA}]), + Fun(b, call, [Nb, M, F, A, infinity]), + catch ?t:stop_node(Nb), + {ok, Nc} = ?t:start_node(list_to_atom(Name++"_c"), slave, + [{args, "-pa " ++ PA}]), + Fun(c, call, [Nc, M, F, A, infinity]), + catch ?t:stop_node(Nc), + %% + {ok, Nd} = ?t:start_node(list_to_atom(Name++"_d"), slave, + [{args, "-pa " ++ PA}]), + Fun(d, block_call, [Nd, M, F, A]), + catch ?t:stop_node(Nd), + {ok, Ne} = ?t:start_node(list_to_atom(Name++"_e"), slave, + [{args, "-pa " ++ PA}]), + Fun(e, block_call, [Ne, M, F, A, infinity]), + catch ?t:stop_node(Ne), + {ok, Nf} = ?t:start_node(list_to_atom(Name++"_f"), slave, + [{args, "-pa " ++ PA}]), + Fun(f, block_call, [Nf, M, F, A, infinity]), + catch ?t:stop_node(Nf), + ok. + + + +called_throws(doc) -> + "OTP-3766"; +called_throws(Config) when is_list(Config) -> + Timetrap = ?t:timetrap(?t:seconds(10)), + ?line PA = filename:dirname(code:which(?MODULE)), + %% + ?line {ok, N} = ?t:start_node(rpc_SUITE_called_throws, slave, + [{args, "-pa " ++ PA}]), + %% + ?line rep(fun (Tag, Call, Args) -> + {Tag,up} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, throw, [up]), + ?line rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',reason}}} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, throw, [{'EXIT',reason}]), + %% + ?line ?t:stop_node(N), + ?t:timetrap_cancel(Timetrap), + ok. + + + +call_benchmark(Config) when is_list(Config) -> + Timetrap = ?t:timetrap(?t:seconds(120)), + ?line PA = filename:dirname(code:which(?MODULE)), + ?line {ok, Node} = ?t:start_node(rpc_SUITE_call_benchmark, slave, + [{args, "-pa " ++ PA}]), + Iter = case erlang:system_info(modified_timing_level) of + undefined -> 10000; + _ -> 500 %Moified timing - spawn is slower + end, + ?line do_call_benchmark(Node, Iter), + ?t:timetrap_cancel(Timetrap), + ok. + +do_call_benchmark(Node, M) when integer(M), M > 0 -> + do_call_benchmark(Node, erlang:now(), 0, M). + +do_call_benchmark(Node, {A,B,C}, M, M) -> + ?line {D,E,F} = erlang:now(), + ?line T = float(D-A)*1000000.0 + float(E-B) + float(F-C)*0.000001, + ?line Q = 3.0 * float(M) / T, + ?line ?t:stop_node(Node), + {comment, + lists:flatten([float_to_list(Q)," RPC calls per second"])}; +do_call_benchmark(Node, Then, I, M) -> + ?line Node = rpc:call(Node, erlang, node, []), + ?line _ = rpc:call(Node, erlang, whereis, [rex]), + ?line 3 = rpc:call(Node, erlang, '+', [1,2]), + ?line do_call_benchmark(Node, Then, I+1, M). + +async_call(Config) when is_list(Config) -> + Dog = ?t:timetrap(?t:seconds(120)), + + %% Note: First part of nodename sets response delay in seconds. + ?line PA = filename:dirname(code:which(?MODULE)), + ?line NodeArgs = [{args,"-pa "++ PA}], + ?line {ok,Node1} = ?t:start_node('1_rpc_SUITE_call', slave, NodeArgs), + ?line {ok,Node2} = ?t:start_node('10_rpc_SUITE_call', slave, NodeArgs), + ?line {ok,Node3} = ?t:start_node('20_rpc_SUITE_call', slave, NodeArgs), + ?line Promise1 = rpc:async_call(Node1, ?MODULE, f, []), + ?line Promise2 = rpc:async_call(Node2, ?MODULE, f, []), + ?line Promise3 = rpc:async_call(Node3, ?MODULE, f, []), + + %% Test fast timeouts. + ?line timeout = rpc:nb_yield(Promise2), + ?line timeout = rpc:nb_yield(Promise2, 10), + + %% Let Node1 finish its work before yielding. + ?t:sleep(?t:seconds(2)), + ?line {hej,_,Node1} = rpc:yield(Promise1), + + %% Wait for the Node2 and Node3. + ?line {value,{hej,_,Node2}} = rpc:nb_yield(Promise2, infinity), + ?line {hej,_,Node3} = rpc:yield(Promise3), + + ?t:timetrap_cancel(Dog), + ok. + +%%% +%%% Utility functions. +%%% + +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(). |