diff options
author | Rickard Green <rickard@erlang.org> | 2021-11-23 22:05:04 +0100 |
---|---|---|
committer | Rickard Green <rickard@erlang.org> | 2022-03-20 18:10:00 +0100 |
commit | 4975e1ecccb64c480857dbd498f16279a8138321 (patch) | |
tree | eb1dd964255a866c29f55e740be45b96b61fcfef /lib/stdlib/test/gen_statem_SUITE.erl | |
parent | 59aa9c55b3c7eb7e646c98b8888af43b135e7664 (diff) | |
download | erlang-4975e1ecccb64c480857dbd498f16279a8138321.tar.gz |
Improve response handling for asynchronous 'gen' requests
receive_response/3, wait_response/3, and check_response/3 in 'gen_server',
'gen_statem', and 'gen_event' can now take a collection of request
identifiers as argument and handle any responses corresponding a request
identifier in the collection.
Diffstat (limited to 'lib/stdlib/test/gen_statem_SUITE.erl')
-rw-r--r-- | lib/stdlib/test/gen_statem_SUITE.erl | 309 |
1 files changed, 308 insertions, 1 deletions
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl index 9ddffcbb72..a34cb55e52 100644 --- a/lib/stdlib/test/gen_statem_SUITE.erl +++ b/lib/stdlib/test/gen_statem_SUITE.erl @@ -43,7 +43,9 @@ all() -> {group, sys}, hibernate, auto_hibernate, enter_loop, {group, undef_callbacks}, undef_in_terminate, {group, format_log}, - reply_by_alias_with_payload]. + reply_by_alias_with_payload, + send_request_receive_reqid_collection, send_request_wait_reqid_collection, + send_request_check_reqid_collection]. groups() -> [{start, [], tcs(start)}, @@ -2305,6 +2307,311 @@ reply_by_alias_with_payload(Config) when is_list(Config) -> ok end. +send_request_receive_reqid_collection(Config) when is_list(Config) -> + {ok,Pid1} = gen_statem:start(?MODULE, start_arg(Config, []), []), + {ok,Pid2} = gen_statem:start(?MODULE, start_arg(Config, []), []), + {ok,Pid3} = gen_statem:start(?MODULE, start_arg(Config, []), []), + send_request_receive_reqid_collection(Pid1, Pid2, Pid3), + send_request_receive_reqid_collection_timeout(Pid1, Pid2, Pid3), + send_request_receive_reqid_collection_error(Pid1, Pid2, Pid3), + stopped = gen_statem:call(Pid1, {stop,shutdown}), + stopped = gen_statem:call(Pid3, {stop,shutdown}), + check_stopped(Pid1), + check_stopped(Pid2), + check_stopped(Pid3), + ok. + +send_request_receive_reqid_collection(Pid1, Pid2, Pid3) -> + + ReqId0 = gen_statem:send_request(Pid1, 'alive?'), + + ReqIdC0 = gen_statem:reqids_new(), + + ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,400}), + ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0), + 1 = gen_statem:reqids_size(ReqIdC1), + + ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1), + 2 = gen_statem:reqids_size(ReqIdC2), + + ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2), + 3 = gen_statem:reqids_size(ReqIdC3), + + {{reply, delayed}, req2, ReqIdC4} = gen_statem:receive_response(ReqIdC3, infinity, true), + 2 = gen_statem:reqids_size(ReqIdC4), + + {{reply, delayed}, req3, ReqIdC5} = gen_statem:receive_response(ReqIdC4, 5678, true), + 1 = gen_statem:reqids_size(ReqIdC5), + + {{reply, delayed}, req1, ReqIdC6} = gen_statem:receive_response(ReqIdC5, 5000, true), + 0 = gen_statem:reqids_size(ReqIdC6), + + no_request = gen_statem:receive_response(ReqIdC6, 5000, true), + + {reply, yes} = gen_statem:receive_response(ReqId0, infinity), + + ok. + +send_request_receive_reqid_collection_timeout(Pid1, Pid2, Pid3) -> + + ReqId0 = gen_statem:send_request(Pid1, 'alive?'), + + ReqIdC0 = gen_statem:reqids_new(), + + ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,1000}), + ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0), + + ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1), + + ReqId3 = gen_statem:send_request(Pid3, {delayed_answer,500}), + ReqIdC3 = gen_statem:reqids_add(ReqId3, req3, ReqIdC2), + + Deadline = erlang:monotonic_time(millisecond) + 100, + + {{reply, delayed}, req2, ReqIdC4} = gen_statem:receive_response(ReqIdC3, {abs, Deadline}, true), + 2 = gen_statem:reqids_size(ReqIdC4), + + timeout = gen_statem:receive_response(ReqIdC4, {abs, Deadline}, true), + + Abandoned = lists:sort([{ReqId1, req1}, {ReqId3, req3}]), + Abandoned = lists:sort(gen_statem:reqids_to_list(ReqIdC4)), + + %% Make sure requests were abandoned... + timeout = gen_statem:receive_response(ReqIdC4, {abs, Deadline+1000}, true), + + {reply, yes} = gen_statem:receive_response(ReqId0, infinity), + + ok. + +send_request_receive_reqid_collection_error(Pid1, Pid2, Pid3) -> + + ReqId0 = gen_statem:send_request(Pid1, 'alive?'), + + ReqIdC0 = gen_statem:reqids_new(), + + ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,400}), + ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0), + try + nope = gen_statem:reqids_add(ReqId1, req2, ReqIdC1) + catch + error:badarg -> ok + end, + + unlink(Pid2), + exit(Pid2, kill), + ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1), + ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2), + 3 = gen_statem:reqids_size(ReqIdC3), + + {{error, {noproc, _}}, req2, ReqIdC4} = gen_statem:receive_response(ReqIdC3, 2000, true), + 2 = gen_statem:reqids_size(ReqIdC4), + + {{reply, delayed}, req3, ReqIdC4} = gen_statem:receive_response(ReqIdC4, infinity, false), + + {{reply, delayed}, req1, ReqIdC4} = gen_statem:receive_response(ReqIdC4, infinity, false), + + {reply, yes} = gen_statem:receive_response(ReqId0, infinity), + + ok. + +send_request_wait_reqid_collection(Config) when is_list(Config) -> + {ok,Pid1} = gen_statem:start(?MODULE, start_arg(Config, []), []), + {ok,Pid2} = gen_statem:start(?MODULE, start_arg(Config, []), []), + {ok,Pid3} = gen_statem:start(?MODULE, start_arg(Config, []), []), + send_request_wait_reqid_collection(Pid1, Pid2, Pid3), + send_request_wait_reqid_collection_timeout(Pid1, Pid2, Pid3), + send_request_wait_reqid_collection_error(Pid1, Pid2, Pid3), + stopped = gen_statem:call(Pid1, {stop,shutdown}), + stopped = gen_statem:call(Pid3, {stop,shutdown}), + check_stopped(Pid1), + check_stopped(Pid2), + check_stopped(Pid3), + ok. + +send_request_wait_reqid_collection(Pid1, Pid2, Pid3) -> + + ReqId0 = gen_statem:send_request(Pid1, 'alive?'), + + ReqIdC0 = gen_statem:reqids_new(), + + ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,400}), + ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0), + 1 = gen_statem:reqids_size(ReqIdC1), + + ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1), + 2 = gen_statem:reqids_size(ReqIdC2), + + ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2), + 3 = gen_statem:reqids_size(ReqIdC3), + + {{reply, delayed}, req2, ReqIdC4} = gen_statem:wait_response(ReqIdC3, infinity, true), + 2 = gen_statem:reqids_size(ReqIdC4), + + {{reply, delayed}, req3, ReqIdC5} = gen_statem:wait_response(ReqIdC4, 5678, true), + 1 = gen_statem:reqids_size(ReqIdC5), + + {{reply, delayed}, req1, ReqIdC6} = gen_statem:wait_response(ReqIdC5, 5000, true), + 0 = gen_statem:reqids_size(ReqIdC6), + + no_request = gen_statem:wait_response(ReqIdC6, 5000, true), + + {reply, yes} = gen_statem:receive_response(ReqId0, infinity), + + ok. + +send_request_wait_reqid_collection_timeout(Pid1, Pid2, Pid3) -> + + ReqId0 = gen_statem:send_request(Pid1, 'alive?'), + + ReqIdC0 = gen_statem:reqids_new(), + + ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,1000}), + ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0), + + ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1), + + ReqId3 = gen_statem:send_request(Pid3, {delayed_answer,500}), + ReqIdC3 = gen_statem:reqids_add(ReqId3, req3, ReqIdC2), + + Deadline = erlang:monotonic_time(millisecond) + 100, + + {{reply, delayed}, req2, ReqIdC4} = gen_statem:wait_response(ReqIdC3, {abs, Deadline}, true), + 2 = gen_statem:reqids_size(ReqIdC4), + + timeout = gen_statem:wait_response(ReqIdC4, {abs, Deadline}, true), + + Unhandled = lists:sort([{ReqId1, req1}, {ReqId3, req3}]), + Unhandled = lists:sort(gen_statem:reqids_to_list(ReqIdC4)), + + %% Make sure requests were not abandoned... + {{reply, delayed}, req3, ReqIdC4} = gen_statem:wait_response(ReqIdC4, {abs, Deadline+1500}, false), + {{reply, delayed}, req1, ReqIdC4} = gen_statem:wait_response(ReqIdC4, {abs, Deadline+1500}, false), + + {reply, yes} = gen_statem:receive_response(ReqId0), + + ok. + +send_request_wait_reqid_collection_error(Pid1, Pid2, Pid3) -> + + ReqId0 = gen_statem:send_request(Pid1, 'alive?'), + + ReqIdC0 = gen_statem:reqids_new(), + + ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,400}), + ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0), + try + nope = gen_statem:reqids_add(ReqId1, req2, ReqIdC1) + catch + error:badarg -> ok + end, + + unlink(Pid2), + exit(Pid2, kill), + ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1), + ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2), + 3 = gen_statem:reqids_size(ReqIdC3), + + {{error, {noproc, _}}, req2, ReqIdC4} = gen_statem:wait_response(ReqIdC3, 2000, true), + 2 = gen_statem:reqids_size(ReqIdC4), + + {{reply, delayed}, req3, ReqIdC4} = gen_statem:wait_response(ReqIdC4, infinity, false), + + {{reply, delayed}, req1, ReqIdC4} = gen_statem:wait_response(ReqIdC4, infinity, false), + + {reply, yes} = gen_statem:receive_response(ReqId0, infinity), + + ok. + +send_request_check_reqid_collection(Config) when is_list(Config) -> + {ok,Pid1} = gen_statem:start(?MODULE, start_arg(Config, []), []), + {ok,Pid2} = gen_statem:start(?MODULE, start_arg(Config, []), []), + {ok,Pid3} = gen_statem:start(?MODULE, start_arg(Config, []), []), + send_request_check_reqid_collection(Pid1, Pid2, Pid3), + send_request_check_reqid_collection_error(Pid1, Pid2, Pid3), + stopped = gen_statem:call(Pid1, {stop,shutdown}), + stopped = gen_statem:call(Pid3, {stop,shutdown}), + check_stopped(Pid1), + check_stopped(Pid2), + check_stopped(Pid3), + ok. + +send_request_check_reqid_collection(Pid1, Pid2, Pid3) -> + + ReqId0 = gen_statem:send_request(Pid1, 'alive?'), + + receive after 100 -> ok end, + + ReqIdC0 = gen_statem:reqids_new(), + + ReqIdC1 = gen_statem:send_request(Pid1, {delayed_answer,400}, req1, ReqIdC0), + 1 = gen_statem:reqids_size(ReqIdC1), + + ReqId2 = gen_statem:send_request(Pid2, {delayed_answer,1}), + ReqIdC2 = gen_statem:reqids_add(ReqId2, req2, ReqIdC1), + 2 = gen_statem:reqids_size(ReqIdC2), + + ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2), + 3 = gen_statem:reqids_size(ReqIdC3), + + Msg0 = next_msg(), + no_reply = gen_statem:check_response(Msg0, ReqIdC3, true), + + {{reply, delayed}, req2, ReqIdC4} = gen_statem:check_response(next_msg(), ReqIdC3, true), + 2 = gen_statem:reqids_size(ReqIdC4), + + {{reply, delayed}, req3, ReqIdC5} = gen_statem:check_response(next_msg(), ReqIdC4, true), + 1 = gen_statem:reqids_size(ReqIdC5), + + {{reply, delayed}, req1, ReqIdC6} = gen_statem:check_response(next_msg(), ReqIdC5, true), + 0 = gen_statem:reqids_size(ReqIdC6), + + no_request = gen_statem:check_response(Msg0, ReqIdC6, true), + + {reply, yes} = gen_statem:check_response(Msg0, ReqId0), + + ok. + +send_request_check_reqid_collection_error(Pid1, Pid2, Pid3) -> + + ReqId0 = gen_statem:send_request(Pid1, 'alive?'), + + receive after 100 -> ok end, + + ReqIdC0 = gen_statem:reqids_new(), + + ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,400}), + ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0), + try + nope = gen_statem:reqids_add(ReqId1, req2, ReqIdC1) + catch + error:badarg -> ok + end, + + unlink(Pid2), + exit(Pid2, kill), + ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1), + + ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2), + 3 = gen_statem:reqids_size(ReqIdC3), + + Msg0 = next_msg(), + + no_reply = gen_statem:check_response(Msg0, ReqIdC3, true), + + {{error, {noproc, _}}, req2, ReqIdC4} = gen_statem:check_response(next_msg(), ReqIdC3, true), + 2 = gen_statem:reqids_size(ReqIdC4), + + {{reply, delayed}, req3, ReqIdC4} = gen_statem:check_response(next_msg(), ReqIdC4, false), + + {{reply, delayed}, req1, ReqIdC4} = gen_statem:check_response(next_msg(), ReqIdC4, false), + + {reply, yes} = gen_statem:check_response(Msg0, ReqId0), + + ok. + +next_msg() -> + receive M -> M end. + %% %% Functionality check %% |