diff options
Diffstat (limited to 'lib/stdlib/test/supervisor_SUITE.erl')
-rw-r--r-- | lib/stdlib/test/supervisor_SUITE.erl | 1203 |
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. |