Erlang - исключение выхода на супервизоре и gen_fsm

У меня есть 3 модуля: calculadora, log_calculadora а также supervisor_calculadora, Calculadora это простой калькулятор, который выполняет суммирование, вычитание, умножение и деление с использованием gen_fsm, а supervisor реализует поведение супервизора. Calculadora работает хорошо, но когда я пробую супервизора, который должен перезапустить calculadora Модуль, когда вы делаете деление 0/0 или исключение, он не работает. Зачем?

PD: модуль log_calculadora просто пишет операции, которые я сделал в calculadora в файле log.txt. Модуль TEST - это тот, кто дает мне выход для исключения.

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}.

Руководитель:

-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]}}.

МОДУЛЬ ИСПЫТАНИЙ

-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.

1 ответ

Решение
proper:quickcheck(calculadora_supervisada_statem:prop_calculadora()).

Запускает следующее

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

В приведенном выше коде я вижу, что supervisor_calculadora запускается для каждого теста (quickchecl?FORALL выполняет блок для каждого Cmd). Он попытается запустить супервизор и зарегистрировать его снова с тем же именем, но не получится.

Другие вопросы по тегам