You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ei...@apache.org on 2015/07/16 22:10:12 UTC
couchdb-khash git commit: Migrate the tests from etap to eunit
Repository: couchdb-khash
Updated Branches:
refs/heads/master f8a77ddb1 -> 6938d72be
Migrate the tests from etap to eunit
All the etap tests converted to eunit.
COUCHDB-2590
Project: http://git-wip-us.apache.org/repos/asf/couchdb-khash/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-khash/commit/6938d72b
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-khash/tree/6938d72b
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-khash/diff/6938d72b
Branch: refs/heads/master
Commit: 6938d72be999c4fb876aa890c1c60105216dc51b
Parents: f8a77dd
Author: Eric Avdey <ei...@eiri.ca>
Authored: Mon May 4 15:38:27 2015 -0300
Committer: Eric Avdey <ei...@eiri.ca>
Committed: Thu Jul 16 16:56:40 2015 -0300
----------------------------------------------------------------------
.gitignore | 2 +
Makefile | 8 +-
test/001-load.t | 19 --
test/002-basics.t | 29 --
test/003-randomized.t | 147 ----------
test/004-race-dict-fetch.t | 59 ----
test/005-race-dict-store.t | 56 ----
test/006-iterators.t | 115 --------
test/etap.erl | 612 ----------------------------------------
test/khash_test.erl | 441 +++++++++++++++++++++++++++++
test/util.erl | 25 --
11 files changed, 447 insertions(+), 1066 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index e57b0dc..ff360df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
c_src/*.o
ebin/
test/*.beam
+.eunit
+.rebar
priv/*.so
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/Makefile
----------------------------------------------------------------------
diff --git a/Makefile b/Makefile
index 5ecb125..73f4484 100644
--- a/Makefile
+++ b/Makefile
@@ -18,15 +18,15 @@ build:
$(REBAR) compile
-etap: test/etap.beam test/util.beam test/gen_term.beam
- prove test/*.t
+eunit:
+ $(REBAR) eunit
-check: build etap
+check: build eunit
%.beam: %.erl
erlc -o test/ $<
-.PHONY: all clean distclean build etap check
+.PHONY: all clean distclean build eunit check
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/test/001-load.t
----------------------------------------------------------------------
diff --git a/test/001-load.t b/test/001-load.t
deleted file mode 100755
index 27b3c07..0000000
--- a/test/001-load.t
+++ /dev/null
@@ -1,19 +0,0 @@
-#! /usr/bin/env escript
-%% This file is part of khash released under the MIT license.
-%% See the LICENSE file for more information.
-%% Copyright 2013 Cloudant, Inc <su...@cloudant.com>
-
-main([]) ->
- code:add_pathz("test"),
- code:add_pathz("ebin"),
-
- Modules = [
- khash
- ],
-
- etap:plan(length(Modules)),
- lists:foreach(fun(M) ->
- etap:loaded_ok(M, "Loaded " ++ atom_to_list(M))
- end, Modules),
- etap:end_tests().
-
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/test/002-basics.t
----------------------------------------------------------------------
diff --git a/test/002-basics.t b/test/002-basics.t
deleted file mode 100755
index 058b820..0000000
--- a/test/002-basics.t
+++ /dev/null
@@ -1,29 +0,0 @@
-#! /usr/bin/env escript
-%% This file is part of khash released under the MIT license.
-%% See the LICENSE file for more information.
-%% Copyright 2013 Cloudant, Inc <su...@cloudant.com>
-
--mode(compile).
-
-main([]) ->
- code:add_pathz("test"),
- util:run(12, fun() ->
- test(),
- ok
- end).
-
-
-test() ->
- {ok, C} = khash:new(),
- etap:is(khash:lookup(C, <<"foo">>), not_found, "Lookup missing is ok"),
- etap:is(khash:get(C, <<"foo">>), undefined, "Get missing is ok"),
- etap:is(khash:del(C, <<"foo">>), not_found, "Del missing is ok"),
- etap:is(khash:put(C, <<"foo">>, bar), ok, "Stored a key"),
- etap:is(khash:lookup(C, <<"foo">>), {value, bar}, "Lookuped a key"),
- etap:is(khash:get(C, <<"foo">>), bar, "Retrieved a key"),
- etap:is(khash:put(C, <<"bar">>, foo), ok, "Stored a key"),
- etap:is(khash:size(C), 2, "Correct size for hash"),
- etap:is(khash:del(C, <<"foo">>), ok, "Deleted a key"),
- etap:is(khash:size(C), 1, "Correct size after delete"),
- etap:is(khash:clear(C), ok, "Cleared the hash"),
- etap:is(khash:size(C), 0, "Correct size after clear").
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/test/003-randomized.t
----------------------------------------------------------------------
diff --git a/test/003-randomized.t b/test/003-randomized.t
deleted file mode 100755
index 5695ce8..0000000
--- a/test/003-randomized.t
+++ /dev/null
@@ -1,147 +0,0 @@
-#! /usr/bin/env escript
-%% This file is part of khash released under the MIT license.
-%% See the LICENSE file for more information.
-%% Copyright 2013 Cloudant, Inc <su...@cloudant.com>
-
--mode(compile).
-
-num_cycles() -> 10000.
-
-main([]) ->
- code:add_pathz("test"),
- random:seed(erlang:now()),
-
- util:run(num_cycles(), fun() ->
- test_random(),
- ok
- end).
-
-
-test_random() ->
- D = dict:new(),
- {ok, H} = khash:new(),
-
- Actions = [
- {0.1, fun(S) -> run_clear(S) end},
- {1.0, fun(S) -> run_get2(S) end},
- {1.0, fun(S) -> run_get3(S) end},
- {1.0, fun(S) -> run_put(S) end},
- {1.0, fun(S) -> run_del(S) end},
- {0.5, fun(S) -> run_size(S) end},
- %{0.3, fun(S) -> run_keys(S) end},
- {0.3, fun(S) -> run_to_list(S) end}
- ],
-
- run(Actions, num_cycles(), {D, H}).
-
-
-run(_, N, _S) when N =< 0 ->
- ok;
-run(Actions, N, S0) ->
- Action = weighted_choice(Actions),
- S1 = Action(S0),
- true = check_state(S1),
- run(Actions, N-1, S1).
-
-
-run_clear({_D0, H}) ->
- ok = khash:clear(H),
- {dict:new(), H}.
-
-
-run_get2({D, H}) ->
- K = random_key(D),
- case dict:find(K, D) of
- {ok, Value} ->
- {value, Value} = khash:lookup(H, K),
- Value = khash:get(H, K);
- error ->
- not_found = khash:lookup(H, K),
- undefined = khash:get(H, K)
- end,
- {D, H}.
-
-
-run_get3({D, H}) ->
- K = random_key(D),
- case dict:find(K, D) of
- {ok, Value} ->
- {value, Value} = khash:lookup(H, K),
- Value = khash:get(H, K);
- error ->
- Val = random_val(),
- Val = khash:get(H, K, Val)
- end,
- {D, H}.
-
-
-run_put({D0, H}) ->
- K = random_key(D0),
- V = random_val(),
- D1 = dict:store(K, V, D0),
- ok = khash:put(H, K, V),
- {D1, H}.
-
-
-run_del({D0, H}) ->
- K = random_key(D0),
- D1 = case dict:is_key(K, D0) of
- true ->
- ok = khash:del(H, K),
- dict:erase(K, D0);
- false ->
- not_found = khash:del(H, K),
- D0
- end,
- {D1, H}.
-
-
-run_size({D, H}) ->
- S = dict:size(D),
- S = khash:size(H),
- {D, H}.
-
-
-%run_keys({D, H}) ->
-% DKeys = lists:sort(dict:fetch_keys(D)),
-% {ok, HKeys0} = khash:keys(H),
-% HKeys = lists:sort(HKeys0),
-% DKeys = HKeys,
-% {D, H}.
-
-
-run_to_list({D, H}) ->
- DKVs = lists:sort(dict:to_list(D)),
- HKVs = lists:sort(khash:to_list(H)),
- DKVs = HKVs,
- {D, H}.
-
-
-check_state({D, H}) ->
- DKVs = lists:sort(dict:to_list(D)),
- HKVs = lists:sort(khash:to_list(H)),
- etap:is(DKVs, HKVs, "State matches dict implementation").
-
-
-weighted_choice(Items0) ->
- Items = lists:sort(Items0),
- Sum = lists:sum([W || {W, _} <- Items]),
- Choice = random:uniform() * Sum,
- weighted_choice(Items, 0.0, Choice).
-
-
-weighted_choice([], _, _) ->
- throw(bad_choice);
-weighted_choice([{W, _} | Rest], S, C) when W + S < C ->
- weighted_choice(Rest, W+S, C);
-weighted_choice([{_, I} | _], _, _) ->
- I.
-
-
-random_key(D) ->
- Keys = lists:usort(dict:fetch_keys(D) ++ [foo]),
- lists:nth(random:uniform(length(Keys)), Keys).
-
-
-random_val() ->
- gen_term:any().
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/test/004-race-dict-fetch.t
----------------------------------------------------------------------
diff --git a/test/004-race-dict-fetch.t b/test/004-race-dict-fetch.t
deleted file mode 100755
index 7102a9f..0000000
--- a/test/004-race-dict-fetch.t
+++ /dev/null
@@ -1,59 +0,0 @@
-#! /usr/bin/env escript
-%% This file is part of khash released under the MIT license.
-%% See the LICENSE file for more information.
-%% Copyright 2013 Cloudant, Inc <su...@cloudant.com>
-
--mode(compile).
-
-num_cycles() -> 10000000.
-num_kvs() -> 5000.
-
-
-main([]) ->
- % Let the VM settle for a bit
- receive after 1000 -> ok end,
-
- code:add_pathz("test"),
- util:run(1, fun() ->
- test(),
- ok
- end).
-
-
-test() ->
- {DTime, _} = timer:tc(fun() -> test_dict() end, []),
- {KTime, _} = timer:tc(fun() -> test_khash() end, []),
- etap:diag("Dict: ~10b", [DTime]),
- etap:diag("KHash: ~10b", [KTime]),
- etap:is_greater(DTime, KTime, "Dict is slower than khash").
-
-
-test_dict() ->
- erlang:garbage_collect(),
- D = dict:from_list(kv_data()),
- test_dict(D, num_cycles()).
-
-
-test_dict(_D, 0) ->
- ok;
-test_dict(D, NumCycles) ->
- bing = dict:fetch(1, D),
- test_dict(D, NumCycles - 1).
-
-
-test_khash() ->
- erlang:garbage_collect(),
- {ok, H} = khash:from_list(kv_data()),
- test_khash(H, num_cycles()).
-
-
-test_khash(_H, 0) ->
- ok;
-test_khash(H, NumCycles) ->
- bing = khash:get(H, 1),
- test_khash(H, NumCycles - 1).
-
-
-kv_data() ->
- [{I, bing} || I <- lists:seq(1, num_kvs())].
-
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/test/005-race-dict-store.t
----------------------------------------------------------------------
diff --git a/test/005-race-dict-store.t b/test/005-race-dict-store.t
deleted file mode 100755
index de73c62..0000000
--- a/test/005-race-dict-store.t
+++ /dev/null
@@ -1,56 +0,0 @@
-#! /usr/bin/env escript
-%% This file is part of khash released under the MIT license.
-%% See the LICENSE file for more information.
-%% Copyright 2013 Cloudant, Inc <su...@cloudant.com>
-
--mode(compile).
-
-num_cycles() -> 1000000.
-num_kvs() -> 10000.
-
-main([]) ->
- % Let the VM settle for a bit
- receive after 1000 -> ok end,
-
- code:add_pathz("test"),
- util:run(1, fun() ->
- test(),
- ok
- end).
-
-
-test() ->
- {DTime, _} = timer:tc(fun() -> test_dict() end, []),
- {KTime, _} = timer:tc(fun() -> test_khash() end, []),
- etap:diag("Dict: ~10b", [DTime]),
- etap:diag("KHash: ~10b", [KTime]),
- etap:is_greater(DTime, KTime, "Dict is slower than khash").
-
-
-test_dict() ->
- D = dict:from_list(kv_data()),
- test_dict(D, num_cycles()).
-
-
-test_dict(_D, 0) ->
- ok;
-test_dict(D, NumCycles) ->
- D2 = dict:store(1, bing, D),
- test_dict(D2, NumCycles - 1).
-
-
-test_khash() ->
- {ok, H} = khash:from_list(kv_data()),
- test_khash(H, num_cycles()).
-
-
-test_khash(_H, 0) ->
- ok;
-test_khash(H, NumCycles) ->
- khash:put(H, 1, bing),
- test_khash(H, NumCycles - 1).
-
-
-kv_data() ->
- [{I, bing} || I <- lists:seq(1, num_kvs())].
-
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/test/006-iterators.t
----------------------------------------------------------------------
diff --git a/test/006-iterators.t b/test/006-iterators.t
deleted file mode 100755
index 02a7478..0000000
--- a/test/006-iterators.t
+++ /dev/null
@@ -1,115 +0,0 @@
-#! /usr/bin/env escript
-%% This file is part of khash released under the MIT license.
-%% See the LICENSE file for more information.
-%% Copyright 2013 Cloudant, Inc <su...@cloudant.com>
-
--mode(compile).
-
-main([]) ->
- code:add_pathz("test"),
- util:run(12, fun() ->
- test(),
- ok
- end).
-
-
-test() ->
- test_basic(),
- test_multi(),
- test_expiration(),
- test_no_expiration(),
- ok.
-
-
-test_basic() ->
- {ok, H} = khash:new(),
- khash:put(H, foo, bar),
- {ok, I} = khash:iter(H),
- etap:is(khash:iter_next(I), {foo,bar}, "Got only kv pair as first element"),
- etap:is(khash:iter_next(I), end_of_table, "Only the one kv pair exists"),
- FoldFun = fun(K, V, Acc) -> [{K,V} | Acc] end,
- etap:is(khash:fold(H, FoldFun, []), [{foo,bar}], "fold works").
-
-
-test_multi() ->
- {ok, H} = khash:new(),
- KVs = [{I, I} || I <- lists:seq(1, 10)],
- lists:foreach(fun({K,V}) -> khash:put(H, K, V) end, KVs),
- {ok, I} = khash:iter(H),
- ReadKVs = test_multi_read(I, []),
- etap:is(ReadKVs, KVs, "Read the same exact key/val pairs").
-
-
-test_multi_read(Iter, KVs) ->
- case khash:iter_next(Iter) of
- {K, V} ->
- test_multi_read(Iter, [{K,V} | KVs]);
- end_of_table ->
- lists:sort(KVs)
- end.
-
-
-test_expiration() ->
- test_expiration("put", fun(H) ->
- khash:put(H, foo, bar2),
- ok
- end),
-
- test_expiration("del", fun(H) ->
- khash:del(H, foo),
- ok
- end),
-
- test_expiration("clear", fun(H) ->
- khash:clear(H),
- ok
- end).
-
-
-test_expiration(FunName, Fun) ->
- Error = {error, expired_iterator},
- {ok, H} = khash:new(),
- khash:put(H, foo, bar),
- {ok, I} = khash:iter(H),
- ok = Fun(H),
- Msg = FunName ++ " expires iterators",
- etap:is(khash:iter_next(I), Error, Msg).
-
-
-test_no_expiration() ->
- test_no_expiration("to_list", fun(H) ->
- [{foo,bar}] = khash:to_list(H),
- ok
- end),
-
- test_no_expiration("lookup", fun(H) ->
- {value,bar} = khash:lookup(H,foo),
- ok
- end),
-
- test_no_expiration("get", fun(H) ->
- bar = khash:get(H, foo),
- ok
- end),
-
- test_no_expiration("size", fun(H) ->
- 1 = khash:size(H),
- ok
- end),
-
- test_no_expiration("iteration", fun(H) ->
- {ok, I} = khash:iter(H),
- {foo, bar} = khash:iter_next(I),
- end_of_table = khash:iter_next(I),
- ok
- end).
-
-
-test_no_expiration(FunName, Fun) ->
- Error = {error, expired_iterator},
- {ok, H} = khash:new(),
- khash:put(H, foo, bar),
- {ok, I} = khash:iter(H),
- ok = Fun(H),
- Msg = FunName ++ " doesn't expire iterators",
- etap:isnt(khash:iter_next(I), Error, Msg).
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/test/etap.erl
----------------------------------------------------------------------
diff --git a/test/etap.erl b/test/etap.erl
deleted file mode 100644
index 6924d09..0000000
--- a/test/etap.erl
+++ /dev/null
@@ -1,612 +0,0 @@
-%% Copyright (c) 2008-2009 Nick Gerakines <ni...@gerakines.net>
-%%
-%% Permission is hereby granted, free of charge, to any person
-%% obtaining a copy of this software and associated documentation
-%% files (the "Software"), to deal in the Software without
-%% restriction, including without limitation the rights to use,
-%% copy, modify, merge, publish, distribute, sublicense, and/or sell
-%% copies of the Software, and to permit persons to whom the
-%% Software is furnished to do so, subject to the following
-%% conditions:
-%%
-%% The above copyright notice and this permission notice shall be
-%% included in all copies or substantial portions of the Software.
-%%
-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-%% OTHER DEALINGS IN THE SOFTWARE.
-%%
-%% @author Nick Gerakines <ni...@gerakines.net> [http://socklabs.com/]
-%% @author Jeremy Wall <je...@marzhillstudios.com>
-%% @version 0.3.4
-%% @copyright 2007-2008 Jeremy Wall, 2008-2009 Nick Gerakines
-%% @reference http://testanything.org/wiki/index.php/Main_Page
-%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol
-%% @todo Finish implementing the skip directive.
-%% @todo Document the messages handled by this receive loop.
-%% @todo Explain in documentation why we use a process to handle test input.
-%% @doc etap is a TAP testing module for Erlang components and applications.
-%% This module allows developers to test their software using the TAP method.
-%%
-%% <blockquote cite="http://en.wikipedia.org/wiki/Test_Anything_Protocol"><p>
-%% TAP, the Test Anything Protocol, is a simple text-based interface between
-%% testing modules in a test harness. TAP started life as part of the test
-%% harness for Perl but now has implementations in C/C++, Python, PHP, Perl
-%% and probably others by the time you read this.
-%% </p></blockquote>
-%%
-%% The testing process begins by defining a plan using etap:plan/1, running
-%% a number of etap tests and then calling eta:end_tests/0. Please refer to
-%% the Erlang modules in the t directory of this project for example tests.
--module(etap).
--vsn("0.3.4").
-
--export([
- ensure_test_server/0,
- start_etap_server/0,
- test_server/1,
- msg/1, msg/2,
- diag/1, diag/2,
- expectation_mismatch_message/3,
- plan/1,
- end_tests/0,
- not_ok/2, ok/2, is_ok/2, is/3, isnt/3, any/3, none/3,
- fun_is/3, expect_fun/3, expect_fun/4,
- is_greater/3,
- skip/1, skip/2,
- datetime/1,
- skip/3,
- bail/0, bail/1,
- test_state/0, failure_count/0
-]).
-
--export([
- contains_ok/3,
- is_before/4
-]).
-
--export([
- is_pid/2,
- is_alive/2,
- is_mfa/3
-]).
-
--export([
- loaded_ok/2,
- can_ok/2, can_ok/3,
- has_attrib/2, is_attrib/3,
- is_behaviour/2
-]).
-
--export([
- dies_ok/2,
- lives_ok/2,
- throws_ok/3
-]).
-
-
--record(test_state, {
- planned = 0,
- count = 0,
- pass = 0,
- fail = 0,
- skip = 0,
- skip_reason = ""
-}).
-
-%% @spec plan(N) -> Result
-%% N = unknown | skip | {skip, string()} | integer()
-%% Result = ok
-%% @doc Create a test plan and boot strap the test server.
-plan(unknown) ->
- ensure_test_server(),
- etap_server ! {self(), plan, unknown},
- ok;
-plan(skip) ->
- io:format("1..0 # skip~n");
-plan({skip, Reason}) ->
- io:format("1..0 # skip ~s~n", [Reason]);
-plan(N) when is_integer(N), N > 0 ->
- ensure_test_server(),
- etap_server ! {self(), plan, N},
- ok.
-
-%% @spec end_tests() -> ok
-%% @doc End the current test plan and output test results.
-%% @todo This should probably be done in the test_server process.
-end_tests() ->
- case whereis(etap_server) of
- undefined -> self() ! true;
- _ -> etap_server ! {self(), state}
- end,
- State = receive X -> X end,
- if
- State#test_state.planned == -1 ->
- io:format("1..~p~n", [State#test_state.count]);
- true ->
- ok
- end,
- case whereis(etap_server) of
- undefined -> ok;
- _ -> etap_server ! done, ok
- end.
-
-bail() ->
- bail("").
-
-bail(Reason) ->
- etap_server ! {self(), diag, "Bail out! " ++ Reason},
- etap_server ! done, ok,
- ok.
-
-%% @spec test_state() -> Return
-%% Return = test_state_record() | {error, string()}
-%% @doc Return the current test state
-test_state() ->
- etap_server ! {self(), state},
- receive
- X when is_record(X, test_state) -> X
- after
- 1000 -> {error, "Timed out waiting for etap server reply.~n"}
- end.
-
-%% @spec failure_count() -> Return
-%% Return = integer() | {error, string()}
-%% @doc Return the current failure count
-failure_count() ->
- case test_state() of
- #test_state{fail=FailureCount} -> FailureCount;
- X -> X
- end.
-
-%% @spec msg(S) -> ok
-%% S = string()
-%% @doc Print a message in the test output.
-msg(S) -> etap_server ! {self(), diag, S}, ok.
-
-%% @spec msg(Format, Data) -> ok
-%% Format = atom() | string() | binary()
-%% Data = [term()]
-%% UnicodeList = [Unicode]
-%% Unicode = int()
-%% @doc Print a message in the test output.
-%% Function arguments are passed through io_lib:format/2.
-msg(Format, Data) -> msg(io_lib:format(Format, Data)).
-
-%% @spec diag(S) -> ok
-%% S = string()
-%% @doc Print a debug/status message related to the test suite.
-diag(S) -> msg("# " ++ S).
-
-%% @spec diag(Format, Data) -> ok
-%% Format = atom() | string() | binary()
-%% Data = [term()]
-%% UnicodeList = [Unicode]
-%% Unicode = int()
-%% @doc Print a debug/status message related to the test suite.
-%% Function arguments are passed through io_lib:format/2.
-diag(Format, Data) -> diag(io_lib:format(Format, Data)).
-
-%% @spec expectation_mismatch_message(Got, Expected, Desc) -> ok
-%% Got = any()
-%% Expected = any()
-%% Desc = string()
-%% @doc Print an expectation mismatch message in the test output.
-expectation_mismatch_message(Got, Expected, Desc) ->
- msg(" ---"),
- msg(" description: ~p", [Desc]),
- msg(" found: ~p", [Got]),
- msg(" wanted: ~p", [Expected]),
- msg(" ..."),
- ok.
-
-% @spec evaluate(Pass, Got, Expected, Desc) -> Result
-%% Pass = true | false
-%% Got = any()
-%% Expected = any()
-%% Desc = string()
-%% Result = true | false
-%% @doc Evaluate a test statement, printing an expectation mismatch message
-%% if the test failed.
-evaluate(Pass, Got, Expected, Desc) ->
- case mk_tap(Pass, Desc) of
- false ->
- expectation_mismatch_message(Got, Expected, Desc),
- false;
- true ->
- true
- end.
-
-%% @spec ok(Expr, Desc) -> Result
-%% Expr = true | false
-%% Desc = string()
-%% Result = true | false
-%% @doc Assert that a statement is true.
-ok(Expr, Desc) -> evaluate(Expr == true, Expr, true, Desc).
-
-%% @spec not_ok(Expr, Desc) -> Result
-%% Expr = true | false
-%% Desc = string()
-%% Result = true | false
-%% @doc Assert that a statement is false.
-not_ok(Expr, Desc) -> evaluate(Expr == false, Expr, false, Desc).
-
-%% @spec is_ok(Expr, Desc) -> Result
-%% Expr = any()
-%% Desc = string()
-%% Result = true | false
-%% @doc Assert that two values are the same.
-is_ok(Expr, Desc) -> evaluate(Expr == ok, Expr, ok, Desc).
-
-%% @spec is(Got, Expected, Desc) -> Result
-%% Got = any()
-%% Expected = any()
-%% Desc = string()
-%% Result = true | false
-%% @doc Assert that two values are the same.
-is(Got, Expected, Desc) -> evaluate(Got == Expected, Got, Expected, Desc).
-
-%% @spec isnt(Got, Expected, Desc) -> Result
-%% Got = any()
-%% Expected = any()
-%% Desc = string()
-%% Result = true | false
-%% @doc Assert that two values are not the same.
-isnt(Got, Expected, Desc) -> evaluate(Got /= Expected, Got, Expected, Desc).
-
-%% @spec is_greater(ValueA, ValueB, Desc) -> Result
-%% ValueA = number()
-%% ValueB = number()
-%% Desc = string()
-%% Result = true | false
-%% @doc Assert that an integer is greater than another.
-is_greater(ValueA, ValueB, Desc) when is_integer(ValueA), is_integer(ValueB) ->
- mk_tap(ValueA > ValueB, Desc).
-
-%% @spec any(Got, Items, Desc) -> Result
-%% Got = any()
-%% Items = [any()]
-%% Desc = string()
-%% Result = true | false
-%% @doc Assert that an item is in a list.
-any(Got, Items, Desc) when is_function(Got) ->
- is(lists:any(Got, Items), true, Desc);
-any(Got, Items, Desc) ->
- is(lists:member(Got, Items), true, Desc).
-
-%% @spec none(Got, Items, Desc) -> Result
-%% Got = any()
-%% Items = [any()]
-%% Desc = string()
-%% Result = true | false
-%% @doc Assert that an item is not in a list.
-none(Got, Items, Desc) when is_function(Got) ->
- is(lists:any(Got, Items), false, Desc);
-none(Got, Items, Desc) ->
- is(lists:member(Got, Items), false, Desc).
-
-%% @spec fun_is(Fun, Expected, Desc) -> Result
-%% Fun = function()
-%% Expected = any()
-%% Desc = string()
-%% Result = true | false
-%% @doc Use an anonymous function to assert a pattern match.
-fun_is(Fun, Expected, Desc) when is_function(Fun) ->
- is(Fun(Expected), true, Desc).
-
-%% @spec expect_fun(ExpectFun, Got, Desc) -> Result
-%% ExpectFun = function()
-%% Got = any()
-%% Desc = string()
-%% Result = true | false
-%% @doc Use an anonymous function to assert a pattern match, using actual
-%% value as the argument to the function.
-expect_fun(ExpectFun, Got, Desc) ->
- evaluate(ExpectFun(Got), Got, ExpectFun, Desc).
-
-%% @spec expect_fun(ExpectFun, Got, Desc, ExpectStr) -> Result
-%% ExpectFun = function()
-%% Got = any()
-%% Desc = string()
-%% ExpectStr = string()
-%% Result = true | false
-%% @doc Use an anonymous function to assert a pattern match, using actual
-%% value as the argument to the function.
-expect_fun(ExpectFun, Got, Desc, ExpectStr) ->
- evaluate(ExpectFun(Got), Got, ExpectStr, Desc).
-
-%% @equiv skip(TestFun, "")
-skip(TestFun) when is_function(TestFun) ->
- skip(TestFun, "").
-
-%% @spec skip(TestFun, Reason) -> ok
-%% TestFun = function()
-%% Reason = string()
-%% @doc Skip a test.
-skip(TestFun, Reason) when is_function(TestFun), is_list(Reason) ->
- begin_skip(Reason),
- catch TestFun(),
- end_skip(),
- ok.
-
-%% @spec skip(Q, TestFun, Reason) -> ok
-%% Q = true | false | function()
-%% TestFun = function()
-%% Reason = string()
-%% @doc Skips a test conditionally. The first argument to this function can
-%% either be the 'true' or 'false' atoms or a function that returns 'true' or
-%% 'false'.
-skip(QFun, TestFun, Reason) when is_function(QFun), is_function(TestFun), is_list(Reason) ->
- case QFun() of
- true -> begin_skip(Reason), TestFun(), end_skip();
- _ -> TestFun()
- end,
- ok;
-
-skip(Q, TestFun, Reason) when is_function(TestFun), is_list(Reason), Q == true ->
- begin_skip(Reason),
- TestFun(),
- end_skip(),
- ok;
-
-skip(_, TestFun, Reason) when is_function(TestFun), is_list(Reason) ->
- TestFun(),
- ok.
-
-%% @private
-begin_skip(Reason) ->
- etap_server ! {self(), begin_skip, Reason}.
-
-%% @private
-end_skip() ->
- etap_server ! {self(), end_skip}.
-
-%% @spec contains_ok(string(), string(), string()) -> true | false
-%% @doc Assert that a string is contained in another string.
-contains_ok(Source, String, Desc) ->
- etap:isnt(
- string:str(Source, String),
- 0,
- Desc
- ).
-
-%% @spec is_before(string(), string(), string(), string()) -> true | false
-%% @doc Assert that a string comes before another string within a larger body.
-is_before(Source, StringA, StringB, Desc) ->
- etap:is_greater(
- string:str(Source, StringB),
- string:str(Source, StringA),
- Desc
- ).
-
-%% @doc Assert that a given variable is a pid.
-is_pid(Pid, Desc) when is_pid(Pid) -> etap:ok(true, Desc);
-is_pid(_, Desc) -> etap:ok(false, Desc).
-
-%% @doc Assert that a given process/pid is alive.
-is_alive(Pid, Desc) ->
- etap:ok(erlang:is_process_alive(Pid), Desc).
-
-%% @doc Assert that the current function of a pid is a given {M, F, A} tuple.
-is_mfa(Pid, MFA, Desc) ->
- etap:is({current_function, MFA}, erlang:process_info(Pid, current_function), Desc).
-
-%% @spec loaded_ok(atom(), string()) -> true | false
-%% @doc Assert that a module has been loaded successfully.
-loaded_ok(M, Desc) when is_atom(M) ->
- etap:fun_is(fun({module, _}) -> true; (_) -> false end, code:load_file(M), Desc).
-
-%% @spec can_ok(atom(), atom()) -> true | false
-%% @doc Assert that a module exports a given function.
-can_ok(M, F) when is_atom(M), is_atom(F) ->
- Matches = [X || {X, _} <- M:module_info(exports), X == F],
- etap:ok(Matches > 0, lists:concat([M, " can ", F])).
-
-%% @spec can_ok(atom(), atom(), integer()) -> true | false
-%% @doc Assert that a module exports a given function with a given arity.
-can_ok(M, F, A) when is_atom(M); is_atom(F), is_number(A) ->
- Matches = [X || X <- M:module_info(exports), X == {F, A}],
- etap:ok(Matches > 0, lists:concat([M, " can ", F, "/", A])).
-
-%% @spec has_attrib(M, A) -> true | false
-%% M = atom()
-%% A = atom()
-%% @doc Asserts that a module has a given attribute.
-has_attrib(M, A) when is_atom(M), is_atom(A) ->
- etap:isnt(
- proplists:get_value(A, M:module_info(attributes), 'asdlkjasdlkads'),
- 'asdlkjasdlkads',
- lists:concat([M, " has attribute ", A])
- ).
-
-%% @spec has_attrib(M, A. V) -> true | false
-%% M = atom()
-%% A = atom()
-%% V = any()
-%% @doc Asserts that a module has a given attribute with a given value.
-is_attrib(M, A, V) when is_atom(M) andalso is_atom(A) ->
- etap:is(
- proplists:get_value(A, M:module_info(attributes)),
- [V],
- lists:concat([M, "'s ", A, " is ", V])
- ).
-
-%% @spec is_behavior(M, B) -> true | false
-%% M = atom()
-%% B = atom()
-%% @doc Asserts that a given module has a specific behavior.
-is_behaviour(M, B) when is_atom(M) andalso is_atom(B) ->
- is_attrib(M, behaviour, B).
-
-%% @doc Assert that an exception is raised when running a given function.
-dies_ok(F, Desc) ->
- case (catch F()) of
- {'EXIT', _} -> etap:ok(true, Desc);
- _ -> etap:ok(false, Desc)
- end.
-
-%% @doc Assert that an exception is not raised when running a given function.
-lives_ok(F, Desc) ->
- etap:is(try_this(F), success, Desc).
-
-%% @doc Assert that the exception thrown by a function matches the given exception.
-throws_ok(F, Exception, Desc) ->
- try F() of
- _ -> etap:ok(nok, Desc)
- catch
- _:E ->
- etap:is(E, Exception, Desc)
- end.
-
-%% @private
-%% @doc Run a function and catch any exceptions.
-try_this(F) when is_function(F, 0) ->
- try F() of
- _ -> success
- catch
- throw:E -> {throw, E};
- error:E -> {error, E};
- exit:E -> {exit, E}
- end.
-
-%% @private
-%% @doc Start the etap_server process if it is not running already.
-ensure_test_server() ->
- case whereis(etap_server) of
- undefined ->
- proc_lib:start(?MODULE, start_etap_server,[]);
- _ ->
- diag("The test server is already running.")
- end.
-
-%% @private
-%% @doc Start the etap_server loop and register itself as the etap_server
-%% process.
-start_etap_server() ->
- catch register(etap_server, self()),
- proc_lib:init_ack(ok),
- etap:test_server(#test_state{
- planned = 0,
- count = 0,
- pass = 0,
- fail = 0,
- skip = 0,
- skip_reason = ""
- }).
-
-
-%% @private
-%% @doc The main etap_server receive/run loop. The etap_server receive loop
-%% responds to seven messages apperatining to failure or passing of tests.
-%% It is also used to initiate the testing process with the {_, plan, _}
-%% message that clears the current test state.
-test_server(State) ->
- NewState = receive
- {_From, plan, unknown} ->
- io:format("# Current time local ~s~n", [datetime(erlang:localtime())]),
- io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]),
- State#test_state{
- planned = -1,
- count = 0,
- pass = 0,
- fail = 0,
- skip = 0,
- skip_reason = ""
- };
- {_From, plan, N} ->
- io:format("# Current time local ~s~n", [datetime(erlang:localtime())]),
- io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]),
- io:format("1..~p~n", [N]),
- State#test_state{
- planned = N,
- count = 0,
- pass = 0,
- fail = 0,
- skip = 0,
- skip_reason = ""
- };
- {_From, begin_skip, Reason} ->
- State#test_state{
- skip = 1,
- skip_reason = Reason
- };
- {_From, end_skip} ->
- State#test_state{
- skip = 0,
- skip_reason = ""
- };
- {_From, pass, Desc} ->
- FullMessage = skip_diag(
- " - " ++ Desc,
- State#test_state.skip,
- State#test_state.skip_reason
- ),
- io:format("ok ~p ~s~n", [State#test_state.count + 1, FullMessage]),
- State#test_state{
- count = State#test_state.count + 1,
- pass = State#test_state.pass + 1
- };
-
- {_From, fail, Desc} ->
- FullMessage = skip_diag(
- " - " ++ Desc,
- State#test_state.skip,
- State#test_state.skip_reason
- ),
- io:format("not ok ~p ~s~n", [State#test_state.count + 1, FullMessage]),
- State#test_state{
- count = State#test_state.count + 1,
- fail = State#test_state.fail + 1
- };
- {From, state} ->
- From ! State,
- State;
- {_From, diag, Message} ->
- io:format("~s~n", [Message]),
- State;
- {From, count} ->
- From ! State#test_state.count,
- State;
- {From, is_skip} ->
- From ! State#test_state.skip,
- State;
- done ->
- exit(normal)
- end,
- test_server(NewState).
-
-%% @private
-%% @doc Process the result of a test and send it to the etap_server process.
-mk_tap(Result, Desc) ->
- IsSkip = lib:sendw(etap_server, is_skip),
- case [IsSkip, Result] of
- [_, true] ->
- etap_server ! {self(), pass, Desc},
- true;
- [1, _] ->
- etap_server ! {self(), pass, Desc},
- true;
- _ ->
- etap_server ! {self(), fail, Desc},
- false
- end.
-
-%% @private
-%% @doc Format a date/time string.
-datetime(DateTime) ->
- {{Year, Month, Day}, {Hour, Min, Sec}} = DateTime,
- io_lib:format("~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B", [Year, Month, Day, Hour, Min, Sec]).
-
-%% @private
-%% @doc Craft an output message taking skip/todo into consideration.
-skip_diag(Message, 0, _) ->
- Message;
-skip_diag(_Message, 1, "") ->
- " # SKIP";
-skip_diag(_Message, 1, Reason) ->
- " # SKIP : " ++ Reason.
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/test/khash_test.erl
----------------------------------------------------------------------
diff --git a/test/khash_test.erl b/test/khash_test.erl
new file mode 100644
index 0000000..cab9820
--- /dev/null
+++ b/test/khash_test.erl
@@ -0,0 +1,441 @@
+-module(khash_test).
+
+-compile([export_all]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-define(NUM_RAND_CYCLES, 10000).
+-define(NUM_CYCLES, 1000000).
+-define(NUM_KVS, 5000).
+
+load_test_() ->
+ {
+ "Loaded khash",
+ ?_assertEqual({module, khash}, code:load_file(khash))
+ }.
+
+basic_test_() ->
+ {
+ "khash basic operations",
+ {setup,
+ local,
+ fun() -> khash:new() end,
+ fun({ok, _}) -> ok end,
+ fun({ok, C}) ->
+ [
+ {
+ "Lookup missing is ok",
+ ?_assertEqual(not_found, khash:lookup(C, <<"foo">>))
+ },
+ {
+ "Get missing is ok",
+ ?_assertEqual(undefined, khash:get(C, <<"foo">>))
+ },
+ {
+ "Del missing is ok",
+ ?_assertEqual(not_found, khash:del(C, <<"foo">>))
+ },
+ {
+ "Stored a key",
+ ?_assertEqual(ok, khash:put(C, <<"foo">>, bar))
+ },
+ {
+ "Lookuped a key",
+ ?_assertEqual({value, bar}, khash:lookup(C, <<"foo">>))
+ },
+ {
+ "Retrieved a key",
+ ?_assertEqual(bar, khash:get(C, <<"foo">>))
+ },
+ {
+ "Stored a key",
+ ?_assertEqual(ok, khash:put(C, <<"bar">>, foo))
+ },
+ {
+ "Correct size for hash",
+ ?_assertEqual(2, khash:size(C))
+ },
+ {
+ "Deleted a key",
+ ?_assertEqual(ok, khash:del(C, <<"foo">>))
+ },
+ {
+ "Correct size after delete",
+ ?_assertEqual(1, khash:size(C))
+ },
+ {
+ "Cleared the hash",
+ ?_assertEqual(ok, khash:clear(C))
+ },
+ {
+ "Correct size after clear",
+ ?_assertEqual(0, khash:size(C))
+ }
+ ]
+ end
+ }
+ }.
+
+randomized_test_() ->
+ {
+ "khash randomized test",
+ {setup,
+ local,
+ fun() ->
+ random:seed(erlang:now()),
+ Dict = dict:new(),
+ {ok, KHash} = khash:new(),
+ Actions = [
+ {0.1, fun run_clear/1},
+ {1.0, fun run_get2/1},
+ {1.0, fun run_get3/1},
+ {1.0, fun run_put/1},
+ {1.0, fun run_del/1},
+ {0.5, fun run_size/1},
+ % {0.3, fun run_keys/1},
+ {0.3, fun run_to_list/1}
+ ],
+ {ok, Actions, ?NUM_RAND_CYCLES, {Dict, KHash}}
+ end,
+ fun(State) ->
+ {
+ "State matches dict implementation",
+ ?_assert(run_randomized(State, true))
+ }
+ end
+ }
+ }.
+
+compare_dict_test_() ->
+ {
+ "khash vs dict",
+ {setup,
+ fun() ->
+ % Let the VM settle for a bit
+ receive after 1000 -> ok end
+ end,
+ fun(ok) ->
+ [{
+ "Dict's fetch is slower than of khash",
+ ?_test(begin
+ {DTime, _} = timer:tc(fun() -> dict_fetch() end, []),
+ {KTime, _} = timer:tc(fun() -> khash_fetch() end, []),
+ ?debugFmt("Dict: ~10b", [DTime]),
+ ?debugFmt("KHash: ~10b", [KTime]),
+ ?assert(DTime > KTime)
+ end)
+ },
+ {
+ "Dict's store is slower than of khash",
+ ?_test(begin
+ {DTime, _} = timer:tc(fun() -> dict_store() end, []),
+ {KTime, _} = timer:tc(fun() -> khash_store() end, []),
+ ?debugFmt("Dict: ~10b", [DTime]),
+ ?debugFmt("KHash: ~10b", [KTime]),
+ ?assert(DTime > KTime)
+ end)
+ }]
+ end
+ }
+ }.
+
+basic_iterators_test_() ->
+ {
+ "khash itrators basics operations",
+ {setup,
+ local,
+ fun() ->
+ {ok, H} = khash:new(),
+ khash:put(H, foo, bar),
+ {ok, I} = khash:iter(H),
+ {ok, H, I}
+ end,
+ fun({ok, H, I}) ->
+ [
+ {
+ "Got only kv pair as first element",
+ ?_assertEqual(khash:iter_next(I), {foo,bar})
+ },
+ {
+ "Only the one kv pair exists",
+ ?_assertEqual(khash:iter_next(I), end_of_table)
+ },
+ {
+ "Fold works",
+ ?_test(begin
+ Fold = fun(K, V, Acc) -> [{K,V} | Acc] end,
+ ?assertEqual(khash:fold(H, Fold, []), [{foo,bar}])
+ end)
+ }
+ ]
+ end
+ }
+ }.
+
+multiread_iterators_test_() ->
+ {
+ "khash iterators multi-read test",
+ {setup,
+ local,
+ fun() ->
+ {ok, H} = khash:new(),
+ KVs = kv_data(10),
+ lists:foreach(fun({K,V}) -> khash:put(H, K, V) end, KVs),
+ {ok, I} = khash:iter(H),
+ {ok, I, KVs}
+ end,
+ fun({ok, I, KVs}) ->
+ {
+ "Read the same exact key/val pairs",
+ ?_assertEqual(khash_iterate(I, []), KVs)
+ }
+ end
+ }
+ }.
+
+expiration_iterators_test_() ->
+ {
+ "khash iterators exipiration functions",
+ {foreach,
+ local,
+ fun() ->
+ Err = {error, expired_iterator},
+ {ok, H} = khash:new(),
+ khash:put(H, foo, bar),
+ {ok, I} = khash:iter(H),
+ {ok, H, I, Err}
+ end,
+ [
+ fun({ok, H, I, Err}) ->
+ khash:put(H, foo, bar2),
+ {
+ "put expires iterators",
+ ?_assertEqual(khash:iter_next(I), Err)
+ }
+ end,
+ fun({ok, H, I, Err}) ->
+ khash:del(H, foo),
+ {
+ "del expires iterators",
+ ?_assertEqual(khash:iter_next(I), Err)
+ }
+ end,
+ fun({ok, H, I, Err}) ->
+ khash:clear(H),
+ {
+ "clear expires iterators",
+ ?_assertEqual(khash:iter_next(I), Err)
+ }
+ end
+ ]
+ }
+ }.
+
+no_expiration_iterators_test_() ->
+ {
+ "khash iterators no exipiration functions",
+ {foreach,
+ local,
+ fun() ->
+ Err = {error, expired_iterator},
+ {ok, H} = khash:new(),
+ khash:put(H, foo, bar),
+ {ok, I} = khash:iter(H),
+ {ok, H, I, Err}
+ end,
+ [
+ fun({ok, H, I, Err}) ->
+ [{foo, bar}] = khash:to_list(H),
+ {
+ "to_list doesn't expire iterators",
+ ?_assertNot(khash:iter_next(I) == Err)
+ }
+ end,
+ fun({ok, H, I, Err}) ->
+ {value, bar} = khash:lookup(H,foo),
+ {
+ "lookup doesn't expire iterators",
+ ?_assertNot(khash:iter_next(I) == Err)
+ }
+ end,
+ fun({ok, H, I, Err}) ->
+ bar = khash:get(H, foo),
+ {
+ "get doesn't expire iterators",
+ ?_assertNot(khash:iter_next(I) == Err)
+ }
+ end,
+ fun({ok, H, I, Err}) ->
+ 1 = khash:size(H),
+ {
+ "size doesn't expire iterators",
+ ?_assertNot(khash:iter_next(I) == Err)
+ }
+ end,
+ fun({ok, H, I, Err}) ->
+ {ok, I} = khash:iter(H),
+ {foo, bar} = khash:iter_next(I),
+ end_of_table = khash:iter_next(I),
+ {
+ "iteration doesn't expire iterators",
+ ?_assertNot(khash:iter_next(I) == Err)
+ }
+ end
+ ]
+ }
+ }.
+
+
+dict_fetch() ->
+ erlang:garbage_collect(),
+ List = kv_data(?NUM_KVS),
+ Dict = dict:from_list(List),
+ dict_fetch(Dict, ?NUM_CYCLES * 10).
+
+dict_fetch(_, 0) ->
+ ok;
+dict_fetch(Dict, NumCycles) ->
+ ?assertMatch(1, dict:fetch(1, Dict)),
+ dict_fetch(Dict, NumCycles - 1).
+
+khash_fetch() ->
+ erlang:garbage_collect(),
+ List = kv_data(?NUM_KVS),
+ {ok, KHash} = khash:from_list(List),
+ khash_fetch(KHash, ?NUM_CYCLES * 10).
+
+khash_fetch(_, 0) ->
+ ok;
+khash_fetch(KHash, NumCycles) ->
+ ?assertMatch(1, khash:get(KHash, 1)),
+ khash_fetch(KHash, NumCycles - 1).
+
+dict_store() ->
+ List = kv_data(?NUM_KVS * 2),
+ Dict = dict:from_list(List),
+ dict_store(Dict, ?NUM_CYCLES).
+
+dict_store(_, 0) ->
+ ok;
+dict_store(Dict, NumCycles) ->
+ Dict2 = dict:store(1, 1, Dict),
+ dict_store(Dict2, NumCycles - 1).
+
+khash_store() ->
+ List = kv_data(?NUM_KVS * 2),
+ {ok, KHash} = khash:from_list(List),
+ khash_store(KHash, ?NUM_CYCLES).
+
+khash_store(_, 0) ->
+ ok;
+khash_store(KHash, NumCycles) ->
+ khash:put(KHash, 1, 1),
+ khash_store(KHash, NumCycles - 1).
+
+khash_iterate(I, Acc) ->
+ case khash:iter_next(I) of
+ {K, V} ->
+ khash_iterate(I, [{K, V}|Acc]);
+ end_of_table ->
+ lists:sort(Acc)
+ end.
+
+kv_data(NumKVs) ->
+ [{I, I} || I <- lists:seq(1, NumKVs)].
+
+run_randomized({ok, _, N, _}, Valid) when N =< 0 ->
+ Valid;
+run_randomized({ok, Actions, N, S0}, Valid) ->
+ Action = weighted_choice(Actions),
+ S1 = Action(S0),
+ Assertion = Valid andalso validate_randomized_state(S1),
+ run_randomized({ok, Actions, N - 1, S1}, Assertion).
+
+validate_randomized_state({D, H}) ->
+ DKVs = lists:sort(dict:to_list(D)),
+ HKVs = lists:sort(khash:to_list(H)),
+ DKVs == HKVs.
+
+run_clear({_D0, H}) ->
+ ?assertMatch(ok, khash:clear(H)),
+ {dict:new(), H}.
+
+run_get2({D, H}) ->
+ K = random_key(D),
+ case dict:find(K, D) of
+ {ok, Value} ->
+ ?assertMatch({value, Value}, khash:lookup(H, K)),
+ ?assertMatch(Value, khash:get(H, K));
+ error ->
+ ?assertMatch(not_found, khash:lookup(H, K)),
+ ?assertMatch(undefined, khash:get(H, K))
+ end,
+ {D, H}.
+
+run_get3({D, H}) ->
+ K = random_key(D),
+ case dict:find(K, D) of
+ {ok, Value} ->
+ ?assertMatch({value, Value}, khash:lookup(H, K)),
+ ?assertMatch(Value, khash:get(H, K));
+ error ->
+ Val = random_val(),
+ ?assertMatch(Val, khash:get(H, K, Val))
+ end,
+ {D, H}.
+
+run_put({D0, H}) ->
+ K = random_key(D0),
+ V = random_val(),
+ D1 = dict:store(K, V, D0),
+ ?assertMatch(ok, khash:put(H, K, V)),
+ {D1, H}.
+
+run_del({D0, H}) ->
+ K = random_key(D0),
+ D1 = case dict:is_key(K, D0) of
+ true ->
+ ?assertMatch(ok, khash:del(H, K)),
+ dict:erase(K, D0);
+ false ->
+ ?assertMatch(not_found, khash:del(H, K)),
+ D0
+ end,
+ {D1, H}.
+
+run_size({D, H}) ->
+ ?assertEqual(dict:size(D), khash:size(H)),
+ {D, H}.
+
+run_keys({D, H}) ->
+ DKeys = dict:fetch_keys(D),
+ {ok, HKeys0} = khash:keys(H),
+ HKeys = lists:sort(HKeys0),
+ ?assertEqual(lists:sort(DKeys), lists:sort(HKeys)),
+ {D, H}.
+
+run_to_list({D, H}) ->
+ DKVs = dict:to_list(D),
+ HKVs = khash:to_list(H),
+ ?assertEqual(lists:sort(DKVs), lists:sort(HKVs)),
+ {D, H}.
+
+weighted_choice(Items0) ->
+ Items = lists:sort(Items0),
+ Sum = lists:sum([W || {W, _} <- Items]),
+ Choice = random:uniform() * Sum,
+ weighted_choice(Items, 0.0, Choice).
+
+weighted_choice([], _, _) ->
+ throw(bad_choice);
+weighted_choice([{W, _} | Rest], S, C) when W + S < C ->
+ weighted_choice(Rest, W+S, C);
+weighted_choice([{_, I} | _], _, _) ->
+ I.
+
+random_key(D) ->
+ Keys = lists:usort(dict:fetch_keys(D) ++ [foo]),
+ lists:nth(random:uniform(length(Keys)), Keys).
+
+random_val() ->
+ gen_term:any().
http://git-wip-us.apache.org/repos/asf/couchdb-khash/blob/6938d72b/test/util.erl
----------------------------------------------------------------------
diff --git a/test/util.erl b/test/util.erl
deleted file mode 100644
index 4f0bbf6..0000000
--- a/test/util.erl
+++ /dev/null
@@ -1,25 +0,0 @@
-%% This file is part of khash released under the MIT license.
-%% See the LICENSE file for more information.
-%% Copyright 2013 Cloudant, Inc <su...@cloudant.com>
-
--module(util).
--compile(export_all).
-
-
-init_code_path() ->
- code:add_pathz("ebin").
-
-
-run(Plan, Fun) ->
- init_code_path(),
- etap:plan(Plan),
- case (catch Fun()) of
- ok ->
- etap:end_tests();
- Other ->
- etap:diag(io_lib:format("Test died abnormally:~n~p", [Other])),
- timer:sleep(500),
- etap:bail(Other)
- end,
- ok.
-