diff options
Diffstat (limited to 'lib/stdlib/test/slave_SUITE.erl')
-rw-r--r-- | lib/stdlib/test/slave_SUITE.erl | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/lib/stdlib/test/slave_SUITE.erl b/lib/stdlib/test/slave_SUITE.erl new file mode 100644 index 0000000000..3b737af64d --- /dev/null +++ b/lib/stdlib/test/slave_SUITE.erl @@ -0,0 +1,261 @@ +%% +%% %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(slave_SUITE). + +-include("test_server.hrl"). + +-export([all/1, t_start/1, t_start_link/1, + start_link_nodedown/1, errors/1]). + +%% Internal exports. +-export([fun_init/1, test_errors/1]). +-export([timeout_test/1, auth_test/1, rsh_test/1, start_a_slave/3]). + +all(suite) -> + [t_start_link, start_link_nodedown, t_start, errors]. + +t_start_link(suite) -> []; +t_start_link(Config) when list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(20)), + + %% Define useful variables. + + ?line Host = host(), + ?line Slave1 = node_name(Host, slave1), + ?line Slave2 = node_name(Host, slave2), + + %% Test slave:start_link() with one, two, and three arguments. + + ?line ThisNode = node(), + ?line {error, {already_running, ThisNode}} = slave:start_link(Host), + ?line {ok, Slave1} = slave:start_link(Host, slave1), + ?line {ok, Slave2} = slave:start_link(Host, slave2, "-my_option 42"), + ?line {ok, [["42"]]} = rpc:call(Slave2, init, get_argument, [my_option]), + + %% Kill the two slave nodes and verify that they are dead. + + ?line rpc:cast(Slave1, erlang, halt, []), + ?line rpc:cast(Slave2, erlang, halt, []), + ?line is_dead(Slave1), + ?line is_dead(Slave2), + + %% Start two slave nodes from another process and verify that + %% the slaves die when that process terminates. + + Parent = self(), + Pid = fun_spawn(fun () -> + {ok, Slave1} = slave:start_link(Host, slave1), + {ok, Slave2} = slave:start_link(Host, slave2), + Parent ! slaves_started, + receive never -> ok end + end), + ?line receive slaves_started -> ok end, + ?line process_flag(trap_exit, true), + ?line wait_alive(Slave1), + ?line wait_alive(Slave2), + ?line exit(Pid, kill), + ?line receive {'EXIT', Pid, killed} -> ok end, + ?line test_server:sleep(250), + ?line is_dead(Slave1), + ?line is_dead(Slave2), + + ?line test_server:timetrap_cancel(Dog), + ok. + +%% Test that slave:start_link() works when the master exits. + +start_link_nodedown(suite) -> []; +start_link_nodedown(Config) when list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(20)), + + %% Define useful variables. + + ?line Host = host(), + ?line Master = node_name(Host, my_master), + ?line Slave = node_name(Host, my_slave), + + ?line Pa = "-pa " ++ filename:dirname(code:which(?MODULE)), + ?line {ok, Master} = slave:start_link(Host, my_master, Pa), + ?line spawn(Master, ?MODULE, start_a_slave, [self(), Host, my_slave]), + ?line {reply, {ok, _Node}} = receive Any -> Any end, + + ?line rpc:call(Master, erlang, halt, []), + ?line receive after 200 -> ok end, + ?line pang = net_adm:ping(Slave), + + ?line test_server:timetrap_cancel(Dog), + ok. + +start_a_slave(ReplyTo, Host, Name) -> + ReplyTo ! {reply, slave:start_link(Host, Name)}, + receive never -> ok end. + +%% Test slave:start(). + +t_start(suite) -> []; +t_start(Config) when list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(20)), + + %% Define useful variables. + + ?line Host = host(), + ?line Slave1 = node_name(Host, slave1), + ?line Slave2 = node_name(Host, slave2), + + %% By running all tests from this master node which is linked + %% to this test case, we ensure that all slaves are killed + %% if this test case fails. (If they are not, and therefore further + %% test cases fail, there is a bug in slave.) + + ?line {ok, Master} = slave:start_link(Host, master), + + %% Test slave:start() with one, two, and three arguments. + + ?line ThisNode = node(), + ?line {error, {already_running, ThisNode}} = slave:start(Host), + ?line {ok, Slave1} = rpc:call(Master, slave, start, [Host, slave1]), + ?line {ok, Slave2} = rpc:call(Master, slave, start, + [Host, slave2, "-my_option 42"]), + ?line {ok, [["42"]]} = rpc:call(Slave2, init, get_argument, [my_option]), + + %% Test that a slave terminates when its master node terminates. + + ?line ok = slave:stop(Slave2), + ?line is_dead(Slave2), + ?line {ok, Slave2} = rpc:call(Slave1, slave, start, [Host, slave2]), + ?line is_alive(Slave2), + ?line rpc:call(Slave1, erlang, halt, []), % Kill master. + receive after 1000 -> ok end, % Make sure slaves have noticed + % their dead master. + ?line is_dead(Slave1), + ?line is_dead(Slave2), % Slave should be dead, too. + + %% Kill all slaves and verify that they are dead. + + ?line ok = slave:stop(Slave1), + ?line ok = slave:stop(Slave2), + ?line is_dead(Slave1), + ?line is_dead(Slave2), + + ?line test_server:timetrap_cancel(Dog), + ok. + +%% Test the various error conditions in parallell (since the timeout +%% in slave is 32 seconds). + +errors(suite) -> []; +errors(Config) when list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(50)), + + ?line process_flag(trap_exit, true), + ?line Pa = filename:dirname(code:which(?MODULE)), + ?line {ok, Master} = slave_start_link(host(), master, + "-rsh no_rsh_program -pa "++Pa++ + " -env ERL_CRASH_DUMP erl_crash_dump.master"), + ?line Pids = rpc:call(Master, ?MODULE, test_errors, [self()]), + ?line wait_for_result(Pids), + + ?line test_server:timetrap_cancel(Dog), + ok. + +wait_for_result([]) -> + ok; +wait_for_result(Pids) -> + ?line receive + {'EXIT', Pid, normal} -> + io:format("Process ~p terminated", [Pid]), + wait_for_result(lists:delete(Pid, Pids)); + {'EXIT', _, Reason} -> + exit(Reason) + end. + +show_process_info(Pid) -> + io:format("~p: ~p", [Pid, catch process_info(Pid, initial_call)]). + +test_errors(ResultTo) -> + %% Sigh! We use ordinary spawn instead of fun_spawn/1 to be able + %% identify the processes by their initial call. + ?line P1 = spawn(?MODULE, timeout_test, [ResultTo]), + ?line P2 = spawn(?MODULE, auth_test, [ResultTo]), + ?line P3 = spawn(?MODULE, rsh_test, [ResultTo]), + Pids =[P1, P2, P3], + ?line lists:foreach(fun show_process_info/1, Pids), + Pids. + +timeout_test(ResultTo) -> + link(ResultTo), + ?line {error, timeout} = slave:start(host(), slave1, "-boot no_boot_script"). + +auth_test(ResultTo) -> + link(ResultTo), + ?line {error, timeout} = slave:start(host(), slave2, + "-setcookie definitely_not_a_cookie"). + +rsh_test(ResultTo) -> + link(ResultTo), + ?line {error, no_rsh} = slave:start(super, slave3). + + +%%% Utilities. + + +wait_alive(Node) -> + wait_alive_1(10, Node). + +wait_alive_1(0, Node) -> + ?t:fail({still_not_alive,Node}); +wait_alive_1(N, Node) -> + case rpc:call(Node, init, get_status, []) of + {started,_} -> + ok; + {starting,_} -> + receive after 1 -> ok end, + wait_alive_1(N-1, Node) + end. + +is_alive(Node) -> + {started, _} = rpc:call(Node, init, get_status, []). + +is_dead(Node) -> + {badrpc, nodedown} = rpc:call(Node, init, get_status, []). + +node_name(Host, Name) -> + list_to_atom(lists:concat([Name, "@", Host])). + +host() -> + from($@, atom_to_list(node())). + +from(H, [H | T]) -> T; +from(H, [_ | T]) -> from(H, T); +from(_H, []) -> []. + +slave_start_link(Host, Name, Args) -> + case slave:start_link(Host, Name, Args) of + {ok, Node} -> + {ok, Node}; + Other -> + io:format("slave:start_link(~p, ~p, ~p) -> ~p", + [Host, Name, Args, Other]) + end. + +fun_spawn(Fun) -> + spawn_link(?MODULE, fun_init, [Fun]). + +fun_init(Fun) -> + Fun(). |