1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 1997-2011. All Rights Reserved.
    5: %%
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %%
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: -module(slave_SUITE).
   20: 
   21: -include_lib("test_server/include/test_server.hrl").
   22: 
   23: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   24: 	 init_per_group/2,end_per_group/2, t_start/1, t_start_link/1,
   25: 	 start_link_nodedown/1, errors/1]).
   26: 
   27: %% Internal exports.
   28: -export([fun_init/1, test_errors/1]).
   29: -export([timeout_test/1, auth_test/1, rsh_test/1, start_a_slave/3]).
   30: 
   31: suite() -> [{ct_hooks,[ts_install_cth]}].
   32: 
   33: all() -> 
   34:     [t_start_link, start_link_nodedown, t_start, errors].
   35: 
   36: groups() -> 
   37:     [].
   38: 
   39: init_per_suite(Config) ->
   40:     Config.
   41: 
   42: end_per_suite(_Config) ->
   43:     ok.
   44: 
   45: init_per_group(_GroupName, Config) ->
   46:     Config.
   47: 
   48: end_per_group(_GroupName, Config) ->
   49:     Config.
   50: 
   51: 
   52: t_start_link(suite) -> [];
   53: t_start_link(Config) when is_list(Config) ->
   54:     ?line Dog = test_server:timetrap(test_server:seconds(20)),
   55: 
   56:     %% Define useful variables.
   57: 
   58:     ?line Host = host(),
   59:     ?line Slave1 = node_name(Host, slave1),
   60:     ?line Slave2 = node_name(Host, slave2),
   61: 
   62:     %% Test slave:start_link() with one, two, and three arguments.
   63: 
   64:     ?line ThisNode = node(),
   65:     ?line {error, {already_running, ThisNode}} = slave:start_link(Host),
   66:     ?line {ok, Slave1} = slave:start_link(Host, slave1),
   67:     ?line {ok, Slave2} = slave:start_link(Host, slave2, "-my_option 42"),
   68:     ?line {ok, [["42"]]} = rpc:call(Slave2, init, get_argument, [my_option]),
   69: 
   70:     %% Kill the two slave nodes and verify that they are dead.
   71: 
   72:     ?line rpc:cast(Slave1, erlang, halt, []),
   73:     ?line rpc:cast(Slave2, erlang, halt, []),
   74:     ?line is_dead(Slave1),
   75:     ?line is_dead(Slave2),
   76: 
   77:     %% Start two slave nodes from another process and verify that
   78:     %% the slaves die when that process terminates.
   79: 
   80:     Parent = self(),
   81:     Pid = fun_spawn(fun () ->
   82: 			    {ok, Slave1} = slave:start_link(Host, slave1),
   83: 			    {ok, Slave2} = slave:start_link(Host, slave2),
   84: 			    Parent ! slaves_started,
   85: 			    receive never -> ok end
   86: 		    end),
   87:     ?line receive slaves_started -> ok end,
   88:     ?line process_flag(trap_exit, true),
   89:     ?line wait_alive(Slave1),
   90:     ?line wait_alive(Slave2),
   91:     ?line exit(Pid, kill),
   92:     ?line receive {'EXIT', Pid, killed} -> ok end,
   93:     ?line test_server:sleep(250),
   94:     ?line is_dead(Slave1),
   95:     ?line is_dead(Slave2),
   96: 		  
   97:     ?line test_server:timetrap_cancel(Dog),
   98:     ok.
   99: 
  100: %% Test that slave:start_link() works when the master exits.
  101: 
  102: start_link_nodedown(suite) -> [];
  103: start_link_nodedown(Config) when is_list(Config) ->
  104:     ?line Dog = test_server:timetrap(test_server:seconds(20)),
  105: 
  106:     %% Define useful variables.
  107: 
  108:     ?line Host = host(),
  109:     ?line Master = node_name(Host, my_master),
  110:     ?line Slave = node_name(Host, my_slave),
  111: 
  112:     ?line Pa = "-pa " ++ filename:dirname(code:which(?MODULE)),
  113:     ?line {ok, Master} = slave:start_link(Host, my_master, Pa),
  114:     ?line spawn(Master, ?MODULE, start_a_slave, [self(), Host, my_slave]),
  115:     ?line {reply, {ok, _Node}} = receive Any -> Any end,
  116:     
  117:     ?line rpc:call(Master, erlang, halt, []),
  118:     ?line receive after 200 -> ok end,
  119:     ?line pang = net_adm:ping(Slave),
  120: 
  121:     ?line test_server:timetrap_cancel(Dog),
  122:     ok.
  123: 
  124: start_a_slave(ReplyTo, Host, Name) ->
  125:     ReplyTo ! {reply, slave:start_link(Host, Name)},
  126:     receive never -> ok end.
  127: 
  128: %% Test slave:start().
  129: 
  130: t_start(suite) -> [];
  131: t_start(Config) when is_list(Config) ->
  132:     ?line Dog = test_server:timetrap(test_server:seconds(20)),
  133:     
  134:     %% Define useful variables.
  135: 
  136:     ?line Host = host(),
  137:     ?line Slave1 = node_name(Host, slave1),
  138:     ?line Slave2 = node_name(Host, slave2),
  139: 
  140:     %% By running all tests from this master node which is linked
  141:     %% to this test case, we ensure that all slaves are killed
  142:     %% if this test case fails.  (If they are not, and therefore further
  143:     %% test cases fail, there is a bug in slave.)
  144: 
  145:     ?line {ok, Master} = slave:start_link(Host, master),
  146:     
  147:     %% Test slave:start() with one, two, and three arguments.
  148: 
  149:     ?line ThisNode = node(),
  150:     ?line {error, {already_running, ThisNode}} = slave:start(Host),
  151:     ?line {ok, Slave1} = rpc:call(Master, slave, start, [Host, slave1]),
  152:     ?line {ok, Slave2} = rpc:call(Master, slave, start,
  153: 				  [Host, slave2, "-my_option 42"]),
  154:     ?line {ok, [["42"]]} = rpc:call(Slave2, init, get_argument, [my_option]),
  155: 
  156:     %% Test that a slave terminates when its master node terminates.
  157: 
  158:     ?line ok = slave:stop(Slave2),
  159:     ?line is_dead(Slave2),
  160:     ?line {ok, Slave2} = rpc:call(Slave1, slave, start, [Host, slave2]),
  161:     ?line is_alive(Slave2),
  162:     ?line rpc:call(Slave1, erlang, halt, []),	% Kill master.
  163:     receive after 1000 -> ok end,		% Make sure slaves have noticed
  164: 						% their dead master.
  165:     ?line is_dead(Slave1),
  166:     ?line is_dead(Slave2),			% Slave should be dead, too.
  167: 
  168:     %% Kill all slaves and verify that they are dead.
  169: 
  170:     ?line ok = slave:stop(Slave1),
  171:     ?line ok = slave:stop(Slave2),
  172:     ?line is_dead(Slave1),
  173:     ?line is_dead(Slave2),
  174: 
  175:     ?line test_server:timetrap_cancel(Dog),
  176:     ok.
  177: 
  178: %% Test the various error conditions in parallell (since the timeout
  179: %% in slave is 32 seconds).
  180: 
  181: errors(suite) -> [];
  182: errors(Config) when is_list(Config) ->
  183:     ?line Dog = test_server:timetrap(test_server:seconds(50)),
  184: 
  185:     ?line process_flag(trap_exit, true),
  186:     ?line Pa = filename:dirname(code:which(?MODULE)),
  187:     ?line {ok, Master} = slave_start_link(host(), master,
  188: 					  "-rsh no_rsh_program -pa "++Pa++
  189: 					  " -env ERL_CRASH_DUMP erl_crash_dump.master"),
  190:     ?line Pids = rpc:call(Master, ?MODULE, test_errors, [self()]),
  191:     ?line wait_for_result(Pids),
  192: 
  193:     ?line test_server:timetrap_cancel(Dog),
  194:     ok.
  195: 
  196: wait_for_result([]) ->
  197:     ok;
  198: wait_for_result(Pids) ->
  199:     ?line receive
  200: 	      {'EXIT', Pid, normal} ->
  201: 		  io:format("Process ~p terminated", [Pid]),
  202: 		  wait_for_result(lists:delete(Pid, Pids));
  203: 	      {'EXIT', _, Reason} ->
  204: 		  exit(Reason)
  205: 	  end.
  206: 
  207: show_process_info(Pid) ->
  208:     io:format("~p: ~p", [Pid, catch process_info(Pid, initial_call)]).
  209: 
  210: test_errors(ResultTo) ->
  211:     %% Sigh!  We use ordinary spawn instead of fun_spawn/1 to be able
  212:     %% identify the processes by their initial call.
  213:     ?line P1 = spawn(?MODULE, timeout_test, [ResultTo]),
  214:     ?line P2 = spawn(?MODULE, auth_test, [ResultTo]),
  215:     ?line P3 = spawn(?MODULE, rsh_test, [ResultTo]),
  216:     Pids =[P1, P2, P3],
  217:     ?line lists:foreach(fun show_process_info/1, Pids),
  218:     Pids.
  219: 
  220: timeout_test(ResultTo) ->
  221:     link(ResultTo),
  222:     ?line {error, timeout} = slave:start(host(), slave1, "-boot no_boot_script").
  223: 
  224: auth_test(ResultTo) ->
  225:     link(ResultTo),
  226:     ?line {error, timeout} = slave:start(host(), slave2,
  227: 					 "-setcookie definitely_not_a_cookie").
  228: 
  229: rsh_test(ResultTo) ->
  230:     link(ResultTo),
  231:     ?line {error, no_rsh} = slave:start(super, slave3).
  232: 
  233: 
  234: %%% Utilities.    
  235: 
  236: 
  237: wait_alive(Node) ->
  238:     wait_alive_1(10, Node).
  239: 
  240: wait_alive_1(0, Node) ->
  241:     ?t:fail({still_not_alive,Node});
  242: wait_alive_1(N, Node) ->
  243:     case rpc:call(Node, init, get_status, []) of
  244: 	{started,_} ->
  245: 	    ok;
  246: 	{starting,_} ->
  247: 	    receive after 1 -> ok end,
  248: 	    wait_alive_1(N-1, Node)
  249:     end.
  250: 
  251: is_alive(Node) ->
  252:     {started, _} = rpc:call(Node, init, get_status, []).
  253: 
  254: is_dead(Node) ->
  255:     {badrpc, nodedown} = rpc:call(Node, init, get_status, []).
  256: 
  257: node_name(Host, Name) ->
  258:     list_to_atom(lists:concat([Name, "@", Host])).
  259: 
  260: host() ->
  261:     from($@, atom_to_list(node())).
  262: 
  263: from(H, [H | T]) -> T;
  264: from(H, [_ | T]) -> from(H, T);
  265: from(_H, []) -> [].
  266: 
  267: slave_start_link(Host, Name, Args) ->
  268:     case slave:start_link(Host, Name, Args) of
  269: 	{ok, Node} ->
  270: 	    {ok, Node};
  271: 	Other ->
  272: 	    io:format("slave:start_link(~p, ~p, ~p) -> ~p",
  273: 		      [Host, Name, Args, Other])
  274:     end.
  275: 
  276: fun_spawn(Fun) ->
  277:     spawn_link(?MODULE, fun_init, [Fun]).
  278: 
  279: fun_init(Fun) ->
  280:     Fun().