%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-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(distr_startup_SUITE).
-compile([export_all]).
%%-define(line_trace,1).
-include("test_server.hrl").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all(suite) -> [reads,writes].
-define(iterations,10000).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
app1() ->
{application, app1,
[{description, "ERTS CXC 138 10"},
{vsn, "2.0"},
{applications, [kernel, stdlib]},
{mod, {ch_sup, {app1, 1, 3}}}]}.
app3() ->
{application, app3,
[{description, "ERTS CXC 138 10"},
{vsn, "2.0"},
{applications, [kernel, stdlib]},
{mod, {ch_sup, {app3, 7, 9}}}]}.
config(Fd,C1,C2,C3) ->
io:format(Fd,
"[{kernel, [{sync_nodes_optional, ['~s','~s','~s']},"
"{sync_nodes_timeout, 1},"
"{distributed, [{app1, ['~s', '~s', '~s']},"
"{app2, 10000, ['~s', '~s', '~s']},"
"{app3, 5000, [{'~s', '~s'}, '~s']}]}]}].~n",
[C1,C2,C3, C1,C2,C3, C1,C2,C3, C1,C2,C3]).
from(H, [H | T]) -> T;
from(H, [_ | T]) -> from(H, T);
from(H, []) -> [].
%%-----------------------------------------------------------------
%% Test suite for distributed applications, tests start, load
%% etc indirectly.
%% Should be started in a CC view with:
%% erl -sname master -rsh ctrsh
%%-----------------------------------------------------------------
start_nodes(Conf) ->
% Write a config file
?line Nodes = ?config(nodes,Conf),
?line [C1,C2,C3|_] = Nodes, %% Need at least 3 nodes
?line Dir = ?config(priv_dir,Conf),
?line {ok, Fd} = file:open(Dir ++ "sys.config", write),
?line config(Fd,C1,C2,C3),
?line file:close(Fd),
?line Config = Dir ++ "sys",
% Test [cp1, cp2, cp3]
?line {ok, Cp1} = start_node(lists:nth(1,Nodes), Config),
?line {ok, Cp2} = start_node(lists:nth(2,Nodes), Config),
?line {ok, Cp3} = start_node(lists:nth(3,Nodes), Config),
% Start app1 and make sure cp1 starts it
%%?line rpc:multicall([Cp1, Cp2, Cp3], application, load, [app1()]),
%%?line rpc:multicall([Cp1, Cp2, Cp3], application, start,[app1,permanent]),
?line test_server:sleep(1000),
{Cp1,Cp2,Cp3}.
stop_nodes({Cp1,Cp2,Cp3}) ->
?line stop_node(Cp1),
?line stop_node(Cp2),
?line stop_node(Cp3).
start_node(NodeAtHost, Config) ->
?line NodeAtHostStr = atom_to_list(NodeAtHost),
?line HostStr = from($@,NodeAtHostStr),
?line NodeStr = lists:reverse(from($@,lists:reverse(NodeAtHostStr))),
?line Host = list_to_atom(HostStr),
?line Node = list_to_atom(NodeStr),
?line io:format("Launching slave node ~p@~p ~p",[Node,Host,Config]),
?line slave:start(Host, Node, lists:concat(["-config ", Config])).
stop_node(Node) ->
?line rpc:cast(Node, erlang, halt, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start_client_process(Cp,Mode,NodeNum) ->
io:format("Starting client process at ~p in mode ~p",[Cp,Mode]),
?line case rpc:call(Cp, erlang, spawn,
[?MODULE, client,
[Mode,NodeNum,self(),random:uniform(1000)]]) of
{badrpc,Reason} ->
?line exit({badrpc,{Cp,Reason}});
Client ->
?line Client
end.
start_clients(Mode,Conf) ->
?line random:seed(4711,0,0),
?line {Cp1,Cp2,Cp3} = start_nodes(Conf),
?line Client1 = start_client_process(Cp1,Mode,1),
?line Client2 = start_client_process(Cp2,Mode,2),
?line Client3 = start_client_process(Cp3,Mode,3),
test_server:format(1,"All 3 nodes started, "
"power off client(s) any time...",[]),
Client1 ! go,
Client2 ! go,
Client3 ! go,
{{Cp1,Cp2,Cp3},{Client1,Client2,Client3}}.
stop_clients(Cps) ->
test_server:format(1,"Test completed.",[]),
?line stop_nodes(Cps).
data() ->
{{self(),foo,bar,[1,2,3,4,5,6,7],{{{{}}}},
"We need pretty long packages, so that there is a big risk "
"of cutting it in the middle when suddenly turning off "
"the power or breaking the connection. "
"We don't check the contents of the data very much, but "
"at least there is a magic cookie at the end (123456)."
"If that one arrives correctly, the link is ok as far "
"as we are concerned."},
123456}.
reads(suite) -> [];
reads(Conf) ->
?line {Cps,_} = start_clients(w,Conf),
?line read_loop(?iterations,0),
?line stop_clients(Cps),
ok.
read_loop(0,M) ->
ok;
read_loop(N,M) ->
?line Dog = test_server:timetrap(test_server:seconds(0.5)),
M2 =
receive
{Node,Count,{_,123456}} ->
?line setelement(Node,M,element(Node,M)+1);
{Node,Count,Data} ->
?line exit({network_transmission_error,Data});
{nodedown,Node} ->
?line test_server:format(1,"Node ~s went down",[Node]),
?line M;
Other ->
?line M
after test_server:seconds(0.1) ->
?line io:format("No message!"),
?line M
end,
?line test_server:timetrap_cancel(Dog),
?line M3 =
case N rem 100 of
0 -> io:format("~p reads to go (~w msgs)",[N,M2]),
{0,0,0};
_ -> M2
end,
?line read_loop(N-1,M3).
client(w,NodeNum,Pid,Seed) ->
random:seed(Seed,0,0),
receive
go -> ok
end,
client_write_loop(Pid,0,NodeNum,data());
client(r,NodeNum,Pid,Seed) ->
random:seed(Seed,0,0),
receive
go -> ok
end,
client_read_loop(0).
client_write_loop(Pid,N,NodeNum,Data) ->
test_server:sleep(random:uniform(20)),
Pid ! {NodeNum,N,Data},
client_write_loop(Pid,N+1,NodeNum,Data).
writes(suite) -> [];
writes(Conf) ->
?line {Cps,{C1,C2,C3}} = start_clients(r,Conf),
?line write_loop(2*?iterations,{C1,C2,C3},data()),
?line stop_clients(Cps),
ok.
write_loop(0,_,_) ->
ok;
write_loop(N,Clients,Data) ->
?line Dog = test_server:timetrap(test_server:seconds(0.5)),
?line Client = element(random:uniform(size(Clients)),Clients),
?line Client ! {node(),N,Data},
?line test_server:timetrap_cancel(Dog),
receive
{nodedown,Node} ->
?line test_server:format(1,"Node ~s went down",[Node])
after 0 ->
?line ok
end,
?line case N rem 100 of
0 -> io:format("~p writes to go",[N]);
_ -> ok
end,
?line write_loop(N-1,Clients,Data).
client_read_loop(N) ->
receive
{Node,Count,{_,123456}} ->
?line ok;
{Node,Count,Data} ->
?line io:format("~p(~p): transmission error from node ~p(~p): ~p",
[node(),N,Node,Count,Data]);
Other ->
?line io:format("~p(~p): got a strange message: ~p",
[node(),N,Other])
end,
client_read_loop(N+1).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%