Statem test #2

Merged
jlouis merged 4 commits from statem-test into master 2012-01-06 22:23:42 +00:00
7 changed files with 302 additions and 72 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@ ebin
deps
*~
.eunit
/.fractal_btree.plt

36
Makefile Normal file
View file

@ -0,0 +1,36 @@
REBAR = ./rebar
DIALYZER = dialyzer
.PHONY: plt analyze all deps compile get-deps clean
all: compile
get-deps:
@$(REBAR) get-deps
compile:
@$(REBAR) compile
clean:
@$(REBAR) clean
eunit: compile clean-test-btrees
@$(REBAR) eunit skip_deps=true
clean-test-btrees:
rm -fr .eunit/Btree_*
plt: compile
$(DIALYZER) --build_plt --output_plt .fractal_btree.plt \
-pa deps/plain_fsm/ebin \
-pa deps/ebloom/ebin \
deps/plain_fsm/ebin \
deps/ebloom/ebin \
--apps kernel stdlib
analyze: compile
$(DIALYZER) --plt .fractal_btree.plt \
-pa deps/plain_fsm/ebin \
-pa deps/ebloom/ebin \
ebin

View file

@ -4,5 +4,7 @@
{deps, [
{plain_fsm, "1.1.*", {git, "git://github.com/uwiger/plain_fsm", {branch, "master"}}},
{ebloom, "1.0.*", {git, "git://github.com/basho/ebloom.git", {branch, "master"}}},
{basho_bench, ".*", {git, "git://github.com/basho/basho_bench.git", {branch, "master"}}}
{basho_bench, ".*", {git, "git://github.com/basho/basho_bench.git", {branch, "master"}}},
{proper, ".*", {git, "git://github.com/manopapad/proper.git",
{branch, "master"}}}
]}.

View file

@ -0,0 +1,88 @@
%% @Doc Drive a set of fractal BTrees
-module(fractal_btree_drv).
-behaviour(gen_server).
%% API
-export([start_link/0]).
-export([open/1,
put/3,
stop/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-record(state, { btrees = dict:new() % Map from a name to its tree
}).
%%%===================================================================
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
call(X) ->
gen_server:call(?SERVER, X, infinity).
open(N) ->
call({open, N}).
put(N, K, V) ->
call({put, N, K, V}).
stop() ->
call(stop).
%%%===================================================================
init([]) ->
{ok, #state{}}.
handle_call({open, N}, _, #state { btrees = D} = State) ->
case fractal_btree:open(N) of
{ok, Tree} ->
{reply, ok, State#state { btrees = dict:store(N, Tree, D)}};
Otherwise ->
{reply, {error, Otherwise}, State}
end;
handle_call({put, N, K, V}, _, #state { btrees = D} = State) ->
Tree = dict:fetch(N, D),
case fractal_btree:put(Tree, K, V) of
ok ->
{reply, ok, State};
Other ->
{reply, {error, Other}, State}
end;
handle_call(stop, _, State) ->
{stop, normal, ok, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
cleanup_trees(State),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%% @todo directory cleanup
cleanup_trees(#state { btrees = BTs }) ->
dict:fold(fun(_Name, Tree, ok) ->
fractal_btree:close(Tree)
end,
ok,
BTs).

View file

@ -0,0 +1,39 @@
-module(fractal_btree_merger_tests).
-ifdef(TEST).
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-endif.
-compile(export_all).
merge_test() ->
{ok, BT1} = fractal_btree_writer:open("test1"),
lists:foldl(fun(N,_) ->
ok = fractal_btree_writer:add(BT1, <<N:128>>, <<"data",N:128>>)
end,
ok,
lists:seq(1,10000,2)),
ok = fractal_btree_writer:close(BT1),
{ok, BT2} = fractal_btree_writer:open("test2"),
lists:foldl(fun(N,_) ->
ok = fractal_btree_writer:add(BT2, <<N:128>>, <<"data",N:128>>)
end,
ok,
lists:seq(2,5001,1)),
ok = fractal_btree_writer:close(BT2),
{Time,{ok,Count}} = timer:tc(fractal_btree_merger2, merge, ["test1", "test2", "test3", 10000]),
error_logger:info_msg("time to merge: ~p/sec (time=~p, count=~p)~n", [1000000/(Time/Count), Time/1000000, Count]),
ok = file:delete("test1"),
ok = file:delete("test2"),
ok = file:delete("test3"),
ok.

View file

@ -1,100 +1,100 @@
-module(fractal_btree_tests).
-ifdef(TEST).
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-endif.
-behaviour(proper_statem).
-compile(export_all).
simple_test() ->
-export([command/1, initial_state/0,
next_state/3, postcondition/3,
precondition/2]).
{ok, BT} = fractal_btree_writer:open("testdata"),
ok = fractal_btree_writer:add(BT, <<"A">>, <<"Avalue">>),
ok = fractal_btree_writer:add(BT, <<"B">>, <<"Bvalue">>),
ok = fractal_btree_writer:close(BT),
-record(state, { open = dict:new(),
closed = dict:new() }).
-define(SERVER, fractal_btree_drv).
{ok, IN} = fractal_btree_reader:open("testdata"),
{ok, <<"Avalue">>} = fractal_btree_reader:lookup(IN, <<"A">>),
ok = fractal_btree_reader:close(IN),
full_test_() ->
{setup,
spawn,
fun () -> ok end,
fun (_) -> ok end,
[{timeout, 120, ?_test(test_proper())},
?_test(test_tree())]}.
ok = file:delete("testdata").
qc_opts() -> [{numtests, 400}].
test_proper() ->
[?assertEqual([], proper:module(?MODULE, qc_opts()))].
simple1_test() ->
initial_state() ->
#state { }.
{ok, BT} = fractal_btree_writer:open("testdata"),
g_btree_name() ->
?LET(I, integer(1,10),
"Btree_" ++ integer_to_list(I)).
Max = 30*1024,
Seq = lists:seq(0, Max),
cmd_close_args(#state { open = Open }) ->
oneof(dict:fetch_keys(Open)).
{Time1,_} = timer:tc(
fun() ->
lists:foreach(
fun(Int) ->
ok = fractal_btree_writer:add(BT, <<Int:128>>, <<"valuevalue/", Int:128>>)
cmd_put_args(#state { open = Open }) ->
?LET({Name, Key, Value},
{oneof(dict:fetch_keys(Open)), binary(), binary()},
[Name, Key, Value]).
command(#state { open = Open} = S) ->
frequency(
[ {100, {call, ?SERVER, open, [g_btree_name()]}} ] ++
[ {2000, {call, ?SERVER, put, cmd_put_args(S)}}
|| dict:size(Open) > 0]).
precondition(#state { open = Open }, {call, ?SERVER, put, [Name, K, V]}) ->
dict:is_key(Name, Open);
precondition(#state { open = Open }, {call, ?SERVER, open, [Name]}) ->
not (dict:is_key(Name, Open)).
next_state(#state { open = Open} = S, _Res,
{call, ?SERVER, put, [Name, Key, Value]}) ->
S#state { open = dict:update(Name,
fun(Dict) ->
dict:store(Key, Value, Dict)
end,
Seq),
ok = fractal_btree_writer:close(BT)
end,
[]),
Open)};
next_state(#state { open = Open} = S, _Res, {call, ?SERVER, open, [Name]}) ->
S#state { open = dict:store(Name, dict:new(), Open) }.
error_logger:info_msg("time to insert: ~p/sec~n", [1000000/(Time1/Max)]),
{ok, IN} = fractal_btree_reader:open("testdata"),
{ok, <<"valuevalue/", 2048:128>>} = fractal_btree_reader:lookup(IN, <<2048:128>>),
postcondition(_S, {call, ?SERVER, put, [_Name, _Key, _Value]}, ok) ->
true;
postcondition(_S, {call, ?SERVER, open, [_Name]}, ok) ->
true;
postcondition(_, _, _) ->
false.
{Time2,Count} = timer:tc(
fun() -> fractal_btree_reader:fold(fun(Key, <<"valuevalue/", Key/binary>>, N) ->
N+1
end,
0,
IN)
end,
[]),
prop_dict_agree() ->
?FORALL(Cmds, commands(?MODULE),
?TRAPEXIT(
begin
fractal_btree_drv:start_link(),
{History,State,Result} = run_commands(?MODULE, Cmds),
fractal_btree_drv:stop(),
?WHENFAIL(io:format("History: ~w\nState: ~w\nResult: ~w\n",
[History,State,Result]),
aggregate(command_names(Cmds), Result =:= ok))
end)).
error_logger:info_msg("time to scan: ~p/sec~n", [1000000/(Time2/Max)]),
Max = Count-1,
%% ----------------------------------------------------------------------
ok = fractal_btree_reader:close(IN),
ok = file:delete("testdata").
%% UNIT TESTS -----------------------------------------------------------------
merge_test() ->
{ok, BT1} = fractal_btree_writer:open("test1"),
lists:foldl(fun(N,_) ->
ok = fractal_btree_writer:add(BT1, <<N:128>>, <<"data",N:128>>)
end,
ok,
lists:seq(1,10000,2)),
ok = fractal_btree_writer:close(BT1),
{ok, BT2} = fractal_btree_writer:open("test2"),
lists:foldl(fun(N,_) ->
ok = fractal_btree_writer:add(BT2, <<N:128>>, <<"data",N:128>>)
end,
ok,
lists:seq(2,5001,1)),
ok = fractal_btree_writer:close(BT2),
{Time,{ok,Count}} = timer:tc(fractal_btree_merger2, merge, ["test1", "test2", "test3", 10000]),
error_logger:info_msg("time to merge: ~p/sec (time=~p, count=~p)~n", [1000000/(Time/Count), Time/1000000, Count]),
ok = file:delete("test1"),
ok = file:delete("test2"),
ok = file:delete("test3"),
ok.
tree_test() ->
test_tree() ->
application:start(sasl),

View file

@ -0,0 +1,64 @@
-module(fractal_btree_writer_tests).
-ifdef(TEST).
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-endif.
-compile(export_all).
simple_test() ->
{ok, BT} = fractal_btree_writer:open("testdata"),
ok = fractal_btree_writer:add(BT, <<"A">>, <<"Avalue">>),
ok = fractal_btree_writer:add(BT, <<"B">>, <<"Bvalue">>),
ok = fractal_btree_writer:close(BT),
{ok, IN} = fractal_btree_reader:open("testdata"),
{ok, <<"Avalue">>} = fractal_btree_reader:lookup(IN, <<"A">>),
ok = fractal_btree_reader:close(IN),
ok = file:delete("testdata").
simple1_test() ->
{ok, BT} = fractal_btree_writer:open("testdata"),
Max = 30*1024,
Seq = lists:seq(0, Max),
{Time1,_} = timer:tc(
fun() ->
lists:foreach(
fun(Int) ->
ok = fractal_btree_writer:add(BT, <<Int:128>>, <<"valuevalue/", Int:128>>)
end,
Seq),
ok = fractal_btree_writer:close(BT)
end,
[]),
error_logger:info_msg("time to insert: ~p/sec~n", [1000000/(Time1/Max)]),
{ok, IN} = fractal_btree_reader:open("testdata"),
{ok, <<"valuevalue/", 2048:128>>} = fractal_btree_reader:lookup(IN, <<2048:128>>),
{Time2,Count} = timer:tc(
fun() -> fractal_btree_reader:fold(fun(Key, <<"valuevalue/", Key/binary>>, N) ->
N+1
end,
0,
IN)
end,
[]),
error_logger:info_msg("time to scan: ~p/sec~n", [1000000/(Time2/Max)]),
Max = Count-1,
ok = fractal_btree_reader:close(IN),
ok = file:delete("testdata").