Skip to content

Commit

Permalink
Adding QC test to exercise merging
Browse files Browse the repository at this point in the history
  • Loading branch information
dizzyd committed May 13, 2010
1 parent 3d9f669 commit aaa421e
Showing 1 changed file with 94 additions and 0 deletions.
94 changes: 94 additions & 0 deletions test/bitcask_qc.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
%% -------------------------------------------------------------------
%%
%% bitcask: Eric Brewer-inspired key/value store
%%
%% Copyright (c) 2010 Basho Technologies, Inc. All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%
%% -------------------------------------------------------------------
-module(bitcask_qc).
-author('Dave Smith <dizzyd@basho.com>').
-author('Justin Sheehy <justin@basho.com>').

-ifdef(EQC).

-include_lib("eqc/include/eqc.hrl").
-include_lib("eunit/include/eunit.hrl").

-compile(export_all).

keys() ->
eqc_gen:non_empty(list(eqc_gen:non_empty(binary()))).

values() ->
eqc_gen:non_empty(list(binary())).

ops(Keys, Values) ->
{oneof([put, delete]), oneof(Keys), oneof(Values)}.

apply_kv_ops([], _Ref, Acc0) ->
Acc0;
apply_kv_ops([{put, K, V} | Rest], Ref, Acc0) ->
ok = bitcask:put(Ref, K, V),
apply_kv_ops(Rest, Ref, orddict:store(K, V, Acc0));
apply_kv_ops([{delete, K, _} | Rest], Ref, Acc0) ->
ok = bitcask:delete(Ref, K),
apply_kv_ops(Rest, Ref, orddict:store(K, deleted, Acc0)).

prop_merge() ->
?LET({Keys, Values}, {keys(), values()},
?FORALL({Ops, M1, M2}, {eqc_gen:non_empty(list(ops(Keys, Values))),
choose(1,128), choose(1,128)},
begin
?cmd("rm -rf /tmp/bc.prop.merge"),

%% Open a bitcask, dump the ops into it and build a model of what SHOULD
%% be in the data.
Ref = bitcask:open("/tmp/bc.prop.merge", [read_write, {max_file_size, M1}]),
Model = apply_kv_ops(Ops, Ref, []),

%% Apply the merge -- note that we keep the bitcask open so that a live keydir
%% is available to the merge.
ok = bitcask:merge("/tmp/bc.prop.merge", [{max_file_size, M2}]),

%% Call needs_merge on the bitcask to close any "dead" files
bitcask:needs_merge(Ref),

%% Traverse the model and verify that retrieving each key
%% returns the expected value. It's important to note that the
%% model keeps tombstones on deleted values so we can attempt to
%% retrieve those deleted values and check the corresponding
%% tombstone path in bitcask. Verify that the bitcask contains
%% exactly what we expect
F = fun({K, deleted}) ->
?assertEqual(not_found, bitcask:get(Ref, K));
({K, V}) ->
?assertEqual({ok, V}, bitcask:get(Ref, K))
end,
lists:map(F, Model),

bitcask:close(Ref),
true
end)).

-define(QC_OUT(P), eqc:on_output(fun(Str, Args) -> ?debugFmt(Str, Args) end, P)).

prop_merge_test_() ->
{timeout, 60, fun() -> ?assert(eqc:quickcheck(?QC_OUT(prop_merge()))) end}.


-endif.

0 comments on commit aaa421e

Please sign in to comment.