0

I have 3 modules: calculadora, log_calculadora and supervisor_calculadora. Calculadora is just a simple calculator that makes sum, subtraction, multiplication and division using gen_fsm and supervisor implements supervisor behaviour. Calculadora works well but when I try the supervisor, who must restart the calculadora module when you make a division 0/0 or an exception, it doesn't work. Why?

PD:The module log_calculadora just writes the operations that I made in calculadora in a log.txt file. The TEST module is the one who gives me the exception exit.

Calculadora:

-module(calculadora).
-author("BreixoCF").
-behaviour(gen_fsm).

%% Public API
-export([on/0, off/0, modo/1, acumular/1]).

%% Internal API (gen_fsm)
-export([init/1, handle_event/3, terminate/3]).

%% Internal API (estados)
-export([suma/2, suma/3, resta/2, resta/3, producto/2, producto/3, division/2, division/3]).

-define(CALC, calculadora).
-define(LOG_MODULE, log_calculadora).
-define(LOG_FILE, "log.txt").

%%%===================================================================
%%% Public API
%%%===================================================================
-spec on() -> ok.
on() ->
    gen_fsm:start_link({local, ?CALC}, ?CALC, 0, []).

-spec off() -> ok.
off() ->
    gen_fsm:send_all_state_event(?CALC, stop).

-spec modo(O::atom())-> ok.
modo(suma) ->
    gen_fsm:send_event(?CALC, {modoSuma});
modo(resta) ->
    gen_fsm:send_event(?CALC, {modoResta});
modo(producto) ->
    gen_fsm:send_event(?CALC, {modoProducto});
modo(division) ->
    gen_fsm:send_event(?CALC, {modoDivision}).

-spec acumular(N::number()) -> number().
acumular(N) ->
    gen_fsm:sync_send_event(?CALC, {numero, N}).

%%%===================================================================
%%% Internal API (gen_fsm)
%%%===================================================================
init(Ac) ->
    {ok, _Pid} = gen_event:start_link({local, logcalc}),
    gen_event:add_handler(logcalc, ?LOG_MODULE, ?LOG_FILE),
    gen_event:notify(logcalc, {on, []}),
    {ok, suma, Ac}.

handle_event(stop, _Estado, _DatosEstado) ->
    {stop, normal, []}.

terminate(normal, _Estado, _DatosEstado) ->
    gen_event:notify(logcalc, {off}),
    gen_event:stop(logcalc),
    ok.

%%%===================================================================
%%% Internal API (estados)
%%%===================================================================
% Cambio Modo SUMA
suma({modoSuma}, Ac) ->
    gen_event:notify(logcalc, {cambio, suma, suma}),
    {next_state, suma, Ac};
suma({modoResta}, Ac) ->
    gen_event:notify(logcalc, {cambio, suma, resta}),
    {next_state, resta, Ac};
suma({modoProducto}, Ac) ->
    gen_event:notify(logcalc, {cambio, suma, producto}),
    {next_state, producto, Ac};
suma({modoDivision}, Ac) ->
    gen_event:notify(logcalc, {cambio, suma, division}),
    {next_state, division, Ac}.
% Cálculo Modo SUMA
suma({numero, N}, _From, Ac) ->
    gen_event:notify(logcalc, {operacion, suma, N, Ac, Ac+N}),
    {reply, Ac+N, suma, Ac+N}.

% Cambio Modo RESTA
resta({modoSuma}, Ac) ->
  gen_event:notify(logcalc, {cambio, resta, suma}),
  {next_state, suma, Ac};
resta({modoResta}, Ac) ->
  gen_event:notify(logcalc, {cambio, resta, resta}),
  {next_state, resta, Ac};
resta({modoProducto}, Ac) ->
  gen_event:notify(logcalc, {cambio, resta, producto}),
  {next_state, producto, Ac};
resta({modoDivision}, Ac) ->
  gen_event:notify(logcalc, {cambio, resta, division}),
  {next_state, division, Ac}.
% Cálculo Modo RESTA
resta({numero, N}, _From, Ac) ->
  gen_event:notify(logcalc, {operacion, resta, N, Ac, Ac-N}),
  {reply, Ac-N, resta, Ac-N}.

% Cambio Modo PRODUCTO
producto({modoSuma}, Ac) ->
  gen_event:notify(logcalc, {cambio, producto, suma}),
  {next_state, suma, Ac};
producto({modoResta}, Ac) ->
  gen_event:notify(logcalc, {cambio, producto, resta}),
  {next_state, resta, Ac};
producto({modoProducto}, Ac) ->
  gen_event:notify(logcalc, {cambio, producto, producto}),
  {next_state, producto, Ac};
producto({modoDivision}, Ac) ->
  gen_event:notify(logcalc, {cambio, producto, division}),
  {next_state, division, Ac}.
% Cálculo Modo PRODUCTO
producto({numero, N}, _From, Ac) ->
  gen_event:notify(logcalc, {operacion, producto, N, Ac, Ac*N}),
  {reply, Ac*N, producto, Ac*N}.

% Cambio Modo DIVISION
division({modoSuma}, Ac) ->
  gen_event:notify(logcalc, {cambio, division, suma}),
  {next_state, suma, Ac};
division({modoResta}, Ac) ->
  gen_event:notify(logcalc, {cambio, division, resta}),
  {next_state, resta, Ac};
division({modoProducto}, Ac) ->
  gen_event:notify(logcalc, {cambio, division, producto}),
  {next_state, producto, Ac};
division({modoDivision}, Ac) ->
  gen_event:notify(logcalc, {cambio, division, division}),
  {next_state, division, Ac}.
% Cálculo Modo DIVISION
division({numero, N}, _From, Ac) ->
  gen_event:notify(logcalc, {operacion, division, N, Ac, Ac/N}),
  {reply, Ac/N, division, Ac/N}.

Supervisor:

-module(supervisor_calculadora).
-author("BreixoCF").

-behaviour(supervisor).

%% API
-export([start/0]).

%% Supervisor callbacks
-export([init/1]).

%%%===================================================================
%%% API functions
%%%===================================================================

start() ->
  supervisor:start_link({local, ?MODULE}, ?MODULE, []).

%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================

init([]) ->
  RestartStrategy = one_for_one,
  MaxRestarts = 10,
  MaxSecondsBetweenRestarts = 5,
  Flags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
  Restart = permanent,
  Shutdown = 2000,
  Type = worker,
  Calculadora = {calculadora, {calculadora, on, []},
  Restart, Shutdown, Type, [calculadora]},
  {ok, {Flags, [Calculadora]}}.

TESTS MODULE

-module(calculadora_supervisada_statem).
-behaviour(proper_statem).

-include_lib("proper/include/proper.hrl").

%% CALLBACKS from proper_statem
-export([initial_state/0, command/1, precondition/2, postcondition/3, next_state/3]).
-export([suma/2, resta/2, producto/2, division/2]).
-export([acumular/1, modo/1]).

initial_state() ->
    {suma, 0}.

command(_S) ->
    frequency([{25, {call, ?MODULE, acumular, [number()]}},
           {20, {call, ?MODULE, modo, [modo()]}}]).

modo() ->
    elements([suma, resta, producto, division, unknown]).


next_state({division, _S}, _V, {call, ?MODULE, acumular, [0]}) ->
    {suma, 0};
next_state({Op,  S}, _V, {call, ?MODULE, acumular, [N]}) ->
    {Op, erlang:apply(?MODULE, Op, [S, N])};
next_state({_Op, _S}, _V, {call, ?MODULE, modo, [unknown]}) ->
    {suma, 0};
next_state({_Op,  S}, _V, {call, ?MODULE, modo, [NewOp]}) ->
    {NewOp, S};
next_state(S, _V, {call, _, _, _}) ->
    S.

precondition(_S, {call, _, _, _}) ->
    true.

postcondition({division, _S}, {call, ?MODULE, acumular, [0]}, {'EXIT',_}) ->
    true;
postcondition({Op, S}, {call, ?MODULE, acumular, [N]}, Res) ->
    Res == ?MODULE:Op(S, N);
postcondition({_Op, _S}, {call, ?MODULE, modo, [_NewOp]}, _Res) ->
    true;
postcondition(_S, {call, _, _, _}, _Res) ->
    false.



prop_calculadora() ->
    ?FORALL(Cmds, commands(?MODULE),
        begin
        supervisor_calculadora:start(),
        {H, S, Res} = run_commands(?MODULE,Cmds),
        cleanup(),
        ?WHENFAIL(io:format("History: ~p\nState: ~p\nRes: ~p\n", [H, S, Res]),
              aggregate(command_names(Cmds), Res == ok))
        end).



%%--------------------------------------------------------------------
%% Internal wrappers and auxiliary functions
%%--------------------------------------------------------------------

acumular(N) ->
    Acc = (catch calculadora:acumular(N)),
    timer:sleep(100),
    Acc.
modo(O) ->
    Acc = (catch calculadora:modo(O)),
    timer:sleep(100),
    Acc.
cleanup() ->
    catch calculadora:off(),
    timer:sleep(100).

suma(A, B) ->
    A + B.
resta(A, B) ->
    A - B.
producto(A, B) ->
    A * B.
division(_A, 0) ->
    0;
division(A, B) ->
    A / B.
  • What error do you get? – legoscia Mar 20 '15 at 18:04
  • proper:quickcheck(calculadora_supervisada_statem:prop_calculadora()). ..........** exception exit: shutdown – Breixo Camiña Fernández Mar 20 '15 at 18:08
  • Did you try if your supervisor works out of the test module. I didn't test it but the child spec looks correct and the fsm should restart. The error shows that the module calculadora_supervisada_statem crashes, this doesn't mean that the supervisor + fsm didn't do their job. Without information about proper_statem behavior, proper.hrl and quickcheck function, it is difficult to go further. – Pascal Mar 21 '15 at 07:01

1 Answers1

2
proper:quickcheck(calculadora_supervisada_statem:prop_calculadora()).

Triggers the following

 prop_calculadora() ->
    ?FORALL(Cmds, commands(?MODULE),
        begin
        supervisor_calculadora:start(),

In the above code I see that supervisor_calculadora is started for each test case (quickchecl ?FORALL executes the block for each Cmd). It will try to start the supervisor and register it again with the same name and fails.

Vinod
  • 2,243
  • 14
  • 18