diff --git a/c_src/stats.h b/c_src/stats.h index 4f3009f..4fa574b 100644 --- a/c_src/stats.h +++ b/c_src/stats.h @@ -128,9 +128,13 @@ static unsigned int __log2_64(uint64_t x) { { \ uint8_t logs[64]; \ uint8_t i, j, max_log = 0; \ - double m = (s->mean + name ## _stat_mean(s) / 2.0); \ + double m = 0.0; \ \ - fprintf(stderr, "%s:async_nif request latency histogram:\n", mod); \ + if (s->n < nsamples) \ + return; \ + \ + fprintf(stderr, "\n%s:async_nif request latency histogram:\n", mod); \ + m = (s->mean + name ## _stat_mean(s) / 2.0); \ for (i = 0; i < 64; i++) { \ logs[i] = LOG2(s->histogram[i]); \ if (logs[i] > max_log) \ @@ -145,7 +149,7 @@ static unsigned int __log2_64(uint64_t x) { fprintf(stderr, logs[j] >= i ? "•" : " "); \ fprintf(stderr, "\n"); \ } \ - if (max_log == 0) { \ + if (max_log == 100) { \ fprintf(stderr, "[empty]\n"); \ } else { \ fprintf(stderr, " ns μs ms s ks\n"); \ diff --git a/rebar b/rebar deleted file mode 100755 index 44053a5..0000000 Binary files a/rebar and /dev/null differ diff --git a/rebar.config b/rebar.config index 559c316..8d2088d 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,6 @@ {erl_opts, [%{d,'DEBUG',true}, debug_info, - fail_on_warning, warn_unused_vars, warn_export_all, warn_shadow_vars, @@ -21,7 +20,8 @@ warn_exported_vars, warn_untyped_record %warn_missing_spec, - %strict_validation + %strict_validation, + %fail_on_warning ]}. {xref_checks, [undefined_function_calls, deprecated_function_calls]}. diff --git a/src/lmdb.erl b/src/lmdb.erl index 160d491..afebbcb 100644 --- a/src/lmdb.erl +++ b/src/lmdb.erl @@ -32,7 +32,7 @@ %% EXPORTS %%==================================================================== -export([ - open/1, + %open/1, open/2, open/3, @@ -53,7 +53,7 @@ %% config for testing -ifdef(TEST). -ifdef(EQC). -include_lib("eqc/include/eqc.hrl"). +-include_lib("eqc/include/eqc.hrl"). -define(QC_OUT(P), eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). -endif. -include_lib("eunit/include/eunit.hrl"). @@ -80,8 +80,8 @@ include_lib("eqc/include/eqc.hrl"). %% @doc Create a new MDB database %% @end %%-------------------------------------------------------------------- -open(DirName) -> - open(DirName, ?MDB_MAP_SIZE). +%open(DirName) -> +% open(DirName, ?MDB_MAP_SIZE). open(DirName, MapSize) when is_integer(MapSize) andalso MapSize > 0 -> @@ -171,20 +171,21 @@ not_loaded(Line) -> %% =================================================================== -ifdef(TEST). --define(TEST_DATA_DIR, "test/basics"). - -open_test_db(DataDir) -> +open_test_db() -> {ok, CWD} = file:get_cwd(), - Path = filename:join([CWD, DataDir]), - ?cmd("rm -rf " ++ Path), - ?assertMatch(ok, filelib:ensure_dir(filename:join([Path, "x"]))), - {ok, Handle} = ?MODULE:open(Path), + DataDir = filename:join([CWD, "test", "eunit"]), + ?cmd("rm -rf " ++ DataDir), + ?assertMatch(ok, filelib:ensure_dir(filename:join([DataDir, "x"]))), + {ok, Handle} = ?MODULE:open(DataDir, 2147483648), + [?MODULE:upd(Handle, crypto:sha(<>), + crypto:rand_bytes(crypto:rand_uniform(128, 4096))) || + X <- lists:seq(1, 100)], Handle. basics_test_() -> {setup, fun() -> - open_test_db(?TEST_DATA_DIR) + open_test_db() end, fun(Handle) -> ok = ?MODULE:close(Handle) @@ -193,16 +194,16 @@ basics_test_() -> {inorder, [{"open and close a database", fun() -> - Handle = open_test_db(Handle) + Handle = open_test_db() end}, {"create, then drop an empty database", fun() -> - Handle = open_test_db(Handle), + Handle = open_test_db(), ?assertMatch(ok, ?MODULE:drop(Handle)) end}, {"create, put an item, get it, then drop the database", fun() -> - Handle = open_test_db(Handle), + Handle = open_test_db(), ?assertMatch(ok, ?MODULE:put(Handle, <<"a">>, <<"apple">>)), ?assertMatch(ok, ?MODULE:put(Handle, <<"b">>, <<"boy">>)), ?assertMatch(ok, ?MODULE:put(Handle, <<"c">>, <<"cat">>)), @@ -216,4 +217,63 @@ basics_test_() -> ]} end}. +-ifdef(EQC). + +qc(P) -> + ?assert(eqc:quickcheck(?QC_OUT(P))). + +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([], _Handle, Acc0) -> + Acc0; +apply_kv_ops([{put, K, V} | Rest], Handle, Acc0) -> + ok = ?MODULE:put(Handle, K, V), + apply_kv_ops(Rest, Handle, orddict:store(K, V, Acc0)); +apply_kv_ops([{del, K, _} | Rest], Handle, Acc0) -> + ok = case ?MODULE:del(Handle, K) of + ok -> + ok; + not_found -> + ok; + Else -> + Else + end, + apply_kv_ops(Rest, Handle, orddict:store(K, deleted, Acc0)). + +prop_put_delete() -> + ?LET({Keys, Values}, {keys(), values()}, + ?FORALL(Ops, eqc_gen:non_empty(list(ops(Keys, Values))), + begin + {ok, CWD} = file:get_cwd(), + DataDir = filename:join([CWD, "test", "eqc"]), + ?cmd("rm -rf " ++ DataDir), + ok = filelib:ensure_dir(filename:join([DataDir, "x"])), + {ok, Handle} = ?MODULE:open(DataDir, 2147483648), + try + Model = apply_kv_ops(Ops, Handle, []), + + %% Validate that all deleted values return not_found + F = fun({K, deleted}) -> + ?assertEqual(not_found, ?MODULE:get(Handle, K)); + ({K, V}) -> + ?assertEqual({ok, V}, ?MODULE:get(Handle, K)) + end, + lists:map(F, Model), + true + after + ?MODULE:close(Handle) + end + end)). + +prop_put_delete_test_() -> + {timeout, 3*60, fun() -> qc(prop_put_delete()) end}. + +-endif. -endif.