aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/test/gen_server_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/test/gen_server_SUITE.erl')
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl1049
1 files changed, 1049 insertions, 0 deletions
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
new file mode 100644
index 0000000000..86a5a65ba3
--- /dev/null
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -0,0 +1,1049 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(gen_server_SUITE).
+
+-include("test_server.hrl").
+-include_lib("kernel/include/inet.hrl").
+
+-export([init_per_testcase/2, fin_per_testcase/2]).
+
+-export([all/1]).
+-export([start/1, crash/1, call/1, cast/1, cast_fast/1,
+ info/1, abcast/1, multicall/1, multicall_down/1,
+ call_remote1/1, call_remote2/1, call_remote3/1,
+ call_remote_n1/1, call_remote_n2/1, call_remote_n3/1, spec_init/1,
+ spec_init_local_registered_parent/1,
+ spec_init_global_registered_parent/1,
+ otp_5854/1, hibernate/1, otp_7669/1
+ ]).
+
+% spawn export
+-export([spec_init_local/2, spec_init_global/2,
+ spec_init_default_timeout/2, spec_init_anonymous/1,
+ spec_init_anonymous_default_timeout/1,
+ spec_init_not_proc_lib/1, cast_fast_messup/0]).
+
+
+% The gen_server behaviour
+-export([init/1, handle_call/3, handle_cast/2,
+ handle_info/2, terminate/2]).
+
+all(suite) ->
+ [start, crash, call, cast, cast_fast, info,
+ abcast, multicall, multicall_down, call_remote1,
+ call_remote2, call_remote3, call_remote_n1,
+ call_remote_n2, call_remote_n3, spec_init,
+ spec_init_local_registered_parent,
+ spec_init_global_registered_parent,
+ otp_5854,hibernate,otp_7669].
+
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_testcase(_Case, Config) ->
+ ?line Dog = ?t:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
+fin_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+
+%% --------------------------------------
+%% Start and stop a gen_server.
+%% --------------------------------------
+
+start(suite) -> [];
+start(Config) when is_list(Config) ->
+ OldFl = process_flag(trap_exit, true),
+
+ %% anonymous
+ ?line {ok, Pid0} = gen_server:start(gen_server_SUITE, [], []),
+ ?line ok = gen_server:call(Pid0, started_p),
+ ?line ok = gen_server:call(Pid0, stop),
+ ?line busy_wait_for_process(Pid0,600),
+ ?line {'EXIT', {noproc,_}} = (catch gen_server:call(Pid0, started_p, 1)),
+
+ %% anonymous with timeout
+ ?line {ok, Pid00} = gen_server:start(gen_server_SUITE, [],
+ [{timeout,1000}]),
+ ?line ok = gen_server:call(Pid00, started_p),
+ ?line ok = gen_server:call(Pid00, stop),
+ ?line {error, timeout} = gen_server:start(gen_server_SUITE, sleep,
+ [{timeout,100}]),
+
+ %% anonymous with ignore
+ ?line ignore = gen_server:start(gen_server_SUITE, ignore, []),
+
+ %% anonymous with stop
+ ?line {error, stopped} = gen_server:start(gen_server_SUITE, stop, []),
+
+ %% anonymous linked
+ ?line {ok, Pid1} =
+ gen_server:start_link(gen_server_SUITE, [], []),
+ ?line ok = gen_server:call(Pid1, started_p),
+ ?line ok = gen_server:call(Pid1, stop),
+ ?line receive
+ {'EXIT', Pid1, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(not_stopped)
+ end,
+
+ %% local register
+ ?line {ok, Pid2} =
+ gen_server:start({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call(my_test_name, started_p),
+ ?line {error, {already_started, Pid2}} =
+ gen_server:start({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call(my_test_name, stop),
+
+ ?line busy_wait_for_process(Pid2,600),
+
+ ?line {'EXIT', {noproc,_}} = (catch gen_server:call(Pid2, started_p, 10)),
+
+ %% local register linked
+ ?line {ok, Pid3} =
+ gen_server:start_link({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call(my_test_name, started_p),
+ ?line {error, {already_started, Pid3}} =
+ gen_server:start({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call(my_test_name, stop),
+ ?line receive
+ {'EXIT', Pid3, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(not_stopped)
+ end,
+
+ %% global register
+ ?line {ok, Pid4} =
+ gen_server:start({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({global, my_test_name}, started_p),
+ ?line {error, {already_started, Pid4}} =
+ gen_server:start({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({global, my_test_name}, stop),
+ test_server:sleep(1),
+ ?line {'EXIT', {noproc,_}} = (catch gen_server:call(Pid4, started_p, 10)),
+
+ %% global register linked
+ ?line {ok, Pid5} =
+ gen_server:start_link({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({global, my_test_name}, started_p),
+ ?line {error, {already_started, Pid5}} =
+ gen_server:start({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({global, my_test_name}, stop),
+ ?line receive
+ {'EXIT', Pid5, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(not_stopped)
+ end,
+
+ test_server:messages_get(),
+
+ %% Must wait for all error messages before going to next test.
+ %% (otherwise it interferes too much with real time characteristics).
+ case os:type() of
+ vxworks ->
+ receive after 5000 -> ok end;
+ _ ->
+ ok
+ end,
+ process_flag(trap_exit, OldFl),
+ ok.
+
+crash(Config) when is_list(Config) ->
+ ?line error_logger_forwarder:register(),
+
+ process_flag(trap_exit, true),
+
+ %% This crash should not generate a crash report.
+ ?line {ok,Pid0} = gen_server:start_link(?MODULE, [], []),
+ ?line {'EXIT',{{shutdown,reason},_}} =
+ (catch gen_server:call(Pid0, shutdown_reason)),
+ receive {'EXIT',Pid0,{shutdown,reason}} -> ok end,
+
+ %% This crash should not generate a crash report.
+ ?line {ok,Pid1} = gen_server:start_link(?MODULE, {state,state1}, []),
+ ?line {'EXIT',{{shutdown,stop_reason},_}} =
+ (catch gen_server:call(Pid1, stop_shutdown_reason)),
+ receive {'EXIT',Pid1,{shutdown,stop_reason}} -> ok end,
+
+ %% This crash should not generate a crash report.
+ ?line {ok,Pid2} = gen_server:start_link(?MODULE, [], []),
+ ?line {'EXIT',{shutdown,_}} =
+ (catch gen_server:call(Pid2, exit_shutdown)),
+ receive {'EXIT',Pid2,shutdown} -> ok end,
+
+ %% This crash should not generate a crash report.
+ ?line {ok,Pid3} = gen_server:start_link(?MODULE, {state,state3}, []),
+ ?line {'EXIT',{shutdown,_}} =
+ (catch gen_server:call(Pid3, stop_shutdown)),
+ receive {'EXIT',Pid3,shutdown} -> ok end,
+
+ process_flag(trap_exit, false),
+
+ %% This crash should generate a crash report and a report
+ %% from gen_server.
+ ?line {ok,Pid4} = gen_server:start(?MODULE, {state,state4}, []),
+ ?line {'EXIT',{crashed,_}} = (catch gen_server:call(Pid4, crash)),
+ receive
+ {error,_GroupLeader4,{Pid4,
+ "** Generic server"++_,
+ [Pid4,crash,state4,crashed]}} ->
+ ok;
+ Other4a ->
+ ?line io:format("Unexpected: ~p", [Other4a]),
+ ?line ?t:fail()
+ end,
+ receive
+ {error_report,_,{Pid4,crash_report,[List4|_]}} ->
+ {exit,crashed,_} = proplists:get_value(error_info, List4),
+ Pid4 = proplists:get_value(pid, List4);
+ Other4 ->
+ ?line io:format("Unexpected: ~p", [Other4]),
+ ?line ?t:fail()
+ end,
+
+ receive
+ Any ->
+ ?line io:format("Unexpected: ~p", [Any]),
+ ?line ?t:fail()
+ after 500 ->
+ ok
+ end,
+
+ ok.
+
+%% --------------------------------------
+%% Test gen_server:call and handle_call.
+%% Test all different return values from
+%% handle_call.
+%% --------------------------------------
+
+call(suite) -> [];
+call(Config) when is_list(Config) ->
+ OldFl = process_flag(trap_exit, true),
+
+ ?line {ok, _Pid} =
+ gen_server:start_link({local, my_test_name},
+ gen_server_SUITE, [], []),
+
+ ?line ok = gen_server:call(my_test_name, started_p),
+ ?line delayed = gen_server:call(my_test_name, {delayed_answer,1}),
+
+ %% two requests within a specified time.
+ ?line ok = gen_server:call(my_test_name, {call_within, 1000}),
+ test_server:sleep(500),
+ ?line ok = gen_server:call(my_test_name, next_call),
+ ?line ok = gen_server:call(my_test_name, {call_within, 1000}),
+ test_server:sleep(1500),
+ ?line false = gen_server:call(my_test_name, next_call),
+
+ %% timeout call.
+ ?line delayed = gen_server:call(my_test_name, {delayed_answer,1}, 30),
+ ?line {'EXIT',{timeout,_}} =
+ (catch gen_server:call(my_test_name, {delayed_answer,30}, 1)),
+
+ %% bad return value in the gen_server loop from handle_call.
+ ?line {'EXIT',{{bad_return_value, badreturn},_}} =
+ (catch gen_server:call(my_test_name, badreturn)),
+
+ process_flag(trap_exit, OldFl),
+ ok.
+
+%% --------------------------------------
+%% Test call to nonexisting processes on remote nodes
+%% --------------------------------------
+
+start_node(Name) ->
+ ?line Pa = filename:dirname(code:which(?MODULE)),
+ ?line N = test_server:start_node(Name, slave, [{args, " -pa " ++ Pa}]),
+ %% After starting a slave, it takes a little while until global knows
+ %% about it, even if nodes() includes it, so we make sure that global
+ %% knows about it before registering something on all nodes.
+ global:sync(),
+ N.
+
+call_remote1(suite) -> [];
+call_remote1(Config) when is_list(Config) ->
+ ?line N = hubba,
+ ?line {ok, Node} = start_node(N),
+ ?line {ok, Pid} = rpc:call(Node, gen_server, start,
+ [{global, N}, ?MODULE, [], []]),
+ ?line ok = (catch gen_server:call({global, N}, started_p, infinity)),
+ ?line exit(Pid, boom),
+ ?line {'EXIT', {Reason, _}} = (catch gen_server:call({global, N},
+ started_p, infinity)),
+ ?line true = (Reason == noproc) orelse (Reason == boom),
+ ok.
+
+call_remote2(suite) -> [];
+call_remote2(Config) when is_list(Config) ->
+ ?line N = hubba,
+ ?line {ok, Node} = start_node(N),
+
+ ?line {ok, Pid} = rpc:call(Node, gen_server, start,
+ [{global, N}, ?MODULE, [], []]),
+ ?line ok = (catch gen_server:call(Pid, started_p, infinity)),
+ ?line exit(Pid, boom),
+ ?line {'EXIT', {Reason, _}} = (catch gen_server:call(Pid,
+ started_p, infinity)),
+ ?line true = (Reason == noproc) orelse (Reason == boom),
+ ok.
+
+call_remote3(suite) -> [];
+call_remote3(Config) when is_list(Config) ->
+ ?line N = hubba,
+ ?line {ok, Node} = start_node(N),
+
+ ?line {ok, Pid} = rpc:call(Node, gen_server, start,
+ [{local, piller}, ?MODULE, [], []]),
+ ?line ok = (catch gen_server:call({piller, Node}, started_p, infinity)),
+ ?line exit(Pid, boom),
+ ?line {'EXIT', {Reason, _}} = (catch gen_server:call({piller, Node},
+ started_p, infinity)),
+ ?line true = (Reason == noproc) orelse (Reason == boom),
+ ok.
+
+%% --------------------------------------
+%% Test call to nonexisting node
+%% --------------------------------------
+
+call_remote_n1(suite) -> [];
+call_remote_n1(Config) when is_list(Config) ->
+ ?line N = hubba,
+ ?line {ok, Node} = start_node(N),
+ ?line {ok, _Pid} = rpc:call(Node, gen_server, start,
+ [{global, N}, ?MODULE, [], []]),
+ ?line _ = test_server:stop_node(Node),
+ ?line {'EXIT', {noproc, _}} =
+ (catch gen_server:call({global, N}, started_p, infinity)),
+
+ ok.
+
+call_remote_n2(suite) -> [];
+call_remote_n2(Config) when is_list(Config) ->
+ ?line N = hubba,
+ ?line {ok, Node} = start_node(N),
+
+ ?line {ok, Pid} = rpc:call(Node, gen_server, start,
+ [{global, N}, ?MODULE, [], []]),
+ ?line _ = test_server:stop_node(Node),
+ ?line {'EXIT', {{nodedown, Node}, _}} = (catch gen_server:call(Pid,
+ started_p, infinity)),
+
+ ok.
+
+call_remote_n3(suite) -> [];
+call_remote_n3(Config) when is_list(Config) ->
+ ?line N = hubba,
+ ?line {ok, Node} = start_node(N),
+
+ ?line {ok, _Pid} = rpc:call(Node, gen_server, start,
+ [{local, piller}, ?MODULE, [], []]),
+ ?line _ = test_server:stop_node(Node),
+ ?line {'EXIT', {{nodedown, Node}, _}} = (catch gen_server:call({piller, Node},
+ started_p, infinity)),
+
+ ok.
+
+%% --------------------------------------
+%% Test gen_server:cast and handle_cast.
+%% Test all different return values from
+%% handle_cast.
+%% --------------------------------------
+
+cast(suite) -> [];
+cast(Config) when is_list(Config) ->
+ ?line {ok, Pid} =
+ gen_server:start({local, my_test_name},
+ gen_server_SUITE, [], []),
+
+ ?line ok = gen_server:call(my_test_name, started_p),
+
+ ?line ok = gen_server:cast(my_test_name, {self(),handle_cast}),
+ ?line receive
+ {Pid, handled_cast} ->
+ ok
+ after 1000 ->
+ test_server:fail(handle_cast)
+ end,
+
+ ?line ok = gen_server:cast(my_test_name, {self(),delayed_cast,1}),
+ ?line receive
+ {Pid, delayed} ->
+ ok
+ after 1000 ->
+ test_server:fail(delayed_cast)
+ end,
+
+ ?line ok = gen_server:cast(my_test_name, {self(),stop}),
+ ?line receive
+ {Pid, stopped} ->
+ ok
+ after 1000 ->
+ test_server:fail(stop)
+ end,
+ ok.
+
+cast_fast(suite) -> [];
+cast_fast(doc) -> ["Test that cast really return immediately"];
+cast_fast(Config) when is_list(Config) ->
+ ?line {ok,Node} = start_node(hubba),
+ ?line {_,"@"++Host} = lists:splitwith(fun ($@) -> false; (_) -> true end,
+ atom_to_list(Node)),
+ ?line FalseNode = list_to_atom("hopp@"++Host),
+ ?line true = rpc:cast(Node, ?MODULE, cast_fast_messup, []),
+% ?line io:format("Nodes ~p~n", [rpc:call(N, ?MODULE, cast_fast_messup, [])]),
+ ?line test_server:sleep(1000),
+ ?line [Node] = nodes(),
+ ?line {Time,ok} = test_server:timecall(gen_server, cast,
+ [{hopp,FalseNode},hopp]),
+ ?line true = test_server:stop_node(Node),
+ ?line if Time > 1.0 -> % Default listen timeout is about 7.0 s
+ test_server:fail(hanging_cast);
+ true ->
+ ok
+ end.
+
+cast_fast_messup() ->
+ %% Register a false node: hopp@hostname
+ unregister(erl_epmd),
+ erl_epmd:start_link(),
+ {ok,S} = gen_tcp:listen(0, []),
+ {ok,P} = inet:port(S),
+ {ok,_Creation} = erl_epmd:register_node(hopp, P),
+ receive after infinity -> ok end.
+
+%% --------------------------------------
+%% Test handle_info.
+%% --------------------------------------
+
+info(suite) -> [];
+info(Config) when is_list(Config) ->
+ ?line {ok, Pid} =
+ gen_server:start({local, my_test_name},
+ gen_server_SUITE, [], []),
+
+ ?line ok = gen_server:call(my_test_name, started_p),
+
+ ?line Pid ! {self(),handle_info},
+ ?line receive
+ {Pid, handled_info} ->
+ ok
+ after 1000 ->
+ test_server:fail(handle_info)
+ end,
+
+ ?line Pid ! {self(),delayed_info,1},
+ ?line receive
+ {Pid, delayed_info} ->
+ ok
+ after 1000 ->
+ test_server:fail(delayed_info)
+ end,
+
+ ?line Pid ! {self(),stop},
+ ?line receive
+ {Pid, stopped_info} ->
+ ok
+ after 1000 ->
+ test_server:fail(stop_info)
+ end,
+ ok.
+
+hibernate(suite) -> [];
+hibernate(Config) when is_list(Config) ->
+ OldFl = process_flag(trap_exit, true),
+ ?line {ok, Pid0} =
+ gen_server:start_link({local, my_test_name_hibernate0},
+ gen_server_SUITE, hibernate, []),
+ ?line receive after 1000 -> ok end,
+ ?line {current_function,{erlang,hibernate,3}} = erlang:process_info(Pid0,current_function),
+ ?line ok = gen_server:call(my_test_name_hibernate0, stop),
+ receive
+ {'EXIT', Pid0, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(gen_server_did_not_die)
+ end,
+
+ ?line {ok, Pid} =
+ gen_server:start_link({local, my_test_name_hibernate},
+ gen_server_SUITE, [], []),
+
+ ?line ok = gen_server:call(my_test_name_hibernate, started_p),
+ ?line true = gen_server:call(my_test_name_hibernate, hibernate),
+ ?line receive after 1000 -> ok end,
+ ?line {current_function,{erlang,hibernate,3}} = erlang:process_info(Pid,current_function),
+ ?line Parent = self(),
+ Fun = fun() ->
+ receive
+ go ->
+ ok
+ end,
+ receive
+ after 1000 ->
+ ok
+ end,
+ X = erlang:process_info(Pid,current_function),
+ Pid ! continue,
+ Parent ! {result,X}
+ end,
+ ?line Pid2 = spawn_link(Fun),
+ ?line true = gen_server:call(my_test_name_hibernate, {hibernate_noreply,Pid2}),
+
+ ?line gen_server:cast(my_test_name_hibernate, hibernate_later),
+ ?line true = ({current_function,{erlang,hibernate,3}} =/= erlang:process_info(Pid,current_function)),
+ ?line receive after 2000 -> ok end,
+ ?line ({current_function,{erlang,hibernate,3}} = erlang:process_info(Pid,current_function)),
+ ?line ok = gen_server:call(my_test_name_hibernate, started_p),
+ ?line true = ({current_function,{erlang,hibernate,3}} =/= erlang:process_info(Pid,current_function)),
+ ?line gen_server:cast(my_test_name_hibernate, hibernate_now),
+ ?line receive after 1000 -> ok end,
+ ?line ({current_function,{erlang,hibernate,3}} = erlang:process_info(Pid,current_function)),
+ ?line ok = gen_server:call(my_test_name_hibernate, started_p),
+ ?line true = ({current_function,{erlang,hibernate,3}} =/= erlang:process_info(Pid,current_function)),
+ ?line Pid ! hibernate_later,
+ ?line true = ({current_function,{erlang,hibernate,3}} =/= erlang:process_info(Pid,current_function)),
+ ?line receive after 2000 -> ok end,
+ ?line ({current_function,{erlang,hibernate,3}} = erlang:process_info(Pid,current_function)),
+ ?line ok = gen_server:call(my_test_name_hibernate, started_p),
+ ?line true = ({current_function,{erlang,hibernate,3}} =/= erlang:process_info(Pid,current_function)),
+ ?line Pid ! hibernate_now,
+ ?line receive after 1000 -> ok end,
+ ?line ({current_function,{erlang,hibernate,3}} = erlang:process_info(Pid,current_function)),
+ ?line ok = gen_server:call(my_test_name_hibernate, started_p),
+ ?line true = ({current_function,{erlang,hibernate,3}} =/= erlang:process_info(Pid,current_function)),
+ ?line receive
+ {result,R} ->
+ ?line {current_function,{erlang,hibernate,3}} = R
+ end,
+ ?line true = gen_server:call(my_test_name_hibernate, hibernate),
+ ?line receive after 1000 -> ok end,
+ ?line {current_function,{erlang,hibernate,3}} = erlang:process_info(Pid,current_function),
+ ?line sys:suspend(my_test_name_hibernate),
+ ?line receive after 1000 -> ok end,
+ ?line {current_function,{erlang,hibernate,3}} = erlang:process_info(Pid,current_function),
+ ?line sys:resume(my_test_name_hibernate),
+ ?line receive after 1000 -> ok end,
+ ?line {current_function,{erlang,hibernate,3}} = erlang:process_info(Pid,current_function),
+ ?line ok = gen_server:call(my_test_name_hibernate, started_p),
+ ?line true = ({current_function,{erlang,hibernate,3}} =/= erlang:process_info(Pid,current_function)),
+
+ ?line ok = gen_server:call(my_test_name_hibernate, stop),
+ receive
+ {'EXIT', Pid, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(gen_server_did_not_die)
+ end,
+ process_flag(trap_exit, OldFl),
+ ok.
+
+%% --------------------------------------
+%% Test gen_server:abcast and handle_cast.
+%% Test all different return values from
+%% handle_cast.
+%% --------------------------------------
+
+abcast(suite) -> [];
+abcast(Config) when is_list(Config) ->
+ ?line {ok, Pid} =
+ gen_server:start({local, my_test_name},
+ gen_server_SUITE, [], []),
+
+ ?line ok = gen_server:call(my_test_name, started_p),
+
+ ?line abcast = gen_server:abcast(my_test_name, {self(),handle_cast}),
+ ?line receive
+ {Pid, handled_cast} ->
+ ok
+ after 1000 ->
+ test_server:fail(abcast)
+ end,
+
+ ?line abcast = gen_server:abcast([node()], my_test_name,
+ {self(),delayed_cast,1}),
+ ?line receive
+ {Pid, delayed} ->
+ ok
+ after 1000 ->
+ test_server:fail(delayed_abcast)
+ end,
+
+ ?line abcast = gen_server:abcast(my_test_name, {self(),stop}),
+ ?line receive
+ {Pid, stopped} ->
+ ok
+ after 1000 ->
+ test_server:fail(abcast_stop)
+ end,
+ ok.
+
+%% --------------------------------------
+%% Test gen_server:multicall and handle_call.
+%% Test all different return values from
+%% handle_call.
+%% --------------------------------------
+
+multicall(suite) -> [];
+multicall(Config) when is_list(Config) ->
+ OldFl = process_flag(trap_exit, true),
+
+ ?line {ok, Pid} =
+ gen_server:start_link({local, my_test_name},
+ gen_server_SUITE, [], []),
+
+ ?line ok = gen_server:call(my_test_name, started_p),
+ Nodes = nodes(),
+ Node = node(),
+ ?line {[{Node,delayed}],Nodes} =
+ gen_server:multi_call(my_test_name, {delayed_answer,1}),
+
+ %% two requests within a specified time.
+ ?line {[{Node,ok}],[]} =
+ gen_server:multi_call([Node], my_test_name, {call_within, 1000}),
+ test_server:sleep(500),
+ ?line {[{Node,ok}],[]} =
+ gen_server:multi_call([Node], my_test_name, next_call),
+ ?line {[{Node,ok}],[]} =
+ gen_server:multi_call([Node], my_test_name, {call_within, 1000}),
+ test_server:sleep(1500),
+ ?line {[{Node,false}],[]} =
+ gen_server:multi_call([Node],my_test_name, next_call),
+
+ %% Stop the server.
+ ?line {[{Node,ok}],[]} =
+ gen_server:multi_call([Node],my_test_name, stop),
+ receive
+ {'EXIT', Pid, stopped} -> ok
+ after 1000 ->
+ test_server:fail(multicall_stop)
+ end,
+
+ process_flag(trap_exit, OldFl),
+
+ ok.
+
+%% OTP-3587
+multicall_down(suite) -> [];
+multicall_down(Config) when is_list(Config) ->
+ %% We need a named host which is inaccessible.
+ ?line Name = node@test01,
+
+ %% We use 'global' as a gen_server to call.
+ ?line {Good, Bad} = gen_server:multi_call([Name, node()],
+ global_name_server,
+ {whereis, gurkburk},
+ 3000),
+ io:format("good = ~p, bad = ~p~n", [Good, Bad]),
+ ?line [Name] = Bad,
+ ok.
+
+busy_wait_for_process(Pid,N) ->
+ case erlang:is_process_alive(Pid) of
+ true ->
+ receive
+ after 100 ->
+ ok
+ end,
+ busy_wait_for_process(Pid,N-1);
+ _ ->
+ ok
+ end.
+%%--------------------------------------------------------------
+spec_init(doc) ->
+ ["Test gen_server:enter_loop/[3,4,5]. Used when you want to write "
+ "your own special init-phase."];
+spec_init(suite) ->
+ [];
+spec_init(Config) when is_list(Config) ->
+
+ OldFlag = process_flag(trap_exit, true),
+
+ ?line {ok, Pid0} = start_link(spec_init_local, [{ok, my_server}, []]),
+ ?line ok = gen_server:call(Pid0, started_p),
+ ?line ok = gen_server:call(Pid0, stop),
+ receive
+ {'EXIT', Pid0, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(gen_server_did_not_die)
+ end,
+
+ ?line {ok, Pid01} = start_link(spec_init_local, [{not_ok, my_server}, []]),
+ receive
+ {'EXIT', Pid01, process_not_registered} ->
+ ok
+ after 5000 ->
+ test_server:fail(gen_server_did_not_die)
+ end,
+
+ ?line {ok, Pid1} = start_link(spec_init_global, [{ok, my_server}, []]),
+ ?line ok = gen_server:call(Pid1, started_p),
+ ?line ok = gen_server:call(Pid1, stop),
+ receive
+ {'EXIT', Pid1, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(gen_server_did_not_die)
+ end,
+
+ ?line {ok, Pid11} =
+ start_link(spec_init_global, [{not_ok, my_server}, []]),
+
+ receive
+ {'EXIT', Pid11, process_not_registered_globally} ->
+ ok
+ after 5000 ->
+ test_server:fail(gen_server_did_not_die)
+ end,
+
+ ?line {ok, Pid2} = start_link(spec_init_anonymous, [[]]),
+ ?line ok = gen_server:call(Pid2, started_p),
+ ?line ok = gen_server:call(Pid2, stop),
+ receive
+ {'EXIT', Pid2, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(gen_server_did_not_die)
+ end,
+
+ ?line {ok, Pid3} = start_link(spec_init_anonymous_default_timeout, [[]]),
+ ?line ok = gen_server:call(Pid3, started_p),
+ ?line ok = gen_server:call(Pid3, stop),
+ receive
+ {'EXIT', Pid3, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(gen_server_did_not_die)
+ end,
+
+ ?line {ok, Pid4} =
+ start_link(spec_init_default_timeout, [{ok, my_server}, []]),
+ ?line ok = gen_server:call(Pid4, started_p),
+ ?line ok = gen_server:call(Pid4, stop),
+ receive
+ {'EXIT', Pid4, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(gen_server_did_not_die)
+ end,
+
+ ?line Pid5 =
+ erlang:spawn_link(?MODULE, spec_init_not_proc_lib, [[]]),
+ receive
+ {'EXIT', Pid5, process_was_not_started_by_proc_lib} ->
+ ok
+ after 5000 ->
+ test_server:fail(gen_server_did_not_die)
+ end,
+ process_flag(trap_exit, OldFlag),
+ ok.
+
+%%--------------------------------------------------------------
+spec_init_local_registered_parent(doc) ->
+ ["Test that terminate is run when the parent is a locally registered "
+ "process OTP-4820"];
+spec_init_local_registered_parent(suite) -> [];
+spec_init_local_registered_parent(Config) when is_list(Config) ->
+
+ register(foobar, self()),
+ process_flag(trap_exit, true),
+
+ ?line {ok, Pid} = start_link(spec_init_local, [{ok, my_server}, []]),
+
+ ?line ok = gen_server:cast(my_server, {self(),stop}),
+ ?line receive
+ {Pid, stopped} ->
+ ok
+ after 1000 ->
+ test_server:fail(stop)
+ end,
+ unregister(foobar),
+ ok.
+%%--------------------------------------------------------------
+spec_init_global_registered_parent(doc) ->
+ ["Test that terminate is run when the parent is a global registered "
+ "process OTP-4820"];
+spec_init_global_registered_parent(suite) -> [];
+spec_init_global_registered_parent(Config) when is_list(Config) ->
+
+ global:register_name(foobar, self()),
+ process_flag(trap_exit, true),
+
+ ?line {ok, Pid} = start_link(spec_init_global, [{ok, my_server}, []]),
+
+ ?line ok = gen_server:call(Pid, started_p),
+ ?line ok = gen_server:cast(Pid, {self(),stop}),
+
+ ?line receive
+ {Pid, stopped} ->
+ ok
+ after 1000 ->
+ test_server:fail(stop)
+ end,
+ global:unregister_name(foobar),
+ ok.
+%%--------------------------------------------------------------
+otp_5854(suite) ->
+ [];
+otp_5854(doc) ->
+ ["Test check for registered name in enter_loop/3,4,5"];
+otp_5854(Config) when is_list(Config) ->
+ OldFlag = process_flag(trap_exit, true),
+
+ %% Make sure gen_server:enter_loop does not accept {local,Name}
+ %% when it's another process than the calling one which is
+ %% registered under that name
+ register(armitage, self()),
+ ?line {ok, Pid1} =
+ start_link(spec_init_local, [{not_ok, armitage}, []]),
+ receive
+ {'EXIT', Pid1, process_not_registered} ->
+ ok
+ after 1000 ->
+ ?line test_server:fail(gen_server_started)
+ end,
+ unregister(armitage),
+
+ %% Make sure gen_server:enter_loop does not accept {global,Name}
+ %% when it's another process than the calling one which is
+ %% registered under that name
+ global:register_name(armitage, self()),
+ ?line {ok, Pid2} =
+ start_link(spec_init_global, [{not_ok, armitage}, []]),
+ receive
+ {'EXIT', Pid2, process_not_registered_globally} ->
+ ok
+ after 1000 ->
+ ?line test_server:fail(gen_server_started)
+ end,
+ global:unregister_name(armitage),
+
+ process_flag(trap_exit, OldFlag),
+ ok.
+
+%% If initialization fails (with ignore or {stop,Reason}),
+%% make sure that the process is not registered when gen_sever:start()
+%% returns.
+
+otp_7669(Config) when is_list(Config) ->
+ ?line ?t:do_times(100, fun do_otp_7669_local_ignore/0),
+ ?line ?t:do_times(100, fun do_otp_7669_global_ignore/0),
+ ?line ?t:do_times(10, fun do_otp_7669_stop/0),
+ ok.
+
+do_otp_7669_local_ignore() ->
+ %% The name should never be registered after the return
+ %% from gen_server:start/3.
+ ?line ignore = gen_server:start({local,?MODULE}, ?MODULE, ignore, []),
+ ?line undefined = whereis(?MODULE),
+ ?line ignore = gen_server:start({local,?MODULE}, ?MODULE, ignore, []),
+ ?line undefined = whereis(?MODULE),
+ ?line ignore = gen_server:start_link({local,?MODULE}, ?MODULE, ignore, []),
+ ?line undefined = whereis(?MODULE).
+
+do_otp_7669_global_ignore() ->
+ ?line ignore = gen_server:start({global,?MODULE}, ?MODULE, ignore, []),
+ ?line undefined = global:whereis_name(?MODULE),
+ ?line ignore = gen_server:start_link({global,?MODULE}, ?MODULE, ignore, []),
+ ?line undefined = global:whereis_name(?MODULE).
+
+do_otp_7669_stop() ->
+ %% The name should never be registered after the return
+ %% from gen_server:start/3.
+ ?line {error,stopped} = gen_server:start({local,?MODULE},
+ ?MODULE, stop, []),
+ ?line undefined = whereis(?MODULE),
+
+ ?line {error,stopped} = gen_server:start({global,?MODULE},
+ ?MODULE, stop, []),
+ ?line undefined = global:whereis_name(?MODULE).
+
+%%--------------------------------------------------------------
+%% Help functions to spec_init_*
+start_link(Init, Options) ->
+ proc_lib:start_link(?MODULE, Init, Options).
+
+spec_init_local({ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ register(Name, self()),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {}, {local, Name}, infinity);
+
+spec_init_local({not_ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {}, {local, Name}, infinity).
+
+spec_init_global({ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ global:register_name(Name, self()),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {}, {global, Name}, infinity);
+
+spec_init_global({not_ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {}, {global, Name}, infinity).
+
+spec_init_default_timeout({ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ register(Name, self()),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {}, {local, Name}).
+
+spec_init_anonymous(Options) ->
+ process_flag(trap_exit, true),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {}, infinity).
+
+spec_init_anonymous_default_timeout(Options) ->
+ process_flag(trap_exit, true),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {}).
+
+spec_init_not_proc_lib(Options) ->
+ gen_server:enter_loop(?MODULE, Options, {}, infinity).
+
+%%% --------------------------------------------------------
+%%% Here is the tested gen_server behaviour.
+%%% --------------------------------------------------------
+
+init([]) ->
+ {ok, []};
+init(ignore) ->
+ ignore;
+init(stop) ->
+ {stop, stopped};
+init(hibernate) ->
+ {ok,[],hibernate};
+init(sleep) ->
+ test_server:sleep(1000),
+ {ok, []};
+init({state,State}) ->
+ {ok, State}.
+
+handle_call(started_p, _From, State) ->
+ io:format("FROZ"),
+ {reply,ok,State};
+handle_call({delayed_answer, T}, From, _State) ->
+ {noreply,{reply_to,From},T};
+handle_call({call_within, T}, _From, _) ->
+ {reply,ok,call_within,T};
+handle_call(next_call, _From, call_within) ->
+ {reply,ok,[]};
+handle_call(next_call, _From, State) ->
+ {reply,false,State};
+handle_call(badreturn, _From, _State) ->
+ badreturn;
+handle_call(hibernate, _From, _State) ->
+ {reply,true,[],hibernate};
+handle_call({hibernate_noreply,Pid}, From, _State) ->
+ Pid ! go,
+ {noreply,From,hibernate};
+handle_call(stop, _From, State) ->
+ {stop,stopped,ok,State};
+handle_call(crash, _From, _State) ->
+ exit(crashed);
+handle_call(exit_shutdown, _From, _State) ->
+ exit(shutdown);
+handle_call(stop_shutdown, _From, State) ->
+ {stop,shutdown,State};
+handle_call(shutdown_reason, _From, _State) ->
+ exit({shutdown,reason});
+handle_call(stop_shutdown_reason, _From, State) ->
+ {stop,{shutdown,stop_reason},State}.
+
+handle_cast({From,handle_cast}, State) ->
+ From ! {self(), handled_cast},
+ {noreply, State};
+handle_cast({From,delayed_cast,T}, _State) ->
+ {noreply, {delayed_cast,From}, T};
+handle_cast(hibernate_now, _State) ->
+ {noreply, [], hibernate};
+handle_cast(hibernate_later, _State) ->
+ timer:send_after(1000,self(),hibernate_now),
+ {noreply, []};
+handle_cast({From, stop}, State) ->
+ io:format("BAZ"),
+ {stop, {From,stopped}, State}.
+
+handle_info(timeout, {reply_to, From}) ->
+ gen_server:reply(From, delayed),
+ {noreply, []};
+handle_info(timeout, hibernate_me) -> % Arrive here from
+ % handle_info(hibernate_later,...)
+ {noreply, [], hibernate};
+handle_info(hibernate_now, _State) -> % Arrive here from
+ % handle_cast({_,hibernate_later},...)
+ % and by direct ! from testcase
+ {noreply, [], hibernate};
+handle_info(hibernate_later, _State) ->
+ {noreply, hibernate_me, 1000};
+handle_info(timeout, call_within) ->
+ {noreply, []};
+handle_info(timeout, {delayed_cast, From}) ->
+ From ! {self(), delayed},
+ {noreply, []};
+handle_info(timeout, {delayed_info, From}) ->
+ From ! {self(), delayed_info},
+ {noreply, []};
+handle_info({From, handle_info}, _State) ->
+ From ! {self(), handled_info},
+ {noreply, []};
+handle_info({From, delayed_info, T}, _State) ->
+ {noreply, {delayed_info, From}, T};
+handle_info(continue, From) ->
+ gen_server:reply(From,true),
+ {noreply, []};
+handle_info({From, stop}, State) ->
+ {stop, {From,stopped_info}, State};
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate({From, stopped}, _State) ->
+ io:format("FOOBAR"),
+ From ! {self(), stopped},
+ ok;
+terminate({From, stopped_info}, _State) ->
+ From ! {self(), stopped_info},
+ ok;
+terminate(_Reason, _State) ->
+ ok.
+
+