blob: 3187b922d5e322b40a7441b1494cf66bf13d25c3 [file] [log] [blame]
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
%%
%% This file is part of Triq - Trifork QuickCheck
%%
%% Copyright (c) 2010-2013 by Trifork
%% Copyright 2013-2018 Triq authors
%%
%% 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).
-export([command/2,
initial_state/1,
next_state/4,
postcondition/4,
precondition/3]).
-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(Module, {FromName, StateData}, Call) ->
case find_next_state(Module, FromName, StateData, Call) of
[ToName] ->
Module:precondition(FromName, ToName, StateData, Call);
_ ->
false
end.
postcondition(Module, {ToName, StateData}, Call, Result) ->
Module:postcondition(undefined, ToName, StateData, Call, Result).
next_state(Module, {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(Module, {FromName, StateData}) ->
Calls = [ Call || {_ToName, Call} <- Module:FromName( StateData ) ],
oneof( Calls ).
initial_state(Module) ->
%% TODO: Some fsm specs return additional data as the 2nd element
%% of a tuple. How should that data be used for the state?
Name = case Module:initial_state() of
{State, _} ->
State;
State when is_atom(State) ->
State
end,
{ 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).