Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 405 lines (358 sloc) 17.53 kb
d47b328 @EiriniArvaniti added copyright notice
EiriniArvaniti authored
1 %%% Copyright 2010-2011 Manolis Papadakis <manopapad@gmail.com>,
2 %%% Eirini Arvaniti <eirinibob@gmail.com>
3 %%% and Kostis Sagonas <kostis@cs.ntua.gr>
4 %%%
5 %%% This file is part of PropEr.
6 %%%
7 %%% PropEr is free software: you can redistribute it and/or modify
8 %%% it under the terms of the GNU General Public License as published by
9 %%% the Free Software Foundation, either version 3 of the License, or
10 %%% (at your option) any later version.
11 %%%
12 %%% PropEr is distributed in the hope that it will be useful,
13 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
14 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 %%% GNU General Public License for more details.
16 %%%
17 %%% You should have received a copy of the GNU General Public License
18 %%% along with PropEr. If not, see <http://www.gnu.org/licenses/>.
19
b36c594 @manopapad Remove e-mail addresses from EDoc output
authored
20 %%% @copyright 2010-2011 Manolis Papadakis, Eirini Arvaniti and Kostis Sagonas
d47b328 @EiriniArvaniti added copyright notice
EiriniArvaniti authored
21 %%% @version {@version}
b36c594 @manopapad Remove e-mail addresses from EDoc output
authored
22 %%% @author Eirini Arvaniti
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
23
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
24 %%% @doc This module defines the `proper_fsm' behaviour, useful for testing
25 %%% systems that can be modeled as finite state machines. That is, a finite
26 %%% collection of named states and transitions between them. `{@module}' is
27 %%% closely related to {@link proper_statem} and is, in fact, implemented in
28 %%% terms of that. Testcases generated using `{@module}' will be on precisely
29 %%% the same form as testcases generated using {@link proper_statem}. The
30 %%% difference lies in the way the callback modules are specified.
31 %%% The relation between {@link proper_statem} and `{@module}' is similar
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
32 %%% to the one between `gen_server' and `gen_fsm' in OTP libraries.
33 %%%
34 %%% Due to name conflicts with functions automatically imported from
35 %%% {@link proper_statem}, a fully qualified call is needed in order to
36 %%% use the <a href="#index">API functions </a> of `{@module}'.
37 %%%
c4a7774 @EiriniArvaniti corrections in documentation of the stateful modules
EiriniArvaniti authored
38 %%% === The states of the finite state machine ===
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
39 %%% Following the convention used in `gen_fsm behaviour', the state is
40 %%% separated into a `StateName::'{@type state_name()} and some
41 %%% `StateData::'{@type state_data()}. `StateName' is used to denote a state
42 %%% of the finite state machine and `StateData' is any relevant information
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
43 %%% that has to be stored in the model state. States are fully
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
44 %%% represented as tuples `{StateName, StateData}'.
45 %%%
46 %%% `StateName' is usually an atom (i.e. the name of the state), but can also
47 %%% be a tuple. In the latter case, the first element of the tuple must be an
48 %%% atom specifying the name of the state, whereas the rest of the elements can
49 %%% be arbitrary terms specifying state attributes. For example, when
50 %%% implementing the fsm of an elevator which can reach N different floors, the
51 %%% `StateName' for each floor could be `{floor,K}, 1 <= K <= N'.<br/>
52 %%% `StateData' can be an arbitrary term, but is usually a record.
53 %%%
c4a7774 @EiriniArvaniti corrections in documentation of the stateful modules
EiriniArvaniti authored
54 %%% === Transitions between states ===
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
55 %%% A transition ({@type transition()}) is represented as a tuple
56 %%% `{TargetState, {call,M,F,A}}'. This means that performing the specified
57 %%% symbolic call at the current state of the fsm will lead to `TargetState'.
58 %%% The atom `history' can be used as `TargetState' to denote that a transition
59 %%% does not change the current state of the fsm.
60 %%%
c4a7774 @EiriniArvaniti corrections in documentation of the stateful modules
EiriniArvaniti authored
61 %%% === The callback functions ===
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
62 %%% The following functions must be exported from the callback module
63 %%% implementing the finite state machine:
64 %%% <ul>
65 %%% <li> `initial_state() ::' {@type state_name()}
66 %%% <p>Specifies the initial state of the finite state machine. As with
67 %%% `proper_statem:initial_state/0', its result should be deterministic.
68 %%% </p></li>
69 %%% <li> `initial_state_data() ::' {@type state_data()}
70 %%% <p>Specifies what the state data should initially contain. Its result
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
71 %%% should be deterministic</p></li>
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
72 %%% <li> `StateName(S::'{@type state_data()}`) ::'
73 %%% `['{@type transition()}`]'
74 %%% <p>There should be one instance of this function for each reachable
75 %%% state `StateName' of the finite state machine. In case `StateName' is a
76 %%% tuple the function takes a different form, described just below. The
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
77 %%% function returns a list of possible transitions ({@type transition()})
78 %%% from the current state.
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
79 %%% At command generation time, the instance of this function with the same
80 %%% name as the current state's name is called to return the list of possible
81 %%% transitions. Then, PropEr will randomly choose a transition and,
82 %%% according to that, generate the next symbolic call to be included in the
83 %%% command sequence. However, before the call is actually included, a
84 %%% precondition that might impose constraints on `StateData' is checked.<br/>
85 %%% Note also that PropEr detects transitions that would raise an exception
86 %%% of class `<error>' at generation time (not earlier) and does not choose
87 %%% them. This feature can be used to include conditional transitions that
88 %%% depend on the `StateData'.</p></li>
89 %%% <li> `StateName(Attr1::term(), ..., AttrN::term(),
90 %%% S::'{@type state_data()}`) ::'
91 %%% `['{@type transition()}`]'
92 %%% <p>There should be one instance of this function for each reachable state
93 %%% `{StateName,Attr1,...,AttrN}' of the finite state machine. The function
94 %%% has similar beaviour to `StateName/1', described above.</p></li>
95 %%% <li> `weight(From::'{@type state_name()}`,
96 %%% Target::'{@type state_name()}`,
97 %%% Call::'{@type symb_call()}`) :: integer()'
98 %%% <p>This is an optional callback. When it is not defined (or not exported),
99 %%% transitions are chosen with equal probability. When it is defined, it
100 %%% assigns an integer weight to transitions from `From' to `Target'
101 %%% triggered by symbolic call `Call'. In this case, each transition is chosen
102 %%% with probability proportional to the weight assigned.</p></li>
103 %%% <li> `precondition(From::'{@type state_name()}`,
104 %%% Target::'{@type state_name()}`,
105 %%% StateData::'{@type state_data()}`,
106 %%% Call::'{@type symb_call()}`) :: boolean()'
107 %%% <p>Similar to `proper_statem:precondition/2'. Specifies the
108 %%% precondition that should hold about `StateData' so that `Call' can be
109 %%% included in the command sequence. In case precondition doesn't hold, a
110 %%% new transition is chosen using the appropriate `StateName/1' generator.
111 %%% It is possible for more than one transitions to be triggered by the same
112 %%% symbolic call and lead to different target states. In this case, at most
113 %%% one of the target states may have a true precondition. Otherwise, PropEr
114 %%% will not be able to detect which transition was chosen and an exception
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
115 %%% will be raised.</p></li>
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
116 %%% <li> `postcondition(From::'{@type state_name()}`,
117 %%% Target::'{@type state_name()}`,
118 %%% StateData::'{@type state_data()}`,
119 %%% Call::'{@type symb_call()}`,
120 %%% Res::'{@type result()}`) :: boolean()'
121 %%% <p>Similar to `proper_statem:postcondition/3'. Specifies the
122 %%% postcondition that should hold about the result `Res' of the evaluation
123 %%% of `Call'.</p></li>
124 %%% <li> `next_state_data(From::'{@type state_name()}`,
125 %%% Target::'{@type state_name()}`,
126 %%% StateData::'{@type state_data()}`,
127 %%% Res::'{@type result()}`,
128 %%% Call::'{@type symb_call()}`) ::'
129 %%% {@type state_data()}
130 %%% <p>Similar to `proper_statem:next_state/3'. Specifies how the
131 %%% transition from `FromState' to `Target' triggered by `Call' affects the
132 %%% `StateData'. `Res' refers to the result of `Call' and can be either
133 %%% symbolic or dynamic.</p></li>
134 %%% </ul>
135 %%%
c4a7774 @EiriniArvaniti corrections in documentation of the stateful modules
EiriniArvaniti authored
136 %%% === The property used ===
137 %%% This is an example of a property that can be used to test a
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
138 %%% finite state machine specification:
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
139 %%%
140 %%% ```prop_fsm() ->
c4a7774 @EiriniArvaniti corrections in documentation of the stateful modules
EiriniArvaniti authored
141 %%% ?FORALL(Cmds, proper_fsm:commands(?MODULE),
142 %%% begin
143 %%% {_History, _State, Result} = proper_fsm:run_commands(?MODULE, Cmds),
144 %%% cleanup(),
145 %%% Result =:= ok
146 %%% end).'''
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
147 %%% @end
d47b328 @EiriniArvaniti added copyright notice
EiriniArvaniti authored
148
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
149 -module(proper_fsm).
150
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
151 -export([behaviour_info/1]).
152 -export([commands/1, commands/2, run_commands/2, run_commands/3,
153 state_names/1]).
154 -export([command/1, precondition/2, next_state/3, postcondition/3]).
0cc4bea @EiriniArvaniti first unit tests for proper_fsm
EiriniArvaniti authored
155 -export([target_states/4]).
156
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
157 -include("proper_internal.hrl").
158
159
160 %% -----------------------------------------------------------------------------
161 %% Type declarations
162 %% -----------------------------------------------------------------------------
163
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
164 -type symb_var() :: proper_statem:symb_var().
165 -type symb_call() :: proper_statem:symb_call().
166 -type fsm_result() :: proper_statem:statem_result().
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
167 -type state_name() :: atom() | tuple().
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
168 %% @type state_data()
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
169 -type state_data() :: term().
170 -type fsm_state() :: {state_name(),state_data()}.
171 -type transition() :: {state_name(),symb_call()}.
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
172 -type command() :: {'set',symb_var(),symb_call()}
173 | {'init',fsm_state()}.
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
174 -type command_list() :: [command()].
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
175 %% @type cmd_result()
176 -type cmd_result() :: term().
177 -type history() :: [{fsm_state(),cmd_result()}].
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
178 -type tmp_command() :: {'init',state()}
179 | {'set',symb_var(),symb_call()}.
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
180
181 -record(state, {name :: state_name(),
182 data :: state_data(),
183 mod :: mod_name()}).
184 -type state() :: #state{}.
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
185
186
187 %% -----------------------------------------------------------------------------
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
188 %% Proper_fsm behaviour
189 %% ----------------------------------------------------------------------------
190
191 %% @doc Specifies the callback functions that should be exported from a module
192 %% implementing the `proper_fsm' behaviour.
193
194 -spec behaviour_info('callbacks') -> [{fun_name(),arity()}].
195 behaviour_info(callbacks) ->
196 [{initial_state,0},
197 {initial_state_data,0},
198 {precondition,4},
199 {postcondition,5},
200 {next_state_data,5}];
201 behaviour_info(_Attribute) ->
202 undefined.
203
204
205 %% -----------------------------------------------------------------------------
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
206 %% API
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
207 %% -----------------------------------------------------------------------------
208
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
209 %% @doc A special PropEr type which generates random command sequences,
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
210 %% according to a finite state machine specification. The function takes as
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
211 %% input the name of a callback module, which contains the fsm specification.
212 %% The initial state is computed by <br/>
213 %% `{Mod:initial_state/0, Mod:initial_state_data/0}'.
214
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
215 -spec commands(mod_name()) -> proper_types:type().
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
216 commands(Mod) ->
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
217 ?LET([_|Cmds],
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
218 proper_statem:commands(?MODULE, initial_state(Mod)),
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
219 Cmds).
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
220
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
221 %% @doc Similar to {@link commands/1}, but generated command sequences always
222 %% start at a given state. In this case, the first command is always <br/>
223 %% `{init, InitialState = {Name,Data}}' and is used to correctly initialize the
224 %% state every time the command sequence is run (i.e. during normal execution,
225 %% while shrinking and when checking a counterexample).
226
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
227 -spec commands(mod_name(), fsm_state()) -> proper_types:type().
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
228 commands(Mod, {Name,Data} = InitialState) ->
229 State = #state{name = Name, data = Data, mod = Mod},
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
230 ?LET([_|Cmds],
231 proper_statem:commands(?MODULE, State),
232 [{init,InitialState}|Cmds]).
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
233
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
234 %% @doc Evaluates a given symbolic command sequence `Cmds' according to the
235 %% finite state machine specified in `Mod'. The result is a triple of the
236 %% form<br/> `{History, FsmState, Result}', similar to
237 %% {@link proper_statem:run_commands/2}.
238
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
239 -spec run_commands(mod_name(), command_list()) ->
9127a34 @EiriniArvaniti clean-ups
EiriniArvaniti authored
240 {history(),fsm_state(),fsm_result()}.
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
241 run_commands(Mod, Cmds) ->
242 run_commands(Mod, Cmds, []).
243
244 %% @doc Similar to {@link run_commands/2}, but also accepts an environment
245 %% used for symbolic variable evaluation, exactly as described in
246 %% {@link proper_statem:run_commands/3}.
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
247
248 -spec run_commands(mod_name(), command_list(), proper_symb:var_values()) ->
d7bf75f @EiriniArvaniti refactoring in proper_fsm
EiriniArvaniti authored
249 {history(),fsm_state(),fsm_result()}.
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
250 run_commands(Mod, Cmds, Env) ->
251 Cmds1 = tmp_commands(Mod, Cmds),
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
252 {H,S,Res} = proper_statem:run_commands(?MODULE, Cmds1, Env),
ba93bfc @EiriniArvaniti more clean-ups
EiriniArvaniti authored
253 History = [{{Name,Data},R} || {#state{name = Name, data = Data},R} <- H],
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
254 State = {S#state.name, S#state.data},
255 {History, State, Res}.
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
256
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
257 %% @doc Extracts the names of the states from a given command execution history.
258 %% It is useful in combination with functions such as {@link proper:aggregate/2}
259 %% in order to collect statistics about state transitions during command
260 %% execution.
261
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
262 -spec state_names(history()) -> [state_name()].
263 state_names(History) ->
264 [SName || {{SName,_},_Res} <- History].
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
265
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
266
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
267 %% -----------------------------------------------------------------------------
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
268 %% Proper_statem bahaviour callback functions
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
269 %% -----------------------------------------------------------------------------
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
270
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
271 -spec initial_state(mod_name()) -> state().
272 initial_state(Mod) ->
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
273 S_name = Mod:initial_state(),
274 S_data = Mod:initial_state_data(),
ba93bfc @EiriniArvaniti more clean-ups
EiriniArvaniti authored
275 #state{name = S_name, data = S_data, mod = Mod}.
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
276
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
277 %% @private
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
278 -spec command(state()) -> proper_types:type().
ba93bfc @EiriniArvaniti more clean-ups
EiriniArvaniti authored
279 command(#state{name = From, data = Data, mod = Mod}) ->
b57f2e2 @EiriniArvaniti code refactoring in proper_fsm
EiriniArvaniti authored
280 choose_transition(Mod, From, get_transitions(Mod, From, Data)).
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
281
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
282 %% @private
4b161ab @EiriniArvaniti better approach to proper_fsm
EiriniArvaniti authored
283 -spec precondition(state(), symb_call()) -> boolean().
ba93bfc @EiriniArvaniti more clean-ups
EiriniArvaniti authored
284 precondition(#state{name = From, data = Data, mod = Mod}, Call) ->
9127a34 @EiriniArvaniti clean-ups
EiriniArvaniti authored
285 Targets = target_states(Mod, From, Data, Call),
286 case [To || To <- Targets,
287 Mod:precondition(From, cook_history(From, To), Data, Call)] of
70c7b01 @EiriniArvaniti printing error message about conflicting target states of a transition
EiriniArvaniti authored
288 [] ->
289 false;
290 [_T] ->
291 true;
292 _ ->
293 io:format(
294 "\nError: The transition from \"~w\" state triggered by ~w "
295 "call leads to multiple target states.\nUse the precondition/5 "
296 "callback to specify which target state should be chosen.\n",
297 [From, get_mfa(Call)]),
298 erlang:error(too_many_targets)
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
299 end.
300
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
301 %% @private
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
302 -spec next_state(state(), symb_var() | cmd_result(), symb_call()) -> state().
ba93bfc @EiriniArvaniti more clean-ups
EiriniArvaniti authored
303 next_state(S = #state{name = From, data = Data, mod = Mod} , Var, Call) ->
d7bf75f @EiriniArvaniti refactoring in proper_fsm
EiriniArvaniti authored
304 To = cook_history(From, transition_target(Mod, From, Data, Call)),
ba93bfc @EiriniArvaniti more clean-ups
EiriniArvaniti authored
305 S#state{name = To,
306 data = Mod:next_state_data(From, To, Data, Var, Call)}.
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
307
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
308 %% @private
3f6ace3 @EiriniArvaniti proper_statem, proper_fsm behaviours introduced
EiriniArvaniti authored
309 -spec postcondition(state(), symb_call(), cmd_result()) -> boolean().
ba93bfc @EiriniArvaniti more clean-ups
EiriniArvaniti authored
310 postcondition(#state{name = From, data = Data, mod = Mod}, Call, Res) ->
d7bf75f @EiriniArvaniti refactoring in proper_fsm
EiriniArvaniti authored
311 To = cook_history(From, transition_target(Mod, From, Data, Call)),
312 Mod:postcondition(From, To, Data, Call, Res).
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
313
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
314
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
315 %% -----------------------------------------------------------------------------
316 %% Utility functions
317 %% -----------------------------------------------------------------------------
318
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
319 -spec tmp_commands(mod_name(), command_list()) -> [tmp_command()].
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
320 tmp_commands(Mod, Cmds) ->
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
321 case Cmds of
322 [{init, {Name,Data}}|Rest] ->
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
323 I = #state{name = Name, data = Data, mod = Mod},
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
324 [{init,I}|Rest];
325 Rest ->
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
326 I = initial_state(Mod),
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
327 [{init,I}|Rest]
328 end.
329
b57f2e2 @EiriniArvaniti code refactoring in proper_fsm
EiriniArvaniti authored
330 -spec get_transitions(mod_name(), state_name(), state_data()) ->
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
331 [transition()].
b57f2e2 @EiriniArvaniti code refactoring in proper_fsm
EiriniArvaniti authored
332 get_transitions(Mod, StateName, Data) ->
333 case StateName of
334 From when is_atom(From) ->
335 Mod:From(Data);
336 From when is_tuple(From) ->
337 Fun = element(1, From),
338 Args = tl(tuple_to_list(From)),
339 apply(Mod, Fun, Args ++ [Data])
340 end.
341
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
342 -spec choose_transition(mod_name(), state_name(), [transition()]) ->
d7bf75f @EiriniArvaniti refactoring in proper_fsm
EiriniArvaniti authored
343 proper_types:type().
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
344 choose_transition(Mod, From, T_list) ->
5ff219d @EiriniArvaniti support for weighting transitions
EiriniArvaniti authored
345 case is_exported(Mod, {weight,3}) of
346 false ->
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
347 choose_uniform_transition(T_list);
5ff219d @EiriniArvaniti support for weighting transitions
EiriniArvaniti authored
348 true ->
f23ae47 @EiriniArvaniti discard exception raising transitions in proper_fsm:is_instance_call/3
EiriniArvaniti authored
349 choose_weighted_transition(Mod, From, T_list)
5ff219d @EiriniArvaniti support for weighting transitions
EiriniArvaniti authored
350 end.
351
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
352 -spec choose_uniform_transition([transition()]) -> proper_types:type().
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
353 choose_uniform_transition(T_list) ->
869f674 @EiriniArvaniti never choose a transition that raises an exception
EiriniArvaniti authored
354 List = [CallGen || {_,CallGen} <- T_list],
fc6af0f @EiriniArvaniti move some private types to their proper place
EiriniArvaniti authored
355 proper_types:safe_union(List).
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
356
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
357 -spec choose_weighted_transition(mod_name(), state_name(), [transition()]) ->
358 proper_types:type().
f23ae47 @EiriniArvaniti discard exception raising transitions in proper_fsm:is_instance_call/3
EiriniArvaniti authored
359 choose_weighted_transition(Mod, From, T_list) ->
d7bf75f @EiriniArvaniti refactoring in proper_fsm
EiriniArvaniti authored
360 List = [{Mod:weight(From, cook_history(From, To), CallGen), CallGen}
869f674 @EiriniArvaniti never choose a transition that raises an exception
EiriniArvaniti authored
361 || {To,CallGen} <- T_list],
fc6af0f @EiriniArvaniti move some private types to their proper place
EiriniArvaniti authored
362 proper_types:safe_weighted_union(List).
d7bf75f @EiriniArvaniti refactoring in proper_fsm
EiriniArvaniti authored
363
364 -spec cook_history(state_name(), state_name()) -> state_name().
365 cook_history(From, history) -> From;
366 cook_history(_, To) -> To.
f23ae47 @EiriniArvaniti discard exception raising transitions in proper_fsm:is_instance_call/3
EiriniArvaniti authored
367
5ff219d @EiriniArvaniti support for weighting transitions
EiriniArvaniti authored
368 -spec is_exported(mod_name(), {fun_name(),arity()}) -> boolean().
369 is_exported(Mod, Fun) ->
370 lists:member(Fun, Mod:module_info(exports)).
371
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
372 -spec transition_target(mod_name(), state_name(), state_data(), symb_call()) ->
d7bf75f @EiriniArvaniti refactoring in proper_fsm
EiriniArvaniti authored
373 state_name().
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
374 transition_target(Mod, From, Data, Call) ->
375 Targets = target_states(Mod, From, Data, Call),
fc6af0f @EiriniArvaniti move some private types to their proper place
EiriniArvaniti authored
376 [To] = [T || T <- Targets,
377 Mod:precondition(From, cook_history(From, T), Data, Call)],
378 To.
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
379
7a261a8 @EiriniArvaniti small change in computation of initial state + cleanups
EiriniArvaniti authored
380 %% @private
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
381 -spec target_states(mod_name(), state_name(), state_data(), symb_call()) ->
9127a34 @EiriniArvaniti clean-ups
EiriniArvaniti authored
382 [state_name()].
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
383 target_states(Mod, From, StateData, Call) ->
384 find_target(get_transitions(Mod, From, StateData), Call, []).
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
385
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
386 -spec find_target([transition()], symb_call(), [state_name()]) ->
9127a34 @EiriniArvaniti clean-ups
EiriniArvaniti authored
387 [state_name()].
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
388 find_target([], _, Accum) -> Accum;
389 find_target(Transitions, Call, Accum) ->
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
390 [{Target,CallGen}|Rest] = Transitions,
869f674 @EiriniArvaniti never choose a transition that raises an exception
EiriniArvaniti authored
391 case is_compatible(Call, CallGen) of
971671d @EiriniArvaniti fixes in proper_fsm.erl
EiriniArvaniti authored
392 true -> find_target(Rest, Call, [Target|Accum]);
393 false -> find_target(Rest, Call, Accum)
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
394 end.
395
5d18293 @EiriniArvaniti some documentation added
EiriniArvaniti authored
396 -spec is_compatible(symb_call(), symb_call()) -> boolean().
f23ae47 @EiriniArvaniti discard exception raising transitions in proper_fsm:is_instance_call/3
EiriniArvaniti authored
397 is_compatible({call,M,F,A1}, {call,M,F,A2})
9127a34 @EiriniArvaniti clean-ups
EiriniArvaniti authored
398 when length(A1) =:= length(A2) ->
399 true;
f23ae47 @EiriniArvaniti discard exception raising transitions in proper_fsm:is_instance_call/3
EiriniArvaniti authored
400 is_compatible(_, _) ->
c307be2 @EiriniArvaniti first commit of proper_fsm
EiriniArvaniti authored
401 false.
70c7b01 @EiriniArvaniti printing error message about conflicting target states of a transition
EiriniArvaniti authored
402
403 -spec get_mfa(symb_call()) -> mfa().
404 get_mfa({call,M,F,A}) -> {M,F,length(A)}.
Something went wrong with that request. Please try again.