You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2009/06/11 03:59:56 UTC

svn commit: r783600 - in /couchdb/trunk: ./ src/couchdb/ test/etap/

Author: davisp
Date: Thu Jun 11 01:59:56 2009
New Revision: 783600

URL: http://svn.apache.org/viewvc?rev=783600&view=rev
Log:
Integrating more ETAP tests from Bob Dionne.


Added:
    couchdb/trunk/test/etap/040-util.t
    couchdb/trunk/test/etap/050-stream.t
    couchdb/trunk/test/etap/060-kt-merging.t
    couchdb/trunk/test/etap/061-kt-missing-leaves.t
    couchdb/trunk/test/etap/062-kt-remove-leaves.t
    couchdb/trunk/test/etap/063-kt-get-leaves.t
    couchdb/trunk/test/etap/064-kt-counting.t
    couchdb/trunk/test/etap/065-kt-stemming.t
    couchdb/trunk/test/etap/070-couch-db.t
Modified:
    couchdb/trunk/THANKS
    couchdb/trunk/src/couchdb/couch_key_tree.erl
    couchdb/trunk/src/couchdb/couch_stream.erl
    couchdb/trunk/test/etap/010-file-basics.t
    couchdb/trunk/test/etap/030-doc-from-json.t

Modified: couchdb/trunk/THANKS
URL: http://svn.apache.org/viewvc/couchdb/trunk/THANKS?rev=783600&r1=783599&r2=783600&view=diff
==============================================================================
--- couchdb/trunk/THANKS (original)
+++ couchdb/trunk/THANKS Thu Jun 11 01:59:56 2009
@@ -30,5 +30,6 @@
  * Brad Anderson <br...@sankatygroup.com>
  * Nick Gerakines <ni...@gerakines.net>
  * Robert Newson <ro...@gmail.com>
+ * Bob Dionne <di...@member.fsf.org>
 
 For a list of authors see the `AUTHORS` file.

Modified: couchdb/trunk/src/couchdb/couch_key_tree.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_key_tree.erl?rev=783600&r1=783599&r2=783600&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_key_tree.erl (original)
+++ couchdb/trunk/src/couchdb/couch_key_tree.erl Thu Jun 11 01:59:56 2009
@@ -322,87 +322,5 @@
             NewTrees
         end, [], Paths2).
 
-test() ->
-    EmptyTree = [],
-    One = [{0, {"1","foo",[]}}],
-    TwoSibs = [{0, {"1","foo",[]}},
-               {0, {"2","foo",[]}}],
-    OneChild = [{0, {"1","foo",[{"1a", "bar", []}]}}],
-    TwoChild = [{0, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}],
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []},
-                                     {"1b", "bar", []}]}}],
-    TwoChildSibs2 = [{0, {"1","foo", [{"1a", "bar", []},
-                                     {"1b", "bar", [{"1bb", "boo", []}]}]}}],
-    Stemmed1b = [{1, {"1a", "bar", []}}],
-    Stemmed1a = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
-    Stemmed1aa = [{2, {"1aa", "bar", []}}],
-    Stemmed1bb = [{2, {"1bb", "boo", []}}],
-    
-    {EmptyTree, no_conflicts} = merge(EmptyTree, EmptyTree),
-    {One, no_conflicts} = merge(EmptyTree, One),
-    {One, no_conflicts} = merge(One, EmptyTree),
-    {TwoSibs, no_conflicts} = merge(One, TwoSibs),
-    {One, no_conflicts} = merge(One, One),
-    {TwoChild, no_conflicts} = merge(TwoChild, TwoChild),
-    {TwoChildSibs, no_conflicts} = merge(TwoChildSibs, TwoChildSibs),
-    {TwoChildSibs, no_conflicts} = merge(TwoChildSibs, Stemmed1b),
-    {TwoChildSibs, no_conflicts} = merge(Stemmed1b, TwoChildSibs),
-    {TwoChildSibs2, no_conflicts} = merge(TwoChildSibs2, Stemmed1bb),
-    {TwoChildSibs2, no_conflicts} = merge(Stemmed1bb, TwoChildSibs2),
-    {TwoChild, no_conflicts} = merge(TwoChild, Stemmed1aa),
-    {TwoChild, no_conflicts} = merge(TwoChild, Stemmed1a),
-    {Stemmed1a, no_conflicts} = merge(Stemmed1a, Stemmed1aa),
-    Expect1 = OneChild ++ Stemmed1aa,
-    {Expect1, conflicts} = merge(OneChild, Stemmed1aa),
-    {TwoChild, no_conflicts} = merge(Expect1, TwoChild),
-    
-    []=find_missing(TwoChildSibs, [{0,"1"}, {1,"1a"}]),
-    [{0, "10"}, {100, "x"}]=find_missing(TwoChildSibs, [{0,"1"}, {0, "10"}, {1,"1a"}, {100, "x"}]),
-    [{0, "1"}, {100, "x"}]=find_missing(Stemmed1a, [{0,"1"}, {1,"1a"}, {100, "x"}]),
-    [{0, "1"}, {1,"1a"}, {100, "x"}]=find_missing(Stemmed1aa, [{0,"1"}, {1,"1a"}, {100, "x"}]),
-    
-    {TwoChildSibs, []} = remove_leafs(TwoChildSibs, []),
-    {TwoChildSibs, []} = remove_leafs(TwoChildSibs, [{0, "1"}]),
-    {OneChild, [{1, "1b"}]} = remove_leafs(TwoChildSibs, [{1, "1b"}]),
-    {[], [{1, "1b"},{1, "1a"}]} = remove_leafs(TwoChildSibs, [{1, "1a"}, {1, "1b"}]),
-    {Stemmed1a, []} = remove_leafs(Stemmed1a, [{1, "1a"}]),
-    {[], [{2, "1aa"}]} = remove_leafs(Stemmed1a, [{2, "1aa"}]),
-    {TwoChildSibs, []} = remove_leafs(TwoChildSibs, []),
-    
-    {[],[{0,"x"}]} = get_key_leafs(TwoChildSibs, [{0, "x"}]),
-    
-    {[{"bar", {1, ["1a","1"]}}],[]} = get_key_leafs(TwoChildSibs, [{1, "1a"}]),
-    {[{"bar", {1, ["1a","1"]}},{"bar",{1, ["1b","1"]}}],[]} = get_key_leafs(TwoChildSibs, [{0, "1"}]),
-    
-    {[{"foo", {0, ["1"]}}],[]} = get(TwoChildSibs, [{0, "1"}]),
-    {[{"bar", {1, ["1a", "1"]}}],[]} = get(TwoChildSibs, [{1, "1a"}]),
+% Tests moved to test/etap/06?-*.t
 
-    {[{0,[{"1", "foo"}]}],[]} = get_full_key_paths(TwoChildSibs, [{0, "1"}]),
-    {[{1,[{"1a", "bar"},{"1", "foo"}]}],[]} = get_full_key_paths(TwoChildSibs, [{1, "1a"}]),
-    
-    [{2, [{"1aa", "bar"},{"1a", "bar"}]}] = get_all_leafs_full(Stemmed1a),
-    [{1, [{"1a", "bar"},{"1", "foo"}]}, {1, [{"1b", "bar"},{"1", "foo"}]}] = get_all_leafs_full(TwoChildSibs),
-    
-    [{"bar", {2, ["1aa","1a"]}}] = get_all_leafs(Stemmed1a),
-    [{"bar", {1, ["1a", "1"]}}, {"bar", {1, ["1b","1"]}}] = get_all_leafs(TwoChildSibs),
-    
-    0 = count_leafs(EmptyTree),
-    1 = count_leafs(One),
-    2 = count_leafs(TwoChildSibs),
-    1 = count_leafs(Stemmed1a),
-    
-    TwoChild = stem(TwoChild, 3),
-    Stemmed1a = stem(TwoChild, 2),
-    Stemmed1aa = stem(TwoChild, 1),
-    ok.
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
\ No newline at end of file

Modified: couchdb/trunk/src/couchdb/couch_stream.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_stream.erl?rev=783600&r1=783599&r2=783600&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_stream.erl (original)
+++ couchdb/trunk/src/couchdb/couch_stream.erl Thu Jun 11 01:59:56 2009
@@ -174,39 +174,5 @@
     old_stream_data(Fd, Sp, Num - ReadAmount, MaxChunk, Fun, Fun(Bin, Acc)).
 
 
-
-%%% Tests %%%
-
-read_all(Fd, PosList) ->
-    iolist_to_binary(foldl(Fd, PosList,
-        fun(Bin, Acc) ->
-            [Bin, Acc]
-        end, [])).
-
-
-test() ->
-    {ok, Fd} = couch_file:open("foo", [create,overwrite]),
-    ok = couch_file:write_header(Fd, {howdy, howdy}),
-    Bin = <<"damienkatz">>,
-    {ok, Pos} = couch_file:append_binary(Fd, Bin),
-    {ok, Bin} = couch_file:pread_binary(Fd, Pos),
-    {ok, {howdy, howdy}} = couch_file:read_header(Fd),
-    ok = couch_file:write_header(Fd, {foo, foo}),
-    {ok, {foo, foo}} = couch_file:read_header(Fd),
-    
-    {ok, Stream} = open(Fd),
-    ok = write(Stream, <<"food">>),
-    ok = write(Stream, <<"foob">>),
-    {PosList, 8} = close(Stream),
-    <<"foodfoob">> = read_all(Fd, PosList),
-    {ok, Stream2} = open(Fd),
-    OneBits = <<1:(8*10)>>,
-    ZeroBits = <<0:(8*10)>>,
-    ok = write(Stream2, OneBits),
-    ok = write(Stream2, ZeroBits),
-    {PosList2, 20} = close(Stream2),
-    AllBits = iolist_to_binary([OneBits,ZeroBits]),
-    AllBits = read_all(Fd, PosList2),
-    couch_file:close(Fd),
-    PosList2.
+% Tests moved to tests/etap/050-stream.t
 

Modified: couchdb/trunk/test/etap/010-file-basics.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/010-file-basics.t?rev=783600&r1=783599&r2=783600&view=diff
==============================================================================
--- couchdb/trunk/test/etap/010-file-basics.t (original)
+++ couchdb/trunk/test/etap/010-file-basics.t Thu Jun 11 01:59:56 2009
@@ -81,4 +81,4 @@
 
     etap:is(ok, couch_file:close(Fd),
         "Files close properly."),
-    ok.
\ No newline at end of file
+    ok.

Modified: couchdb/trunk/test/etap/030-doc-from-json.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/030-doc-from-json.t?rev=783600&r1=783599&r2=783600&view=diff
==============================================================================
--- couchdb/trunk/test/etap/030-doc-from-json.t (original)
+++ couchdb/trunk/test/etap/030-doc-from-json.t Thu Jun 11 01:59:56 2009
@@ -206,4 +206,4 @@
                 _:_ -> etap:ok(true, Mesg)
             end
     end, Cases),
-    ok.
\ No newline at end of file
+    ok.

Added: couchdb/trunk/test/etap/040-util.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/040-util.t?rev=783600&view=auto
==============================================================================
--- couchdb/trunk/test/etap/040-util.t (added)
+++ couchdb/trunk/test/etap/040-util.t Thu Jun 11 01:59:56 2009
@@ -0,0 +1,72 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+main(_) ->
+    code:add_pathz("src/couchdb"),
+    application:start(crypto),
+
+    etap:plan(11),
+    case (catch test()) of
+        ok ->
+            etap:end_tests();
+        Other ->
+            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+            etap:bail(Other)
+    end,
+    ok.
+
+test() ->
+    % to_existing_atom
+    etap:is(true, couch_util:to_existing_atom(true), "An atom is an atom."),
+    etap:is(foo, couch_util:to_existing_atom(<<"foo">>),
+        "A binary foo is the atom foo."),    
+    etap:is(foobarbaz, couch_util:to_existing_atom("foobarbaz"),
+        "A list of atoms is one munged atom."),
+
+    % terminate_linked
+    Self = self(),
+    spawn(fun() -> 
+		  ChildPid = spawn_link(fun() -> receive shutdown -> ok end end),
+		  couch_util:terminate_linked(normal),
+		  Self ! {pid, ChildPid}
+	  end),
+    receive
+        {pid, Pid} ->
+	    etap:ok(not is_process_alive(Pid), "why wont this work?")
+    end,    
+
+    % new_uuid
+    etap:isnt(couch_util:new_uuid(), couch_util:new_uuid(),
+        "A guid ought to be unique."),
+
+    % implode
+    etap:is([1, 38, 2, 38, 3], couch_util:implode([1,2,3],"&"),
+        "use & as separator in list."),
+
+    % trim
+    Strings = [" foo", "foo ", "\tfoo", " foo ", "foo\t", "foo\n", "\nfoo"],
+    etap:ok(lists:all(fun(S) -> couch_util:trim(S) == "foo" end, Strings),
+        "everything here trimmed should be foo."),
+
+    % abs_pathname
+    {ok, Cwd} = file:get_cwd(),
+    etap:is(Cwd ++ "/foo", couch_util:abs_pathname("./foo"),
+        "foo is in this directory."),
+
+    % should_flush
+    etap:ok(not couch_util:should_flush(),
+        "Not using enough memory to flush."),
+    AcquireMem = fun() ->
+        IntsToAGazillion = lists:seq(1, 200000),
+        LotsOfData = lists:map(
+            fun(Int) -> {Int, <<"foobar">>} end,
+        lists:seq(1, 200000)),
+        etap:ok(couch_util:should_flush(),
+            "Allocation 200K tuples puts us above the memory threshold.")
+    end,
+    AcquireMem(),
+
+    etap:ok(not couch_util:should_flush(),
+        "Checking to flush invokes GC."),
+    
+    ok.

Added: couchdb/trunk/test/etap/050-stream.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/050-stream.t?rev=783600&view=auto
==============================================================================
--- couchdb/trunk/test/etap/050-stream.t (added)
+++ couchdb/trunk/test/etap/050-stream.t Thu Jun 11 01:59:56 2009
@@ -0,0 +1,75 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+main(_) ->
+    code:add_pathz("src/couchdb"),
+    etap:plan(unknown),
+    case (catch test()) of
+        ok ->
+            etap:end_tests();
+        Other ->
+            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+            etap:bail(Other)
+    end,
+    ok.
+
+read_all(Fd, PosList) ->
+    Data = couch_stream:foldl(Fd, PosList, fun(Bin, Acc) -> [Bin, Acc] end, []),
+    iolist_to_binary(Data).
+
+test() ->
+    {ok, Fd} = couch_file:open("test/etap/temp.050", [create,overwrite]),
+    {ok, Stream} = couch_stream:open(Fd),
+    
+    etap:is(ok, couch_stream:write(Stream, <<"food">>),
+        "Writing to streams works."),
+
+    etap:is(ok, couch_stream:write(Stream, <<"foob">>),
+        "Consecutive writing to streams works."),
+
+    etap:is(ok, couch_stream:write(Stream, <<>>),
+        "Writing an empty binary does nothing."),
+
+    {Ptrs, Length} = couch_stream:close(Stream),
+    etap:is(Ptrs, [0], "Close returns the file pointers."),
+    etap:is(Length, 8, "Close also returns the number of bytes written."),
+    etap:is(<<"foodfoob">>, read_all(Fd, Ptrs), "Returned pointers are valid."),
+
+    % Remeber where we expect the pointer to be.
+    {ok, ExpPtr} = couch_file:bytes(Fd),
+    {ok, Stream2} = couch_stream:open(Fd),
+    OneBits = <<1:(8*10)>>,
+    etap:is(ok, couch_stream:write(Stream2, OneBits),
+        "Successfully wrote 80 1 bits."),
+
+    ZeroBits = <<0:(8*10)>>,
+    etap:is(ok, couch_stream:write(Stream2, ZeroBits), 
+        "Successfully wrote 80 0 bits."),
+    
+    {Ptrs2, Length2} = couch_stream:close(Stream2),
+    etap:is(Ptrs2, [ExpPtr], "Closing stream returns the file pointers."),
+    etap:is(Length2, 20, "Length written is 160 bytes."),
+
+    AllBits = iolist_to_binary([OneBits,ZeroBits]),
+    etap:is(AllBits, read_all(Fd, Ptrs2), "Returned pointers are valid."),
+    
+    % Stream more the 4K chunk size.
+    {ok, ExpPtr2} = couch_file:bytes(Fd),
+    {ok, Stream3} = couch_stream:open(Fd),
+    Acc2 = lists:foldl(fun(_, Acc) ->
+        Data = <<"a1b2c">>,
+        couch_stream:write(Stream3, Data),
+        [Data | Acc]
+    end, [], lists:seq(1, 1024)),
+    {Ptrs3, Length3} = couch_stream:close(Stream3),
+
+    % 4095 because of 5 * 4096 rem 5 (last write before exceeding threshold)
+    % + 5 puts us over the threshold
+    % + 4 bytes for the term_to_binary adding a length header
+    % + 1 byte every 4K for tail append headers
+    SecondPtr = ExpPtr2 + 4095 + 5 + 4 + 1,
+    etap:is(Ptrs3, [ExpPtr2, SecondPtr], "Pointers every 4K bytes."),
+    etap:is(Length3, 5120, "Wrote the expected 5K bytes."),
+
+    couch_file:close(Fd),
+    ok.

Added: couchdb/trunk/test/etap/060-kt-merging.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/060-kt-merging.t?rev=783600&view=auto
==============================================================================
--- couchdb/trunk/test/etap/060-kt-merging.t (added)
+++ couchdb/trunk/test/etap/060-kt-merging.t Thu Jun 11 01:59:56 2009
@@ -0,0 +1,128 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+main(_) ->
+    code:add_pathz("src/couchdb"),
+    etap:plan(unknown),
+    case (catch test()) of
+        ok ->
+            etap:end_tests();
+        Other ->
+            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+            etap:bail(Other)
+    end,
+    ok.
+
+test() ->
+    EmptyTree = [],
+    One = [{0, {"1","foo",[]}}],
+    TwoSibs = [{0, {"1","foo",[]}},
+               {0, {"2","foo",[]}}],
+    OneChild = [{0, {"1","foo",[{"1a", "bar", []}]}}],
+    TwoChild = [{0, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}],
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []},
+                                     {"1b", "bar", []}]}}],
+    TwoChildSibs2 = [{0, {"1","foo", [{"1a", "bar", []},
+                                     {"1b", "bar", [{"1bb", "boo", []}]}]}}],
+    Stemmed1b = [{1, {"1a", "bar", []}}],
+    Stemmed1a = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+    Stemmed1aa = [{2, {"1aa", "bar", []}}],
+    Stemmed1bb = [{2, {"1bb", "boo", []}}],
+    
+    etap:is(
+        {EmptyTree, no_conflicts},
+        couch_key_tree:merge(EmptyTree, EmptyTree),
+        "Merging two empty trees yields an empty tree."
+    ),
+
+    etap:is(
+        {One, no_conflicts},
+        couch_key_tree:merge(EmptyTree, One),
+        "The empty tree is the identity for merge."
+    ),
+
+    etap:is(
+        {One, no_conflicts},
+        couch_key_tree:merge(One, EmptyTree),
+        "Merging is commutative."
+    ),
+
+    etap:is(
+        {TwoSibs, no_conflicts},
+        couch_key_tree:merge(One, TwoSibs),
+        "Merging a prefix of a tree with the tree yields the tree."
+    ),
+
+    etap:is(
+        {One, no_conflicts},
+        couch_key_tree:merge(One, One),
+        "Merging is reflexive."
+    ),
+
+    etap:is(
+        {TwoChild, no_conflicts},
+        couch_key_tree:merge(TwoChild, TwoChild),
+        "Merging two children is still reflexive."
+    ),
+
+    etap:is(
+        {TwoChildSibs, no_conflicts},
+        couch_key_tree:merge(TwoChildSibs, TwoChildSibs),
+        "Merging a tree to itself is itself."),
+    
+    etap:is(
+        {TwoChildSibs, no_conflicts},
+        couch_key_tree:merge(TwoChildSibs, Stemmed1b),
+        "Merging a tree with a stem."
+    ),
+
+    etap:is(
+        {TwoChildSibs, no_conflicts},
+        couch_key_tree:merge(Stemmed1b, TwoChildSibs),
+        "Merging in the opposite direction."
+    ),
+
+    etap:is(
+        {TwoChildSibs2, no_conflicts},
+        couch_key_tree:merge(TwoChildSibs2, Stemmed1bb),
+        "Merging a stem at a deeper level."
+    ),
+    
+    etap:is(
+        {TwoChildSibs2, no_conflicts},
+        couch_key_tree:merge(Stemmed1bb, TwoChildSibs2),
+        "Merging a deeper level in opposite order."
+    ),
+
+    etap:is(
+        {TwoChild, no_conflicts},
+        couch_key_tree:merge(TwoChild, Stemmed1aa),
+        "Merging a single tree with a deeper stem."
+    ),
+
+    etap:is(
+        {TwoChild, no_conflicts},
+        couch_key_tree:merge(TwoChild, Stemmed1a),
+        "Merging a larger stem."
+    ),
+
+    etap:is(
+        {Stemmed1a, no_conflicts},
+        couch_key_tree:merge(Stemmed1a, Stemmed1aa),
+        "More merging."
+    ),
+
+    Expect1 = OneChild ++ Stemmed1aa,
+    etap:is(
+        {Expect1, conflicts},
+        couch_key_tree:merge(OneChild, Stemmed1aa),
+        "Merging should create conflicts."
+    ),
+    
+    etap:is(
+        {TwoChild, no_conflicts},
+        couch_key_tree:merge(Expect1, TwoChild),
+        "Merge should have no conflicts."
+    ),
+  
+    ok.

Added: couchdb/trunk/test/etap/061-kt-missing-leaves.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/061-kt-missing-leaves.t?rev=783600&view=auto
==============================================================================
--- couchdb/trunk/test/etap/061-kt-missing-leaves.t (added)
+++ couchdb/trunk/test/etap/061-kt-missing-leaves.t Thu Jun 11 01:59:56 2009
@@ -0,0 +1,53 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+main(_) ->
+    code:add_pathz("src/couchdb"),
+    etap:plan(unknown),
+    case (catch test()) of
+        ok ->
+            etap:end_tests();
+        Other ->
+            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+            etap:bail(Other)
+    end,
+    ok.
+
+test() ->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    Stemmed1 = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+    Stemmed2 = [{2, {"1aa", "bar", []}}],
+    
+    etap:is(
+        [],
+        couch_key_tree:find_missing(TwoChildSibs, [{0,"1"}, {1,"1a"}]),
+        "Look for missing keys."
+    ),
+
+    etap:is(
+        [{0, "10"}, {100, "x"}],
+        couch_key_tree:find_missing(
+            TwoChildSibs,
+            [{0,"1"}, {0, "10"}, {1,"1a"}, {100, "x"}]
+        ),
+        "Look for missing keys."
+    ),
+
+    etap:is(
+        [{0, "1"}, {100, "x"}],
+        couch_key_tree:find_missing(
+            Stemmed1,
+            [{0,"1"}, {1,"1a"}, {100, "x"}]
+        ),
+        "Look for missing keys."
+    ),
+    etap:is(
+        [{0, "1"}, {1,"1a"}, {100, "x"}],
+        couch_key_tree:find_missing(
+            Stemmed2,
+            [{0,"1"}, {1,"1a"}, {100, "x"}]
+        ),
+        "Look for missing keys."
+    ),
+
+    ok.

Added: couchdb/trunk/test/etap/062-kt-remove-leaves.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/062-kt-remove-leaves.t?rev=783600&view=auto
==============================================================================
--- couchdb/trunk/test/etap/062-kt-remove-leaves.t (added)
+++ couchdb/trunk/test/etap/062-kt-remove-leaves.t Thu Jun 11 01:59:56 2009
@@ -0,0 +1,57 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+main(_) ->
+    code:add_pathz("src/couchdb"),
+    etap:plan(unknown),
+    case (catch test()) of
+        ok ->
+            etap:end_tests();
+        Other ->
+            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+            etap:bail(Other)
+    end,
+    ok.
+
+test() ->
+    OneChild = [{0, {"1","foo",[{"1a", "bar", []}]}}],
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+
+    etap:is(
+        {TwoChildSibs, []},
+        couch_key_tree:remove_leafs(TwoChildSibs, []),
+        "Removing no leaves has no effect on the tree."
+    ),
+    
+    etap:is(
+        {TwoChildSibs, []},
+        couch_key_tree:remove_leafs(TwoChildSibs, [{0, "1"}]),
+        "Removing a non-existant branch has no effect."
+    ),
+    
+    etap:is(
+        {OneChild, [{1, "1b"}]},
+        couch_key_tree:remove_leafs(TwoChildSibs, [{1, "1b"}]),
+        "Removing a leaf removes the leaf."
+    ),
+    
+    etap:is(
+        {[], [{1, "1b"},{1, "1a"}]},
+        couch_key_tree:remove_leafs(TwoChildSibs, [{1, "1a"}, {1, "1b"}]),
+        "Removing all leaves returns an empty tree."
+    ),
+    
+    etap:is(
+        {Stemmed, []},
+        couch_key_tree:remove_leafs(Stemmed, [{1, "1a"}]),
+        "Removing a non-existant node has no effect."
+    ),
+    
+    etap:is(
+        {[], [{2, "1aa"}]},
+        couch_key_tree:remove_leafs(Stemmed, [{2, "1aa"}]),
+        "Removing the last leaf returns an empty tree."
+    ),
+
+    ok.

Added: couchdb/trunk/test/etap/063-kt-get-leaves.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/063-kt-get-leaves.t?rev=783600&view=auto
==============================================================================
--- couchdb/trunk/test/etap/063-kt-get-leaves.t (added)
+++ couchdb/trunk/test/etap/063-kt-get-leaves.t Thu Jun 11 01:59:56 2009
@@ -0,0 +1,86 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+main(_) ->
+    code:add_pathz("src/couchdb"),
+    etap:plan(unknown),
+    case (catch test()) of
+        ok ->
+            etap:end_tests();
+        Other ->
+            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+            etap:bail(Other)
+    end,
+    ok.
+
+test() ->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+
+    etap:is(
+        {[{"foo", {0, ["1"]}}],[]},
+        couch_key_tree:get(TwoChildSibs, [{0, "1"}]),
+        "extract a subtree."
+    ),
+    
+    etap:is(
+        {[{"bar", {1, ["1a", "1"]}}],[]},
+        couch_key_tree:get(TwoChildSibs, [{1, "1a"}]),
+        "extract a subtree."
+    ),
+
+    etap:is(
+        {[],[{0,"x"}]},
+        couch_key_tree:get_key_leafs(TwoChildSibs, [{0, "x"}]),
+        "gather up the leaves."
+    ),
+    
+    etap:is(
+        {[{"bar", {1, ["1a","1"]}}],[]},
+        couch_key_tree:get_key_leafs(TwoChildSibs, [{1, "1a"}]),
+        "gather up the leaves."
+    ),
+    
+    etap:is(
+        {[{"bar", {1, ["1a","1"]}},{"bar",{1, ["1b","1"]}}],[]},
+        couch_key_tree:get_key_leafs(TwoChildSibs, [{0, "1"}]),
+        "gather up the leaves."
+    ),
+
+    etap:is(
+        {[{0,[{"1", "foo"}]}],[]},
+        couch_key_tree:get_full_key_paths(TwoChildSibs, [{0, "1"}]),
+        "retrieve full key paths."
+    ),
+    
+    etap:is(
+        {[{1,[{"1a", "bar"},{"1", "foo"}]}],[]},
+        couch_key_tree:get_full_key_paths(TwoChildSibs, [{1, "1a"}]),
+        "retrieve full key paths."
+    ),
+
+    etap:is(
+        [{2, [{"1aa", "bar"},{"1a", "bar"}]}],
+        couch_key_tree:get_all_leafs_full(Stemmed),
+        "retrieve all leaves."
+    ),
+    
+    etap:is(
+        [{1, [{"1a", "bar"},{"1", "foo"}]}, {1, [{"1b", "bar"},{"1", "foo"}]}],
+        couch_key_tree:get_all_leafs_full(TwoChildSibs),
+        "retrieve all the leaves."
+    ),
+    
+    etap:is(
+        [{"bar", {2, ["1aa","1a"]}}],
+        couch_key_tree:get_all_leafs(Stemmed),
+        "retrieve all leaves."
+    ),
+    
+    etap:is(
+        [{"bar", {1, ["1a", "1"]}}, {"bar", {1, ["1b","1"]}}],
+        couch_key_tree:get_all_leafs(TwoChildSibs),
+        "retrieve all the leaves."
+    ),
+
+    ok.

Added: couchdb/trunk/test/etap/064-kt-counting.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/064-kt-counting.t?rev=783600&view=auto
==============================================================================
--- couchdb/trunk/test/etap/064-kt-counting.t (added)
+++ couchdb/trunk/test/etap/064-kt-counting.t Thu Jun 11 01:59:56 2009
@@ -0,0 +1,34 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+main(_) ->
+    code:add_pathz("src/couchdb"),
+    etap:plan(unknown),
+    case (catch test()) of
+        ok ->
+            etap:end_tests();
+        Other ->
+            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+            etap:bail(Other)
+    end,
+    ok.
+
+test() ->
+    EmptyTree = [],
+    One = [{0, {"1","foo",[]}}],
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    Stemmed = [{2, {"1bb", "boo", []}}],
+    
+    etap:is(0, couch_key_tree:count_leafs(EmptyTree),
+        "Empty trees have no leaves."),
+
+    etap:is(1, couch_key_tree:count_leafs(One),
+        "Single node trees have a single leaf."),
+
+    etap:is(2, couch_key_tree:count_leafs(TwoChildSibs),
+        "Two children siblings counted as two leaves."),
+    
+    etap:is(1, couch_key_tree:count_leafs(Stemmed),
+        "Stemming does not affect leaf counting."),
+    
+    ok.

Added: couchdb/trunk/test/etap/065-kt-stemming.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/065-kt-stemming.t?rev=783600&view=auto
==============================================================================
--- couchdb/trunk/test/etap/065-kt-stemming.t (added)
+++ couchdb/trunk/test/etap/065-kt-stemming.t Thu Jun 11 01:59:56 2009
@@ -0,0 +1,30 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+main(_) ->
+    code:add_pathz("src/couchdb"),
+    etap:plan(unknown),
+    case (catch test()) of
+        ok ->
+            etap:end_tests();
+        Other ->
+            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+            etap:bail(Other)
+    end,
+    ok.
+
+test() ->
+    TwoChild = [{0, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}],
+    Stemmed1 = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+    Stemmed2 = [{2, {"1aa", "bar", []}}],
+
+    etap:is(TwoChild, couch_key_tree:stem(TwoChild, 3),
+        "Stemming more levels than what exists does nothing."),
+
+    etap:is(Stemmed1, couch_key_tree:stem(TwoChild, 2),
+        "Stemming with a depth of two returns the deepest two nodes."),
+
+    etap:is(Stemmed2, couch_key_tree:stem(TwoChild, 1),
+        "Stemming to a depth of one returns the deepest node."),
+
+    ok.

Added: couchdb/trunk/test/etap/070-couch-db.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/070-couch-db.t?rev=783600&view=auto
==============================================================================
--- couchdb/trunk/test/etap/070-couch-db.t (added)
+++ couchdb/trunk/test/etap/070-couch-db.t Thu Jun 11 01:59:56 2009
@@ -0,0 +1,63 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+main(_) ->
+    code:add_pathz("src/couchdb"),
+    code:add_pathz("src/mochiweb"),
+    
+    etap:plan(unknown),
+    case (catch test()) of
+        ok ->
+            etap:end_tests();
+        Other ->
+            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+            etap:bail(Other)
+    end,
+    ok.
+
+test() ->
+    
+    couch_server:start(
+        ["etc/couchdb/default_dev.ini", "etc/couchdb/local_dev.ini"]
+    ),
+
+    couch_db:create(<<"etap-test-db">>, []),
+    {ok, AllDbs} = couch_server:all_databases(),
+    etap:ok(lists:member(<<"etap-test-db">>, AllDbs), "Database was created."),
+
+    couch_server:delete(<<"etap-test-db">>, []),
+    {ok, AllDbs2} = couch_server:all_databases(),
+    etap:ok(not lists:member(<<"etap-test-db">>, AllDbs2),
+        "Database was deleted."),
+
+    MkDbName = fun(Int) -> list_to_binary("lru-" ++ integer_to_list(Int)) end,
+
+    lists:foreach(fun(Int) ->
+        {ok, TestDbs} = couch_server:all_databases(),
+        ok = case lists:member(MkDbName(Int), TestDbs) of
+            true -> couch_server:delete(MkDbName(Int), []);
+            _ -> ok
+        end,
+		{ok, Db} = couch_db:create(MkDbName(Int), []),
+		ok = couch_db:close(Db)
+    end, lists:seq(1, 200)),
+
+    {ok, AllDbs3} = couch_server:all_databases(),
+    NumCreated = lists:foldl(fun(Int, Acc) ->
+        true = lists:member(MkDbName(Int), AllDbs3),
+        Acc+1
+    end, 0, lists:seq(1, 200)),
+    etap:is(200, NumCreated, "Created all databases."),
+    
+    lists:foreach(fun(Int) ->
+        ok = couch_server:delete(MkDbName(Int), [])
+    end, lists:seq(1, 200)),
+
+    {ok, AllDbs4} = couch_server:all_databases(),
+    NumDeleted = lists:foldl(fun(Int, Acc) ->
+        false = lists:member(MkDbName(Int), AllDbs4),
+        Acc+1
+    end, 0, lists:seq(1, 200)),
+    etap:is(200, NumDeleted, "Deleted all databases."),
+    
+    ok.