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.
-