aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/test/supervisor_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/test/supervisor_SUITE.erl')
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl1203
1 files changed, 1203 insertions, 0 deletions
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
new file mode 100644
index 0000000000..b5d9ca44bf
--- /dev/null
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -0,0 +1,1203 @@
+%%
+%% %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%
+%%
+%% Description: Tests supervisor.erl
+
+-module(supervisor_SUITE).
+
+-include("test_server.hrl").
+
+%% Testserver specific export
+-export([all/1]).
+
+%% Indirect spawn export
+-export([init/1]).
+
+%% API tests
+-export([sup_start/1, sup_start_normal/1, sup_start_ignore_init/1,
+ sup_start_ignore_child/1, sup_start_error_return/1,
+ sup_start_fail/1, sup_stop/1, sup_stop_infinity/1,
+ sup_stop_timeout/1, sup_stop_brutal_kill/1, child_adm/1,
+ child_adm_simple/1, child_specs/1, extra_return/1]).
+
+%% Tests concept permanent, transient and temporary
+-export([normal_termination/1, permanent_normal/1, transient_normal/1,
+ temporary_normal/1, abnormal_termination/1,
+ permanent_abnormal/1, transient_abnormal/1,
+ temporary_abnormal/1]).
+
+%% Restart strategy tests
+-export([restart_one_for_one/1, one_for_one/1,
+ one_for_one_escalation/1, restart_one_for_all/1, one_for_all/1,
+ one_for_all_escalation/1, restart_simple_one_for_one/1,
+ simple_one_for_one/1, simple_one_for_one_escalation/1,
+ restart_rest_for_one/1, rest_for_one/1, rest_for_one_escalation/1,
+ simple_one_for_one_extra/1]).
+
+%% Misc tests
+-export([child_unlink/1, tree/1]).
+
+%-------------------------------------------------------------------------
+
+all(suite) ->
+ {req,[stdlib],
+ [sup_start, sup_stop, child_adm,
+ child_adm_simple, extra_return, child_specs,
+ restart_one_for_one, restart_one_for_all,
+ restart_simple_one_for_one, restart_rest_for_one,
+ normal_termination, abnormal_termination, child_unlink, tree]}.
+
+
+start(InitResult) ->
+ supervisor:start_link({local, sup_test}, ?MODULE, InitResult).
+
+%% Simulate different supervisors callback.
+init(fail) ->
+ erlang:error({badmatch,2});
+init(InitResult) ->
+ InitResult.
+
+%-------------------------------------------------------------------------
+%
+% Test cases starts here.
+%
+%-------------------------------------------------------------------------
+
+sup_start(doc) ->
+ ["Test start of a supervisor."];
+sup_start(suite) ->
+ [sup_start_normal, sup_start_ignore_init, sup_start_ignore_child,
+ sup_start_error_return, sup_start_fail].
+
+%-------------------------------------------------------------------------
+sup_start_normal(doc) ->
+ ["Tests that the supervisor process starts correctly and that it "
+ "can be terminated gracefully."];
+sup_start_normal(suite) -> [];
+sup_start_normal(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ ?line {ok, Pid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ ?line exit(Pid, shutdown),
+ receive
+ {'EXIT', Pid, shutdown} ->
+ ok;
+ {'EXIT', Pid, Else} ->
+ ?line test_server:fail({bad_exit_reason, Else})
+ after
+ 2000 ->
+ ?line test_server:fail(no_exit_reason)
+ end,
+ ok.
+%-------------------------------------------------------------------------
+sup_start_ignore_init(doc) ->
+ ["Tests what happens if init-callback returns ignore"];
+sup_start_ignore_init(suite) -> [];
+sup_start_ignore_init(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ ?line ignore = start(ignore),
+
+ receive
+ {'EXIT', _Pid, normal} ->
+ ok;
+ {'EXIT', _Pid, Else} ->
+ ?line test_server:fail({bad_exit_reason, Else})
+ after
+ 2000 ->
+ ?line test_server:fail(no_exit_reason)
+ end,
+ ok.
+
+
+%-------------------------------------------------------------------------
+sup_start_ignore_child(doc) ->
+ ["Tests what happens if init-callback returns ignore"];
+sup_start_ignore_child(suite) -> [];
+sup_start_ignore_child(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ ?line {ok, _Pid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ Child1 = {child1, {supervisor_1, start_child, [ignore]},
+ permanent, 1000, worker, []},
+ Child2 = {child2, {supervisor_1, start_child, []}, permanent,
+ 1000, worker, []},
+
+ ?line {ok, undefined} = supervisor:start_child(sup_test, Child1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2),
+
+ ?line [{child2, CPid2, worker, []},{child1, undefined, worker, []}]
+ = supervisor:which_children(sup_test),
+ ok.
+
+%-------------------------------------------------------------------------
+sup_start_error_return(doc) ->
+ ["Tests what happens if init-callback returns a invalid value"];
+sup_start_error_return(suite) -> [];
+sup_start_error_return(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ ?line {error, Term} = start(invalid),
+
+ receive
+ {'EXIT', _Pid, Term} ->
+ ok;
+ {'EXIT', _Pid, Else} ->
+ ?line test_server:fail({bad_exit_reason, Else})
+ after
+ 2000 ->
+ ?line test_server:fail(no_exit_reason)
+ end,
+ ok.
+
+%-------------------------------------------------------------------------
+sup_start_fail(doc) ->
+ ["Tests what happens if init-callback fails"];
+sup_start_fail(suite) -> [];
+sup_start_fail(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ ?line {error, Term} = start(fail),
+
+ receive
+ {'EXIT', _Pid, Term} ->
+ ok;
+ {'EXIT', _Pid, Else} ->
+ ?line test_server:fail({bad_exit_reason, Else})
+ after
+ 2000 ->
+ ?line test_server:fail(no_exit_reason)
+ end,
+ ok.
+%-------------------------------------------------------------------------
+sup_stop(doc) ->
+ ["Tests that the supervisor shoutdowns its children if it is "
+ "shutdown itself."];
+sup_stop(suite) -> [sup_stop_infinity, sup_stop_timeout, sup_stop_brutal_kill].
+
+%-------------------------------------------------------------------------
+
+sup_stop_infinity(doc) ->
+ ["See sup_stop/1 when Shutdown = infinity, this walue is only allowed "
+ "for children of type supervisor"];
+sup_stop_infinity(suite) -> [];
+
+sup_stop_infinity(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ ?line {ok, Pid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ Child1 = {child1, {supervisor_1, start_child, []},
+ permanent, infinity, supervisor, []},
+ Child2 = {child2, {supervisor_1, start_child, []}, permanent,
+ infinity, worker, []},
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ link(CPid1),
+ ?line {error, {invalid_shutdown,infinity}} =
+ supervisor:start_child(sup_test, Child2),
+
+ ?line exit(Pid, shutdown),
+
+ receive
+ {'EXIT', Pid, shutdown} ->
+ ok;
+ {'EXIT', Pid, Else} ->
+ ?line test_server:fail({bad_exit_reason, Else})
+ after
+ 5000 ->
+ ?line test_server:fail(no_exit_reason)
+ end,
+ receive
+ {'EXIT', CPid1, shutdown} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ after
+ 2000 -> ?line test_server:fail(no_exit_reason)
+ end,
+ ok.
+
+%-------------------------------------------------------------------------
+
+sup_stop_timeout(doc) ->
+ ["See sup_stop/1 when Shutdown = 1000"];
+sup_stop_timeout(suite) -> [];
+
+sup_stop_timeout(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ ?line {ok, Pid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ Child1 = {child1, {supervisor_1, start_child, []},
+ permanent, 1000, worker, []},
+ Child2 = {child2, {supervisor_1, start_child, []}, permanent,
+ 1000, worker, []},
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ link(CPid1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2),
+ link(CPid2),
+
+ CPid2 ! {sleep, 200000},
+
+ ?line exit(Pid, shutdown),
+
+ receive
+ {'EXIT', Pid, shutdown} ->
+ ok;
+ {'EXIT', Pid, Else} ->
+ ?line test_server:fail({bad_exit_reason, Else})
+ after
+ 5000 ->
+ ?line test_server:fail(no_exit_reason)
+ end,
+
+ receive
+ {'EXIT', CPid1, shutdown} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason,Reason})
+ after
+ 2000 -> ?line test_server:fail(no_exit_reason)
+ end,
+
+ receive
+ {'EXIT', CPid2, killed} -> ok;
+ {'EXIT', CPid2, Reason2} ->
+ ?line test_server:fail({bad_exit_reason, Reason2})
+ after
+ 2000 -> ?line test_server:fail(no_exit_reason)
+ end,
+ ok.
+
+%-------------------------------------------------------------------------
+sup_stop_brutal_kill(doc) ->
+ ["See sup_stop/1 when Shutdown = brutal_kill"];
+sup_stop_brutal_kill(suite) -> [];
+
+sup_stop_brutal_kill(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ ?line {ok, Pid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ Child1 = {child1, {supervisor_1, start_child, []},
+ permanent, 1000, worker, []},
+ Child2 = {child2, {supervisor_1, start_child, []}, permanent,
+ brutal_kill, worker, []},
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ link(CPid1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2),
+ link(CPid2),
+
+ ?line exit(Pid, shutdown),
+
+ receive
+ {'EXIT', Pid, shutdown} ->
+ ok;
+ {'EXIT', Pid, Else} ->
+ ?line test_server:fail({bad_exit_reason, Else})
+ after
+ 5000 ->
+ ?line test_server:fail(no_exit_reason)
+ end,
+
+ receive
+ {'EXIT', CPid1, shutdown} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ after
+ 2000 -> ?line test_server:fail(no_exit_reason)
+ end,
+ receive
+ {'EXIT', CPid2, killed} -> ok;
+ {'EXIT', CPid2, Reason2} ->
+ ?line test_server:fail({bad_exit_reason, Reason2})
+ after
+ 2000 -> ?line test_server:fail(no_exit_reason)
+ end,
+ ok.
+
+%-------------------------------------------------------------------------
+extra_return(doc) ->
+ ["The start function provided to start a child may "
+ "return {ok, Pid} or {ok, Pid, Info}, if it returns "
+ "the later check that the supervisor ignores the Info, "
+ "and includes it unchanged in return from start_child/2 "
+ "and restart_child/2"];
+extra_return(suite) -> [];
+
+extra_return(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child = {child1, {supervisor_1, start_child, [extra_return]},
+ permanent, 1000,
+ worker, []},
+ ?line {ok, _Pid} = start({ok, {{one_for_one, 2, 3600}, [Child]}}),
+ ?line [{child1, CPid, worker, []}] = supervisor:which_children(sup_test),
+ link(CPid),
+ ?line {error, not_found} = supervisor:terminate_child(sup_test, hej),
+ ?line {error, not_found} = supervisor:delete_child(sup_test, hej),
+ ?line {error, not_found} = supervisor:restart_child(sup_test, hej),
+ ?line {error, running} = supervisor:delete_child(sup_test, child1),
+ ?line {error, running} = supervisor:restart_child(sup_test, child1),
+ ?line [{child1, CPid, worker, []}] = supervisor:which_children(sup_test),
+ ?line ok = supervisor:terminate_child(sup_test, child1),
+ receive
+ {'EXIT', CPid, shutdown} -> ok;
+ {'EXIT', CPid, Reason} ->
+ ?line test_server:fail({bad_reason, Reason})
+ after 1000 ->
+ ?line test_server:fail(no_child_termination)
+ end,
+ ?line [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test),
+ ?line {ok, CPid2,extra_return} =
+ supervisor:restart_child(sup_test, child1),
+ ?line [{child1, CPid2, worker, []}] = supervisor:which_children(sup_test),
+ ?line ok = supervisor:terminate_child(sup_test, child1),
+ ?line ok = supervisor:terminate_child(sup_test, child1),
+ ?line ok = supervisor:delete_child(sup_test, child1),
+ ?line {error, not_found} = supervisor:restart_child(sup_test, child1),
+ ?line [] = supervisor:which_children(sup_test),
+ ?line {ok, CPid3, extra_return} = supervisor:start_child(sup_test, Child),
+ ?line [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test),
+ ok.
+%-------------------------------------------------------------------------
+child_adm(doc)->
+ ["Test API functions start_child/2, terminate_child/2, delete_child/2 "
+ "restart_child/2, which_children/1. Only correct childspecs are used, "
+ "handling of incorrect childspecs is tested in child_specs/1"];
+child_adm(suite) -> [];
+child_adm(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child = {child1, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ ?line {ok, _Pid} = start({ok, {{one_for_one, 2, 3600}, [Child]}}),
+ ?line [{child1, CPid, worker, []}] = supervisor:which_children(sup_test),
+ link(CPid),
+
+ %% Start of an already runnig process
+ ?line {error,{already_started, CPid}} =
+ supervisor:start_child(sup_test, Child),
+
+ %% Termination
+ ?line {error, not_found} = supervisor:terminate_child(sup_test, hej),
+ ?line {'EXIT',{noproc,{gen_server,call, _}}} =
+ (catch supervisor:terminate_child(foo, child1)),
+ ?line ok = supervisor:terminate_child(sup_test, child1),
+ receive
+ {'EXIT', CPid, shutdown} -> ok;
+ {'EXIT', CPid, Reason} ->
+ ?line test_server:fail({bad_reason, Reason})
+ after 1000 ->
+ ?line test_server:fail(no_child_termination)
+ end,
+ ?line [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test),
+ %% Like deleting something that does not exist, it will succeed!
+ ?line ok = supervisor:terminate_child(sup_test, child1),
+
+ %% Start of already existing but not running process
+ ?line {error,already_present} =
+ supervisor:start_child(sup_test, Child),
+
+ %% Restart
+ ?line {ok, CPid2} = supervisor:restart_child(sup_test, child1),
+ ?line [{child1, CPid2, worker, []}] = supervisor:which_children(sup_test),
+ ?line {error, running} = supervisor:restart_child(sup_test, child1),
+ ?line {error, not_found} = supervisor:restart_child(sup_test, child2),
+
+ %% Deletion
+ ?line {error, running} = supervisor:delete_child(sup_test, child1),
+ ?line {error, not_found} = supervisor:delete_child(sup_test, hej),
+ ?line {'EXIT',{noproc,{gen_server,call, _}}} =
+ (catch supervisor:delete_child(foo, child1)),
+ ?line ok = supervisor:terminate_child(sup_test, child1),
+ ?line ok = supervisor:delete_child(sup_test, child1),
+ ?line {error, not_found} = supervisor:restart_child(sup_test, child1),
+ ?line [] = supervisor:which_children(sup_test),
+
+ %% Start
+ ?line {'EXIT',{noproc,{gen_server,call, _}}} =
+ (catch supervisor:start_child(foo, Child)),
+ ?line {ok, CPid3} = supervisor:start_child(sup_test, Child),
+ ?line [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test),
+
+ ?line {'EXIT',{noproc,{gen_server,call,[foo,which_children,infinity]}}}
+ = (catch supervisor:which_children(foo)),
+ ok.
+%-------------------------------------------------------------------------
+child_adm_simple(doc) ->
+ ["The API functions terminate_child/2, delete_child/2 "
+ "restart_child/2 are not valid for a simple_one_for_one supervisor "
+ "check that the correct error message is returned."];
+child_adm_simple(suite) -> [];
+child_adm_simple(Config) when list(Config) ->
+ Child = {child, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ ?line {ok, _Pid} = start({ok, {{simple_one_for_one, 2, 3600}, [Child]}}),
+ %% In simple_one_for_one all children are added dynamically
+ ?line [] = supervisor:which_children(sup_test),
+
+ %% Start
+ ?line {'EXIT',{noproc,{gen_server,call, _}}} =
+ (catch supervisor:start_child(foo, [])),
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, []),
+ ?line [{undefined, CPid1, worker, []}] =
+ supervisor:which_children(sup_test),
+
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, []),
+ ?line Children = supervisor:which_children(sup_test),
+ ?line 2 = length(Children),
+ ?line true = lists:member({undefined, CPid2, worker, []}, Children),
+ ?line true = lists:member({undefined, CPid1, worker, []}, Children),
+
+ %% Termination
+ ?line {error, simple_one_for_one} =
+ supervisor:terminate_child(sup_test, child1),
+
+ %% Restart
+ ?line {error, simple_one_for_one} =
+ supervisor:restart_child(sup_test, child1),
+
+ %% Deletion
+ ?line {error, simple_one_for_one} =
+ supervisor:delete_child(sup_test, child1),
+ ok.
+
+%-------------------------------------------------------------------------
+child_specs(doc) ->
+ ["Tests child specs, invalid formats should be rejected."];
+child_specs(suite) -> [];
+child_specs(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ ?line {ok, _Pid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ ?line {error, _} = supervisor:start_child(sup_test, hej),
+
+ %% Bad child specs
+ B1 = {child, mfa, permanent, 1000, worker, []},
+ B2 = {child, {m,f,[a]}, prmanent, 1000, worker, []},
+ B3 = {child, {m,f,[a]}, permanent, -10, worker, []},
+ B4 = {child, {m,f,[a]}, permanent, 10, wrker, []},
+ B5 = {child, {m,f,[a]}, permanent, infinity, worker, []},
+ B6 = {child, {m,f,[a]}, permanent, 1000, worker, dy},
+ B7 = {child, {m,f,[a]}, permanent, 1000, worker, [1,2,3]},
+
+ %% Correct child specs!
+ %% <Modules> (last parameter in a child spec) can be [] as we do
+ %% not test code upgrade here.
+ C1 = {child, {m,f,[a]}, permanent, infinity, supervisor, []},
+ C2 = {child, {m,f,[a]}, permanent, 1000, supervisor, []},
+ C3 = {child, {m,f,[a]}, temporary, 1000, worker, dynamic},
+ C4 = {child, {m,f,[a]}, transient, 1000, worker, [m]},
+
+ ?line {error, {invalid_mfa,mfa}} = supervisor:start_child(sup_test, B1),
+ ?line {error, {invalid_restart_type, prmanent}} =
+ supervisor:start_child(sup_test, B2),
+ ?line {error, {invalid_shutdown,-10}}
+ = supervisor:start_child(sup_test, B3),
+ ?line {error, {invalid_child_type,wrker}}
+ = supervisor:start_child(sup_test, B4),
+ ?line {error, _} = supervisor:start_child(sup_test, B5),
+ ?line {error, {invalid_modules,dy}}
+ = supervisor:start_child(sup_test, B6),
+
+ ?line {error, {invalid_mfa,mfa}} = supervisor:check_childspecs([B1]),
+ ?line {error, {invalid_restart_type,prmanent}} =
+ supervisor:check_childspecs([B2]),
+ ?line {error, {invalid_shutdown,-10}} = supervisor:check_childspecs([B3]),
+ ?line {error, {invalid_child_type,wrker}}
+ = supervisor:check_childspecs([B4]),
+ ?line {error, _} = supervisor:check_childspecs([B5]),
+ ?line {error, {invalid_modules,dy}} = supervisor:check_childspecs([B6]),
+ ?line {error, {invalid_module, 1}} =
+ supervisor:check_childspecs([B7]),
+
+ ?line ok = supervisor:check_childspecs([C1]),
+ ?line ok = supervisor:check_childspecs([C2]),
+ ?line ok = supervisor:check_childspecs([C3]),
+ ?line ok = supervisor:check_childspecs([C4]),
+ ok.
+%-------------------------------------------------------------------------
+normal_termination(doc) ->
+ ["Testes the supervisors behaviour if a child dies with reason normal"];
+normal_termination(suite) ->
+ [permanent_normal, transient_normal, temporary_normal].
+
+%-------------------------------------------------------------------------
+permanent_normal(doc) ->
+ ["A permanent child should always be restarted"];
+permanent_normal(suite) -> [];
+permanent_normal(Config) when list(Config) ->
+ ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+
+ CPid1 ! stop,
+ test_server:sleep(100),
+ ?line [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test),
+ case is_pid(Pid) of
+ true ->
+ ok;
+ false ->
+ ?line test_server:fail({permanent_child_not_restarted, Child1})
+ end.
+
+%-------------------------------------------------------------------------
+transient_normal(doc) ->
+ ["A transient child should not be restarted if it exits with "
+ "reason normal"];
+transient_normal(suite) -> [];
+transient_normal(Config) when list(Config) ->
+ ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ Child1 = {child1, {supervisor_1, start_child, []}, transient, 1000,
+ worker, []},
+
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+
+ CPid1 ! stop,
+ test_server:sleep(100),
+
+ ?line [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test).
+
+%-------------------------------------------------------------------------
+temporary_normal(doc) ->
+ ["A temporary process should never be restarted"];
+temporary_normal(suite) -> [];
+temporary_normal(Config) when list(Config) ->
+ ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ Child1 = {child1, {supervisor_1, start_child, []}, temporary, 1000,
+ worker, []},
+
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+
+ CPid1 ! stop,
+ test_server:sleep(100),
+
+ ?line [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test).
+
+%-------------------------------------------------------------------------
+abnormal_termination(doc) ->
+ ["Testes the supervisors behaviour if a child dies with reason abnormal"];
+abnormal_termination(suite) ->
+ [permanent_abnormal, transient_abnormal, temporary_abnormal].
+
+%-------------------------------------------------------------------------
+permanent_abnormal(doc) ->
+ ["A permanent child should always be restarted"];
+permanent_abnormal(suite) -> [];
+permanent_abnormal(Config) when list(Config) ->
+ ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+
+ CPid1 ! die,
+ test_server:sleep(100),
+ ?line [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test),
+ case is_pid(Pid) of
+ true ->
+ ok;
+ false ->
+ ?line test_server:fail({permanent_child_not_restarted, Child1})
+ end.
+
+%-------------------------------------------------------------------------
+transient_abnormal(doc) ->
+ ["A transient child should be restarted if it exits with "
+ "reason abnormal"];
+transient_abnormal(suite) -> [];
+transient_abnormal(Config) when list(Config) ->
+ ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ Child1 = {child1, {supervisor_1, start_child, []}, transient, 1000,
+ worker, []},
+
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+
+ CPid1 ! die,
+ test_server:sleep(100),
+
+ ?line [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test),
+ case is_pid(Pid) of
+ true ->
+ ok;
+ false ->
+ ?line test_server:fail({transient_child_not_restarted, Child1})
+ end.
+
+
+%-------------------------------------------------------------------------
+temporary_abnormal(doc) ->
+ ["A temporary process should never be restarted"];
+temporary_abnormal(suite) -> [];
+temporary_abnormal(Config) when list(Config) ->
+ ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ Child1 = {child1, {supervisor_1, start_child, []}, temporary, 1000,
+ worker, []},
+
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+
+ CPid1 ! die,
+ test_server:sleep(100),
+
+ ?line [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test).
+
+%-------------------------------------------------------------------------
+restart_one_for_one(doc) ->
+ ["Test that the one_for_one strategy works."];
+
+restart_one_for_one(suite) -> [one_for_one, one_for_one_escalation].
+
+%-------------------------------------------------------------------------
+one_for_one(doc) ->
+ ["Test the one_for_one base case."];
+one_for_one(suite) -> [];
+one_for_one(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ ?line {ok, Pid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ link(CPid1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2),
+ link(CPid2),
+ CPid1 ! die,
+ receive
+ {'EXIT', CPid1, died} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ end,
+ test_server:sleep(100),
+ Children = supervisor:which_children(sup_test),
+ if length(Children) == 2 ->
+ case lists:keysearch(CPid2, 2, Children) of
+ {value, _} -> ok;
+ _ -> ?line test_server:fail(bad_child)
+ end;
+ true -> ?line test_server:fail({bad_child_list, Children})
+ end,
+
+ %% Test restart frequency property
+ CPid2 ! die,
+ receive
+ {'EXIT', CPid2, _} -> ok
+ end,
+ test_server:sleep(100),
+ [{_, Pid4, _, _}|_] = supervisor:which_children(sup_test),
+ Pid4 ! die,
+ receive
+ {'EXIT', Pid, _} -> ok
+ after 3000 -> ?line test_server:fail(restart_failed)
+ end,
+ ok.
+%-------------------------------------------------------------------------
+one_for_one_escalation(doc) ->
+ ["Test restart escalation on a one_for_one supervisor."];
+one_for_one_escalation(suite) -> [];
+one_for_one_escalation(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = {child1, {supervisor_1, start_child, [error]},
+ permanent, 1000,
+ worker, []},
+ Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ ?line {ok, Pid} = start({ok, {{one_for_one, 4, 3600}, []}}),
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ link(CPid1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2),
+ link(CPid2),
+ CPid1 ! die,
+ receive
+ {'EXIT', CPid1, died} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ end,
+ receive
+ {'EXIT', Pid, _} -> ok
+ after
+ 2000 -> ?line test_server:fail(supervisor_alive)
+ end,
+ receive
+ {'EXIT', CPid2, _} -> ok
+ after
+ 4000 -> ?line test_server:fail(all_not_terminated)
+ end,
+ ok.
+%-------------------------------------------------------------------------
+restart_one_for_all(doc) ->
+ ["Test that the one_for_all strategy works."];
+
+restart_one_for_all(suite) ->
+ [one_for_all, one_for_all_escalation].
+
+%-------------------------------------------------------------------------
+one_for_all(doc) ->
+ ["Test the one_for_all base case."];
+one_for_all(suite) -> [];
+one_for_all(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ ?line {ok, Pid} = start({ok, {{one_for_all, 2, 3600}, []}}),
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ link(CPid1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2),
+ link(CPid2),
+ CPid1 ! die,
+ receive
+ {'EXIT', CPid1, died} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ end,
+ receive
+ {'EXIT', CPid2, _} -> ok
+ end,
+ test_server:sleep(100),
+ Children = supervisor:which_children(sup_test),
+ if length(Children) == 2 -> ok;
+ true -> ?line test_server:fail({bad_child_list, Children})
+ end,
+ %% Test that no old children is still alive
+ SCh = lists:map(fun({_,P,_,_}) -> P end, Children),
+ case lists:member(CPid1, SCh) of
+ true -> ?line test_server:fail(bad_child);
+ false -> ok
+ end,
+ case lists:member(CPid2, SCh) of
+ true -> ?line test_server:fail(bad_child);
+ false -> ok
+ end,
+ %%% Test restart frequency property
+ [{_, Pid3, _, _}|_] = supervisor:which_children(sup_test),
+ Pid3 ! die,
+ test_server:sleep(100),
+ [{_, Pid4, _, _}|_] = supervisor:which_children(sup_test),
+ Pid4 ! die,
+ receive
+ {'EXIT', Pid, _} -> ok
+ after 3000 -> ?line test_server:fail(restart_failed)
+ end,
+ exit(Pid, shutdown).
+
+%-------------------------------------------------------------------------
+one_for_all_escalation(doc) ->
+ ["Test restart escalation on a one_for_all supervisor."];
+one_for_all_escalation(suite) -> [];
+one_for_all_escalation(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ Child2 = {child2, {supervisor_1, start_child, [error]},
+ permanent, 1000,
+ worker, []},
+ ?line {ok, Pid} = start({ok, {{one_for_all, 4, 3600}, []}}),
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ link(CPid1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2),
+ link(CPid2),
+ CPid1 ! die,
+ receive
+ {'EXIT', CPid1, died} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ end,
+ receive
+ {'EXIT', CPid2, _} -> ok
+ after
+ 2000 -> ?line test_server:fail(all_not_terminated)
+ end,
+ receive
+ {'EXIT', Pid, _} -> ok
+ after
+ 4000 -> ?line test_server:fail(supervisor_alive)
+ end,
+ ok.
+
+%-------------------------------------------------------------------------
+restart_simple_one_for_one(doc) ->
+ ["Test that the simple_one_for_one strategy works."];
+
+restart_simple_one_for_one(suite) ->
+ [simple_one_for_one, simple_one_for_one_extra,
+ simple_one_for_one_escalation].
+
+%-------------------------------------------------------------------------
+simple_one_for_one(doc) ->
+ ["Test the simple_one_for_one base case."];
+simple_one_for_one(suite) -> [];
+simple_one_for_one(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child = {child, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ ?line {ok, Pid} = start({ok, {{simple_one_for_one, 2, 3600}, [Child]}}),
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, []),
+ link(CPid1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, []),
+ link(CPid2),
+ CPid1 ! die,
+ receive
+ {'EXIT', CPid1, died} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ end,
+ test_server:sleep(100),
+ Children = supervisor:which_children(sup_test),
+ if length(Children) == 2 ->
+ case lists:keysearch(CPid2, 2, Children) of
+ {value, _} -> ok;
+ _ -> ?line test_server:fail(bad_child)
+ end;
+ true -> ?line test_server:fail({bad_child_list, Children})
+ end,
+ %% Test restart frequency property
+ CPid2 ! die,
+ receive
+ {'EXIT', CPid2, _} -> ok
+ end,
+ test_server:sleep(100),
+ [{_, Pid4, _, _}|_] = supervisor:which_children(sup_test),
+ Pid4 ! die,
+ receive
+ {'EXIT', Pid, _} -> ok
+ after 3000 -> ?line test_server:fail(restart_failed)
+ end,
+ ok.
+%-------------------------------------------------------------------------
+simple_one_for_one_extra(doc) ->
+ ["Tests automatic restart of children "
+ "who's start function return extra info."];
+simple_one_for_one_extra(suite) -> [];
+simple_one_for_one_extra(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child = {child, {supervisor_1, start_child, [extra_info]},
+ permanent, 1000, worker, []},
+ ?line {ok, Pid} = start({ok, {{simple_one_for_one, 2, 3600}, [Child]}}),
+ ?line {ok, CPid1, extra_info} = supervisor:start_child(sup_test, []),
+ link(CPid1),
+ ?line {ok, CPid2, extra_info} = supervisor:start_child(sup_test, []),
+ link(CPid2),
+ CPid1 ! die,
+ receive
+ {'EXIT', CPid1, died} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ end,
+ test_server:sleep(100),
+ Children = supervisor:which_children(sup_test),
+ if length(Children) == 2 ->
+ case lists:keysearch(CPid2, 2, Children) of
+ {value, _} -> ok;
+ _ -> ?line test_server:fail(bad_child)
+ end;
+ true -> ?line test_server:fail({bad_child_list, Children})
+ end,
+ CPid2 ! die,
+ receive
+ {'EXIT', CPid2, _} -> ok
+ end,
+ test_server:sleep(100),
+ [{_, Pid4, _, _}|_] = supervisor:which_children(sup_test),
+ Pid4 ! die,
+ receive
+ {'EXIT', Pid, _} -> ok
+ after 3000 -> ?line test_server:fail(restart_failed)
+ end,
+ ok.
+%-------------------------------------------------------------------------
+simple_one_for_one_escalation(doc) ->
+ ["Test restart escalation on a simple_one_for_one supervisor."];
+simple_one_for_one_escalation(suite) -> [];
+simple_one_for_one_escalation(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child = {child, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ ?line {ok, Pid} = start({ok, {{simple_one_for_one, 4, 3600}, [Child]}}),
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, [error]),
+ link(CPid1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, []),
+ link(CPid2),
+ CPid1 ! die,
+ receive
+ {'EXIT', CPid1, died} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ end,
+ receive
+ {'EXIT', Pid, _} -> ok
+ after
+ 2000 -> ?line test_server:fail(supervisor_alive)
+ end,
+ receive
+ {'EXIT', CPid2, _} -> ok
+ after
+ 2000 -> ?line test_server:fail(all_not_terminated)
+ end,
+ ok.
+%-------------------------------------------------------------------------
+restart_rest_for_one(doc) ->
+ ["Test that the rest_for_one strategy works."];
+restart_rest_for_one(suite) -> [rest_for_one, rest_for_one_escalation].
+
+%-------------------------------------------------------------------------
+rest_for_one(doc) ->
+ ["Test the rest_for_one base case."];
+rest_for_one(suite) -> [];
+rest_for_one(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ Child3 = {child3, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ ?line {ok, Pid} = start({ok, {{rest_for_one, 2, 3600}, []}}),
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ link(CPid1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2),
+ link(CPid2),
+ ?line {ok, CPid3} = supervisor:start_child(sup_test, Child3),
+ link(CPid3),
+ CPid2 ! die,
+ receive
+ {'EXIT', CPid2, died} -> ok;
+ {'EXIT', CPid2, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ after 2000 ->
+ ?line test_server:fail(no_exit)
+ end,
+ %% Check that Cpid3 did die
+ receive
+ {'EXIT', CPid3, _} -> ok
+ after 2000 ->
+ ?line test_server:fail(no_exit)
+ end,
+ %% Check that Cpid1 didn't die
+ receive
+ {'EXIT', CPid1, _} ->
+ ?line test_server:fail(bad_exit)
+ after
+ 100 -> ok
+ end,
+ Children = supervisor:which_children(sup_test),
+ if length(Children) == 3 -> ok;
+ true -> ?line test_server:fail({bad_child_list, Children})
+ end,
+ %% Test that no old children is still alive
+ SCh = lists:map(fun({_,P,_,_}) -> P end, Children),
+ case lists:member(CPid1, SCh) of
+ true -> ok;
+ false -> ?line test_server:fail(bad_child)
+ end,
+ case lists:member(CPid2, SCh) of
+ true -> ?line test_server:fail(bad_child);
+ false -> ok
+ end,
+ case lists:member(CPid3, SCh) of
+ true -> ?line test_server:fail(bad_child);
+ false -> ok
+ end,
+
+ %% Test restart frequency property
+ [{child3, Pid3, _, _}|_] = supervisor:which_children(sup_test),
+ Pid3 ! die,
+ test_server:sleep(100),
+ [_,{child2, Pid4, _, _}|_] = supervisor:which_children(sup_test),
+ Pid4 ! die,
+ receive
+ {'EXIT', Pid, _} -> ok
+ after 3000 -> ?line test_server:fail(restart_failed)
+ end,
+ exit(Pid, shutdown).
+
+%-------------------------------------------------------------------------
+rest_for_one_escalation(doc) ->
+ ["Test restart escalation on a rest_for_one supervisor."];
+rest_for_one_escalation(suite) -> [];
+rest_for_one_escalation(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000,
+ worker, []},
+ Child2 = {child2, {supervisor_1, start_child, [error]},
+ permanent, 1000,
+ worker, []},
+ ?line {ok, Pid} = start({ok, {{rest_for_one, 4, 3600}, []}}),
+ ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ link(CPid1),
+ ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2),
+ link(CPid2),
+ CPid1 ! die,
+ receive
+ {'EXIT', CPid1, died} -> ok;
+ {'EXIT', CPid1, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ end,
+ receive
+ {'EXIT', CPid2, _} -> ok
+ after
+ 2000 -> ?line test_server:fail(not_terminated)
+ end,
+ receive
+ {'EXIT', Pid, _} -> ok
+ after
+ 4000 -> ?line test_server:fail(supervisor_alive)
+ end,
+ ok.
+
+%-------------------------------------------------------------------------
+child_unlink(doc)-> ["Test that the supervisor does not hang forever if "
+ "the child unliks and then is terminated by the supervisor."];
+child_unlink(suite) -> [];
+child_unlink(Config) when list(Config) ->
+
+ ?line {ok, SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}),
+
+ Child = {naughty_child, {naughty_child,
+ start_link, [SupPid]}, permanent,
+ 1000, worker, [supervisor_SUITE]},
+
+ ?line {ok, _ChildPid} = supervisor:start_child(sup_test, Child),
+
+ Pid = spawn(supervisor, terminate_child, [SupPid, naughty_child]),
+
+ SupPid ! foo,
+ timer:sleep(5000),
+ %% If the supervisor did not hang it will have got rid of the
+ %% foo message that we sent.
+ case erlang:process_info(SupPid, message_queue_len) of
+ {message_queue_len, 0}->
+ ok;
+ _ ->
+ exit(Pid, kill),
+ ?line test_server:fail(supervisor_hangs)
+ end.
+%-------------------------------------------------------------------------
+
+tree(doc) ->
+ ["Test a basic supervison tree."];
+tree(suite) ->
+ [];
+tree(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+
+ Child1 = {child1, {supervisor_1, start_child, []},
+ permanent, 1000,
+ worker, []},
+ Child2 = {child2, {supervisor_1, start_child, []},
+ permanent, 1000,
+ worker, []},
+ Child3 = {child3, {supervisor_1, start_child, [error]},
+ permanent, 1000,
+ worker, []},
+ Child4 = {child4, {supervisor_1, start_child, []},
+ permanent, 1000,
+ worker, []},
+
+ ChildSup1 = {supchild1,
+ {supervisor, start_link,
+ [?MODULE, {ok, {{one_for_one, 4, 3600}, [Child1, Child2]}}]},
+ permanent, infinity,
+ supervisor, []},
+ ChildSup2 = {supchild2,
+ {supervisor, start_link,
+ [?MODULE, {ok, {{one_for_one, 4, 3600}, []}}]},
+ permanent, infinity,
+ supervisor, []},
+
+ %% Top supervisor
+ ?line {ok, Pid} = start({ok, {{one_for_all, 4, 3600}, []}}),
+
+ %% Child supervisors
+ ?line {ok, Sup1} = supervisor:start_child(Pid, ChildSup1),
+ ?line {ok, Sup2} = supervisor:start_child(Pid, ChildSup2),
+
+ %% Workers
+
+ ?line [{_, CPid2, _, _},{_, CPid1, _, _}] =
+ supervisor:which_children(Sup1),
+
+ %% Dynamic children
+ ?line {ok, CPid3} = supervisor:start_child(Sup2, Child3),
+ ?line {ok, CPid4} = supervisor:start_child(Sup2, Child4),
+
+ link(Sup1),
+ link(Sup2),
+ link(CPid1),
+ link(CPid2),
+ link(CPid3),
+ link(CPid4),
+
+ %% Test that the only the process that dies is restarted
+ CPid4 ! die,
+
+ receive
+ {'EXIT', CPid4, _} -> ?line ok
+ after 10000 ->
+ ?line test_server:fail(child_was_not_killed)
+ end,
+
+ test_server:sleep(100),
+
+ ?line [{_, CPid2, _, _},{_, CPid1, _, _}] =
+ supervisor:which_children(Sup1),
+
+ ?line [{_, NewCPid4, _, _},{_, CPid3, _, _}] =
+ supervisor:which_children(Sup2),
+
+ link(NewCPid4),
+
+ %% Test that supervisor tree is restarted, but not dynamic children.
+ CPid3 ! die,
+
+ receive
+ {'EXIT', CPid3, died} -> ?line ok;
+ {'EXIT', CPid3, Reason} ->
+ ?line test_server:fail({bad_exit_reason, Reason})
+ after 1000 ->
+ ?line test_server:fail(child_was_not_killed)
+ end,
+
+ test_server:sleep(1000),
+
+ receive
+ {'EXIT', NewCPid4, _} -> ?line ok
+ after 1000 ->
+ ?line test_server:fail(child_was_not_killed)
+ end,
+
+ receive
+ {'EXIT', Sup2, _} -> ?line ok
+ after 1000 ->
+ ?line test_server:fail(child_was_not_killed)
+ end,
+
+ receive
+ {'EXIT', CPid1, _} -> ?line ok
+ after 1000 ->
+ ?line test_server:fail(child_was_not_killed)
+ end,
+
+ receive
+ {'EXIT', CPid2, _} -> ?line ok
+ after 1000 ->
+ ?line test_server:fail(child_was_not_killed)
+ end,
+
+ receive
+ {'EXIT', Sup1, _} -> ?line ok
+ after 1000 ->
+ ?line test_server:fail(child_was_not_killed)
+ end,
+
+ ?line [{supchild2, NewSup2, _, _},{supchild1, NewSup1, _, _}] =
+ supervisor:which_children(Pid),
+
+ ?line [{child2, _, _, _},{child1, _, _, _}] =
+ supervisor:which_children(NewSup1),
+ ?line [] = supervisor:which_children(NewSup2),
+
+ ok.