Merge pull request #45 from basho/ku/file-reopen
Add stop and trim command to eqc_statem test on file_proxy
This commit is contained in:
commit
fd649c00d2
1 changed files with 65 additions and 53 deletions
|
@ -31,6 +31,7 @@
|
||||||
-define(QC_OUT(P),
|
-define(QC_OUT(P),
|
||||||
eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)).
|
eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)).
|
||||||
|
|
||||||
|
-define(TESTDIR, "./eqc").
|
||||||
|
|
||||||
%% EUNIT TEST DEFINITION
|
%% EUNIT TEST DEFINITION
|
||||||
eqc_test_() ->
|
eqc_test_() ->
|
||||||
|
@ -106,13 +107,20 @@ get_written_interval(L) ->
|
||||||
%% INITIALIZATION
|
%% INITIALIZATION
|
||||||
|
|
||||||
-record(state, {pid, prev_extra = 0,
|
-record(state, {pid, prev_extra = 0,
|
||||||
|
filename = undefined,
|
||||||
planned_writes=[],
|
planned_writes=[],
|
||||||
planned_trims=[],
|
planned_trims=[],
|
||||||
written=[],
|
written=[],
|
||||||
trimmed=[]}).
|
trimmed=[]}).
|
||||||
|
|
||||||
initial_state() -> #state{written=[{0,1024}]}.
|
initial_state() ->
|
||||||
initial_state(I, T) -> #state{written=[{0,1024}],
|
{_, _, MS} = os:timestamp(),
|
||||||
|
Filename = test_server:temp_name("eqc_data") ++ "." ++ integer_to_list(MS),
|
||||||
|
#state{filename=Filename, written=[{0,1024}]}.
|
||||||
|
|
||||||
|
initial_state(I, T) ->
|
||||||
|
S=initial_state(),
|
||||||
|
S#state{written=[{0,1024}],
|
||||||
planned_writes=I,
|
planned_writes=I,
|
||||||
planned_trims=T}.
|
planned_trims=T}.
|
||||||
|
|
||||||
|
@ -136,11 +144,6 @@ get_overlaps(Offset, Len, [{Pos, Sz} = Ck|T], Acc0)
|
||||||
(Pos < Offset + Len andalso Offset + Len < Pos + Sz) ->
|
(Pos < Offset + Len andalso Offset + Len < Pos + Sz) ->
|
||||||
get_overlaps(Offset, Len, T, [Ck|Acc0]);
|
get_overlaps(Offset, Len, T, [Ck|Acc0]);
|
||||||
get_overlaps(Offset, Len, [_Ck|T], Acc0) ->
|
get_overlaps(Offset, Len, [_Ck|T], Acc0) ->
|
||||||
%% ?debugVal({Offset, Len, _Ck}),
|
|
||||||
%% ?debugVal(Offset =< Pos andalso Pos < Offset + Len andalso Offset + Len =< Pos + Sz),
|
|
||||||
%% ?debugVal(Offset =< Pos andalso Pos + Sz < Offset + Len),
|
|
||||||
%% ?debugVal(Pos < Offset andalso Offset < Pos + Sz andalso Pos + Sz < Offset + Len),
|
|
||||||
%% ?debugVal(Pos < Offset + Len andalso Offset + Len < Pos + Sz),
|
|
||||||
get_overlaps(Offset, Len, T, Acc0).
|
get_overlaps(Offset, Len, T, Acc0).
|
||||||
|
|
||||||
%% Inefficient but simple easy code to verify by eyes - returns all
|
%% Inefficient but simple easy code to verify by eyes - returns all
|
||||||
|
@ -209,8 +212,6 @@ last_byte(L0) ->
|
||||||
L1 = lists:map(fun({Pos, Sz}) -> Pos + Sz end, L0),
|
L1 = lists:map(fun({Pos, Sz}) -> Pos + Sz end, L0),
|
||||||
lists:last(lists:sort(L1)).
|
lists:last(lists:sort(L1)).
|
||||||
|
|
||||||
-define(TESTDIR, "./eqc").
|
|
||||||
|
|
||||||
cleanup() ->
|
cleanup() ->
|
||||||
[begin
|
[begin
|
||||||
Fs = filelib:wildcard(?TESTDIR ++ Glob),
|
Fs = filelib:wildcard(?TESTDIR ++ Glob),
|
||||||
|
@ -228,14 +229,12 @@ start_pre(S) ->
|
||||||
start_command(S) ->
|
start_command(S) ->
|
||||||
{call, ?MODULE, start, [S]}.
|
{call, ?MODULE, start, [S]}.
|
||||||
|
|
||||||
start(_S) ->
|
start(#state{filename=File}) ->
|
||||||
{_, _, MS} = os:timestamp(),
|
|
||||||
File = test_server:temp_name("eqc_data") ++ "." ++ integer_to_list(MS),
|
|
||||||
{ok, Pid} = machi_file_proxy:start_link(some_flu, File, ?TESTDIR),
|
{ok, Pid} = machi_file_proxy:start_link(some_flu, File, ?TESTDIR),
|
||||||
unlink(Pid),
|
unlink(Pid),
|
||||||
Pid.
|
Pid.
|
||||||
|
|
||||||
start_next(S, Pid, _Args) ->
|
start_next(S, Pid, _) ->
|
||||||
S#state{pid = Pid}.
|
S#state{pid = Pid}.
|
||||||
|
|
||||||
%% read
|
%% read
|
||||||
|
@ -244,25 +243,19 @@ read_pre(S) ->
|
||||||
S#state.pid /= undefined.
|
S#state.pid /= undefined.
|
||||||
|
|
||||||
read_args(S) ->
|
read_args(S) ->
|
||||||
[S#state.pid, offset(), len()].
|
[S#state.pid, oneof([offset(), big_offset()]), len()].
|
||||||
|
|
||||||
read_post(S, [_Pid, Off, L], Res) ->
|
read_post(S, [_Pid, Off, L], Res) ->
|
||||||
Written = get_overlaps(Off, L, S#state.written, []),
|
Written = get_overlaps(Off, L, S#state.written, []),
|
||||||
Chopped = chop(Off, L, Written),
|
Chopped = chop(Off, L, Written),
|
||||||
Trimmed = get_overlaps(Off, L, S#state.trimmed, []),
|
Trimmed = get_overlaps(Off, L, S#state.trimmed, []),
|
||||||
Eof = lists:max([Pos+Sz||{Pos,Sz}<-S#state.written]),
|
Eof = lists:max([Pos+Sz||{Pos,Sz}<-S#state.written]),
|
||||||
%% ?debugVal({Off, L}),
|
|
||||||
%% ?debugVal(S),
|
|
||||||
case Res of
|
case Res of
|
||||||
{ok, {Written0, Trimmed0}} ->
|
{ok, {Written0, Trimmed0}} ->
|
||||||
Written1 = lists:map(fun({_, Pos, Chunk, _}) ->
|
Written1 = lists:map(fun({_, Pos, Chunk, _}) ->
|
||||||
{Pos, iolist_size(Chunk)}
|
{Pos, iolist_size(Chunk)}
|
||||||
end, Written0),
|
end, Written0),
|
||||||
Trimmed1 = lists:map(fun({_, Pos, Sz}) -> {Pos, Sz} end, Trimmed0),
|
Trimmed1 = lists:map(fun({_, Pos, Sz}) -> {Pos, Sz} end, Trimmed0),
|
||||||
%% ?debugVal({Written, Chopped, Written1}),
|
|
||||||
%% ?debugVal({Trimmed, Trimmed1}),
|
|
||||||
%% ?assertEqual(Chopped, Written1),
|
|
||||||
%% ?assertEqual(Trimmed, Trimmed1),
|
|
||||||
Chopped =:= Written1
|
Chopped =:= Written1
|
||||||
andalso Trimmed =:= Trimmed1;
|
andalso Trimmed =:= Trimmed1;
|
||||||
%% TODO: such response are ugly, rethink the SPEC
|
%% TODO: such response are ugly, rethink the SPEC
|
||||||
|
@ -270,8 +263,7 @@ read_post(S, [_Pid, Off, L], Res) ->
|
||||||
true;
|
true;
|
||||||
{error, not_written} when Chopped =:= [] andalso Trimmed =:= [] ->
|
{error, not_written} when Chopped =:= [] andalso Trimmed =:= [] ->
|
||||||
true;
|
true;
|
||||||
Other ->
|
_Other ->
|
||||||
?debugVal(Other),
|
|
||||||
is_error(Res)
|
is_error(Res)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -328,7 +320,6 @@ write(Pid, Offset, {Bin, Tag, Csum}) ->
|
||||||
%% append
|
%% append
|
||||||
|
|
||||||
append_pre(S) ->
|
append_pre(S) ->
|
||||||
?assert(undefined =/= S#state.written),
|
|
||||||
S#state.pid /= undefined.
|
S#state.pid /= undefined.
|
||||||
|
|
||||||
%% do not allow appends with empty binary data
|
%% do not allow appends with empty binary data
|
||||||
|
@ -349,18 +340,30 @@ append_next(S, Res, [_Pid, Extra, {Bin, _Tag, _Csum}]) ->
|
||||||
case is_ok(Res) of
|
case is_ok(Res) of
|
||||||
true ->
|
true ->
|
||||||
Offset = get_offset(Res),
|
Offset = get_offset(Res),
|
||||||
Expected = erlang:max(last_byte(S#state.written) + S#state.prev_extra,
|
S#state{prev_extra = Extra,
|
||||||
last_byte(S#state.trimmed)),
|
written = lists:sort(S#state.written ++ [{Offset, iolist_size(Bin)}])};
|
||||||
?assertEqual(Expected, Offset),
|
_Other ->
|
||||||
S#state{prev_extra = Extra, written = lists:sort(S#state.written ++ [{Offset, iolist_size(Bin)}])};
|
|
||||||
_ ->
|
|
||||||
S
|
S
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% appends should always succeed unless the disk is full
|
%% appends should always succeed unless the disk is full
|
||||||
%% or there's a hardware failure.
|
%% or there's a hardware failure.
|
||||||
append_post(_S, _Args, Res) ->
|
append_post(S, _Args, Res) ->
|
||||||
true == is_ok(Res).
|
case is_ok(Res) of
|
||||||
|
true ->
|
||||||
|
Offset = get_offset(Res),
|
||||||
|
case erlang:max(last_byte(S#state.written),
|
||||||
|
last_byte(S#state.trimmed)) + S#state.prev_extra of
|
||||||
|
Offset ->
|
||||||
|
true;
|
||||||
|
UnexpectedByte ->
|
||||||
|
{wrong_offset_after_append,
|
||||||
|
{Offset, UnexpectedByte},
|
||||||
|
{S#state.written, S#state.prev_extra}}
|
||||||
|
end;
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
%% rewrite
|
%% rewrite
|
||||||
|
|
||||||
|
@ -415,6 +418,20 @@ trim_next(S, Res, [_Pid, Offset, Length]) ->
|
||||||
S1#state{prev_extra=0,
|
S1#state{prev_extra=0,
|
||||||
planned_trims=tl(S#state.planned_trims)}.
|
planned_trims=tl(S#state.planned_trims)}.
|
||||||
|
|
||||||
|
stop_pre(S) ->
|
||||||
|
S#state.pid /= undefined.
|
||||||
|
|
||||||
|
stop_args(S) ->
|
||||||
|
[S#state.pid].
|
||||||
|
|
||||||
|
stop(Pid) ->
|
||||||
|
catch machi_file_proxy:stop(Pid).
|
||||||
|
|
||||||
|
stop_post(_, _, _) -> true.
|
||||||
|
|
||||||
|
stop_next(S, _, _) ->
|
||||||
|
S#state{pid=undefined, prev_extra=0}.
|
||||||
|
|
||||||
%% Property
|
%% Property
|
||||||
|
|
||||||
prop_ok() ->
|
prop_ok() ->
|
||||||
|
@ -424,15 +441,10 @@ prop_ok() ->
|
||||||
?FORALL(Cmds, parallel_commands(?MODULE, initial_state(I, T)),
|
?FORALL(Cmds, parallel_commands(?MODULE, initial_state(I, T)),
|
||||||
begin
|
begin
|
||||||
{H, S, Res} = run_parallel_commands(?MODULE, Cmds),
|
{H, S, Res} = run_parallel_commands(?MODULE, Cmds),
|
||||||
%% case S#state.pid of
|
cleanup(),
|
||||||
%% undefined -> noop;
|
|
||||||
%% Pid ->
|
|
||||||
%% machi_file_proxy:stop(Pid)
|
|
||||||
%% end,
|
|
||||||
pretty_commands(?MODULE, Cmds, {H, S, Res},
|
pretty_commands(?MODULE, Cmds, {H, S, Res},
|
||||||
aggregate(command_names(Cmds), Res == ok))
|
aggregate(command_names(Cmds), Res == ok))
|
||||||
end)
|
end)).
|
||||||
).
|
|
||||||
|
|
||||||
%% Test for tester functions
|
%% Test for tester functions
|
||||||
chopper_test_() ->
|
chopper_test_() ->
|
||||||
|
|
Loading…
Reference in a new issue