summaryrefslogtreecommitdiff
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.erl344
1 files changed, 339 insertions, 5 deletions
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index e29195e895..8015126b0d 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -26,7 +26,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([start/1, crash/1, call/1, cast/1, cast_fast/1,
+-export([start/1, crash/1, call/1, send_request/1, cast/1, cast_fast/1,
continue/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,
@@ -38,7 +38,9 @@
undef_handle_call/1, undef_handle_cast/1, undef_handle_info/1,
undef_init/1, undef_code_change/1, undef_terminate1/1,
undef_terminate2/1, undef_in_terminate/1, undef_in_handle_info/1,
- undef_handle_continue/1
+ undef_handle_continue/1,
+
+ format_log_1/1, format_log_2/1
]).
-export([stop1/1, stop2/1, stop3/1, stop4/1, stop5/1, stop6/1, stop7/1,
@@ -61,7 +63,7 @@ suite() ->
{timetrap,{minutes,1}}].
all() ->
- [start, {group,stop}, crash, call, cast, cast_fast, info, abcast,
+ [start, {group,stop}, crash, call, send_request, cast, cast_fast, info, abcast,
continue, multicall, multicall_down, call_remote1, call_remote2,
call_remote3, call_remote_n1, call_remote_n2,
call_remote_n3, spec_init,
@@ -71,7 +73,8 @@ all() ->
call_format_status, error_format_status, terminate_crash_format,
get_state, replace_state,
call_with_huge_message_queue, {group, undef_callbacks},
- undef_in_terminate, undef_in_handle_info].
+ undef_in_terminate, undef_in_handle_info,
+ format_log_1, format_log_2].
groups() ->
[{stop, [],
@@ -104,7 +107,8 @@ init_per_testcase(Case, Config) when Case == call_remote1;
Case == call_remote3;
Case == call_remote_n1;
Case == call_remote_n2;
- Case == call_remote_n3 ->
+ Case == call_remote_n3;
+ Case == send_request ->
{ok,N} = start_node(hubba),
[{node,N} | Config];
@@ -161,6 +165,18 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% anonymous monitored
+ {ok, {Pid1b, Mon1b}} =
+ gen_server:start_monitor(gen_server_SUITE, [], []),
+ ok = gen_server:call(Pid1b, started_p),
+ ok = gen_server:call(Pid1b, stop),
+ receive
+ {'DOWN', Mon1b, process, Pid1b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% local register
{ok, Pid2} =
gen_server:start({local, my_test_name},
@@ -191,6 +207,22 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% local register monitored
+ {ok, {Pid3b, Mon3b}} =
+ gen_server:start_monitor({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call(my_test_name, started_p),
+ {error, {already_started, Pid3b}} =
+ gen_server:start_monitor({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call(my_test_name, stop),
+ receive
+ {'DOWN', Mon3b, process, Pid3b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% global register
{ok, Pid4} =
gen_server:start({global, my_test_name},
@@ -219,6 +251,22 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% global register monitored
+ {ok, {Pid5b, Mon5b}} =
+ gen_server:start_monitor({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call({global, my_test_name}, started_p),
+ {error, {already_started, Pid5b}} =
+ gen_server:start_monitor({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call({global, my_test_name}, stop),
+ receive
+ {'DOWN', Mon5b, process, Pid5b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% via register
dummy_via:reset(),
{ok, Pid6} =
@@ -459,6 +507,90 @@ call(Config) when is_list(Config) ->
ok.
%% --------------------------------------
+%% Test gen_server:send_request.
+%% --------------------------------------
+
+send_request(Config) when is_list(Config) ->
+ OldFl = process_flag(trap_exit, true),
+
+ {ok, Pid} = gen_server:start_link({local, my_test_name},
+ gen_server_SUITE, [], []),
+
+ Async = fun(Process, Req) ->
+ try
+ Promise = gen_server:send_request(Process, Req),
+ gen_server:wait_response(Promise, infinity)
+ catch _:Reason:ST ->
+ {'did_exit', Reason, ST}
+ end
+ end,
+ {reply,ok} = Async(my_test_name, started_p),
+
+ {reply,delayed} = Async(Pid, {delayed_answer,1}),
+
+ %% two requests within a specified time.
+ Promise1 = gen_server:send_request(my_test_name, {call_within, 1000}),
+ Promise2 = gen_server:send_request(my_test_name, next_call),
+ {reply, ok} = gen_server:wait_response(Promise1, infinity),
+ {reply, ok} = gen_server:wait_response(Promise2, infinity),
+
+ Promise3 = gen_server:send_request(my_test_name, {call_within, 1000}),
+ no_reply = gen_server:check_response({foo, bar}, Promise3),
+ receive {{'$gen_request_id',Ref},_} = Msg when is_reference(Ref) ->
+ {reply, ok} = gen_server:check_response(Msg, Promise3)
+ after 1000 ->
+ %% Format not yet doumented so it might be ok
+ %% This test is just to make you aware that you have changed it
+ exit(message_format_changed)
+ end,
+ timer:sleep(1500),
+
+ {reply, false} = Async(my_test_name, next_call),
+
+ %% timeout
+ Promise5 = gen_server:send_request(my_test_name, {delayed_answer,50}),
+ timeout = gen_server:wait_response(Promise5, 0),
+ {reply, delayed} = gen_server:wait_response(Promise5, infinity),
+
+ %% bad return value in the gen_server loop from handle_call.
+ {error,{{bad_return_value, badreturn},_}} = Async(my_test_name, badreturn),
+
+ %% Test other error cases
+ {error, {noproc,_}} = Async(Pid, started_p),
+ {error, {noproc,_}} = Async(my_test_name, started_p),
+ {error, {noconnection, _}} = Async({my_test_name, foo@node}, started_p),
+
+ {error, {noproc,_}} = Async({global, non_existing}, started_p),
+ catch exit(whereis(dummy_via), foo),
+ {'EXIT', {badarg,_}} =
+ (catch gen_server:send_request({via, dummy_via, non_existing}, started_p)),
+
+ %% Remote nodes
+ Via = dummy_via:reset(),
+ Remote = proplists:get_value(node,Config),
+ {ok, RPid} = rpc:call(Remote, gen_server, start, [{global, remote}, ?MODULE, [], []]),
+ dummy_via:register_name(remote, RPid),
+ {reply, ok} = Async(RPid, started_p),
+ {reply, ok} = Async({global, remote}, started_p),
+ {reply, ok} = Async({via, dummy_via, remote}, started_p),
+ {error, {shutdown, _}} = Async({global, remote}, stop_shutdown),
+ {error, {noproc, _}} = Async({global, remote}, started_p),
+ {error, {noproc, _}} = Async({via, dummy_via, remote}, started_p),
+ {error, {noproc, _}} = Async({via, dummy_via, non_existing}, started_p),
+
+ {ok, _} = rpc:call(Remote, gen_server, start, [{local, remote}, ?MODULE, [], []]),
+ {reply, ok} = Async({remote, Remote}, started_p),
+ {error, {shutdown, _}} = Async({remote, Remote}, stop_shutdown),
+ {error, {noproc, _}} = Async({remote, Remote}, started_p),
+
+ %% Cleanup
+ catch exit(Via, foo2),
+ receive {'EXIT', Via, foo2} -> ok end,
+ process_flag(trap_exit, OldFl),
+ ok.
+
+
+%% --------------------------------------
%% Test handle_continue.
%% --------------------------------------
@@ -1528,6 +1660,208 @@ wait_until_processed(Pid, Message, N) ->
ok
end.
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel,error_logger_format_depth),
+ application:unset_env(kernel,error_logger_format_depth),
+ Term = lists:seq(1,15),
+ Name = self(),
+ Report = #{label=>{gen_server,terminate},
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ log=>[],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}},
+ {F1,A1} = gen_server:format_log(Report),
+ FExpected1 = "** Generic server ~tp terminating \n"
+ "** Last message in was ~tp~n"
+ "** When Server state == ~tp~n"
+ "** Reason for termination ==~n** ~tp~n"
+ "** Client ~tp stacktrace~n"
+ "** ~tp~n",
+ ct:log("F1: ~ts~nA1: ~tp",[F1,A1]),
+ FExpected1=F1,
+ [Name,Term,Term,Term,clientname,[]] = A1,
+
+ Warning = #{label=>{gen_server,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ {WF1,WA1} = gen_server:format_log(Warning),
+ WFExpected1 = "** Undefined handle_info in ~p~n"
+ "** Unhandled message: ~tp~n",
+ ct:log("WF1: ~ts~nWA1: ~tp",[WF1,WA1]),
+ WFExpected1=WF1,
+ [?MODULE,Term] = WA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel,error_logger_format_depth,Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = gen_server:format_log(#{label=>{gen_server,terminate},
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ log=>[],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}}),
+ FExpected2 = "** Generic server ~tP terminating \n"
+ "** Last message in was ~tP~n"
+ "** When Server state == ~tP~n"
+ "** Reason for termination ==~n** ~tP~n"
+ "** Client ~tP stacktrace~n"
+ "** ~tP~n",
+ ct:log("F2: ~ts~nA2: ~tp",[F2,A2]),
+ FExpected2=F2,
+ [Name,Depth,Limited,Depth,Limited,Depth,Limited,Depth,
+ clientname,Depth,[],Depth] = A2,
+
+ {WF2,WA2} = gen_server:format_log(Warning),
+ WFExpected2 = "** Undefined handle_info in ~p~n"
+ "** Unhandled message: ~tP~n",
+ ct:log("WF2: ~ts~nWA2: ~tp",[WF2,WA2]),
+ WFExpected2=WF2,
+ [?MODULE,Limited,Depth] = WA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel,error_logger_format_depth);
+ _ ->
+ application:set_env(kernel,error_logger_format_depth,FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1,15),
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Report = #{label=>{gen_server,terminate},
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ log=>[],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report,FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** Generic server "++NameStr++" terminating \n"
+ "** Last message in was ",
+ ct:log("Str1: ~ts",[Str1]),
+ ct:log("length(Str1): ~p",[L1]),
+ true = lists:prefix(Expected1,Str1),
+
+ Warning = #{label=>{gen_server,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ WStr1 = flatten_format_log(Warning,FormatOpts1),
+ WL1 = length(WStr1),
+ WExpected1 = "** Undefined handle_info in gen_server_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr1: ~ts",[WStr1]),
+ ct:log("length(WStr1): ~p",[WL1]),
+ true = lists:prefix(WExpected1,WStr1),
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report,FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** Generic server "++NameStr++" terminating \n"
+ "** Last message in was ",
+ ct:log("Str2: ~ts",[Str2]),
+ ct:log("length(Str2): ~p",[L2]),
+ true = lists:prefix(Expected2,Str2),
+ true = L2<L1,
+
+ WStr2 = flatten_format_log(Warning,FormatOpts2),
+ WL2 = length(WStr2),
+ WExpected2 = "** Undefined handle_info in gen_server_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr2: ~ts",[WStr2]),
+ ct:log("length(WStr2): ~p",[WL2]),
+ true = lists:prefix(WExpected2,WStr2),
+ true = WL2<WL1,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report,FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** Generic server "++NameStr++" terminating \n"
+ "** Last message in was ",
+ ct:log("Str3: ~ts",[Str3]),
+ ct:log("length(Str3): ~p",[L3]),
+ true = lists:prefix(Expected3,Str3),
+ true = L3<L1,
+
+ WFormatOpts3 = #{chars_limit=>80},
+ WStr3 = flatten_format_log(Warning,WFormatOpts3),
+ WL3 = length(WStr3),
+ WExpected3 = "** Undefined handle_info in gen_server_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr3: ~ts",[WStr3]),
+ ct:log("length(WStr3): ~p",[WL3]),
+ true = lists:prefix(WExpected3,WStr3),
+ true = WL3<WL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report,FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "Generic server "++NameStr++" terminating. Reason: ",
+ ct:log("Str4: ~ts",[Str4]),
+ ct:log("length(Str4): ~p",[L4]),
+ true = lists:prefix(Expected4,Str4),
+ true = L4<L1,
+
+ WStr4 = flatten_format_log(Warning,FormatOpts4),
+ WL4 = length(WStr4),
+ WExpected4 = "Undefined handle_info in gen_server_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr4: ~ts",[WStr4]),
+ ct:log("length(WStr4): ~p",[WL4]),
+ true = lists:prefix(WExpected4,WStr4),
+ true = WL4<WL1,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report,FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "Generic server "++NameStr++" terminating. Reason: ",
+ ct:log("Str5: ~ts",[Str5]),
+ ct:log("length(Str5): ~p",[L5]),
+ true = lists:prefix(Expected5,Str5),
+ true = L5<L4,
+
+ WStr5 = flatten_format_log(Warning,FormatOpts5),
+ WL5 = length(WStr5),
+ WExpected5 = "Undefined handle_info in gen_server_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr5: ~ts",[WStr5]),
+ ct:log("length(WStr5): ~p",[WL5]),
+ true = lists:prefix(WExpected5,WStr5),
+ true = WL5<WL4,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>200},
+ Str6 = flatten_format_log(Report,FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "Generic server "++NameStr++" terminating. Reason: ",
+ ct:log("Str6: ~ts",[Str6]),
+ ct:log("length(Str6): ~p",[L6]),
+ true = lists:prefix(Expected6,Str6),
+ true = L6<L4,
+
+ WFormatOpts6 = #{single_line=>true, chars_limit=>80},
+ WStr6 = flatten_format_log(Warning,WFormatOpts6),
+ WL6 = length(WStr6),
+ WExpected6 = "Undefined handle_info in gen_server_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr6: ~ts",[WStr6]),
+ ct:log("length(WStr6): ~p",[WL6]),
+ true = lists:prefix(WExpected6,WStr6),
+ true = WL6<WL4,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(gen_server:format_log(Report, Format)).
+
%%--------------------------------------------------------------
%% Help functions to spec_init_*
start_link(Init, Options) ->