1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2016-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(gen_statem_SUITE).
21
22-include_lib("common_test/include/ct.hrl").
23
24-compile([export_all, nowarn_export_all]).
25-behaviour(gen_statem).
26
27%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
28
29suite() ->
30    [{ct_hooks,[ts_install_cth]},
31     {timetrap,{minutes,1}}].
32
33all() ->
34    [{group, start},
35     {group, start_handle_event},
36     {group, stop},
37     {group, stop_handle_event},
38     {group, abnormal},
39     {group, abnormal_handle_event},
40     shutdown, stop_and_reply, state_enter, event_order,
41     state_timeout, event_types, generic_timers, code_change,
42     {group, sys},
43     hibernate, auto_hibernate, enter_loop, {group, undef_callbacks},
44     undef_in_terminate].
45
46groups() ->
47    [{start, [], tcs(start)},
48     {start_handle_event, [], tcs(start)},
49     {stop, [], tcs(stop)},
50     {stop_handle_event, [], tcs(stop)},
51     {abnormal, [], tcs(abnormal)},
52     {abnormal_handle_event, [], tcs(abnormal)},
53     {sys, [], tcs(sys)},
54     {sys_handle_event, [], tcs(sys)},
55     {undef_callbacks, [], tcs(undef_callbacks)}].
56
57tcs(start) ->
58    [start1, start2, start3, start4, start5, start6, start7,
59     start8, start9, start10, start11, start12, next_events];
60tcs(stop) ->
61    [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10];
62tcs(abnormal) ->
63    [abnormal1, abnormal1clean, abnormal1dirty,
64     abnormal2, abnormal3, abnormal4];
65tcs(sys) ->
66    [sys1, call_format_status,
67     error_format_status, terminate_crash_format,
68     get_state, replace_state];
69tcs(undef_callbacks) ->
70    [undef_code_change, undef_terminate1, undef_terminate2].
71
72init_per_suite(Config) ->
73    Config.
74
75end_per_suite(_Config) ->
76    ok.
77
78init_per_group(GroupName, Config)
79  when GroupName =:= start_handle_event;
80       GroupName =:= stop_handle_event;
81       GroupName =:= abnormal_handle_event;
82       GroupName =:= sys_handle_event ->
83    [{callback_mode,handle_event_function}|Config];
84init_per_group(undef_callbacks, Config) ->
85    DataDir = ?config(data_dir, Config),
86    StatemPath = filename:join(DataDir, "oc_statem.erl"),
87    {ok, oc_statem} = compile:file(StatemPath),
88    Config;
89init_per_group(_GroupName, Config) ->
90    Config.
91
92end_per_group(_GroupName, Config) ->
93    Config.
94
95init_per_testcase(_CaseName, Config) ->
96    flush(),
97%%%    dbg:tracer(),
98%%%    dbg:p(all, c),
99%%%    dbg:tpl(gen_statem, cx),
100%%%    dbg:tpl(proc_lib, cx),
101%%%    dbg:tpl(gen, cx),
102%%%    dbg:tpl(sys, cx),
103    Config.
104
105end_per_testcase(_CaseName, Config) ->
106%%%    dbg:stop(),
107    Config.
108
109%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
110-define(EXPECT_FAILURE(Code, Reason),
111	try begin Code end of
112	    Reason ->
113		ct:fail({unexpected,Reason})
114	catch
115	    error:Reason -> Reason;
116	    exit:Reason -> Reason
117	end).
118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119
120%% anonymous
121start1(Config) ->
122    %%OldFl = process_flag(trap_exit, true),
123
124    {ok,Pid0} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
125    ok = do_func_test(Pid0),
126    ok = do_sync_func_test(Pid0),
127    stop_it(Pid0),
128%%    stopped = gen_statem:call(Pid0, stop),
129%%    timeout =
130%%	?EXPECT_FAILURE(gen_statem:call(Pid0, hej), Reason),
131
132    %%process_flag(trap_exit, OldFl),
133    ok = verify_empty_msgq().
134
135%% anonymous w. shutdown
136start2(Config) ->
137    %% Dont link when shutdown
138    {ok,Pid0} = gen_statem:start(?MODULE, start_arg(Config, []), []),
139    ok = do_func_test(Pid0),
140    ok = do_sync_func_test(Pid0),
141    stopped = gen_statem:call(Pid0, {stop,shutdown}),
142    check_stopped(Pid0),
143    ok = verify_empty_msgq().
144
145%% anonymous with timeout
146start3(Config) ->
147    %%OldFl = process_flag(trap_exit, true),
148
149    {ok,Pid0} =
150	gen_statem:start(?MODULE, start_arg(Config, []), [{timeout,5}]),
151    ok = do_func_test(Pid0),
152    ok = do_sync_func_test(Pid0),
153    stop_it(Pid0),
154
155    {error,timeout} =
156	gen_statem:start(
157	  ?MODULE, start_arg(Config, sleep), [{timeout,5}]),
158
159    %%process_flag(trap_exit, OldFl),
160    ok = verify_empty_msgq().
161
162%% anonymous with ignore
163start4(Config) ->
164    OldFl = process_flag(trap_exit, true),
165
166    ignore = gen_statem:start(?MODULE, start_arg(Config, ignore), []),
167
168    process_flag(trap_exit, OldFl),
169    ok = verify_empty_msgq().
170
171%% anonymous with stop
172start5(Config) ->
173    OldFl = process_flag(trap_exit, true),
174
175    {error,stopped} = gen_statem:start(?MODULE, start_arg(Config, stop), []),
176
177    process_flag(trap_exit, OldFl),
178    ok = verify_empty_msgq().
179
180%% anonymous linked
181start6(Config) ->
182    {ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
183    ok = do_func_test(Pid),
184    ok = do_sync_func_test(Pid),
185    stop_it(Pid),
186
187    ok = verify_empty_msgq().
188
189%% global register linked
190start7(Config) ->
191    STM = {global,my_stm},
192
193    {ok,Pid} =
194	gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
195    {error,{already_started,Pid}} =
196	gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
197    {error,{already_started,Pid}} =
198	gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
199
200    ok = do_func_test(Pid),
201    ok = do_sync_func_test(Pid),
202    ok = do_func_test(STM),
203    ok = do_sync_func_test(STM),
204    stop_it(STM),
205
206    ok = verify_empty_msgq().
207
208
209%% local register
210start8(Config) ->
211    %%OldFl = process_flag(trap_exit, true),
212    Name = my_stm,
213    STM = {local,Name},
214
215    {ok,Pid} =
216	gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
217    {error,{already_started,Pid}} =
218	gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
219
220    ok = do_func_test(Pid),
221    ok = do_sync_func_test(Pid),
222    ok = do_func_test(Name),
223    ok = do_sync_func_test(Name),
224    stop_it(Pid),
225
226    %%process_flag(trap_exit, OldFl),
227    ok = verify_empty_msgq().
228
229%% local register linked
230start9(Config) ->
231    %%OldFl = process_flag(trap_exit, true),
232    Name = my_stm,
233    STM = {local,Name},
234
235    {ok,Pid} =
236	gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
237    {error,{already_started,Pid}} =
238	gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
239
240    ok = do_func_test(Pid),
241    ok = do_sync_func_test(Pid),
242    ok = do_func_test(Name),
243    ok = do_sync_func_test(Name),
244    stop_it(Pid),
245
246    %%process_flag(trap_exit, OldFl),
247    ok = verify_empty_msgq().
248
249%% global register
250start10(Config) ->
251    STM = {global,my_stm},
252
253    {ok,Pid} =
254	gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
255    {error,{already_started,Pid}} =
256	gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
257    {error,{already_started,Pid}} =
258	gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
259
260    ok = do_func_test(Pid),
261    ok = do_sync_func_test(Pid),
262    ok = do_func_test(STM),
263    ok = do_sync_func_test(STM),
264    stop_it(STM),
265
266    ok = verify_empty_msgq().
267
268%% Stop registered processes
269start11(Config) ->
270    Name = my_stm,
271    LocalSTM = {local,Name},
272    GlobalSTM = {global,Name},
273
274    {ok,Pid} =
275	gen_statem:start_link(LocalSTM, ?MODULE, start_arg(Config, []), []),
276    stop_it(Pid),
277
278    {ok,_Pid1} =
279	gen_statem:start_link(LocalSTM, ?MODULE, start_arg(Config, []), []),
280    stop_it(Name),
281
282    {ok,Pid2} =
283	gen_statem:start(GlobalSTM, ?MODULE, start_arg(Config, []), []),
284    stop_it(Pid2),
285    receive after 1 -> true end,
286    Result =
287	gen_statem:start(GlobalSTM, ?MODULE, start_arg(Config, []), []),
288    ct:log("Result = ~p~n",[Result]),
289    {ok,_Pid3} = Result,
290    stop_it(GlobalSTM),
291
292    ok = verify_empty_msgq().
293
294%% Via register linked
295start12(Config) ->
296    dummy_via:reset(),
297    VIA = {via,dummy_via,my_stm},
298
299    {ok,Pid} =
300	gen_statem:start_link(VIA, ?MODULE, start_arg(Config, []), []),
301    {error,{already_started,Pid}} =
302	gen_statem:start_link(VIA, ?MODULE, start_arg(Config, []), []),
303    {error,{already_started,Pid}} =
304	gen_statem:start(VIA, ?MODULE, start_arg(Config, []), []),
305
306    ok = do_func_test(Pid),
307    ok = do_sync_func_test(Pid),
308    ok = do_func_test(VIA),
309    ok = do_sync_func_test(VIA),
310    stop_it(VIA),
311
312    ok = verify_empty_msgq().
313
314
315%% Anonymous, reason 'normal'
316stop1(Config) ->
317    {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
318    ok = gen_statem:stop(Pid),
319    false = erlang:is_process_alive(Pid),
320    noproc =
321	?EXPECT_FAILURE(gen_statem:stop(Pid), Reason).
322
323%% Anonymous, other reason
324stop2(Config) ->
325    {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
326    ok = gen_statem:stop(Pid, other_reason, infinity),
327    false = erlang:is_process_alive(Pid),
328    ok.
329
330%% Anonymous, invalid timeout
331stop3(Config) ->
332    {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
333    _ =
334	?EXPECT_FAILURE(
335	   gen_statem:stop(Pid, other_reason, invalid_timeout),
336	   Reason),
337    true = erlang:is_process_alive(Pid),
338    ok = gen_statem:stop(Pid),
339    false = erlang:is_process_alive(Pid),
340    ok.
341
342%% Registered name
343stop4(Config) ->
344    {ok,Pid} =
345	gen_statem:start(
346	  {local,to_stop},?MODULE, start_arg(Config, []), []),
347    ok = gen_statem:stop(to_stop),
348    false = erlang:is_process_alive(Pid),
349    noproc =
350	?EXPECT_FAILURE(gen_statem:stop(to_stop), Reason),
351    ok.
352
353%% Registered name and local node
354stop5(Config) ->
355    Name = to_stop,
356    {ok,Pid} =
357	gen_statem:start(
358	  {local,Name},?MODULE, start_arg(Config, []), []),
359    ok = gen_statem:stop({Name,node()}),
360    false = erlang:is_process_alive(Pid),
361    noproc =
362	?EXPECT_FAILURE(gen_statem:stop({Name,node()}), Reason),
363    ok.
364
365%% Globally registered name
366stop6(Config) ->
367    STM = {global,to_stop},
368    {ok,Pid} = gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
369    ok = gen_statem:stop(STM),
370    false = erlang:is_process_alive(Pid),
371    noproc =
372	?EXPECT_FAILURE(gen_statem:stop(STM), Reason),
373    ok.
374
375%% 'via' registered name
376stop7(Config) ->
377    VIA = {via,dummy_via,to_stop},
378    dummy_via:reset(),
379    {ok,Pid} = gen_statem:start(VIA, ?MODULE, start_arg(Config, []), []),
380    ok = gen_statem:stop(VIA),
381    false = erlang:is_process_alive(Pid),
382    noproc =
383	?EXPECT_FAILURE(gen_statem:stop(VIA), Reason),
384    ok.
385
386%% Anonymous on remote node
387stop8(Config) ->
388    Node = gen_statem_stop8,
389    {ok,NodeName} = ct_slave:start(Node),
390    Dir = filename:dirname(code:which(?MODULE)),
391    rpc:call(NodeName, code, add_path, [Dir]),
392    {ok,Pid} =
393	rpc:call(
394	  NodeName, gen_statem,start,
395	  [?MODULE,start_arg(Config, []),[]]),
396    ok = gen_statem:stop(Pid),
397    false = rpc:call(NodeName, erlang, is_process_alive, [Pid]),
398    noproc =
399	?EXPECT_FAILURE(gen_statem:stop(Pid), Reason1),
400    {ok,NodeName} = ct_slave:stop(Node),
401    {{nodedown,NodeName},{sys,terminate,_}} =
402	?EXPECT_FAILURE(gen_statem:stop(Pid), Reason2),
403    ok.
404
405%% Registered name on remote node
406stop9(Config) ->
407    Name = to_stop,
408    LocalSTM = {local,Name},
409    Node = gen_statem__stop9,
410    {ok,NodeName} = ct_slave:start(Node),
411    STM = {Name,NodeName},
412    Dir = filename:dirname(code:which(?MODULE)),
413    rpc:call(NodeName, code, add_path, [Dir]),
414    {ok,Pid} =
415	rpc:call(
416	  NodeName, gen_statem, start,
417	  [LocalSTM,?MODULE,start_arg(Config, []),[]]),
418    ok = gen_statem:stop(STM),
419    undefined = rpc:call(NodeName,erlang,whereis,[Name]),
420    false = rpc:call(NodeName,erlang,is_process_alive,[Pid]),
421    noproc =
422	?EXPECT_FAILURE(gen_statem:stop(STM), Reason1),
423    {ok,NodeName} = ct_slave:stop(Node),
424    {{nodedown,NodeName},{sys,terminate,_}} =
425	?EXPECT_FAILURE(gen_statem:stop(STM), Reason2),
426    ok.
427
428%% Globally registered name on remote node
429stop10(Config) ->
430    Node = gen_statem_stop10,
431    STM = {global,to_stop},
432    {ok,NodeName} = ct_slave:start(Node),
433    Dir = filename:dirname(code:which(?MODULE)),
434    rpc:call(NodeName,code,add_path,[Dir]),
435    {ok,Pid} =
436	rpc:call(
437	  NodeName, gen_statem, start,
438	  [STM,?MODULE,start_arg(Config, []),[]]),
439    global:sync(),
440    ok = gen_statem:stop(STM),
441    false = rpc:call(NodeName, erlang, is_process_alive, [Pid]),
442    noproc =
443	?EXPECT_FAILURE(gen_statem:stop(STM), Reason1),
444    {ok,NodeName} = ct_slave:stop(Node),
445    noproc =
446	?EXPECT_FAILURE(gen_statem:stop(STM), Reason2),
447    ok.
448
449%% Check that time outs in calls work
450abnormal1(Config) ->
451    Name = abnormal1,
452    LocalSTM = {local,Name},
453
454    {ok, _Pid} =
455	gen_statem:start(LocalSTM, ?MODULE, start_arg(Config, []), []),
456
457    %% timeout call.
458    delayed = gen_statem:call(Name, {delayed_answer,1}, 100),
459    {timeout,_} =
460	?EXPECT_FAILURE(
461	   gen_statem:call(Name, {delayed_answer,1000}, 10),
462	   Reason),
463    ok = gen_statem:stop(Name),
464    ?t:sleep(1100),
465    ok = verify_empty_msgq().
466
467%% Check that time outs in calls work
468abnormal1clean(Config) ->
469    Name = abnormal1clean,
470    LocalSTM = {local,Name},
471
472    {ok, _Pid} =
473	gen_statem:start(LocalSTM, ?MODULE, start_arg(Config, []), []),
474
475    %% timeout call.
476    delayed =
477	gen_statem:call(Name, {delayed_answer,1}, {clean_timeout,100}),
478    {timeout,_} =
479	?EXPECT_FAILURE(
480	   gen_statem:call(
481	     Name, {delayed_answer,1000}, {clean_timeout,10}),
482	   Reason),
483    ok = gen_statem:stop(Name),
484    ?t:sleep(1100),
485    ok = verify_empty_msgq().
486
487%% Check that time outs in calls work
488abnormal1dirty(Config) ->
489    Name = abnormal1dirty,
490    LocalSTM = {local,Name},
491
492    {ok, _Pid} =
493	gen_statem:start(LocalSTM, ?MODULE, start_arg(Config, []), []),
494
495    %% timeout call.
496    delayed =
497	gen_statem:call(Name, {delayed_answer,1}, {dirty_timeout,100}),
498    {timeout,_} =
499	?EXPECT_FAILURE(
500	   gen_statem:call(
501	     Name, {delayed_answer,1000}, {dirty_timeout,10}),
502	   Reason),
503    ok = gen_statem:stop(Name),
504    ?t:sleep(1100),
505    case flush() of
506	[{Ref,delayed}] when is_reference(Ref) ->
507	    ok
508    end.
509
510%% Check that bad return values makes the stm crash. Note that we must
511%% trap exit since we must link to get the real bad_return_ error
512abnormal2(Config) ->
513    OldFl = process_flag(trap_exit, true),
514    {ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
515
516    %% bad return value in the gen_statem loop
517    {{{bad_return_from_state_function,badreturn},_},_} =
518	?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),
519    receive
520	{'EXIT',Pid,{{bad_return_from_state_function,badreturn},_}} -> ok
521    after 5000 ->
522	    ct:fail(gen_statem_did_not_die)
523    end,
524
525    process_flag(trap_exit, OldFl),
526    ok = verify_empty_msgq().
527
528%% Check that bad return actions makes the stm crash. Note that we must
529%% trap exit since we must link to get the real bad_return_ error
530abnormal3(Config) ->
531    OldFl = process_flag(trap_exit, true),
532    {ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
533
534    %% bad return value in the gen_statem loop
535    {{{bad_action_from_state_function,badaction},_},_} =
536	?EXPECT_FAILURE(gen_statem:call(Pid, badaction), Reason),
537    receive
538	{'EXIT',Pid,{{bad_action_from_state_function,badaction},_}} -> ok
539    after 5000 ->
540	    ct:fail(gen_statem_did_not_die)
541    end,
542
543    process_flag(trap_exit, OldFl),
544    ok = verify_empty_msgq().
545
546%% Check that bad timeout actions makes the stm crash. Note that we must
547%% trap exit since we must link to get the real bad_return_ error
548abnormal4(Config) ->
549    OldFl = process_flag(trap_exit, true),
550    {ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
551
552    %% bad return value in the gen_statem loop
553    BadTimeout = {badtimeout,4711,ouch},
554    {{{bad_action_from_state_function,BadTimeout},_},_} =
555	?EXPECT_FAILURE(gen_statem:call(Pid, BadTimeout), Reason),
556    receive
557	{'EXIT',Pid,{{bad_action_from_state_function,BadTimeout},_}} -> ok
558    after 5000 ->
559	    ct:fail(gen_statem_did_not_die)
560    end,
561
562    process_flag(trap_exit, OldFl),
563    ok = verify_empty_msgq().
564
565shutdown(Config) ->
566    process_flag(trap_exit, true),
567
568    {ok,Pid0} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
569    ok = do_func_test(Pid0),
570    ok = do_sync_func_test(Pid0),
571    stopped = gen_statem:call(Pid0, {stop,{shutdown,reason}}),
572    receive {'EXIT',Pid0,{shutdown,reason}} -> ok end,
573    process_flag(trap_exit, false),
574
575    {noproc,_} =
576	?EXPECT_FAILURE(gen_statem:call(Pid0, hej), Reason),
577
578    receive
579	Any ->
580	    ct:log("Unexpected: ~p", [Any]),
581	    ct:fail({unexpected,Any})
582    after 500 ->
583	    ok
584    end.
585
586
587
588stop_and_reply(_Config) ->
589    process_flag(trap_exit, true),
590
591    Machine =
592	%% Abusing the internal format of From...
593	#{init =>
594	      fun () ->
595		      {ok,start,undefined}
596	      end,
597	  start =>
598	      fun (cast, {echo,From1,Reply1}, undefined) ->
599		      {next_state,wait,{reply,From1,Reply1}}
600	      end,
601	  wait =>
602	      fun (cast, {stop_and_reply,Reason,From2,Reply2},R1) ->
603		      {stop_and_reply,Reason,
604		       [R1,{reply,From2,Reply2}]}
605	      end},
606    {ok,STM} =
607	gen_statem:start_link(?MODULE, {map_statem,Machine,[]}, []),
608
609    Self = self(),
610    Tag1 = make_ref(),
611    gen_statem:cast(STM, {echo,{Self,Tag1},reply1}),
612    Tag2 = make_ref(),
613    gen_statem:cast(STM, {stop_and_reply,reason,{Self,Tag2},reply2}),
614    case flush() of
615	[{Tag1,reply1},{Tag2,reply2},{'EXIT',STM,reason}] ->
616	    ok;
617	Other1 ->
618	    ct:fail({unexpected,Other1})
619    end,
620
621    {noproc,_} =
622	?EXPECT_FAILURE(gen_statem:call(STM, hej), Reason),
623    case flush() of
624	[] ->
625	    ok;
626	Other2 ->
627	    ct:fail({unexpected,Other2})
628    end.
629
630
631
632state_enter(_Config) ->
633    process_flag(trap_exit, true),
634    Self = self(),
635
636    Machine =
637	%% Abusing the internal format of From...
638	#{init =>
639	      fun () ->
640		      {ok,start,1}
641	      end,
642	  start =>
643	      fun (enter, Prev, N) ->
644		      Self ! {enter,start,Prev,N},
645		      {keep_state,N + 1};
646		  (internal, Prev, N) ->
647		      Self ! {internal,start,Prev,N},
648		      {keep_state,N + 1};
649		  ({call,From}, repeat, N) ->
650		      {repeat_state,N + 1,
651		       [{reply,From,{repeat,start,N}}]};
652		  ({call,From}, echo, N) ->
653		      {next_state,wait,N + 1,
654		       {reply,From,{echo,start,N}}};
655		  ({call,From}, {stop,Reason}, N) ->
656		      {stop_and_reply,Reason,
657		       [{reply,From,{stop,N}}],N + 1}
658	      end,
659	  wait =>
660	      fun (enter, Prev, N) when N < 5 ->
661		      {repeat_state,N + 1,
662		       {reply,{Self,N},{enter,Prev}}};
663		  (enter, Prev, N) ->
664		      Self ! {enter,wait,Prev,N},
665		      {keep_state,N + 1};
666		  ({call,From}, repeat, N) ->
667		      {repeat_state_and_data,
668		       [{reply,From,{repeat,wait,N}}]};
669		  ({call,From}, echo, N) ->
670		      {next_state,start,N + 1,
671		       [{next_event,internal,wait},
672			{reply,From,{echo,wait,N}}]}
673	      end},
674    {ok,STM} =
675	gen_statem:start_link(
676	  ?MODULE, {map_statem,Machine,[state_enter]}, []),
677
678    [{enter,start,start,1}] = flush(),
679    {echo,start,2} = gen_statem:call(STM, echo),
680    [{3,{enter,start}},{4,{enter,start}},{enter,wait,start,5}] = flush(),
681    {wait,[6|_]} = sys:get_state(STM),
682    {repeat,wait,6} = gen_statem:call(STM, repeat),
683    [{enter,wait,wait,6}] = flush(),
684    {echo,wait,7} = gen_statem:call(STM, echo),
685    [{enter,start,wait,8},{internal,start,wait,9}] = flush(),
686    {repeat,start,10} = gen_statem:call(STM, repeat),
687    [{enter,start,start,11}] = flush(),
688    {stop,12} = gen_statem:call(STM, {stop,bye}),
689    [{'EXIT',STM,bye}] = flush(),
690
691    {noproc,_} =
692	?EXPECT_FAILURE(gen_statem:call(STM, hej), Reason),
693    case flush() of
694	[] ->
695	    ok;
696	Other2 ->
697	    ct:fail({unexpected,Other2})
698    end.
699
700
701
702event_order(_Config) ->
703    process_flag(trap_exit, true),
704
705    Machine =
706	%% Abusing the internal format of From...
707	#{init =>
708	      fun () ->
709		      {ok,start,undefined}
710	      end,
711	  start =>
712	      fun (cast, _, _) ->
713		      {keep_state_and_data,postpone}; %% Handled in 'buffer'
714		  ({call,From}, {buffer,Pid,[Tag3,Tag4,Tag5]},
715		   undefined) ->
716		      {next_state,buffer,[],
717		       [{next_event,internal,{reply,{Pid,Tag3},ok3}},
718			{next_event,internal,{reply,{Pid,Tag4},ok4}},
719			{timeout,0,{reply,{Pid,Tag5},ok5}},
720			%% The timeout should not happen since there
721			%% are events that cancel it i.e next_event
722			%% and postponed
723			{reply,From,ok}]}
724	      end,
725	  buffer =>
726	      fun (internal, Reply, Replies) ->
727		      {keep_state,[Reply|Replies]};
728		  (timeout, Reply, Replies) ->
729		      {keep_state,[Reply|Replies]};
730		  (cast, Reply, Replies) ->
731		      {keep_state,[Reply|Replies]};
732		  ({call,From}, {stop,Reason}, Replies) ->
733		      {next_state,stop,undefined,
734		       lists:reverse(
735			 Replies,
736			 [{reply,From,ok},
737			  {next_event,internal,{stop,Reason}}])}
738	      end,
739	  stop =>
740	      fun (internal, Result, undefined) ->
741		      Result
742	      end},
743
744    {ok,STM} = gen_statem:start_link(?MODULE, {map_statem,Machine,[]}, []),
745    Self = self(),
746    Tag1 = make_ref(),
747    gen_statem:cast(STM, {reply,{Self,Tag1},ok1}),
748    Tag2 = make_ref(),
749    gen_statem:cast(STM, {reply,{Self,Tag2},ok2}),
750    Tag3 = make_ref(),
751    Tag4 = make_ref(),
752    Tag5 = make_ref(),
753    ok = gen_statem:call(STM, {buffer,Self,[Tag3,Tag4,Tag5]}),
754    ok = gen_statem:call(STM, {stop,reason}),
755    case flush() of
756	[{Tag3,ok3},{Tag4,ok4},{Tag1,ok1},{Tag2,ok2},
757	 {'EXIT',STM,reason}] ->
758	    ok;
759	Other1 ->
760	    ct:fail({unexpected,Other1})
761    end,
762
763    {noproc,_} =
764	?EXPECT_FAILURE(gen_statem:call(STM, hej), Reason),
765    case flush() of
766	[] ->
767	    ok;
768	Other2 ->
769	    ct:fail({unexpected,Other2})
770    end.
771
772
773
774state_timeout(_Config) ->
775    process_flag(trap_exit, true),
776
777    Machine =
778	#{init =>
779	      fun () ->
780		      {ok,start,0}
781	      end,
782	  start =>
783	      fun
784		  ({call,From}, {go,Time}, 0)  ->
785		      self() ! message_to_self,
786		      {next_state, state1, {Time,From},
787		       %% Verify that internal events goes before external
788		       [{state_timeout,Time,1},
789			{next_event,internal,1}]}
790	      end,
791	  state1 =>
792	      fun
793		  (internal, 1, Data) ->
794		      %% Verify that a state change cancels timeout 1
795		      {next_state, state2, Data,
796		       [{timeout,0,2},
797			{state_timeout,0,2},
798			{next_event,internal,2}]}
799	      end,
800	  state2 =>
801	      fun
802		  (internal, 2, Data) ->
803		      %% Verify that {state_timeout,0,_}
804		      %% comes after next_event and that
805		      %% {timeout,0,_} is cancelled by
806		      %% pending {state_timeout,0,_}
807		      {keep_state, {ok,2,Data},
808		       [{timeout,0,3}]};
809		  (state_timeout, 2, {ok,2,Data}) ->
810		      %% Verify that timeout 0's are processed
811		      %% in order
812		      {keep_state, {ok,3,Data},
813		       [{timeout,0,4},{state_timeout,0,5}]};
814		  (timeout, 4, {ok,3,Data}) ->
815		      %% Verify that timeout 0 is cancelled by
816		      %% enqueued state_timeout 0 and that
817		      %% multiple state_timeout 0 can be enqueued
818		      {keep_state, {ok,4,Data},
819		       [{state_timeout,0,6},{timeout,0,7}]};
820		  (state_timeout, 5, {ok,4,Data}) ->
821		      {keep_state, {ok,5,Data}};
822		  (state_timeout, 6, {ok,5,{Time,From}}) ->
823		      {next_state, state3, 6,
824		       [{reply,From,ok},
825			{state_timeout,Time,8}]}
826	      end,
827	  state3 =>
828	      fun
829		  (info, message_to_self, 6) ->
830		      {keep_state, 7};
831		  ({call,From}, check, 7) ->
832		      {keep_state, From};
833		  (state_timeout, 8, From) ->
834		      {stop_and_reply, normal,
835		       {reply,From,ok}}
836	      end},
837
838    {ok,STM} = gen_statem:start_link(?MODULE, {map_statem,Machine,[]}, []),
839    sys:trace(STM, true),
840    TRef = erlang:start_timer(1000, self(), kull),
841    ok = gen_statem:call(STM, {go,500}),
842    ok = gen_statem:call(STM, check),
843    receive
844	{timeout,TRef,kull} ->
845	    ct:fail(late_timeout)
846    after 0 ->
847	    receive
848		{timeout,TRef,kull} ->
849		    ok
850	    after 1000 ->
851		    ct:fail(no_check_timeout)
852	    end
853    end,
854    receive
855	{'EXIT',STM,normal} ->
856	    ok
857    after 500 ->
858	    ct:fail(did_not_stop)
859    end,
860
861    verify_empty_msgq().
862
863
864
865%% Test that all event types can be sent with {next_event,EventType,_}
866event_types(_Config) ->
867    process_flag(trap_exit, true),
868
869    Machine =
870	%% Abusing the internal format of From...
871	#{init =>
872	      fun () ->
873		      {ok, start1, undefined,
874		       [{next_event,internal,0}]}
875	      end,
876	  start1 =>
877	      fun (internal, 0, undefined) ->
878		      {next_state, start2, undefined}
879	      end,
880	  start2 =>
881	      fun ({call,_} = Call, Req, undefined) ->
882		      {next_state, state1, undefined,
883		       [{next_event,internal,1},
884			{next_event,state_timeout,2},
885			{next_event,timeout,3},
886			{next_event,info,4},
887			{next_event,cast,5},
888			{next_event,{timeout,6}, 6},
889			{next_event,Call,Req}]}
890	      end,
891	  state1 =>
892	      fun (internal, 1, undefined) ->
893		      {next_state, state2, undefined}
894	      end,
895	  state2 =>
896	      fun (state_timeout, 2, undefined) ->
897		      {next_state, state3, undefined}
898	      end,
899	  state3 =>
900	      fun (timeout, 3, undefined) ->
901		      {next_state, state4, undefined}
902	      end,
903	  state4 =>
904	      fun (info, 4, undefined) ->
905		      {next_state, state5, undefined}
906	      end,
907	  state5 =>
908	      fun (cast, 5, undefined) ->
909		      {next_state, state6, undefined}
910	      end,
911	  state6 =>
912	      fun ({timeout,6}, 6, undefined) ->
913		      {next_state, state7, undefined}
914	      end,
915	  state7 =>
916	      fun ({call,From}, stop, undefined) ->
917		      {stop_and_reply, shutdown,
918		       [{reply,From,stopped}]}
919	      end},
920    {ok,STM} =
921	gen_statem:start_link(
922	  ?MODULE, {map_statem,Machine,[]}, [{debug,[trace]}]),
923
924    stopped = gen_statem:call(STM, stop),
925    receive
926	{'EXIT',STM,shutdown} ->
927	    ok
928    after 500 ->
929	    ct:fail(did_not_stop)
930    end,
931
932    {noproc,_} =
933	?EXPECT_FAILURE(gen_statem:call(STM, hej), Reason),
934    case flush() of
935	[] ->
936	    ok;
937	Other2 ->
938	    ct:fail({unexpected,Other2})
939    end.
940
941
942
943generic_timers(_Config) ->
944    process_flag(trap_exit, true),
945
946    Machine =
947	%% Abusing the internal format of From...
948	#{init =>
949	      fun () ->
950		      {ok, start, undefined}
951	      end,
952	  start =>
953	      fun ({call,_} = Call, Req, undefined) ->
954		      {next_state, state1, undefined,
955		       [{{timeout,a},1500,1},
956			{state_timeout,1500,1},
957			{{timeout,b},1000,1},
958			{next_event,Call,Req}]}
959	      end,
960	  state1 =>
961	      fun ({call,_} = Call, Req, undefined) ->
962		      T = erlang:monotonic_time(millisecond) + 500,
963		      {next_state, state2, undefined,
964		       [{{timeout,c},T,2,{abs,true}},
965			{{timeout,d},0,2,[{abs,false}]},
966			{timeout,0,2},
967			{{timeout,b},infinity,2},
968			{{timeout,a},1000,{Call,Req}}]}
969	      end,
970	  state2 =>
971	      fun ({timeout,d}, 2, undefined) ->
972		      {next_state, state3, undefined}
973	      end,
974	  state3 =>
975	      fun ({timeout,c}, 2, undefined) ->
976		      {next_state, state4, undefined}
977	      end,
978	  state4 =>
979	      fun ({timeout,a}, {{call,From},stop}, undefined) ->
980		      {stop_and_reply, shutdown,
981		       [{reply,From,stopped}]}
982	      end},
983    {ok,STM} =
984	gen_statem:start_link(
985	  ?MODULE, {map_statem,Machine,[]}, [{debug,[trace]}]),
986
987    stopped = gen_statem:call(STM, stop),
988    receive
989	{'EXIT',STM,shutdown} ->
990	    ok
991    after 500 ->
992	    ct:fail(did_not_stop)
993    end,
994
995    {noproc,_} =
996	?EXPECT_FAILURE(gen_statem:call(STM, hej), Reason),
997    case flush() of
998	[] ->
999	    ok;
1000	Other2 ->
1001	    ct:fail({unexpected,Other2})
1002    end.
1003
1004
1005
1006sys1(Config) ->
1007    {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
1008    {status, Pid, {module,gen_statem}, _} = sys:get_status(Pid),
1009    sys:suspend(Pid),
1010    Parent = self(),
1011    Tag = make_ref(),
1012    Caller =
1013	spawn(
1014	  fun () ->
1015		  Parent ! {Tag,gen_statem:call(Pid, hej)}
1016	  end),
1017    receive
1018	{Tag,_} ->
1019	    ct:fail(should_be_suspended)
1020    after 3000 ->
1021	    exit(Caller, ok)
1022    end,
1023
1024    %% {timeout,_} =
1025    %% 	?EXPECT_FAILURE(gen_statem:call(Pid, hej), Reason),
1026    sys:resume(Pid),
1027    stop_it(Pid).
1028
1029code_change(_Config) ->
1030    {ok,Pid} =
1031	gen_statem:start(
1032	  ?MODULE, {callback_mode,state_functions,[]}, []),
1033    {idle,data} = sys:get_state(Pid),
1034    sys:suspend(Pid),
1035    Mode = handle_event_function,
1036    sys:change_code(Pid, ?MODULE, old_vsn, Mode),
1037    sys:resume(Pid),
1038    {idle,{old_vsn,data,Mode}} = sys:get_state(Pid),
1039    Mode = gen_statem:call(Pid, get_callback_mode),
1040    stop_it(Pid).
1041
1042call_format_status(Config) ->
1043    {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
1044    Status = sys:get_status(Pid),
1045    {status,Pid,_Mod,[_PDict,running,_,_, Data]} = Status,
1046    [format_status_called|_] = lists:reverse(Data),
1047    stop_it(Pid),
1048
1049    %% check that format_status can handle a name being an atom (pid is
1050    %% already checked by the previous test)
1051    {ok, Pid2} =
1052	gen_statem:start(
1053	  {local, gstm}, ?MODULE, start_arg(Config, []), []),
1054    Status2 = sys:get_status(gstm),
1055    {status,Pid2,_Mod,[_PDict2,running,_,_,Data2]} = Status2,
1056    [format_status_called|_] = lists:reverse(Data2),
1057    stop_it(Pid2),
1058
1059    %% check that format_status can handle a name being a term other than a
1060    %% pid or atom
1061    GlobalName1 = {global,"CallFormatStatus"},
1062    {ok,Pid3} =
1063	gen_statem:start(
1064	  GlobalName1, ?MODULE, start_arg(Config, []), []),
1065    Status3 = sys:get_status(GlobalName1),
1066    {status,Pid3,_Mod,[_PDict3,running,_,_,Data3]} = Status3,
1067    [format_status_called|_] = lists:reverse(Data3),
1068    stop_it(Pid3),
1069    GlobalName2 = {global,{name, "term"}},
1070    {ok,Pid4} =
1071	gen_statem:start(
1072	  GlobalName2, ?MODULE, start_arg(Config, []), []),
1073    Status4 = sys:get_status(GlobalName2),
1074    {status,Pid4,_Mod,[_PDict4,running,_,_, Data4]} = Status4,
1075    [format_status_called|_] = lists:reverse(Data4),
1076    stop_it(Pid4),
1077
1078    %% check that format_status can handle a name being a term other than a
1079    %% pid or atom
1080    dummy_via:reset(),
1081    ViaName1 = {via,dummy_via,"CallFormatStatus"},
1082    {ok,Pid5} = gen_statem:start(ViaName1, ?MODULE, start_arg(Config, []), []),
1083    Status5 = sys:get_status(ViaName1),
1084    {status,Pid5,_Mod, [_PDict5,running,_,_, Data5]} = Status5,
1085    [format_status_called|_] = lists:reverse(Data5),
1086    stop_it(Pid5),
1087    ViaName2 = {via,dummy_via,{name,"term"}},
1088    {ok, Pid6} =
1089	gen_statem:start(
1090	  ViaName2, ?MODULE, start_arg(Config, []), []),
1091    Status6 = sys:get_status(ViaName2),
1092    {status,Pid6,_Mod,[_PDict6,running,_,_,Data6]} = Status6,
1093    [format_status_called|_] = lists:reverse(Data6),
1094    stop_it(Pid6).
1095
1096
1097
1098error_format_status(Config) ->
1099    error_logger_forwarder:register(),
1100    OldFl = process_flag(trap_exit, true),
1101    Data = "called format_status",
1102    {ok,Pid} =
1103	gen_statem:start(
1104	  ?MODULE, start_arg(Config, {data,Data}), []),
1105    %% bad return value in the gen_statem loop
1106    {{{bad_return_from_state_function,badreturn},_},_} =
1107	?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),
1108    receive
1109	{error,_,
1110	 {Pid,
1111	  "** State machine"++_,
1112	  [Pid,{{call,_},badreturn},
1113	   {formatted,idle,Data},
1114	   error,{bad_return_from_state_function,badreturn}|_]}} ->
1115	    ok;
1116	Other when is_tuple(Other), element(1, Other) =:= error ->
1117	    error_logger_forwarder:unregister(),
1118	    ct:fail({unexpected,Other})
1119    after 1000 ->
1120	    error_logger_forwarder:unregister(),
1121	    ct:fail(timeout)
1122    end,
1123    process_flag(trap_exit, OldFl),
1124    error_logger_forwarder:unregister(),
1125    receive
1126	%% Comes with SASL
1127	{error_report,_,{Pid,crash_report,_}} ->
1128	    ok
1129    after 500 ->
1130	    ok
1131    end,
1132    ok = verify_empty_msgq().
1133
1134terminate_crash_format(Config) ->
1135    error_logger_forwarder:register(),
1136    OldFl = process_flag(trap_exit, true),
1137    Data = crash_terminate,
1138    {ok,Pid} =
1139	gen_statem:start(
1140	  ?MODULE, start_arg(Config, {data,Data}), []),
1141    stop_it(Pid),
1142    Self = self(),
1143    receive
1144	{error,_GroupLeader,
1145	 {Pid,
1146	  "** State machine"++_,
1147	  [Pid,
1148	   {{call,{Self,_}},stop},
1149	   {formatted,idle,Data},
1150	   exit,{crash,terminate}|_]}} ->
1151	    ok;
1152	Other when is_tuple(Other), element(1, Other) =:= error ->
1153	    error_logger_forwarder:unregister(),
1154	    ct:fail({unexpected,Other})
1155    after 1000 ->
1156	    error_logger_forwarder:unregister(),
1157	    ct:fail(timeout)
1158    end,
1159    process_flag(trap_exit, OldFl),
1160    error_logger_forwarder:unregister(),
1161    receive
1162	%% Comes with SASL
1163	{error_report,_,{Pid,crash_report,_}} ->
1164	    ok
1165    after 500 ->
1166	    ok
1167    end,
1168    ok = verify_empty_msgq().
1169
1170
1171get_state(Config) ->
1172    State = self(),
1173    {ok,Pid} =
1174	gen_statem:start(
1175	  ?MODULE, start_arg(Config, {data,State}), []),
1176    {idle,State} = sys:get_state(Pid),
1177    {idle,State} = sys:get_state(Pid, 5000),
1178    stop_it(Pid),
1179
1180    %% check that get_state can handle a name being an atom (pid is
1181    %% already checked by the previous test)
1182    {ok,Pid2} =
1183	gen_statem:start(
1184	  {local,gstm}, ?MODULE, start_arg(Config, {data,State}), []),
1185    {idle,State} = sys:get_state(gstm),
1186    {idle,State} = sys:get_state(gstm, 5000),
1187    stop_it(Pid2),
1188
1189    %% check that get_state works when pid is sys suspended
1190    {ok,Pid3} =
1191	gen_statem:start(
1192	  ?MODULE, start_arg(Config, {data,State}), []),
1193    {idle,State} = sys:get_state(Pid3),
1194    ok = sys:suspend(Pid3),
1195    {idle,State} = sys:get_state(Pid3, 5000),
1196    ok = sys:resume(Pid3),
1197    stop_it(Pid3),
1198    ok = verify_empty_msgq().
1199
1200replace_state(Config) ->
1201    State = self(),
1202    {ok, Pid} =
1203	gen_statem:start(
1204	  ?MODULE, start_arg(Config, {data,State}), []),
1205    {idle,State} = sys:get_state(Pid),
1206    NState1 = "replaced",
1207    Replace1 = fun({StateName, _}) -> {StateName,NState1} end,
1208    {idle,NState1} = sys:replace_state(Pid, Replace1),
1209    {idle,NState1} = sys:get_state(Pid),
1210    NState2 = "replaced again",
1211    Replace2 = fun({idle, _}) -> {state0,NState2} end,
1212    {state0,NState2} = sys:replace_state(Pid, Replace2, 5000),
1213    {state0,NState2} = sys:get_state(Pid),
1214    %% verify no change in state if replace function crashes
1215    Replace3 = fun(_) -> error(fail) end,
1216    {callback_failed,
1217     {gen_statem,system_replace_state},{error,fail}} =
1218	?EXPECT_FAILURE(sys:replace_state(Pid, Replace3), Reason),
1219    {state0, NState2} = sys:get_state(Pid),
1220    %% verify state replaced if process sys suspended
1221    ok = sys:suspend(Pid),
1222    Suffix2 = " and again",
1223    NState3 = NState2 ++ Suffix2,
1224    Replace4 = fun({StateName, _}) -> {StateName, NState3} end,
1225    {state0,NState3} = sys:replace_state(Pid, Replace4),
1226    ok = sys:resume(Pid),
1227    {state0,NState3} = sys:get_state(Pid, 5000),
1228    stop_it(Pid),
1229    ok = verify_empty_msgq().
1230
1231%% Hibernation
1232hibernate(Config) ->
1233    OldFl = process_flag(trap_exit, true),
1234
1235    {ok,Pid0} =
1236	gen_statem:start_link(
1237	  ?MODULE, start_arg(Config, hiber_now), []),
1238    is_in_erlang_hibernate(Pid0),
1239    stop_it(Pid0),
1240    receive
1241	{'EXIT',Pid0,normal} -> ok
1242    after 5000 ->
1243	    ct:fail(gen_statem_did_not_die)
1244    end,
1245
1246    {ok,Pid} =
1247	gen_statem:start_link(?MODULE, start_arg(Config, hiber), []),
1248    true = ({current_function,{erlang,hibernate,3}} =/=
1249		erlang:process_info(Pid,current_function)),
1250    hibernating = gen_statem:call(Pid, hibernate_sync),
1251    is_in_erlang_hibernate(Pid),
1252    good_morning = gen_statem:call(Pid, wakeup_sync),
1253    is_not_in_erlang_hibernate(Pid),
1254    hibernating = gen_statem:call(Pid, hibernate_sync),
1255    is_in_erlang_hibernate(Pid),
1256    please_just_five_more = gen_statem:call(Pid, snooze_sync),
1257    is_in_erlang_hibernate(Pid),
1258    good_morning = gen_statem:call(Pid, wakeup_sync),
1259    is_not_in_erlang_hibernate(Pid),
1260    ok = gen_statem:cast(Pid, hibernate_async),
1261    is_in_erlang_hibernate(Pid),
1262    ok = gen_statem:cast(Pid, wakeup_async),
1263    is_not_in_erlang_hibernate(Pid),
1264    ok = gen_statem:cast(Pid, hibernate_async),
1265    is_in_erlang_hibernate(Pid),
1266    ok = gen_statem:cast(Pid, snooze_async),
1267    is_in_erlang_hibernate(Pid),
1268    ok = gen_statem:cast(Pid, wakeup_async),
1269    is_not_in_erlang_hibernate(Pid),
1270
1271    Pid ! hibernate_later,
1272    true =
1273	({current_function,{erlang,hibernate,3}} =/=
1274	     erlang:process_info(Pid, current_function)),
1275    is_in_erlang_hibernate(Pid),
1276
1277    'alive!' = gen_statem:call(Pid, 'alive?'),
1278    true =
1279	({current_function,{erlang,hibernate,3}} =/=
1280	     erlang:process_info(Pid, current_function)),
1281    Pid ! hibernate_now,
1282    is_in_erlang_hibernate(Pid),
1283
1284    'alive!' = gen_statem:call(Pid, 'alive?'),
1285    true =
1286	({current_function,{erlang,hibernate,3}} =/=
1287	     erlang:process_info(Pid, current_function)),
1288
1289    hibernating = gen_statem:call(Pid, hibernate_sync),
1290    is_in_erlang_hibernate(Pid),
1291    good_morning = gen_statem:call(Pid, wakeup_sync),
1292    is_not_in_erlang_hibernate(Pid),
1293    hibernating = gen_statem:call(Pid, hibernate_sync),
1294    is_in_erlang_hibernate(Pid),
1295    please_just_five_more = gen_statem:call(Pid, snooze_sync),
1296    is_in_erlang_hibernate(Pid),
1297    good_morning = gen_statem:call(Pid, wakeup_sync),
1298    is_not_in_erlang_hibernate(Pid),
1299    ok = gen_statem:cast(Pid, hibernate_async),
1300    is_in_erlang_hibernate(Pid),
1301    ok  = gen_statem:cast(Pid, wakeup_async),
1302    is_not_in_erlang_hibernate(Pid),
1303    ok = gen_statem:cast(Pid, hibernate_async),
1304    is_in_erlang_hibernate(Pid),
1305    ok = gen_statem:cast(Pid, snooze_async),
1306    is_in_erlang_hibernate(Pid),
1307    ok = gen_statem:cast(Pid, wakeup_async),
1308    is_not_in_erlang_hibernate(Pid),
1309
1310    hibernating = gen_statem:call(Pid, hibernate_sync),
1311    is_in_erlang_hibernate(Pid),
1312    sys:suspend(Pid),
1313    is_in_erlang_hibernate(Pid),
1314    sys:resume(Pid),
1315    is_in_erlang_hibernate(Pid),
1316    receive after 1000 -> ok end,
1317    is_in_erlang_hibernate(Pid),
1318
1319    good_morning  = gen_statem:call(Pid, wakeup_sync),
1320    is_not_in_erlang_hibernate(Pid),
1321    stop_it(Pid),
1322    process_flag(trap_exit, OldFl),
1323    receive
1324	{'EXIT',Pid,normal} -> ok
1325    after 5000 ->
1326	    ct:fail(gen_statem_did_not_die)
1327    end,
1328    ok = verify_empty_msgq().
1329
1330%% Auto-hibernation timeout
1331auto_hibernate(Config) ->
1332    OldFl = process_flag(trap_exit, true),
1333    HibernateAfterTimeout = 100,
1334
1335    {ok,Pid} =
1336        gen_statem:start_link(
1337            ?MODULE, start_arg(Config, []), [{hibernate_after, HibernateAfterTimeout}]),
1338    %% After init test
1339    is_not_in_erlang_hibernate(Pid),
1340    timer:sleep(HibernateAfterTimeout),
1341    is_in_erlang_hibernate(Pid),
1342    %% After info test
1343    Pid ! {hping, self()},
1344    receive
1345        {Pid, hpong} ->
1346            ok
1347    after 1000 ->
1348        ct:fail(info)
1349    end,
1350    is_not_in_erlang_hibernate(Pid),
1351    timer:sleep(HibernateAfterTimeout),
1352    is_in_erlang_hibernate(Pid),
1353    %% After cast test
1354    ok = gen_statem:cast(Pid, {hping, self()}),
1355    receive
1356        {Pid, hpong} ->
1357            ok
1358    after 1000 ->
1359        ct:fail(cast)
1360    end,
1361    is_not_in_erlang_hibernate(Pid),
1362    timer:sleep(HibernateAfterTimeout),
1363    is_in_erlang_hibernate(Pid),
1364    %% After call test
1365    hpong = gen_statem:call(Pid, hping),
1366    is_not_in_erlang_hibernate(Pid),
1367    timer:sleep(HibernateAfterTimeout),
1368    is_in_erlang_hibernate(Pid),
1369    %% Timer test 1
1370    TimerTimeout1 = 50,
1371    ok = gen_statem:call(Pid, {arm_htimer, self(), TimerTimeout1}),
1372    is_not_in_erlang_hibernate(Pid),
1373    timer:sleep(TimerTimeout1),
1374    is_not_in_erlang_hibernate(Pid),
1375    receive
1376        {Pid, htimer_armed} ->
1377            ok
1378    after 1000 ->
1379        ct:fail(timer1)
1380    end,
1381    is_not_in_erlang_hibernate(Pid),
1382    timer:sleep(HibernateAfterTimeout),
1383    is_in_erlang_hibernate(Pid),
1384    %% Timer test 2
1385    TimerTimeout2 = 150,
1386    ok = gen_statem:call(Pid, {arm_htimer, self(), TimerTimeout2}),
1387    is_not_in_erlang_hibernate(Pid),
1388    timer:sleep(HibernateAfterTimeout),
1389    is_in_erlang_hibernate(Pid),
1390    receive
1391        {Pid, htimer_armed} ->
1392            ok
1393    after 1000 ->
1394        ct:fail(timer2)
1395    end,
1396    is_not_in_erlang_hibernate(Pid),
1397    timer:sleep(HibernateAfterTimeout),
1398    is_in_erlang_hibernate(Pid),
1399    stop_it(Pid),
1400    process_flag(trap_exit, OldFl),
1401    receive
1402        {'EXIT',Pid,normal} -> ok
1403    after 5000 ->
1404        ct:fail(gen_statem_did_not_die)
1405    end,
1406    ok = verify_empty_msgq().
1407
1408is_in_erlang_hibernate(Pid) ->
1409    receive after 1 -> ok end,
1410    is_in_erlang_hibernate_1(200, Pid).
1411
1412is_in_erlang_hibernate_1(0, Pid) ->
1413    ct:log("~p\n", [erlang:process_info(Pid, current_function)]),
1414    ct:fail(not_in_erlang_hibernate_3);
1415is_in_erlang_hibernate_1(N, Pid) ->
1416    {current_function,MFA} = erlang:process_info(Pid, current_function),
1417    case MFA of
1418	{erlang,hibernate,3} ->
1419	    ok;
1420	_ ->
1421	    receive after 10 -> ok end,
1422	    is_in_erlang_hibernate_1(N-1, Pid)
1423    end.
1424
1425is_not_in_erlang_hibernate(Pid) ->
1426    receive after 1 -> ok end,
1427    is_not_in_erlang_hibernate_1(200, Pid).
1428
1429is_not_in_erlang_hibernate_1(0, Pid) ->
1430    ct:log("~p\n", [erlang:process_info(Pid, current_function)]),
1431    ct:fail(not_in_erlang_hibernate_3);
1432is_not_in_erlang_hibernate_1(N, Pid) ->
1433    {current_function,MFA} = erlang:process_info(Pid, current_function),
1434    case MFA of
1435	{erlang,hibernate,3} ->
1436	    receive after 10 -> ok end,
1437	    is_not_in_erlang_hibernate_1(N-1, Pid);
1438	_ ->
1439	    ok
1440    end.
1441
1442
1443enter_loop(_Config) ->
1444    OldFlag = process_flag(trap_exit, true),
1445
1446    dummy_via:reset(),
1447
1448    %% Locally registered process + {local,Name}
1449    {ok,Pid1a} =
1450	proc_lib:start_link(?MODULE, enter_loop, [local,local]),
1451    yes = gen_statem:call(Pid1a, 'alive?'),
1452    stopped = gen_statem:call(Pid1a, stop),
1453    receive
1454	{'EXIT',Pid1a,normal} ->
1455	    ok
1456    after 5000 ->
1457	    ct:fail(gen_statem_did_not_die)
1458    end,
1459
1460    %% Unregistered process + {local,Name}
1461    {ok,Pid1b} =
1462	proc_lib:start_link(?MODULE, enter_loop, [anon,local]),
1463    receive
1464	{'EXIT',Pid1b,process_not_registered} ->
1465	    ok
1466    after 5000 ->
1467	    ct:fail(gen_statem_did_not_die)
1468    end,
1469
1470    %% Globally registered process + {global,Name}
1471    {ok,Pid2a} =
1472	proc_lib:start_link(?MODULE, enter_loop, [global,global]),
1473    yes = gen_statem:call(Pid2a, 'alive?'),
1474    stopped = gen_statem:call(Pid2a, stop),
1475    receive
1476	{'EXIT',Pid2a,normal} ->
1477	    ok
1478    after 5000 ->
1479	    ct:fail(gen_statem_did_not_die)
1480    end,
1481
1482    %% Unregistered process + {global,Name}
1483    {ok,Pid2b} =
1484	proc_lib:start_link(?MODULE, enter_loop, [anon,global]),
1485    receive
1486	{'EXIT',Pid2b,process_not_registered_globally} ->
1487	    ok
1488    after 5000 ->
1489	    ct:fail(gen_statem_did_not_die)
1490    end,
1491
1492    %% Unregistered process + no name
1493    {ok,Pid3} =
1494	proc_lib:start_link(?MODULE, enter_loop, [anon,anon]),
1495    yes = gen_statem:call(Pid3, 'alive?'),
1496    stopped = gen_statem:call(Pid3, stop),
1497    receive
1498	{'EXIT',Pid3,normal} ->
1499	    ok
1500    after 5000 ->
1501	    ct:fail(gen_statem_did_not_die)
1502    end,
1503
1504    %% Process not started using proc_lib
1505    Pid4 = spawn_link(gen_statem, enter_loop, [?MODULE,[],state0,[]]),
1506    receive
1507	{'EXIT',Pid4,process_was_not_started_by_proc_lib} ->
1508	    ok
1509    after 5000 ->
1510	    ct:fail(gen_statem_did_not_die)
1511    end,
1512
1513    %% Make sure I am the parent, ie that ordering a shutdown will
1514    %% result in the process terminating with Reason==shutdown
1515    {ok,Pid5} =
1516	proc_lib:start_link(?MODULE, enter_loop, [anon,anon]),
1517    yes = gen_statem:call(Pid5, 'alive?'),
1518    exit(Pid5, shutdown),
1519    receive
1520	{'EXIT',Pid5,shutdown} ->
1521	    ok
1522    after 5000 ->
1523	    ct:fail(gen_statem_did_not_die)
1524    end,
1525
1526    %% Make sure gen_statem:enter_loop does not accept {local,Name}
1527    %% when it's another process than the calling one which is
1528    %% registered under that name
1529    register(armitage, self()),
1530    {ok,Pid6a} =
1531	proc_lib:start_link(?MODULE, enter_loop, [anon,local]),
1532    receive
1533	{'EXIT',Pid6a,process_not_registered} ->
1534	    ok
1535    after 1000 ->
1536	    ct:fail(gen_statem_started)
1537    end,
1538    unregister(armitage),
1539
1540    %% Make sure gen_statem:enter_loop does not accept {global,Name}
1541    %% when it's another process than the calling one which is
1542    %% registered under that name
1543    global:register_name(armitage, self()),
1544    {ok,Pid6b} =
1545	proc_lib:start_link(?MODULE, enter_loop, [anon,global]),
1546    receive
1547	{'EXIT',Pid6b,process_not_registered_globally} ->
1548	    ok
1549    after 1000 ->
1550	    ct:fail(gen_statem_started)
1551    end,
1552    global:unregister_name(armitage),
1553
1554    dummy_via:register_name(armitage, self()),
1555    {ok,Pid6c} =
1556	proc_lib:start_link(?MODULE, enter_loop, [anon,via]),
1557    receive
1558	{'EXIT',Pid6c,{process_not_registered_via,dummy_via}} ->
1559	    ok
1560    after 1000 ->
1561	    ct:fail(
1562	      {gen_statem_started,
1563	       process_info(self(), messages)})
1564    end,
1565    dummy_via:unregister_name(armitage),
1566
1567    process_flag(trap_exit, OldFlag),
1568    ok = verify_empty_msgq().
1569
1570enter_loop(Reg1, Reg2) ->
1571    process_flag(trap_exit, true),
1572    case Reg1 of
1573	local -> register(armitage, self());
1574	global -> global:register_name(armitage, self());
1575	via -> dummy_via:register_name(armitage, self());
1576	anon -> ignore
1577    end,
1578    proc_lib:init_ack({ok, self()}),
1579    case Reg2 of
1580	local ->
1581	    gen_statem:enter_loop(
1582	      ?MODULE, [], state0, [], {local,armitage});
1583	global ->
1584	    gen_statem:enter_loop(
1585	      ?MODULE, [], state0, [], {global,armitage});
1586	via ->
1587	    gen_statem:enter_loop(
1588	      ?MODULE, [], state0, [], {via, dummy_via, armitage});
1589	anon ->
1590	    gen_statem:enter_loop(?MODULE, [], state0, [])
1591    end.
1592
1593undef_code_change(_Config) ->
1594    {ok, Statem} = gen_statem:start(oc_statem, [], []),
1595    {error, {'EXIT',
1596             {undef, [{oc_statem, code_change, [_, _, _, _], _}|_]}}}
1597        = fake_upgrade(Statem, oc_statem).
1598
1599fake_upgrade(Pid, Mod) ->
1600    sys:suspend(Pid),
1601    sys:replace_state(Pid, fun(State) -> {new, State} end),
1602    Ret = sys:change_code(Pid, Mod, old_vsn, []),
1603    ok = sys:resume(Pid),
1604    Ret.
1605
1606undef_terminate1(_Config) ->
1607    {ok, Statem} = gen_statem:start(oc_statem, [], []),
1608    MRef = monitor(process, Statem),
1609    ok = gen_statem:stop(Statem),
1610    verify_down(Statem, MRef, normal),
1611    ok.
1612
1613undef_terminate2(_Config) ->
1614    Reason = {error, test},
1615    {ok, Statem} = oc_statem:start(),
1616    MRef = monitor(process, Statem),
1617    ok = gen_statem:stop(Statem, Reason, infinity),
1618    verify_down(Statem, MRef, Reason).
1619
1620undef_in_terminate(_Config) ->
1621    Data =  {undef_in_terminate, {?MODULE, terminate}},
1622    {ok, Statem} = gen_statem:start(?MODULE, {data, Data}, []),
1623    try
1624        gen_statem:stop(Statem),
1625        ct:fail(should_crash)
1626    catch
1627        exit:{undef, [{?MODULE, terminate, _, _}|_]} ->
1628            ok
1629    end.
1630
1631verify_down(Statem, MRef, Reason) ->
1632    receive
1633        {'DOWN', MRef, process, Statem, Reason} ->
1634            ok
1635    after 5000 ->
1636        ct:fail(default_terminate_failed)
1637    end.
1638
1639%% Test the order for multiple {next_event,T,C}
1640next_events(Config) ->
1641    {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
1642    ok = gen_statem:cast(Pid, next_event),
1643    {state,next_events,[]} = gen_statem:call(Pid, get),
1644    ok = gen_statem:stop(Pid),
1645    false = erlang:is_process_alive(Pid),
1646    noproc =
1647	?EXPECT_FAILURE(gen_statem:stop(Pid), Reason).
1648
1649
1650%%
1651%% Functionality check
1652%%
1653
1654wfor(Msg) ->
1655    receive
1656	Msg -> ok
1657    after 5000 ->
1658	    error(timeout)
1659    end.
1660
1661
1662stop_it(STM) ->
1663    stopped = gen_statem:call(STM, stop),
1664    check_stopped(STM).
1665
1666
1667check_stopped(STM) ->
1668    Call = there_you_are,
1669    {_,{gen_statem,call,[_,Call,infinity]}} =
1670	?EXPECT_FAILURE(gen_statem:call(STM, Call), Reason),
1671    ok.
1672
1673
1674do_func_test(STM) ->
1675    ok = gen_statem:cast(STM, {'alive?',self()}),
1676    wfor(yes),
1677    ok = do_connect(STM),
1678    ok = gen_statem:cast(STM, {'alive?',self()}),
1679    wfor(yes),
1680    ?t:do_times(3, ?MODULE, do_msg, [STM]),
1681    ok = gen_statem:cast(STM, {'alive?',self()}),
1682    wfor(yes),
1683    ok = do_disconnect(STM),
1684    ok = gen_statem:cast(STM, {'alive?',self()}),
1685    wfor(yes),
1686    ok.
1687
1688
1689do_connect(STM) ->
1690    check_state(STM, idle),
1691    gen_statem:cast(STM, {connect,self()}),
1692    wfor(accept),
1693    check_state(STM, wfor_conf),
1694    Tag = make_ref(),
1695    gen_statem:cast(STM, {ping,self(),Tag}),
1696    gen_statem:cast(STM, confirm),
1697    wfor({pong,Tag}),
1698    check_state(STM, connected),
1699    ok.
1700
1701do_msg(STM) ->
1702    check_state(STM, connected),
1703    R = make_ref(),
1704    ok = gen_statem:cast(STM, {msg,self(),R}),
1705    wfor({ack,R}).
1706
1707
1708do_disconnect(STM) ->
1709    ok = gen_statem:cast(STM, disconnect),
1710    check_state(STM, idle).
1711
1712check_state(STM, State) ->
1713    case gen_statem:call(STM, get) of
1714	{state, State, _} -> ok
1715    end.
1716
1717do_sync_func_test(STM) ->
1718    yes = gen_statem:call(STM, 'alive?'),
1719    ok = do_sync_connect(STM),
1720    yes = gen_statem:call(STM, 'alive?'),
1721    ?t:do_times(3, ?MODULE, do_sync_msg, [STM]),
1722    yes = gen_statem:call(STM, 'alive?'),
1723    ok = do_sync_disconnect(STM),
1724    yes = gen_statem:call(STM, 'alive?'),
1725    check_state(STM, idle),
1726    ok = gen_statem:call(STM, {timeout,200}),
1727    yes = gen_statem:call(STM, 'alive?'),
1728    check_state(STM, idle),
1729    ok.
1730
1731
1732do_sync_connect(STM) ->
1733    check_state(STM, idle),
1734    accept = gen_statem:call(STM, connect),
1735    check_state(STM, wfor_conf),
1736    Tag = make_ref(),
1737    gen_statem:cast(STM, {ping,self(),Tag}),
1738    yes = gen_statem:call(STM, confirm),
1739    wfor({pong,Tag}),
1740    check_state(STM, connected),
1741    ok.
1742
1743do_sync_msg(STM) ->
1744    check_state(STM, connected),
1745    R = make_ref(),
1746    {ack,R} = gen_statem:call(STM, {msg,R}),
1747    ok.
1748
1749do_sync_disconnect(STM) ->
1750    yes = gen_statem:call(STM, disconnect),
1751    check_state(STM, idle).
1752
1753
1754verify_empty_msgq() ->
1755    [] = flush(),
1756    ok.
1757
1758start_arg(Config, Arg) ->
1759    case lists:keyfind(callback_mode, 1, Config) of
1760	{_,CallbackMode} ->
1761	    {callback_mode,CallbackMode,Arg};
1762	false ->
1763	    Arg
1764    end.
1765
1766%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1767%%
1768%% The State Machine
1769%%
1770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1771
1772init(ignore) ->
1773    ignore;
1774init(stop) ->
1775    {stop,stopped};
1776init(stop_shutdown) ->
1777    {stop,shutdown};
1778init(sleep) ->
1779    ?t:sleep(1000),
1780    {ok,idle,data};
1781init(hiber) ->
1782    {ok,hiber_idle,[]};
1783init(hiber_now) ->
1784    {ok,hiber_idle,[],[hibernate]};
1785init({data, Data}) ->
1786    {ok,idle,Data};
1787init({callback_mode,CallbackMode,Arg}) ->
1788    ets:new(?MODULE, [named_table,private]),
1789    ets:insert(?MODULE, {callback_mode,CallbackMode}),
1790    init(Arg);
1791init({map_statem,#{init := Init}=Machine,Modes}) ->
1792    ets:new(?MODULE, [named_table,private]),
1793    ets:insert(?MODULE, {callback_mode,[handle_event_function|Modes]}),
1794    case Init() of
1795	{ok,State,Data,Ops} ->
1796	    {ok,State,[Data|Machine],Ops};
1797	{ok,State,Data} ->
1798	    {ok,State,[Data|Machine]};
1799	Other ->
1800	    Other
1801    end;
1802init([]) ->
1803    {ok,idle,data}.
1804
1805callback_mode() ->
1806    try ets:lookup(?MODULE, callback_mode) of
1807	[{callback_mode,CallbackMode}] ->
1808	    CallbackMode
1809    catch
1810	error:badarg ->
1811	    state_functions
1812    end.
1813
1814terminate(_, _State, crash_terminate) ->
1815    exit({crash,terminate});
1816terminate(_, _State, {undef_in_terminate, {Mod, Fun}}) ->
1817    Mod:Fun(),
1818    ok;
1819terminate({From,stopped}, State, _Data) ->
1820    From ! {self(),{stopped,State}},
1821    ok;
1822terminate(_Reason, _State, _Data) ->
1823    ok.
1824
1825
1826%% State functions
1827
1828idle(info, {hping,Pid}, _Data) ->
1829    Pid ! {self(), hpong},
1830    keep_state_and_data;
1831idle(cast, {hping,Pid}, Data) ->
1832    Pid ! {self(), hpong},
1833    {keep_state, Data};
1834idle({call, From}, hping, _Data) ->
1835    {keep_state_and_data, [{reply, From, hpong}]};
1836idle({call, From}, {arm_htimer, Pid, Timeout}, _Data) ->
1837    {keep_state_and_data, [{reply, From, ok}, {timeout, Timeout, {arm_htimer, Pid}}]};
1838idle(timeout, {arm_htimer, Pid}, _Data) ->
1839    Pid ! {self(), htimer_armed},
1840    keep_state_and_data;
1841idle(cast, {connect,Pid}, Data) ->
1842    Pid ! accept,
1843    {next_state,wfor_conf,Data,infinity}; % NoOp timeout just to test API
1844idle({call,From}, connect, Data) ->
1845    gen_statem:reply(From, accept),
1846    {next_state,wfor_conf,Data,infinity}; % NoOp timeout just to test API
1847idle({call,_From}, badreturn, _Data) ->
1848    badreturn;
1849idle({call,_From}, badaction, Data) ->
1850    {keep_state, Data, [badaction]};
1851idle({call,_From}, {badtimeout,_,_} = BadTimeout, Data) ->
1852    {keep_state, Data, BadTimeout};
1853idle({call,From}, {delayed_answer,T}, Data) ->
1854    receive
1855    after T ->
1856	    gen_statem:reply({reply,From,delayed}),
1857	    throw({keep_state,Data})
1858    end;
1859idle({call,From}, {timeout,Time}, _Data) ->
1860    AbsTime = erlang:monotonic_time(millisecond) + Time,
1861    {next_state,timeout,{From,Time},
1862     {timeout,AbsTime,idle,[{abs,true}]}};
1863idle(cast, next_event, _Data) ->
1864    {next_state,next_events,[a,b,c],
1865     [{next_event,internal,a},
1866      {next_event,internal,b},
1867      {next_event,internal,c}]};
1868idle(Type, Content, Data) ->
1869    case handle_common_events(Type, Content, idle, Data) of
1870	undefined ->
1871	    case Type of
1872		{call,From} ->
1873		    throw({keep_state,Data,[{reply,From,'eh?'}]});
1874		_ ->
1875		    throw(
1876		      {stop,{unexpected,idle,Type,Content}})
1877	    end;
1878	Result ->
1879	    Result
1880    end.
1881
1882timeout(timeout, idle, {From,Time}) ->
1883    TRef = erlang:start_timer(Time, self(), ok),
1884    {keep_state,{From,TRef},0}; % Immediate timeout 0
1885timeout(timeout, 0, {From,TRef}) ->
1886    {next_state,timeout2,{From,TRef},
1887     [{timeout,1,should_be_cancelled},
1888      postpone]}; % Should cancel state timeout
1889timeout(_, _, _) ->
1890    keep_state_and_data.
1891
1892timeout2(timeout, 0, _) ->
1893    keep_state_and_data;
1894timeout2(timeout, Reason, _) ->
1895    {stop,Reason};
1896timeout2(info, {timeout,TRef,Result}, {From,TRef}) ->
1897    gen_statem:reply([{reply,From,Result}]),
1898    {next_state,idle,state};
1899timeout2(_, _, _) ->
1900    {keep_state_and_data,[]}.
1901
1902wfor_conf({call,From}, confirm, Data) ->
1903    {next_state,connected,Data,
1904     {reply,From,yes}};
1905wfor_conf(cast, {ping,_,_}, _) ->
1906    {keep_state_and_data,[postpone]};
1907wfor_conf(cast, confirm, Data) ->
1908    {next_state,connected,Data};
1909wfor_conf(Type, Content, Data) ->
1910    case handle_common_events(Type, Content, wfor_conf, Data) of
1911	undefined ->
1912	    case Type of
1913		{call,From} ->
1914		    {next_state,idle,Data,
1915		     [{reply,From,'eh?'}]};
1916		_ ->
1917		    throw(keep_state_and_data)
1918	    end;
1919	Result ->
1920	    Result
1921    end.
1922
1923connected({call,From}, {msg,Ref}, Data) ->
1924    {keep_state,Data,
1925     {reply,From,{ack,Ref}}};
1926connected(cast, {msg,From,Ref}, Data) ->
1927    From ! {ack,Ref},
1928    {keep_state,Data};
1929connected({call,From}, disconnect, Data) ->
1930    {next_state,idle,Data,
1931     [{reply,From,yes}]};
1932connected(cast, disconnect, Data) ->
1933    {next_state,idle,Data};
1934connected(cast, {ping,Pid,Tag}, Data) ->
1935    Pid ! {pong,Tag},
1936    {keep_state,Data};
1937connected(Type, Content, Data) ->
1938    case handle_common_events(Type, Content, connected, Data) of
1939	undefined ->
1940	    case Type of
1941		{call,From} ->
1942		    {keep_state,Data,
1943		     [{reply,From,'eh?'}]};
1944		_ ->
1945		    {keep_state,Data}
1946	    end;
1947	Result ->
1948	    Result
1949    end.
1950
1951state0({call,From}, stop, Data) ->
1952    {stop_and_reply,normal,[{reply,From,stopped}],Data};
1953state0(Type, Content, Data) ->
1954    case handle_common_events(Type, Content, state0, Data) of
1955	undefined ->
1956	    {keep_state,Data};
1957	Result ->
1958	    Result
1959    end.
1960
1961hiber_idle({call,From}, 'alive?', Data) ->
1962    {keep_state,Data,
1963     [{reply,From,'alive!'}]};
1964hiber_idle({call,From}, hibernate_sync, Data) ->
1965    {next_state,hiber_wakeup,Data,
1966     [{reply,From,hibernating},
1967      hibernate]};
1968hiber_idle(info, hibernate_later, _) ->
1969    Tref = erlang:start_timer(1000, self(), hibernate),
1970    {keep_state,Tref};
1971hiber_idle(info, hibernate_now, Data) ->
1972    {keep_state,Data,
1973     [hibernate]};
1974hiber_idle(info, {timeout,Tref,hibernate}, Tref) ->
1975    {keep_state,[],
1976     [hibernate]};
1977hiber_idle(cast, hibernate_async, Data) ->
1978    {next_state,hiber_wakeup,Data,
1979     [hibernate]};
1980hiber_idle(Type, Content, Data) ->
1981    case handle_common_events(Type, Content, hiber_idle, Data) of
1982	undefined ->
1983	    {keep_state,Data};
1984	Result ->
1985	    Result
1986    end.
1987
1988hiber_wakeup({call,From}, wakeup_sync, Data) ->
1989    {next_state,hiber_idle,Data,
1990     [{reply,From,good_morning}]};
1991hiber_wakeup({call,From}, snooze_sync, Data) ->
1992    {keep_state,Data,
1993     [{reply,From,please_just_five_more},
1994      hibernate]};
1995hiber_wakeup(cast, wakeup_async, Data) ->
1996    {next_state,hiber_idle,Data};
1997hiber_wakeup(cast, snooze_async, Data) ->
1998    {keep_state,Data,
1999     [hibernate]};
2000hiber_wakeup(Type, Content, Data) ->
2001    case handle_common_events(Type, Content, hiber_wakeup, Data) of
2002	undefined ->
2003	    {keep_state,Data};
2004	Result ->
2005	    Result
2006    end.
2007
2008next_events(internal, Msg, [Msg|Msgs]) ->
2009    {keep_state,Msgs};
2010next_events(Type, Content, Data) ->
2011    case handle_common_events(Type, Content, next_events, Data) of
2012	undefined ->
2013	    {keep_state,Data};
2014	Result ->
2015	    Result
2016    end.
2017
2018
2019handle_common_events({call,From}, get_callback_mode, _, _) ->
2020    {keep_state_and_data,{reply,From,state_functions}};
2021handle_common_events({call,From}, get, State, Data) ->
2022    {keep_state,Data,
2023     [{reply,From,{state,State,Data}}]};
2024handle_common_events(cast, {get,Pid}, State, Data) ->
2025    Pid ! {state,State,Data},
2026    {keep_state,Data};
2027handle_common_events({call,From}, stop, _, Data) ->
2028    {stop_and_reply,normal,[{reply,From,stopped}],Data};
2029handle_common_events(cast, stop, _, _) ->
2030    stop;
2031handle_common_events({call,From}, {stop,Reason}, _, Data) ->
2032    {stop_and_reply,Reason,{reply,From,stopped},Data};
2033handle_common_events(cast, {stop,Reason}, _, _) ->
2034    {stop,Reason};
2035handle_common_events({call,From}, 'alive?', _, Data) ->
2036    {keep_state,Data,
2037     [{reply,From,yes}]};
2038handle_common_events(cast, {'alive?',Pid}, _, Data) ->
2039    Pid ! yes,
2040    {keep_state,Data};
2041handle_common_events(_, _, _, _) ->
2042    undefined.
2043
2044handle_event({call,From}, get_callback_mode, _, _) ->
2045    {keep_state_and_data,{reply,From,handle_event_function}};
2046%% Wrapper state machine that uses a map state machine spec
2047handle_event(
2048  Type, Event, State, [Data|Machine])
2049  when is_map(Machine) ->
2050    #{State := HandleEvent} = Machine,
2051    case
2052	try HandleEvent(Type, Event, Data) of
2053	    Result ->
2054		Result
2055	catch
2056	    Result ->
2057		Result
2058	end of
2059	{stop,Reason,NewData} ->
2060	    {stop,Reason,[NewData|Machine]};
2061	{next_state,NewState,NewData} ->
2062	    {next_state,NewState,[NewData|Machine]};
2063	{next_state,NewState,NewData,Ops} ->
2064	    {next_state,NewState,[NewData|Machine],Ops};
2065	{keep_state,NewData} ->
2066	    {keep_state,[NewData|Machine]};
2067	{keep_state,NewData,Ops} ->
2068	    {keep_state,[NewData|Machine],Ops};
2069	{repeat_state,NewData} ->
2070	    {repeat_state,[NewData|Machine]};
2071	{repeat_state,NewData,Ops} ->
2072	    {repeat_state,[NewData|Machine],Ops};
2073	Other ->
2074	    Other
2075    end;
2076%%
2077%% Dispatcher to test callback_mode handle_event_function
2078%%
2079%% Wrap the state in a 1 element list just to test non-atom
2080%% states.  Note that the state from init/1 is not wrapped
2081%% so both atom and non-atom states are tested.
2082handle_event(Type, Event, State, Data) ->
2083    StateName = unwrap_state(State),
2084    try ?MODULE:StateName(Type, Event, Data) of
2085	Result ->
2086	    wrap_result(Result)
2087    catch
2088	throw:Result:Stacktrace ->
2089	    erlang:raise(
2090	      throw, wrap_result(Result), Stacktrace)
2091    end.
2092
2093unwrap_state([State]) ->
2094    State;
2095unwrap_state(State) ->
2096    State.
2097
2098wrap_result(Result) ->
2099    case Result of
2100	{next_state,NewState,NewData} ->
2101	    {next_state,[NewState],NewData};
2102	{next_state,NewState,NewData,StateOps} ->
2103	    {next_state,[NewState],NewData,StateOps};
2104	Other ->
2105	    Other
2106    end.
2107
2108
2109
2110code_change(OldVsn, State, Data, CallbackMode) ->
2111    io:format(
2112      "code_change(~p, ~p, ~p, ~p)~n", [OldVsn,State,Data,CallbackMode]),
2113    ets:insert(?MODULE, {callback_mode,CallbackMode}),
2114    io:format(
2115      "code_change(~p, ~p, ~p, ~p)~n", [OldVsn,State,Data,CallbackMode]),
2116    {ok,State,{OldVsn,Data,CallbackMode}}.
2117
2118format_status(terminate, [_Pdict,State,Data]) ->
2119    {formatted,State,Data};
2120format_status(normal, [_Pdict,_State,_Data]) ->
2121    [format_status_called].
2122
2123flush() ->
2124    receive
2125	Msg ->
2126	    [Msg|flush()]
2127    after 500 ->
2128	    []
2129    end.
2130