Skip to content

Commit

Permalink
- allow for removal of fuses
Browse files Browse the repository at this point in the history
- update fuse_eqc test with remove command
- update fuse_SUITE with common test for remove
  • Loading branch information
zeeshanlakhani committed Oct 3, 2015
1 parent 78246f5 commit 3f2df1f
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 27 deletions.
9 changes: 9 additions & 0 deletions src/fuse.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
ask/2,
install/2,
melt/1,
remove/1,
reset/1,
run/3
]).
Expand Down Expand Up @@ -69,6 +70,14 @@ reset(Name) ->
melt(Name) ->
fuse_server:melt(Name).

%% @doc remove/1 removs a fuse
%% Given `remove(N)' this removes the fuse under the name `N'. This fuse will no longer exist.
%% @end
-spec remove(Name) -> ok
when Name :: atom().
remove(Name) ->
fuse_server:remove(Name).

%% Internal functions
%% -----------------------

Expand Down
30 changes: 27 additions & 3 deletions src/fuse_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
ask/2,
install/2,
melt/1,
remove/1,
reset/1,
run/3]).

Expand Down Expand Up @@ -96,7 +97,14 @@ reset(Name) ->
when Name :: atom().
melt(Name) ->
gen_server:call(?MODULE, {melt, Name}).


%% @doc remove/1 removes the fuse
%% The documentation is (@see fuse:remove/1)
%% @end
-spec remove(atom()) -> ok | {error, not_found}.
remove(Name) ->
gen_server:call(?MODULE, {remove, Name}).

%% sync/0 syncs the server. For internal use only in tests
%% @private
sync() ->
Expand Down Expand Up @@ -147,6 +155,9 @@ handle_call({install, #fuse { name = Name } = Fuse}, _From, #state { fuses = Fs
handle_call({reset, Name}, _From, State) ->
{Reply, State2} = handle_reset(Name, State, reset),
{reply, Reply, State2};
handle_call({remove, Name}, _From, State) ->
{Reply, State2} = handle_remove(Name, State),
{reply, Reply, State2};
handle_call({melt, Name}, _From, State) ->
Now = ?TIME:monotonic_time(),
{Res, State2} = with_fuse(Name, State, fun(F) -> add_restart(Now, F) end),
Expand All @@ -165,7 +176,7 @@ handle_call(q_melts, _From, #state { fuses = Fs } = State) ->
{reply, [{N, Ms} || #fuse { name = N, melt_history = Ms } <- Fs], State};
handle_call(_M, _F, State) ->
{reply, {error, unknown}, State}.

%% @private
handle_cast(_M, State) ->
{noreply, State}.
Expand Down Expand Up @@ -206,6 +217,14 @@ handle_reset(Name, State, ResetType) ->
not_found -> {{error, not_found}, State2}
end.

handle_remove(Name, #state { fuses = Fs } = State) ->
case lists:keytake(Name, #fuse.name, Fs) of
false -> {{error, not_found}, State};
{value, F, OtherFs} ->
delete(F),
{ok, State#state { fuses = OtherFs }}
end.

init_state(Name, {{standard, MaxR, MaxT}, {reset, Reset}}) ->
NativePeriod = ?TIME:convert_time_unit(MaxT, milli_seconds, native),
#fuse { name = Name, intensity = MaxR, period = NativePeriod, heal_time = Reset }.
Expand Down Expand Up @@ -237,7 +256,7 @@ add_restart_([R|Restarts], Now, Period) ->
false -> []
end;
add_restart_([], _, _) -> [].

in_period(Time, Now, Period) when (Now - Time) > Period -> false;
in_period(_, _, _) -> true.

Expand All @@ -251,6 +270,11 @@ fix(#fuse { name = Name }) ->
fuse_event:notify({Name, ok}),
ok.

delete(#fuse { name = Name }) ->
ets:delete(?TAB, Name),
fuse_event:notify({Name, removed}),
ok.

install_metrics(#fuse { name = N }) ->
StatsPlugin = application:get_env(fuse, stats_plugin, fuse_stats_ets),
_ = StatsPlugin:init(N),
Expand Down
42 changes: 34 additions & 8 deletions test/fuse_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
%% Tests.
-export([
simple_test/1,
reset_test/1
reset_test/1,
remove_test/1
]).

-ifdef(EQC_TESTING).
Expand All @@ -24,11 +25,12 @@
%% ct.
all() -> [
simple_test,
reset_test
reset_test,
remove_test
].

groups() -> [].

suite() ->
[{timetrap, {minutes, 2}}].

Expand All @@ -45,15 +47,15 @@ init_per_suite(Config) ->
true ->
{skip, running_eqc}
end.
end_per_suite(_Config) ->

end_per_suite(_Config) ->
application:stop(fuse),
application:stop(sasl),
ok.

init_per_group(_Group, Config) ->
Config.

end_per_group(_Group, _Config) ->
ok.

Expand Down Expand Up @@ -114,3 +116,27 @@ reset_test(_Config) ->
2 = proplists:get_value(ok, Stats),
1 = proplists:get_value(blown, Stats),
ok.

-define(FUSE_REMOVE, remove_fuse).
remove_test(_Config) ->
ct:log("Install a fuse, melt it, blow it, then remove it, then recreate it"),
ok = fuse:install(?FUSE_REMOVE, {{standard, 2, 60}, {reset, 5000}}),
ok = fuse:ask(?FUSE_REMOVE, sync),
ok = fuse:melt(?FUSE_REMOVE),
ok = fuse:melt(?FUSE_REMOVE),
ok = fuse:melt(?FUSE_REMOVE),
blown = fuse:ask(?FUSE_REMOVE, sync),
Stats = fuse_stats_ets:counters(?FUSE_REMOVE),
3 = proplists:get_value(melt, Stats),
1 = proplists:get_value(ok, Stats),
1 = proplists:get_value(blown, Stats),
ok = fuse:remove(?FUSE_REMOVE),
{error, not_found} = fuse:ask(?FUSE_REMOVE, sync),
{error, not_found} = fuse:remove(?FUSE_REMOVE),
ok = fuse:install(?FUSE_REMOVE, {{standard, 2, 60}, {reset, 5000}}),
ok = fuse:ask(?FUSE_REMOVE, sync),
Stats2 = fuse_stats_ets:counters(?FUSE_REMOVE),
0 = proplists:get_value(melt, Stats2),
1 = proplists:get_value(ok, Stats2),
0 = proplists:get_value(blown, Stats2),
ok.
65 changes: 49 additions & 16 deletions test/fuse_eqc.erl
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ g_strategy() ->
%% g_refresh()/0 generates a refresh setting.
g_refresh() ->
{reset, 60000}.

%% g_options() generates install options
g_options() ->
{g_strategy(), g_refresh()}.
Expand All @@ -80,7 +80,7 @@ initial_state() -> #state{}.
%% at a later point than normally.
elapse_time(N) ->
fuse_time_mock:elapse_time(N).

elapse_time_args(_S) -> [g_time_inc()].

elapse_time_next(#state { time = T } = State, _V, [N]) ->
Expand Down Expand Up @@ -202,7 +202,7 @@ reset_features(S, [Name], _V) ->
%% Split into two variants
ask_installed(Name) ->
fuse:ask(Name, ?CONTEXT).

ask_installed_pre(S) -> has_fuses_installed(S).

ask_installed_args(_S) -> [g_name()].
Expand All @@ -219,11 +219,11 @@ ask_installed_return(S, [Name]) ->

ask(Name) ->
fuse:ask(Name, ?CONTEXT).

ask_pre(S) -> has_fuses_installed(S).

ask_args(_S) -> [g_name()].

ask_features(S, [Name], _V) ->
case is_installed(Name, S) of
true -> [{fuse_eqc, r15, ask_installed}];
Expand All @@ -245,7 +245,7 @@ ask_return(S, [Name]) ->
%% ---------------------------------------------------------------
run(Name, _Result, _Return, Fun) ->
fuse:run(Name, Fun, ?CONTEXT).

run_pre(S) ->
has_fuses_installed(S).

Expand Down Expand Up @@ -307,14 +307,14 @@ run_return(S, [Name, _Result, Return, _]) ->
%% fuses which are installed, since we assume the interesting aspects affects these.
melt_installed(Name) ->
fuse:melt(Name).

melt_installed_pre(S) -> has_fuses_installed(S).

melt_installed_args(_S) -> [g_name()].

melt_installed_pre(S, [Name]) ->
is_installed(Name, S).

melt_installed_next(#state { time = Ts } = S, _V, [Name]) ->
M = val(record_melt(Name, Ts, S)),
{_, NewState} =
Expand Down Expand Up @@ -365,9 +365,41 @@ melt_features(#state { time = Ts } = S, [Name], _V) ->
[{fuse_eqc, r11, melt_installed_fuse}] ++ Features;
false -> [{fuse_eqc, r12, melt_uninstalled_fuse}]
end.

melt_return(_S, _) -> ok.

%% remove/1 removes a fuse
%% ---------------------------------------------------------------
remove(Name) ->
fuse:remove(Name).

%% Generate arguments to remove from a fuse that's actually installed vs
%% fuses that are not.
remove_args(#state { installed = Is } = _S) ->
frequency(
[ {20, ?LET(F, elements(Is), [element(1, F)])} || Is /= [] ] ++
[ {1, ?SUCHTHAT([F], [g_name()], lists:keymember(F, 1, Is) == false)} ]).

remove_return(S, [Name]) ->
case is_installed(Name, S) of
true -> ok;
false -> {error, not_found}
end.

%% Removing a fuse, removes it from the list of installed fuses.
remove_next(#state{ installed = Is } = S, _V, [Name]) ->
case is_installed(Name, S) of
false -> S;
true ->
S#state { installed = lists:keydelete(Name, 1, Is) }
end.

remove_features(S, [Name], _V) ->
case is_installed(Name, S) of
false -> [{fuse_eqc, r17, remove_uninstalled_fuse}];
true -> [{fuse_eqc, r18, remove_installed_fuse}]
end.

%%% Command weight distribution
%% ---------------------------------------------------------------
weight(_, elapse_time) -> 5;
Expand All @@ -378,7 +410,8 @@ weight(_, melt) -> 1;
weight(_, melt_installed) -> 40;
weight(_, fuse_reset) -> 100;
weight(_, ask) -> 1;
weight(_, ask_installed) -> 30.
weight(_, ask_installed) -> 30;
weight(_, remove) -> 1.

%%% PROPERTIES
%% ---------------------------------------------------------------
Expand Down Expand Up @@ -412,7 +445,7 @@ prop_model_par() ->
fun() -> ok end
end,
fault_rate(1, 40,
?LET(Shrinking, parameter(shrinking, false),
?LET(Shrinking, parameter(shrinking, false),
?FORALL(Cmds, more_commands(2, parallel_commands(?MODULE)),
?ALWAYS(if not Shrinking -> 1;
Shrinking -> 20
Expand Down Expand Up @@ -478,13 +511,13 @@ valid_opts({{standard, K, R}, {reset, T}})
true;
valid_opts(_) ->
false.

melt_state(Name, S) ->
count_state(fuse_intensity(Name, S) - count_melts(Name, S)).

is_blown(Name, #state { blown = BlownFuses }) ->
lists:member(Name, BlownFuses).

fuse_intensity(Name, #state { installed = Inst }) ->
{Name, Count, _} = lists:keyfind(Name, 1, Inst),
Count.
Expand Down Expand Up @@ -526,7 +559,7 @@ expire_melts(Period, Who, #state { time = Now, melts = Ms } = S) ->

clear_blown(Name, #state { blown = Rs } = S) ->
S#state { blown = [N || N <- Rs, N /= Name] }.

clear_melts(Name, #state { melts = Ms } = S) ->
S#state { melts = [{N, Ts} || {N, Ts} <- Ms, N /= Name] }.

Expand All @@ -547,7 +580,7 @@ t(pulse, {T, Unit}) ->
eqc:testing_time(eval_time(T, Unit), x_prop_model_pulse());
t(pulse, N) when is_integer(N) ->
eqc:numtests(N, x_prop_model_pulse()).


eval_time(N, h) -> eval_time(N*60, min);
eval_time(N, min) -> eval_time(N*60, sec);
Expand All @@ -562,7 +595,7 @@ rv(What, T) ->
pulse_instrument() ->
[ pulse_instrument(File) || File <- filelib:wildcard("../src/*.erl") ++ filelib:wildcard("../eqc_test/*.erl") ],
load_sasl().

load_sasl() ->
application:load(sasl),
application:set_env(sasl, sasl_error_logger, false),
Expand Down

0 comments on commit 3f2df1f

Please sign in to comment.