Skip to content

Commit

Permalink
compiling ok, tests broken
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim Watson committed Jun 1, 2012
1 parent e7fa572 commit 70be474
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 43 deletions.
111 changes: 111 additions & 0 deletions src/systest_cleaner.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
%% ----------------------------------------------------------------------------
%%
%% Copyright (c) 2005 - 2012 Nebularis.
%%
%% Permission is hereby granted, free of charge, to any person obtaining a copy
%% of this software and associated documentation files (the "Software"), deal
%% in the Software without restriction, including without limitation the rights
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%% copies of the Software, and to permit persons to whom the Software is
%% furnished to do so, subject to the following conditions:
%%
%% The above copyright notice and this permission notice shall be included in
%% all copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
%% IN THE SOFTWARE.
%% ----------------------------------------------------------------------------
-module(systest_cleaner).

-record(state, {killer, targets, victims}).

-behaviour(gen_server).

%% API Exports

-export([start/1, kill/1, kill/2]).

%% OTP gen_server Exports

-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).

%%
%% Public API
%%

start(Killer) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [Killer], []).

kill(Targets) ->
kill(Targets, infinity).

kill(Targets, Timeout) ->
gen_server:call(?MODULE, {kill, Targets}),
receive
{ok, Targets} -> ok
after Timeout -> ok
end.

%%
%% OTP gen_server API
%%

init([Killer]) ->
process_flag(trap_exit, true),
{ok, #state{killer=Killer,
targets=queue:new(),
victims=[]}}.

handle_call({kill, Targets}, From,
State=#state{targets=KillQ, killer=Kill}) ->
[dispose(Pid, Kill) || Pid <- Targets],
Q2 = queue:snoc(KillQ, {From, Targets}),
{noreply, State#state{targets=Q2}};
handle_call(_Msg, _From, State) ->
{reply, {error, noapi}, State}.

handle_cast(_Msg, State) ->
{noreply, State}.

handle_info({'EXIT', Pid, _},
State=#state{targets=Targets, victims=Victims}) ->
{Client, Nodes} = queue:head(Targets),
case lists:member(Pid, Nodes) of
false ->
{stop, {unexpected_exit, Pid}, State};
true ->
T2 = lists:delete(Pid, Nodes),
V2 = [Pid|Victims],
case T2 of
[] ->
gen_server:reply(Client, {ok, V2}),
Q = queue:tail(Targets),
case queue:is_empty(Q) of
true ->
{stop, normal, State};
false ->
{noreply, State#state{targets=Q, victims=[]}}
end;
_More ->
{noreply, State#state{targets=Targets, victims=V2}}
end
end.

dispose(Pid, Kill) ->
catch link(Pid),
Kill(Pid).

terminate(_Reason, _State) ->
ok.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.

25 changes: 12 additions & 13 deletions src/systest_cli.erl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
%% API Exports

-export([init/1, handle_stop/2, handle_kill/2]).
-export([handle_status/1, handle_interaction/2,
-export([handle_status/2, handle_interaction/3,
handle_msg/3, terminate/3]).

%% private record for tracking...
Expand All @@ -55,7 +55,7 @@
%% systest_node API
%%

init(Node=#'systest.node_info'{config=Config, host=Host, name=Name}) ->
init(Node=#'systest.node_info'{config=Config}) ->
%% TODO: provide a 'get_multi' version that avoids traversing repeatedly
Cmd = systest_config:eval("flags.start.program", Config,
[{callback, {node, fun systest_node:get_node_info/2}},
Expand All @@ -76,7 +76,6 @@ init(Node=#'systest.node_info'{config=Config, host=Host, name=Name}) ->

Startup = ?CONFIG(startup, Config, []),
Detached = ?REQUIRE(detached, Startup),
LogEnabled = ?CONFIG(log_enabled, Startup, true),
{RpcEnabled, ShutdownSpec} = ?CONFIG(rpc_enabled,
Startup, {true, default}),

Expand All @@ -99,11 +98,11 @@ init(Node=#'systest.node_info'{config=Config, host=Host, name=Name}) ->
true -> ok
end,

on_startup(Scope, Id, Port, Detached, RpcEnabled, Config,
on_startup(Scope, Id, Port, Detached, RpcEnabled, Env, Config,
fun(Port2, Pid, LogFd) ->
%% NB: as not all kinds of nodes can be contacted
%% via rpc, we have to do this manually here....
if RpcEnabled =:= true -> monitor_node({Id, true});
if RpcEnabled =:= true -> erlang:monitor_node({Id, true});
true -> ok
end,

Expand All @@ -116,7 +115,7 @@ init(Node=#'systest.node_info'{config=Config, host=Host, name=Name}) ->
shutdown=Shutdown,
command=ExecutableCommand,
args=Args,
env=Env
env=Env,
state=running},
ct:pal(info,
"External Process Handler ~p::~p"
Expand Down Expand Up @@ -187,16 +186,14 @@ handle_stop(Node, Sh=#sh{shutdown=script_stop}) ->
{Env, Args, Prog} = convert_flags(stop, Node, Flags, Config),
run_shutdown_hook(Sh, Prog, Args, Env);
%% TODO: could this be core node behaviour?
handle_stop(Node, Sh=#sh{shutdown=Shutdown, rpc_enabled=true}) ->
handle_stop(_Node, Sh=#sh{shutdown=Shutdown, rpc_enabled=true}) ->
%% TODO: this rpc/call logic should move into systest_node
Halt = case Shutdown of
default -> {init, stop, []};
Custom -> Custom
end,
% apply(rpc, call, [Node#'systest.node_info'.id|tuple_to_list(Halt)]),
{rpc_stop, Halt, Sh#sh{state=stopped}}.
handle_stop(_Node, Sh=#sh{shutdown=stopped}) ->
Sh.
%% TODO: when rpc_enabled=false and shutdown is undefined???

%% @doc handles generic messages from the server.
Expand All @@ -206,7 +203,8 @@ handle_stop(_Node, Sh=#sh{shutdown=stopped}) ->
%% {stop, Reason, NewState} |
%% {NewNode, NewState} |
%% NewState.
handle_msg(sigkill, #'systest.node_info'{os_pid=OsPid}, Sh#{state=running}) ->
handle_msg(sigkill, #'systest.node_info'{os_pid=OsPid},
Sh=#sh{state=running}) ->
systest:sigkill(OsPid),
Sh#sh{state=killed};
handle_msg({Port, {data, {_, Line}}}, Node,
Expand Down Expand Up @@ -253,10 +251,10 @@ handle_msg({'EXIT', Pid, ok}, _Node, Sh=#sh{shutdown_port=Pid,
true -> {stop, normal, Sh};
false -> Sh
end;
handle_msg({'EXIT', Pid, {error, Rc}}, Sh=#sh{shutdown_port=Pid}) ->
handle_msg({'EXIT', Pid, {error, Rc}}, _Node, Sh=#sh{shutdown_port=Pid}) ->
ct:pal("Termination Port stopped abnormally (status ~p)~n", [Rc]),
{stop, termination_port_error, Sh};
handle_msg(Info, Sh=#sh{state=St, port=P, shutdown_port=SP}) ->
handle_msg(Info, _Node, Sh=#sh{state=St, port=P, shutdown_port=SP}) ->
ct:log("Ignoring Info Message: ~p~n"
"State: ~p~n"
"Port: ~p~n"
Expand All @@ -282,10 +280,11 @@ terminate(Reason, _Node, #sh{port=Port, log=Fd}) ->
%% Private API
%%

on_startup(Scope, Id, Port, Detached, RpcEnabled, Config, StartFun) ->
on_startup(Scope, Id, Port, Detached, RpcEnabled, Env, Config, StartFun) ->
%% we do the initial receive stuff up-front
%% just to avoid any initial ordering problems...

Startup = ?CONFIG(startup, Config, []),
LogEnabled = ?CONFIG(log_enabled, Startup, true),
{LogName, LogFd} = case LogEnabled of
true ->
Expand Down
49 changes: 28 additions & 21 deletions src/systest_node.erl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
-record(state, {
node :: node_info(),
handler :: module(),
hander_state :: term(),
handler_state :: term(),
activity_state :: activity_state()
}).

Expand All @@ -72,7 +72,7 @@ behaviour_info(callbacks) ->
{handle_stop, 2},
{handle_kill, 2},
{handle_status, 2},
{handle_interaction, 2},
{handle_interaction, 3},
{handle_msg, 3},
{terminate, 3}];
behaviour_info(_) ->
Expand Down Expand Up @@ -110,11 +110,11 @@ kill(NodeRef) ->

-spec stop_and_wait(node_ref()) -> 'ok'.
stop_and_wait(NodeRef) ->
shutdown_and_wait(NI, fun stop/1).
shutdown_and_wait(NodeRef, fun stop/1).

-spec kill_and_wait(node_ref()) -> 'ok'.
kill_and_wait(NodeRef) ->
shutdown_and_wait(NI, fun kill/1).
shutdown_and_wait(NodeRef, fun kill/1).

-spec status(node_ref()) -> 'nodeup' | {'nodedown', term()}.
status(NodeRef) ->
Expand All @@ -127,10 +127,11 @@ shutdown_and_wait(Owner, ShutdownOp) when is_pid(Owner) ->
case (Owner == self()) orelse not(is_process_alive(Owner)) of
true -> ok;
false -> link(Owner),
ct:log("Stopping ~p ....~n"
"Waiting for port owning process (~p) to exit...~n",
[NI#'systest.node_info'.id, Owner]),
ok = ShutdownOp(NI),
ct:pal("Waiting for ~p to exit~n", [Owner]),
%ct:log("Stopping ~p ....~n"
% "Waiting for port owning process (~p) to exit...~n",
% [NI#'systest.node_info'.id, Owner]),
ok = ShutdownOp(Owner),
receive
{'EXIT', Owner, _Reason} -> ok;
Other -> ct:pal("Other ~p~n", [Other])
Expand Down Expand Up @@ -159,10 +160,10 @@ init([NodeInfo=#'systest.node_info'{handler=Callback}]) ->
%% TODO: validate that these succeed and shutdown when they don't
case NI2#'systest.node_info'.apps of
[] -> ok;
Apps -> [setup(NI2, App) || App <- Apps]
Apps -> [setup(NI2, App, HState) || App <- Apps]
end,

State = #state{node=NI2, handler=Callback, hander_state=HState},
State = #state{node=NI2, handler=Callback, handler_state=HState},

%% TODO: validate that these succeed and shutdown when they don't
case NI2#'systest.node_info'.on_start of
Expand All @@ -180,14 +181,16 @@ init([NodeInfo=#'systest.node_info'{handler=Callback}]) ->
handle_call(Msg, From, State) ->
handle_msg(Msg, State, From).

handle_cast(Msg, St) ->
handle_cast(Msg, State) ->
handle_msg(Msg, State).

handle_info(Info, State) ->
handle_msg(Info, State).

terminate(Reason, #state{handler=Mod}) ->
ok = Mod:terminate(Reason, Node, State),
terminate(Reason, #state{node=Node,
handler=Mod,
handler_state=ModState}) ->
ok = Mod:terminate(Reason, Node, ModState),
ok.

code_change(_OldVsn, State, _Extra) ->
Expand All @@ -197,8 +200,8 @@ code_change(_OldVsn, State, _Extra) ->
%% Private API
%%

start(NodeInfo=#'systest.node_info'{handler=Handler, link=ShouldLink,
host=Host, name=Name, config=BaseConf}) ->
start(NodeInfo=#'systest.node_info'{handler=Handler, host=Host,
name=Name, config=BaseConf}) ->
ct:pal("Starting ~p on ~p~n", [Name, Host]),

%% are there hidden traps here, when (for example) we're running
Expand Down Expand Up @@ -251,13 +254,14 @@ handle_msg(stop, State=#state{activity_state=killed}, _) ->
% handle_msg(kill, State=#state{activity_state=stopped}, _) ->
% {noreply, State};
handle_msg(stop, State=#state{node=Node, handler=Mod,
on_stop=Shutdown, handler_state=ModState}, ReplyTo) ->
case Shutdown of
handler_state=ModState}, ReplyTo) ->
case Node#'systest.node_info'.on_stop of
[] -> ok;
%% TODO: consider whether this is structured correctly - it *feels*
%% a little hackish - and perhaps having a supervising process deal
%% with these 'interactions' a little better
_ -> [ct:pal("~p~n", [interact(NI, In, ModState)]) || In <- Shutdown]
Shutdown -> [ct:pal("~p~n",
[interact(Node, In, ModState)]) || In <- Shutdown]
end,
handle_callback(stopping_callback(Mod:handle_stop(Node, ModState)),
State#state{activity_state=stopped}, ReplyTo);
Expand Down Expand Up @@ -285,7 +289,8 @@ handle_msg({interaction, InputData},
%% our catch-all, which defers to Mod:handler_state/3 to see if the
%% callback module knows what to do with Msg or not - this also allows the
%% handler an opportunity to decide how to deal with unexpected messages
handle_msg(Msg, State#state{node=Node, handler_state=ModState}, ReplyTo) ->
handle_msg(Msg, State=#state{node=Node, handler=Mod,
handler_state=ModState}, ReplyTo) ->
handle_callback(Mod:handle_msg(Msg, Node, ModState), State, ReplyTo).

stopping_callback(Result) ->
Expand All @@ -296,10 +301,12 @@ stopping_callback(Result) ->
Other
end.

handle_callback(CallbackResult, State, ReplyTo) ->
handle_callback(CallbackResult,
State=#state{node=Node,
handler=Mod}, ReplyTo) ->
try
case CallbackResult of
{rpc_stop, {Mod, Func, Args}, NewState} ->
{rpc_stop, Halt, NewState} ->
apply(rpc, call,
[Node#'systest.node_info'.id|tuple_to_list(Halt)]),
{noreply, State#state{handler_state=NewState}};
Expand Down
2 changes: 1 addition & 1 deletion src/systest_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
{ok, { {one_for_one, 5, 10}, [
{ok, { {rest_for_one, 5, 10}, [
{systest_config_server,
{systest_config, start_link, []},
permanent, 5000, worker, [gen_server]}]}}.
Loading

0 comments on commit 70be474

Please sign in to comment.