Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add triq_fsm (based on triq_statem)

This is "poor mans" equivalent of eqc_fsm; notable
limitations:

Module:postcondition/4 -- always called with a first
   argument (from state) of undefined.

Module:State/1 -- can only transition to exactly
   one other state per {M,F,length(A)} pattern;
   i.e. the same function call with different 
   args are not allowed to go to different states.
  • Loading branch information...
commit 7fb90cd1ca1fc5bd6b9c752a4299eb3df9bf7535 1 parent 2c2a280
@krestenkrab authored
Showing with 166 additions and 0 deletions.
  1. +78 −0 src/triq_fsm.erl
  2. +88 −0 src/triq_fsm_stub.erl
View
78 src/triq_fsm.erl
@@ -0,0 +1,78 @@
+%%
+%% This file is part of Triq - Trifork QuickCheck
+%%
+%% Copyright (c) 2010 by Trifork
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+-module(triq_fsm).
+
+-export([commands/1, commands/2, run_commands/2, run_commands/3, state_names/1]).
+-import(triq_expr, [eval/2, free_vars/1]).
+-import(triq_dom, [oneof/1]).
+
+
+%% FSM API
+
+commands(Module) when is_atom(Module) ->
+ StubModule = triq_fsm_stub:new(Module),
+ triq_statem:commands(StubModule).
+
+commands(Module, {InitialName, State}=Init) when is_atom(Module) ->
+ StubModule = triq_fsm_stub:new(Module),
+ triq_statem:commands(StubModule, {InitialName, State}).
+
+run_commands(Module, Commands) ->
+ run_commands(Module, Commands, []).
+
+run_commands(Module,Commands,Env) ->
+ StubModule = triq_fsm_stub:new(Module),
+ do_run_command(Commands,
+ Env,
+ StubModule,
+ [],
+ StubModule:initial_state()).
+
+do_run_command(Commands, Env, Module, History, State) ->
+ case Commands of
+ [] ->
+ {History, eval(Env,State), ok};
+
+ [{init,S}|Rest] ->
+ do_run_command(Rest, Env, Module, History, S);
+
+ [{set, {var,V}=Var, {call,M,F,A}=SymCall}|Rest] ->
+ M2=eval(Env,M),
+ F2=eval(Env,F),
+ A2=eval(Env,A),
+
+ Res = apply(M2,F2,A2), % Same as eval(Env, SymCall), but we need to log in History.
+
+ SubstCall = {call, M2,F2,A2},
+ {Name, _} = State,
+ History2 = [{{Name,SubstCall},Res}|History],
+
+ case Module:postcondition(State,SubstCall,Res) of
+ true ->
+ Env2 = [{V,Res}|proplists:delete(V,Env)],
+ State2 = Module:next_state(State,Var,SymCall),
+ do_run_command(Rest, Env2, Module, History2, State2);
+
+ Other ->
+ {History, eval(Env,State), {postcondition, Other}}
+ end
+ end.
+
+state_names(H) ->
+ [N || {{N,_},_} <- H].
+
View
88 src/triq_fsm_stub.erl
@@ -0,0 +1,88 @@
+%%
+%% This file is part of Triq - Trifork QuickCheck
+%%
+%% Copyright (c) 2010 by Trifork
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+-module(triq_fsm_stub, [Module]).
+
+-export([command/1, initial_state/0,
+ next_state/3, postcondition/3,
+ precondition/2]).
+-import(triq_dom, [oneof/1]).
+
+%%
+%% An instance of this module implements the "statem" API, but
+%% delegates calls to an underlying implementation of the "fsm" API.
+%%
+
+precondition({FromName, StateData}, Call) ->
+ case find_next_state(Module, FromName, StateData, Call) of
+ [ToName] ->
+ Module:precondition(FromName, ToName, StateData, Call);
+ _ ->
+ false
+ end.
+
+postcondition({ToName, StateData}, Call, Result) ->
+ Module:postcondition(undefined, ToName, StateData, Call, Result).
+
+next_state({FromName, StateData}, Result, Call) ->
+ case find_next_state(Module, FromName, StateData, Call) of
+ [ToName] ->
+ ToStateData =
+ Module:next_state_data(FromName,
+ ToName,
+ StateData,
+ Result,
+ Call),
+ {ToName, ToStateData};
+ [_|_] ->
+ exit({multiple_choise, FromName, Call});
+ [] ->
+ exit({no_transition, FromName, Call})
+ end.
+
+command({FromName, StateData}) ->
+ Calls = [ Call || {_ToName, Call} <- Module:FromName( StateData ) ],
+ oneof( Calls ).
+
+initial_state() ->
+ {Name, _} = Module:initial_state(),
+ { Name, Module:initial_state_data() }.
+
+
+%% INTERNAL
+
+find_next_state(Module, FromName, StateData, {call, M,F,Arg}=_Call) ->
+ Candidates1 = Module:FromName(StateData),
+ Candidates2 =
+ lists:foldl(fun ({ToName, {call, M2, F2, ArgDom}}, Acc)
+ when M2 == M, F2 == F
+ ->
+ case length(ArgDom) == length(Arg) of
+ true when ToName == history ->
+ ordsets:add_element(FromName, Acc);
+ true ->
+ ordsets:add_element(ToName, Acc);
+ false ->
+ Acc
+ end;
+ (_, Acc) ->
+ Acc
+ end,
+ ordsets:new(),
+ Candidates1),
+ ordsets:to_list(Candidates2).
+
Please sign in to comment.
Something went wrong with that request. Please try again.