You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ch...@apache.org on 2014/08/26 22:51:21 UTC

[01/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Repository: couchdb-couch
Updated Branches:
  refs/heads/1963-eunit-bigcouch c8698058e -> c3f38073f (forced update)


Port 090-task-status.t etap test suite to eunit

Split huge test case into multiple ones. Fix issue with get_task_prop
when Acc may be reset if searched task isn't last in the list.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/1667f9c3
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/1667f9c3
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/1667f9c3

Branch: refs/heads/1963-eunit-bigcouch
Commit: 1667f9c346b4532e4f7d9f602f27fba41ebf43e2
Parents: f51ec46
Author: Alexander Shorin <kx...@apache.org>
Authored: Mon May 26 20:23:41 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:38 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couch_task_status_tests.erl | 225 ++++++++++++++++++++++++++
 1 file changed, 225 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1667f9c3/test/couchdb/couch_task_status_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_task_status_tests.erl b/test/couchdb/couch_task_status_tests.erl
new file mode 100644
index 0000000..f71ad2b
--- /dev/null
+++ b/test/couchdb/couch_task_status_tests.erl
@@ -0,0 +1,225 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_task_status_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(TIMEOUT, 1000).
+
+
+setup() ->
+    {ok, TaskStatusPid} = couch_task_status:start_link(),
+    TaskUpdaterPid = spawn(fun() -> loop() end),
+    {TaskStatusPid, TaskUpdaterPid}.
+
+teardown({TaskStatusPid, _}) ->
+    erlang:monitor(process, TaskStatusPid),
+    couch_task_status:stop(),
+    receive
+        {'DOWN', _, _, TaskStatusPid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw(timeout_error)
+    end.
+
+
+couch_task_status_test_() ->
+    {
+        "CouchDB task status updates",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_register_task/1,
+                fun should_set_task_startup_time/1,
+                fun should_have_update_time_as_startup_before_any_progress/1,
+                fun should_set_task_type/1,
+                fun should_not_register_multiple_tasks_for_same_pid/1,
+                fun should_set_task_progress/1,
+                fun should_update_task_progress/1,
+                fun should_update_time_changes_on_task_progress/1,
+                fun should_control_update_frequency/1,
+                fun should_reset_control_update_frequency/1,
+                fun should_track_multiple_tasks/1,
+                fun should_finish_task/1
+
+            ]
+        }
+    }.
+
+
+should_register_task({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?_assertEqual(1, length(couch_task_status:all())).
+
+should_set_task_startup_time({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?_assert(is_integer(get_task_prop(Pid, started_on))).
+
+should_have_update_time_as_startup_before_any_progress({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    StartTime = get_task_prop(Pid, started_on),
+    ?_assertEqual(StartTime, get_task_prop(Pid, updated_on)).
+
+should_set_task_type({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?_assertEqual(replication, get_task_prop(Pid, type)).
+
+should_not_register_multiple_tasks_for_same_pid({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?_assertEqual({add_task_error, already_registered},
+                  call(Pid, add, [{type, compaction}, {progress, 0}])).
+
+should_set_task_progress({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?_assertEqual(0, get_task_prop(Pid, progress)).
+
+should_update_task_progress({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    call(Pid, update, [{progress, 25}]),
+    ?_assertEqual(25, get_task_prop(Pid, progress)).
+
+should_update_time_changes_on_task_progress({_, Pid}) ->
+    ?_assert(
+        begin
+            ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+            ok = timer:sleep(1000),  % sleep awhile to customize update time
+            call(Pid, update, [{progress, 25}]),
+            get_task_prop(Pid, updated_on) > get_task_prop(Pid, started_on)
+        end).
+
+should_control_update_frequency({_, Pid}) ->
+    ?_assertEqual(66,
+        begin
+            ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+            call(Pid, update, [{progress, 50}]),
+            call(Pid, update_frequency, 500),
+            call(Pid, update, [{progress, 66}]),
+            call(Pid, update, [{progress, 77}]),
+            get_task_prop(Pid, progress)
+        end).
+
+should_reset_control_update_frequency({_, Pid}) ->
+    ?_assertEqual(87,
+        begin
+            ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+            call(Pid, update, [{progress, 50}]),
+            call(Pid, update_frequency, 500),
+            call(Pid, update, [{progress, 66}]),
+            call(Pid, update, [{progress, 77}]),
+            call(Pid, update_frequency, 0),
+            call(Pid, update, [{progress, 87}]),
+            get_task_prop(Pid, progress)
+        end).
+
+should_track_multiple_tasks(_) ->
+    ?_assert(run_multiple_tasks()).
+
+should_finish_task({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?assertEqual(1, length(couch_task_status:all())),
+    ok = call(Pid, done),
+    ?_assertEqual(0, length(couch_task_status:all())).
+
+
+run_multiple_tasks() ->
+    Pid1 = spawn(fun() -> loop() end),
+    Pid2 = spawn(fun() -> loop() end),
+    Pid3 = spawn(fun() -> loop() end),
+    call(Pid1, add, [{type, replication}, {progress, 0}]),
+    call(Pid2, add, [{type, compaction}, {progress, 0}]),
+    call(Pid3, add, [{type, indexer}, {progress, 0}]),
+
+    ?assertEqual(3, length(couch_task_status:all())),
+    ?assertEqual(replication, get_task_prop(Pid1, type)),
+    ?assertEqual(compaction, get_task_prop(Pid2, type)),
+    ?assertEqual(indexer, get_task_prop(Pid3, type)),
+
+    call(Pid2, update, [{progress, 33}]),
+    call(Pid3, update, [{progress, 42}]),
+    call(Pid1, update, [{progress, 11}]),
+    ?assertEqual(42, get_task_prop(Pid3, progress)),
+    call(Pid1, update, [{progress, 72}]),
+    ?assertEqual(72, get_task_prop(Pid1, progress)),
+    ?assertEqual(33, get_task_prop(Pid2, progress)),
+
+    call(Pid1, done),
+    ?assertEqual(2, length(couch_task_status:all())),
+    call(Pid3, done),
+    ?assertEqual(1, length(couch_task_status:all())),
+    call(Pid2, done),
+    ?assertEqual(0, length(couch_task_status:all())),
+
+    true.
+
+
+loop() ->
+    receive
+        {add, Props, From} ->
+            Resp = couch_task_status:add_task(Props),
+            From ! {ok, self(), Resp},
+            loop();
+        {update, Props, From} ->
+            Resp = couch_task_status:update(Props),
+            From ! {ok, self(), Resp},
+            loop();
+        {update_frequency, Msecs, From} ->
+            Resp = couch_task_status:set_update_frequency(Msecs),
+            From ! {ok, self(), Resp},
+            loop();
+        {done, From} ->
+            From ! {ok, self(), ok}
+    end.
+
+call(Pid, Command) ->
+    Pid ! {Command, self()},
+    wait(Pid).
+
+call(Pid, Command, Arg) ->
+    Pid ! {Command, Arg, self()},
+    wait(Pid).
+
+wait(Pid) ->
+    receive
+        {ok, Pid, Msg} ->
+            Msg
+    after ?TIMEOUT ->
+        throw(timeout_error)
+    end.
+
+get_task_prop(Pid, Prop) ->
+    From = list_to_binary(pid_to_list(Pid)),
+    Element = lists:foldl(
+        fun(PropList, Acc) ->
+            case couch_util:get_value(pid, PropList) of
+                From ->
+                    [PropList | Acc];
+                _ ->
+                    Acc
+            end
+        end,
+        [], couch_task_status:all()
+    ),
+    case couch_util:get_value(Prop, hd(Element), nil) of
+        nil ->
+            erlang:error({assertion_failed,
+                         [{module, ?MODULE},
+                          {line, ?LINE},
+                          {reason, "Could not get property '"
+                                   ++ couch_util:to_list(Prop)
+                                   ++ "' for task "
+                                   ++ pid_to_list(Pid)}]});
+        Value ->
+            Value
+    end.


[07/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 150-invalid-view-seq.t etap test suite to eunit

Merged into couchdb_views_tests suite.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/4206af51
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/4206af51
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/4206af51

Branch: refs/heads/1963-eunit-bigcouch
Commit: 4206af51a27057d798c0faafa141d2c14a81effb
Parents: 456e0b9
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Jun 3 08:49:32 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_views_tests.erl | 102 ++++++++++++++++++++++++++++--
 1 file changed, 96 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/4206af51/test/couchdb/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_views_tests.erl b/test/couchdb/couchdb_views_tests.erl
index e44cef6..d10b567 100644
--- a/test/couchdb/couchdb_views_tests.erl
+++ b/test/couchdb/couchdb_views_tests.erl
@@ -36,13 +36,22 @@ setup() ->
     DbName = ?tempdb(),
     {ok, _} = couch_db:create(DbName, [?ADMIN_USER]),
     FooRev = create_design_doc(DbName, <<"_design/foo">>, <<"bar">>),
-    ok = query_view(DbName, "foo", "bar"),
+    query_view(DbName, "foo", "bar"),
     BooRev = create_design_doc(DbName, <<"_design/boo">>, <<"baz">>),
-    ok = query_view(DbName, "boo", "baz"),
+    query_view(DbName, "boo", "baz"),
     {DbName, {FooRev, BooRev}}.
 
+setup_with_docs() ->
+    DbName = ?tempdb(),
+    {ok, _} = couch_db:create(DbName, [?ADMIN_USER]),
+    create_docs(DbName),
+    create_design_doc(DbName, <<"_design/foo">>, <<"bar">>),
+    DbName.
+
 teardown({DbName, _}) ->
-    ok = couch_server:delete(DbName, []),
+    teardown(DbName);
+teardown(DbName) when is_binary(DbName) ->
+    ok = couch_server:delete(DbName, [?ADMIN_USER]),
     ok.
 
 
@@ -64,6 +73,36 @@ view_indexes_cleanup_test_() ->
         }
     }.
 
+should_not_remember_docs_in_index_after_backup_restore_test() ->
+    %% COUCHDB-640
+    start(),
+    DbName = setup_with_docs(),
+
+    ok = backup_db_file(DbName),
+    create_doc(DbName),
+
+    Body0 = query_view(DbName, "foo", "bar"),
+    ViewJson0 = ejson:decode(Body0),
+    Rows0 = couch_util:get_nested_json_value(ViewJson0, [<<"rows">>]),
+    ?assert(has_doc("doc1", Rows0)),
+    ?assert(has_doc("doc2", Rows0)),
+    ?assert(has_doc("doc3", Rows0)),
+    ?assert(has_doc("doc666", Rows0)),
+
+    restore_backup_db_file(DbName),
+
+    Body1 = query_view(DbName, "foo", "bar"),
+    ViewJson1 = ejson:decode(Body1),
+    Rows1 = couch_util:get_nested_json_value(ViewJson1, [<<"rows">>]),
+    ?assert(has_doc("doc1", Rows1)),
+    ?assert(has_doc("doc2", Rows1)),
+    ?assert(has_doc("doc3", Rows1)),
+    ?assertNot(has_doc("doc666", Rows1)),
+
+    teardown(DbName),
+    stop(whereis(couch_server_sup)).
+
+
 should_have_two_indexes_alive_before_deletion({DbName, _}) ->
     view_cleanup(DbName),
     ?_assertEqual(2, count_index_files(DbName)).
@@ -80,6 +119,38 @@ should_cleanup_all_index_files({DbName, {FooRev, BooRev}})->
     ?_assertEqual(0, count_index_files(DbName)).
 
 
+create_doc(DbName) ->
+    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
+    Doc666 = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"doc666">>},
+        {<<"value">>, 999}
+
+    ]}),
+    {ok, _} = couch_db:update_docs(Db, [Doc666]),
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db).
+
+create_docs(DbName) ->
+    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
+    Doc1 = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"doc1">>},
+        {<<"value">>, 1}
+
+    ]}),
+    Doc2 = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"doc2">>},
+        {<<"value">>, 2}
+
+    ]}),
+    Doc3 = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"doc3">>},
+        {<<"value">>, 3}
+
+    ]}),
+    {ok, _} = couch_db:update_docs(Db, [Doc1, Doc2, Doc3]),
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db).
+
 create_design_doc(DbName, DDName, ViewName) ->
     {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
     DDoc = couch_doc:from_json_obj({[
@@ -109,13 +180,13 @@ delete_design_doc(DbName, DDName, Rev) ->
 db_url(DbName) ->
     Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
     Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
-    "http://" ++ Addr ++ ":" ++ Port ++ "/" ++ binary_to_list(DbName).
+    "http://" ++ Addr ++ ":" ++ Port ++ "/" ++ ?b2l(DbName).
 
 query_view(DbName, DDoc, View) ->
-    {ok, Code, _Headers, _Body} = test_request:get(
+    {ok, Code, _Headers, Body} = test_request:get(
         db_url(DbName) ++ "/_design/" ++ DDoc ++ "/_view/" ++ View),
     ?assertEqual(200, Code),
-    ok.
+    Body.
 
 view_cleanup(DbName) ->
     {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
@@ -127,3 +198,22 @@ count_index_files(DbName) ->
     RootDir = couch_config:get("couchdb", "view_index_dir"),
     length(filelib:wildcard(RootDir ++ "/." ++
         binary_to_list(DbName) ++ "_design"++"/mrview/*")).
+
+has_doc(DocId1, Rows) ->
+    DocId = iolist_to_binary(DocId1),
+    lists:any(fun({R}) -> lists:member({<<"id">>, DocId}, R) end, Rows).
+
+backup_db_file(DbName) ->
+    DbDir = couch_config:get("couchdb", "database_dir"),
+    DbFile = filename:join([DbDir, ?b2l(DbName) ++ ".couch"]),
+    {ok, _} = file:copy(DbFile, DbFile ++ ".backup"),
+    ok.
+
+restore_backup_db_file(DbName) ->
+    DbDir = couch_config:get("couchdb", "database_dir"),
+    stop(whereis(couch_server_sup)),
+    DbFile = filename:join([DbDir, ?b2l(DbName) ++ ".couch"]),
+    ok = file:delete(DbFile),
+    ok = file:rename(DbFile ++ ".backup", DbFile),
+    start(),
+    ok.


[35/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Config set requires a value as a list, not a binary


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/1354bd15
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/1354bd15
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/1354bd15

Branch: refs/heads/1963-eunit-bigcouch
Commit: 1354bd159068d0c80e34925f520e177d451ff18b
Parents: 7fba43a
Author: Russell Branca <ch...@apache.org>
Authored: Fri Aug 15 12:31:59 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:24:50 2014 -0700

----------------------------------------------------------------------
 test/couchdb_cors_tests.erl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1354bd15/test/couchdb_cors_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_cors_tests.erl b/test/couchdb_cors_tests.erl
index 02f9da9..52d1797 100644
--- a/test/couchdb_cors_tests.erl
+++ b/test/couchdb_cors_tests.erl
@@ -244,7 +244,7 @@ should_make_origin_request_with_auth(_, {_, _, Url, DefaultHeaders}) ->
     ?_assertEqual("http://example.com",
         begin
             Hashed = couch_passwords:hash_admin_password(<<"test">>),
-            config:set("admins", "test", Hashed, false),
+            config:set("admins", "test", ?b2l(Hashed), false),
             {ok, _, Resp, _} = test_request:get(
                 Url, DefaultHeaders, [{basic_auth, {"test", "test"}}]),
             config:delete("admins", "test", false),
@@ -255,7 +255,7 @@ should_make_preflight_request_with_auth(_, {_, _, Url, DefaultHeaders}) ->
     ?_assertEqual(?SUPPORTED_METHODS,
         begin
             Hashed = couch_passwords:hash_admin_password(<<"test">>),
-            config:set("admins", "test", Hashed, false),
+            config:set("admins", "test", ?b2l(Hashed), false),
             Headers = DefaultHeaders
                       ++ [{"Access-Control-Request-Method", "GET"}],
             {ok, _, Resp, _} = test_request:options(


[18/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 201-view-group-shutdown.t etap test suite to eunit

Merged into couchdb_views_tests suite. Database population reduced
to speedup test and removed second view index call which leaded to
race condition when compaction becomes completed in time of view index
update call and before assertion check for {error, all_dbs_active}.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/820b0e26
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/820b0e26
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/820b0e26

Branch: refs/heads/1963-eunit-bigcouch
Commit: 820b0e2629b7c4a21072b11d9cc6a90e4541228e
Parents: c98d19f
Author: Alexander Shorin <kx...@apache.org>
Authored: Sat Jun 7 01:23:38 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:40 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_views_tests.erl | 175 ++++++++++++++++++++++++++++++
 1 file changed, 175 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/820b0e26/test/couchdb/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_views_tests.erl b/test/couchdb/couchdb_views_tests.erl
index 77ee4f5..57d5a14 100644
--- a/test/couchdb/couchdb_views_tests.erl
+++ b/test/couchdb/couchdb_views_tests.erl
@@ -94,6 +94,16 @@ view_group_db_leaks_test_() ->
         }
     }.
 
+view_group_shutdown_test_() ->
+    {
+        "View group shutdown",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [couchdb_1283()]
+        }
+    }.
+
 
 should_not_remember_docs_in_index_after_backup_restore_test() ->
     %% COUCHDB-640
@@ -229,6 +239,87 @@ couchdb_1309(DbName) ->
         end
     end).
 
+couchdb_1283() ->
+    ?_test(begin
+        ok = couch_config:set("couchdb", "max_dbs_open", "3", false),
+        ok = couch_config:set("couchdb", "delayed_commits", "false", false),
+
+        {ok, MDb1} = couch_db:create(?tempdb(), [?ADMIN_USER]),
+        DDoc = couch_doc:from_json_obj({[
+            {<<"_id">>, <<"_design/foo">>},
+            {<<"language">>, <<"javascript">>},
+            {<<"views">>, {[
+                {<<"foo">>, {[
+                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+                ]}},
+                {<<"foo2">>, {[
+                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+                ]}},
+                {<<"foo3">>, {[
+                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+                ]}},
+                {<<"foo4">>, {[
+                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+                ]}},
+                {<<"foo5">>, {[
+                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+                ]}}
+            ]}}
+        ]}),
+        {ok, _} = couch_db:update_doc(MDb1, DDoc, []),
+        ok = populate_db(MDb1, 100, 100),
+        query_view(MDb1#db.name, "foo", "foo"),
+        ok = couch_db:close(MDb1),
+
+        {ok, Db1} = couch_db:create(?tempdb(), [?ADMIN_USER]),
+        ok = couch_db:close(Db1),
+        {ok, Db2} = couch_db:create(?tempdb(), [?ADMIN_USER]),
+        ok = couch_db:close(Db2),
+        {ok, Db3} = couch_db:create(?tempdb(), [?ADMIN_USER]),
+        ok = couch_db:close(Db3),
+
+        Writer1 = spawn_writer(Db1#db.name),
+        Writer2 = spawn_writer(Db2#db.name),
+
+        ?assert(is_process_alive(Writer1)),
+        ?assert(is_process_alive(Writer2)),
+
+        ?assertEqual(ok, get_writer_status(Writer1)),
+        ?assertEqual(ok, get_writer_status(Writer2)),
+
+        {ok, MonRef} = couch_mrview:compact(MDb1#db.name, <<"_design/foo">>,
+                                            [monitor]),
+
+        Writer3 = spawn_writer(Db3#db.name),
+        ?assert(is_process_alive(Writer3)),
+        ?assertEqual({error, all_dbs_active}, get_writer_status(Writer3)),
+
+        ?assert(is_process_alive(Writer1)),
+        ?assert(is_process_alive(Writer2)),
+        ?assert(is_process_alive(Writer3)),
+
+        receive
+            {'DOWN', MonRef, process, _, Reason} ->
+                ?assertEqual(normal, Reason)
+        after ?TIMEOUT ->
+            erlang:error(
+                {assertion_failed,
+                 [{module, ?MODULE}, {line, ?LINE},
+                  {reason, "Failure compacting view group"}]})
+        end,
+
+        ?assertEqual(ok, writer_try_again(Writer3)),
+        ?assertEqual(ok, get_writer_status(Writer3)),
+
+        ?assert(is_process_alive(Writer1)),
+        ?assert(is_process_alive(Writer2)),
+        ?assert(is_process_alive(Writer3)),
+
+        ?assertEqual(ok, stop_writer(Writer1)),
+        ?assertEqual(ok, stop_writer(Writer2)),
+        ?assertEqual(ok, stop_writer(Writer3))
+    end).
+
 create_doc(DbName, DocId) when is_list(DocId) ->
     create_doc(DbName, ?l2b(DocId));
 create_doc(DbName, DocId) when is_binary(DocId) ->
@@ -262,6 +353,20 @@ create_docs(DbName) ->
     couch_db:ensure_full_commit(Db),
     couch_db:close(Db).
 
+populate_db(Db, BatchSize, N) when N > 0 ->
+    Docs = lists:map(
+        fun(_) ->
+            couch_doc:from_json_obj({[
+                {<<"_id">>, couch_uuids:new()},
+                {<<"value">>, base64:encode(crypto:rand_bytes(1000))}
+            ]})
+        end,
+        lists:seq(1, BatchSize)),
+    {ok, _} = couch_db:update_docs(Db, Docs, []),
+    populate_db(Db, BatchSize, N - length(Docs));
+populate_db(_Db, _, _) ->
+    ok.
+
 create_design_doc(DbName, DDName, ViewName) ->
     {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
     DDoc = couch_doc:from_json_obj({[
@@ -423,3 +528,73 @@ wait_view_compact_done(DbName, DDocId, N) ->
             ok = timer:sleep(?DELAY),
             wait_view_compact_done(DbName, DDocId, N - 1)
     end.
+
+spawn_writer(DbName) ->
+    Parent = self(),
+    spawn(fun() ->
+        process_flag(priority, high),
+        writer_loop(DbName, Parent)
+    end).
+
+get_writer_status(Writer) ->
+    Ref = make_ref(),
+    Writer ! {get_status, Ref},
+    receive
+        {db_open, Ref} ->
+            ok;
+        {db_open_error, Error, Ref} ->
+            Error
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+writer_try_again(Writer) ->
+    Ref = make_ref(),
+    Writer ! {try_again, Ref},
+    receive
+        {ok, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+stop_writer(Writer) ->
+    Ref = make_ref(),
+    Writer ! {stop, Ref},
+    receive
+        {ok, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        erlang:error({assertion_failed,
+                      [{module, ?MODULE},
+                       {line, ?LINE},
+                       {reason, "Timeout on stopping process"}]})
+    end.
+
+writer_loop(DbName, Parent) ->
+    case couch_db:open_int(DbName, []) of
+        {ok, Db} ->
+            writer_loop_1(Db, Parent);
+        Error ->
+            writer_loop_2(DbName, Parent, Error)
+    end.
+
+writer_loop_1(Db, Parent) ->
+    receive
+        {get_status, Ref} ->
+            Parent ! {db_open, Ref},
+            writer_loop_1(Db, Parent);
+        {stop, Ref} ->
+            ok = couch_db:close(Db),
+            Parent ! {ok, Ref}
+    end.
+
+writer_loop_2(DbName, Parent, Error) ->
+    receive
+        {get_status, Ref} ->
+            Parent ! {db_open_error, Error, Ref},
+            writer_loop_2(DbName, Parent, Error);
+        {try_again, Ref} ->
+            Parent ! {ok, Ref},
+            writer_loop(DbName, Parent)
+    end.


[15/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 250-upgrade-legacy-view-files.t etap test suite to eunit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/8c955aa9
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/8c955aa9
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/8c955aa9

Branch: refs/heads/1963-eunit-bigcouch
Commit: 8c955aa91964ca5e41f03f93735c86bdf5ec9494
Parents: ec28de1
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Jun 10 23:55:32 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:40 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_views_tests.erl            |  69 +++++++++++++++++++
 .../3b835456c235b1827e012e25666152f3.view       | Bin 0 -> 4192 bytes
 test/couchdb/fixtures/test.couch                | Bin 0 -> 16482 bytes
 3 files changed, 69 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/8c955aa9/test/couchdb/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_views_tests.erl b/test/couchdb/couchdb_views_tests.erl
index 57d5a14..6d81f32 100644
--- a/test/couchdb/couchdb_views_tests.erl
+++ b/test/couchdb/couchdb_views_tests.erl
@@ -14,6 +14,7 @@
 
 -include("couch_eunit.hrl").
 -include_lib("couchdb/couch_db.hrl").
+-include_lib("couch_mrview/include/couch_mrview.hrl").
 
 -define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(DELAY, 100).
@@ -131,6 +132,68 @@ should_not_remember_docs_in_index_after_backup_restore_test() ->
     stop(whereis(couch_server_sup)).
 
 
+should_upgrade_legacy_view_files_test() ->
+    start(),
+
+    ok = couch_config:set("query_server_config", "commit_freq", "0", false),
+
+    DbName = <<"test">>,
+    DbFileName = "test.couch",
+    DbFilePath = filename:join([?FIXTURESDIR, DbFileName]),
+    OldViewName = "3b835456c235b1827e012e25666152f3.view",
+    FixtureViewFilePath = filename:join([?FIXTURESDIR, OldViewName]),
+    NewViewName = "a1c5929f912aca32f13446122cc6ce50.view",
+
+    DbDir = couch_config:get("couchdb", "database_dir"),
+    ViewDir = couch_config:get("couchdb", "view_index_dir"),
+    OldViewFilePath = filename:join([ViewDir, ".test_design", OldViewName]),
+    NewViewFilePath = filename:join([ViewDir, ".test_design", "mrview",
+                                     NewViewName]),
+
+    % cleanup
+    Files = [
+        filename:join([DbDir, DbFileName]),
+        OldViewFilePath,
+        NewViewFilePath
+    ],
+    lists:foreach(fun(File) -> file:delete(File) end, Files),
+
+    % copy old db file into db dir
+    {ok, _} = file:copy(DbFilePath, filename:join([DbDir, DbFileName])),
+
+    % copy old view file into view dir
+    ok = filelib:ensure_dir(filename:join([ViewDir, ".test_design"])),
+    {ok, _} = file:copy(FixtureViewFilePath, OldViewFilePath),
+
+    % ensure old header
+    OldHeader = read_header(OldViewFilePath),
+    ?assertMatch(#index_header{}, OldHeader),
+
+    % query view for expected results
+    Rows0 = query_view(DbName, "test", "test"),
+    ?assertEqual(2, length(Rows0)),
+
+    % ensure old file gone
+    ?assertNot(filelib:is_regular(OldViewFilePath)),
+
+    % add doc to trigger update
+    DocUrl = db_url(DbName) ++ "/boo",
+    {ok, _, _, _} = test_request:put(
+        DocUrl, [{"Content-Type", "application/json"}], <<"{\"a\":3}">>),
+
+    % query view for expected results
+    Rows1 = query_view(DbName, "test", "test"),
+    ?assertEqual(3, length(Rows1)),
+
+    % ensure new header
+    timer:sleep(2000),  % have to wait for awhile to upgrade the index
+    NewHeader = read_header(NewViewFilePath),
+    ?assertMatch(#mrheader{}, NewHeader),
+
+    teardown(DbName),
+    stop(whereis(couch_server_sup)).
+
+
 should_have_two_indexes_alive_before_deletion({DbName, _}) ->
     view_cleanup(DbName),
     ?_assertEqual(2, count_index_files(DbName)).
@@ -598,3 +661,9 @@ writer_loop_2(DbName, Parent, Error) ->
             Parent ! {ok, Ref},
             writer_loop(DbName, Parent)
     end.
+
+read_header(File) ->
+    {ok, Fd} = couch_file:open(File),
+    {ok, {_Sig, Header}} = couch_file:read_header(Fd),
+    couch_file:close(Fd),
+    Header.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/8c955aa9/test/couchdb/fixtures/3b835456c235b1827e012e25666152f3.view
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/3b835456c235b1827e012e25666152f3.view b/test/couchdb/fixtures/3b835456c235b1827e012e25666152f3.view
new file mode 100644
index 0000000..9c67648
Binary files /dev/null and b/test/couchdb/fixtures/3b835456c235b1827e012e25666152f3.view differ

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/8c955aa9/test/couchdb/fixtures/test.couch
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/test.couch b/test/couchdb/fixtures/test.couch
new file mode 100644
index 0000000..32c79af
Binary files /dev/null and b/test/couchdb/fixtures/test.couch differ


[17/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 210-os-proc-pool.t etap test suite to eunit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/7a6d78e2
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/7a6d78e2
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/7a6d78e2

Branch: refs/heads/1963-eunit-bigcouch
Commit: 7a6d78e28745b64afccefcd9055a1ea82002693b
Parents: 820b0e2
Author: Alexander Shorin <kx...@apache.org>
Authored: Sun Jun 8 13:08:50 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:40 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_os_proc_pool.erl | 179 +++++++++++++++++++++++++++++
 1 file changed, 179 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/7a6d78e2/test/couchdb/couchdb_os_proc_pool.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_os_proc_pool.erl b/test/couchdb/couchdb_os_proc_pool.erl
new file mode 100644
index 0000000..1bb266e
--- /dev/null
+++ b/test/couchdb/couchdb_os_proc_pool.erl
@@ -0,0 +1,179 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_os_proc_pool).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(TIMEOUT, 3000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    couch_config:set("query_server_config", "os_process_limit", "3", false),
+    Pid.
+
+stop(Pid) ->
+    couch_server_sup:stop(),
+    erlang:monitor(process, Pid),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+
+os_proc_pool_test_() ->
+    {
+        "OS processes pool tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [
+                should_block_new_proc_on_full_pool(),
+                should_free_slot_on_proc_unexpected_exit()
+            ]
+        }
+    }.
+
+
+should_block_new_proc_on_full_pool() ->
+    ?_test(begin
+        Client1 = spawn_client(),
+        Client2 = spawn_client(),
+        Client3 = spawn_client(),
+
+        ?assertEqual(ok, ping_client(Client1)),
+        ?assertEqual(ok, ping_client(Client2)),
+        ?assertEqual(ok, ping_client(Client3)),
+
+        Proc1 = get_client_proc(Client1, "1"),
+        Proc2 = get_client_proc(Client2, "2"),
+        Proc3 = get_client_proc(Client3, "3"),
+
+        ?assertNotEqual(Proc1, Proc2),
+        ?assertNotEqual(Proc2, Proc3),
+        ?assertNotEqual(Proc3, Proc1),
+
+        Client4 = spawn_client(),
+        ?assertEqual(timeout, ping_client(Client4)),
+
+        ?assertEqual(ok, stop_client(Client1)),
+        ?assertEqual(ok, ping_client(Client4)),
+
+        Proc4 = get_client_proc(Client4, "4"),
+        ?assertEqual(Proc1, Proc4),
+
+        lists:map(fun(C) ->
+            ?assertEqual(ok, stop_client(C))
+        end, [Client2, Client3, Client4])
+    end).
+
+should_free_slot_on_proc_unexpected_exit() ->
+    ?_test(begin
+        Client1 = spawn_client(),
+        Client2 = spawn_client(),
+        Client3 = spawn_client(),
+
+        ?assertEqual(ok, ping_client(Client1)),
+        ?assertEqual(ok, ping_client(Client2)),
+        ?assertEqual(ok, ping_client(Client3)),
+
+        Proc1 = get_client_proc(Client1, "1"),
+        Proc2 = get_client_proc(Client2, "2"),
+        Proc3 = get_client_proc(Client3, "3"),
+
+        ?assertNotEqual(Proc1, Proc2),
+        ?assertNotEqual(Proc2, Proc3),
+        ?assertNotEqual(Proc3, Proc1),
+
+        ?assertEqual(ok, kill_client(Client1)),
+
+        Client4 = spawn_client(),
+        ?assertEqual(ok, ping_client(Client4)),
+
+        Proc4 = get_client_proc(Client4, "4"),
+        ?assertNotEqual(Proc4, Proc1),
+        ?assertNotEqual(Proc2, Proc4),
+        ?assertNotEqual(Proc3, Proc4),
+
+        lists:map(fun(C) ->
+            ?assertEqual(ok, stop_client(C))
+        end, [Client2, Client3, Client4])
+    end).
+
+
+spawn_client() ->
+    Parent = self(),
+    Ref = make_ref(),
+    Pid = spawn(fun() ->
+        Proc = couch_query_servers:get_os_process(<<"javascript">>),
+        loop(Parent, Ref, Proc)
+    end),
+    {Pid, Ref}.
+
+ping_client({Pid, Ref}) ->
+    Pid ! ping,
+    receive
+        {pong, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+get_client_proc({Pid, Ref}, ClientName) ->
+    Pid ! get_proc,
+    receive
+        {proc, Ref, Proc} -> Proc
+    after ?TIMEOUT ->
+        erlang:error({assertion_failed,
+                     [{module, ?MODULE},
+                      {line, ?LINE},
+                      {reason, "Timeout getting client "
+                               ++ ClientName ++ " proc"}]})
+    end.
+
+stop_client({Pid, Ref}) ->
+    Pid ! stop,
+    receive
+        {stop, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+kill_client({Pid, Ref}) ->
+    Pid ! die,
+    receive
+        {die, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+loop(Parent, Ref, Proc) ->
+    receive
+        ping ->
+            Parent ! {pong, Ref},
+            loop(Parent, Ref, Proc);
+        get_proc  ->
+            Parent ! {proc, Ref, Proc},
+            loop(Parent, Ref, Proc);
+        stop ->
+            couch_query_servers:ret_os_process(Proc),
+            Parent ! {stop, Ref};
+        die ->
+            Parent ! {die, Ref},
+            exit(some_error)
+    end.


[36/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
WIP: Add test_util

The addition of start_config here is suboptimal, but starting couch
requires config to be running, and a number of the tests also want
config running, so this is a (temporary?) workaround to make sure you
can always get a fresh config instance.

Currently the request function is unused, but I think we should
migrate tests making requests to use it.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/097c7f19
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/097c7f19
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/097c7f19

Branch: refs/heads/1963-eunit-bigcouch
Commit: 097c7f196ecc8f77a54fd1ea6e8a036653ea6564
Parents: 1354bd1
Author: Russell Branca <ch...@apache.org>
Authored: Fri Aug 15 12:56:57 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:24:50 2014 -0700

----------------------------------------------------------------------
 src/test_util.erl | 142 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 142 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/097c7f19/src/test_util.erl
----------------------------------------------------------------------
diff --git a/src/test_util.erl b/src/test_util.erl
new file mode 100644
index 0000000..787ff01
--- /dev/null
+++ b/src/test_util.erl
@@ -0,0 +1,142 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(test_util).
+
+-export([init_code_path/0]).
+-export([source_file/1, build_file/1, config_files/0]).
+%% -export([run/2]).
+-export([request/3, request/4]).
+-export([start_couch/0, start_couch/1, stop_couch/0, stop_couch/1]).
+-export([start_config/1, stop_config/1]).
+
+
+srcdir() ->
+    code:priv_dir(couch) ++ "/../../".
+
+builddir() ->
+    code:priv_dir(couch) ++ "/../../../".
+
+init_code_path() ->
+    Paths = [
+        "etap",
+        "couchdb",
+        "ejson",
+        "oauth",
+        "ibrowse",
+        "mochiweb",
+        "snappy"
+    ],
+    lists:foreach(fun(Name) ->
+        code:add_patha(filename:join([builddir(), "src", Name]))
+    end, Paths).
+
+source_file(Name) ->
+    filename:join([srcdir(), Name]).
+
+build_file(Name) ->
+    filename:join([builddir(), Name]).
+
+config_files() ->
+    [
+        build_file("etc/couchdb/default_dev.ini"),
+        build_file("test/random_port.ini"),
+        build_file("etc/couchdb/local_dev.ini")
+    ].
+
+
+request(Url, Headers, Method) ->
+    request(Url, Headers, Method, []).
+
+request(Url, Headers, Method, Body) ->
+    request(Url, Headers, Method, Body, 3).
+
+request(_Url, _Headers, _Method, _Body, 0) ->
+    {error, request_failed};
+request(Url, Headers, Method, Body, N) ->
+    case code:is_loaded(ibrowse) of
+    false ->
+        {ok, _} = ibrowse:start();
+    _ ->
+        ok
+    end,
+    case ibrowse:send_req(Url, Headers, Method, Body) of
+    {ok, Code0, RespHeaders, RespBody0} ->
+        Code = list_to_integer(Code0),
+        RespBody = iolist_to_binary(RespBody0),
+        {ok, Code, RespHeaders, RespBody};
+    {error, {'EXIT', {normal, _}}} ->
+        % Connection closed right after a successful request that
+        % used the same connection.
+        request(Url, Headers, Method, Body, N - 1);
+    Error ->
+        Error
+    end.
+
+
+start_couch() ->
+    start_couch(config_files()).
+
+
+start_couch(IniFiles) ->
+    ok = application:set_env(config, ini_files, IniFiles),
+    ok = lager:start(),
+    ok = start_applications([config, couch]),
+    ok.
+
+
+stop_couch() ->
+    ok = application:stop(couch),
+    ok = application:stop(lager),
+    ok = application:stop(goldrush),
+    ok = application:stop(config),
+    ok.
+
+
+stop_couch(_) ->
+    stop_couch().
+
+
+start_applications([]) ->
+    ok;
+start_applications([App|Apps]) ->
+    case application:start(App) of
+        {error, {already_started, _}} ->
+            ok;
+        {error, {not_started, Dep}} ->
+            start_applications([Dep, App | Apps]);
+        ok ->
+            ok
+    end,
+    start_applications(Apps).
+
+
+start_config(Chain) ->
+    case config:start_link(Chain) of
+        {ok, Pid} ->
+            {ok, Pid};
+        {error, {already_started, OldPid}}  ->
+            ok = stop_config(OldPid),
+            start_config(Chain)
+    end.
+
+
+stop_config(Pid) ->
+    Timeout = 1000,
+    erlang:monitor(process, Pid),
+    config:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after Timeout ->
+        throw({timeout_error, config_stop})
+    end.


[25/42] Move files out of test/couchdb into top level test/ folder

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_cors_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_cors_tests.erl b/test/couchdb/couchdb_cors_tests.erl
deleted file mode 100644
index 4e88ae7..0000000
--- a/test/couchdb/couchdb_cors_tests.erl
+++ /dev/null
@@ -1,344 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_cors_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
-
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
--define(SUPPORTED_METHODS,
-        "GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, COPY, OPTIONS").
--define(TIMEOUT, 1000).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    ok = couch_config:set("httpd", "enable_cors", "true", false),
-    ok = couch_config:set("vhosts", "example.com", "/", false),
-    Pid.
-
-stop(Pid) ->
-    couch_server_sup:stop(),
-    erlang:monitor(process, Pid),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-setup() ->
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
-    couch_db:close(Db),
-
-    couch_config:set("cors", "credentials", "false", false),
-    couch_config:set("cors", "origins", "http://example.com", false),
-
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
-    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
-    Host = "http://" ++ Addr ++ ":" ++ Port,
-    {Host, ?b2l(DbName)}.
-
-setup({Mod, VHost}) ->
-    {Host, DbName} = setup(),
-    Url = case Mod of
-        server ->
-            Host;
-        db ->
-            Host ++ "/" ++ DbName
-    end,
-    DefaultHeaders = [{"Origin", "http://example.com"}]
-                     ++ maybe_append_vhost(VHost),
-    {Host, DbName, Url, DefaultHeaders}.
-
-teardown(DbName) when is_list(DbName) ->
-    ok = couch_server:delete(?l2b(DbName), [?ADMIN_USER]),
-    ok;
-teardown({_, DbName}) ->
-    teardown(DbName).
-
-teardown(_, {_, DbName, _, _}) ->
-    teardown(DbName).
-
-
-cors_test_() ->
-    Funs = [
-        fun should_not_allow_origin/2,
-        fun should_not_allow_origin_with_port_mismatch/2,
-        fun should_not_allow_origin_with_scheme_mismatch/2,
-        fun should_not_all_origin_due_case_mismatch/2,
-        fun should_make_simple_request/2,
-        fun should_make_preflight_request/2,
-        fun should_make_prefligh_request_with_port/2,
-        fun should_make_prefligh_request_with_scheme/2,
-        fun should_make_prefligh_request_with_wildcard_origin/2,
-        fun should_make_request_with_credentials/2,
-        fun should_make_origin_request_with_auth/2,
-        fun should_make_preflight_request_with_auth/2
-    ],
-    {
-        "CORS (COUCHDB-431)",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            [
-                cors_tests(Funs),
-                vhost_cors_tests(Funs),
-                headers_tests()
-            ]
-        }
-    }.
-
-headers_tests() ->
-    {
-        "Various headers tests",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [
-                fun should_not_return_cors_headers_for_invalid_origin/1,
-                fun should_not_return_cors_headers_for_invalid_origin_preflight/1,
-                fun should_make_request_against_attachment/1,
-                fun should_make_range_request_against_attachment/1,
-                fun should_make_request_with_if_none_match_header/1
-            ]
-        }
-    }.
-
-cors_tests(Funs) ->
-    {
-        "CORS tests",
-        [
-            make_test_case(server, false, Funs),
-            make_test_case(db, false, Funs)
-        ]
-    }.
-
-vhost_cors_tests(Funs) ->
-    {
-        "Virtual Host CORS",
-        [
-            make_test_case(server, true, Funs),
-            make_test_case(db, true, Funs)
-        ]
-    }.
-
-make_test_case(Mod, UseVhost, Funs) ->
-    {
-        case Mod of server -> "Server"; db -> "Database" end,
-        {foreachx, fun setup/1, fun teardown/2, [{{Mod, UseVhost}, Fun}
-                                                 || Fun <- Funs]}
-    }.
-
-
-should_not_allow_origin(_, {_, _, Url, Headers0}) ->
-    ?_assertEqual(undefined,
-        begin
-            couch_config:delete("cors", "origins", false),
-            Headers1 = proplists:delete("Origin", Headers0),
-            Headers = [{"Origin", "http://127.0.0.1"}]
-                      ++ Headers1,
-            {ok, _, Resp, _} = test_request:get(Url, Headers),
-            proplists:get_value("Access-Control-Allow-Origin", Resp)
-        end).
-
-should_not_allow_origin_with_port_mismatch({_, VHost}, {_, _, Url, _}) ->
-    ?_assertEqual(undefined,
-        begin
-            Headers = [{"Origin", "http://example.com:5984"},
-                       {"Access-Control-Request-Method", "GET"}]
-                      ++ maybe_append_vhost(VHost),
-            {ok, _, Resp, _} = test_request:options(Url, Headers),
-            proplists:get_value("Access-Control-Allow-Origin", Resp)
-        end).
-
-should_not_allow_origin_with_scheme_mismatch({_, VHost}, {_, _, Url, _}) ->
-    ?_assertEqual(undefined,
-        begin
-            Headers = [{"Origin", "http://example.com:5984"},
-                       {"Access-Control-Request-Method", "GET"}]
-                      ++ maybe_append_vhost(VHost),
-            {ok, _, Resp, _} = test_request:options(Url, Headers),
-            proplists:get_value("Access-Control-Allow-Origin", Resp)
-        end).
-
-should_not_all_origin_due_case_mismatch({_, VHost}, {_, _, Url, _}) ->
-    ?_assertEqual(undefined,
-        begin
-            Headers = [{"Origin", "http://ExAmPlE.CoM"},
-                       {"Access-Control-Request-Method", "GET"}]
-                      ++ maybe_append_vhost(VHost),
-            {ok, _, Resp, _} = test_request:options(Url, Headers),
-            proplists:get_value("Access-Control-Allow-Origin", Resp)
-        end).
-
-should_make_simple_request(_, {_, _, Url, DefaultHeaders}) ->
-    ?_test(begin
-        {ok, _, Resp, _} = test_request:get(Url, DefaultHeaders),
-        ?assertEqual(
-            undefined,
-            proplists:get_value("Access-Control-Allow-Credentials", Resp)),
-        ?assertEqual(
-            "http://example.com",
-            proplists:get_value("Access-Control-Allow-Origin", Resp)),
-        ?assertEqual(
-            "Cache-Control, Content-Type, Server",
-            proplists:get_value("Access-Control-Expose-Headers", Resp))
-    end).
-
-should_make_preflight_request(_, {_, _, Url, DefaultHeaders}) ->
-    ?_assertEqual(?SUPPORTED_METHODS,
-        begin
-            Headers = DefaultHeaders
-                      ++ [{"Access-Control-Request-Method", "GET"}],
-            {ok, _, Resp, _} = test_request:options(Url, Headers),
-            proplists:get_value("Access-Control-Allow-Methods", Resp)
-        end).
-
-should_make_prefligh_request_with_port({_, VHost}, {_, _, Url, _}) ->
-    ?_assertEqual("http://example.com:5984",
-        begin
-            couch_config:set("cors", "origins", "http://example.com:5984",
-                             false),
-            Headers = [{"Origin", "http://example.com:5984"},
-                       {"Access-Control-Request-Method", "GET"}]
-                      ++ maybe_append_vhost(VHost),
-            {ok, _, Resp, _} = test_request:options(Url, Headers),
-            proplists:get_value("Access-Control-Allow-Origin", Resp)
-        end).
-
-should_make_prefligh_request_with_scheme({_, VHost}, {_, _, Url, _}) ->
-    ?_assertEqual("https://example.com:5984",
-        begin
-            couch_config:set("cors", "origins", "https://example.com:5984",
-                             false),
-            Headers = [{"Origin", "https://example.com:5984"},
-                       {"Access-Control-Request-Method", "GET"}]
-                      ++ maybe_append_vhost(VHost),
-            {ok, _, Resp, _} = test_request:options(Url, Headers),
-            proplists:get_value("Access-Control-Allow-Origin", Resp)
-        end).
-
-should_make_prefligh_request_with_wildcard_origin({_, VHost}, {_, _, Url, _}) ->
-    ?_assertEqual("https://example.com:5984",
-        begin
-            couch_config:set("cors", "origins", "*", false),
-            Headers = [{"Origin", "https://example.com:5984"},
-                       {"Access-Control-Request-Method", "GET"}]
-                      ++ maybe_append_vhost(VHost),
-            {ok, _, Resp, _} = test_request:options(Url, Headers),
-            proplists:get_value("Access-Control-Allow-Origin", Resp)
-        end).
-
-should_make_request_with_credentials(_, {_, _, Url, DefaultHeaders}) ->
-    ?_assertEqual("true",
-        begin
-            ok = couch_config:set("cors", "credentials", "true", false),
-            {ok, _, Resp, _} = test_request:options(Url, DefaultHeaders),
-            proplists:get_value("Access-Control-Allow-Credentials", Resp)
-        end).
-
-should_make_origin_request_with_auth(_, {_, _, Url, DefaultHeaders}) ->
-    ?_assertEqual("http://example.com",
-        begin
-            Hashed = couch_passwords:hash_admin_password(<<"test">>),
-            couch_config:set("admins", "test", Hashed, false),
-            {ok, _, Resp, _} = test_request:get(
-                Url, DefaultHeaders, [{basic_auth, {"test", "test"}}]),
-            couch_config:delete("admins", "test", false),
-            proplists:get_value("Access-Control-Allow-Origin", Resp)
-        end).
-
-should_make_preflight_request_with_auth(_, {_, _, Url, DefaultHeaders}) ->
-    ?_assertEqual(?SUPPORTED_METHODS,
-        begin
-            Hashed = couch_passwords:hash_admin_password(<<"test">>),
-            couch_config:set("admins", "test", Hashed, false),
-            Headers = DefaultHeaders
-                      ++ [{"Access-Control-Request-Method", "GET"}],
-            {ok, _, Resp, _} = test_request:options(
-                Url, Headers, [{basic_auth, {"test", "test"}}]),
-            couch_config:delete("admins", "test", false),
-            proplists:get_value("Access-Control-Allow-Methods", Resp)
-        end).
-
-should_not_return_cors_headers_for_invalid_origin({Host, _}) ->
-    ?_assertEqual(undefined,
-        begin
-            Headers = [{"Origin", "http://127.0.0.1"}],
-            {ok, _, Resp, _} = test_request:get(Host, Headers),
-            proplists:get_value("Access-Control-Allow-Origin", Resp)
-        end).
-
-should_not_return_cors_headers_for_invalid_origin_preflight({Host, _}) ->
-    ?_assertEqual(undefined,
-        begin
-            Headers = [{"Origin", "http://127.0.0.1"},
-                       {"Access-Control-Request-Method", "GET"}],
-            {ok, _, Resp, _} = test_request:options(Host, Headers),
-            proplists:get_value("Access-Control-Allow-Origin", Resp)
-        end).
-
-should_make_request_against_attachment({Host, DbName}) ->
-    {"COUCHDB-1689",
-     ?_assertEqual(200,
-         begin
-             Url = Host ++ "/" ++ DbName,
-             {ok, Code0, _, _} = test_request:put(
-                 Url ++ "/doc/file.txt", [{"Content-Type", "text/plain"}],
-                 "hello, couch!"),
-             ?assert(Code0 =:= 201),
-             {ok, Code, _, _} = test_request:get(
-                 Url ++ "/doc?attachments=true",
-                 [{"Origin", "http://example.com"}]),
-             Code
-         end)}.
-
-should_make_range_request_against_attachment({Host, DbName}) ->
-    {"COUCHDB-1689",
-     ?_assertEqual(206,
-         begin
-             Url = Host ++ "/" ++ DbName,
-             {ok, Code0, _, _} = test_request:put(
-                 Url ++ "/doc/file.txt",
-                 [{"Content-Type", "application/octet-stream"}],
-                 "hello, couch!"),
-             ?assert(Code0 =:= 201),
-             {ok, Code, _, _} = test_request:get(
-                 Url ++ "/doc/file.txt", [{"Origin", "http://example.com"},
-                                          {"Range", "bytes=0-6"}]),
-             Code
-         end)}.
-
-should_make_request_with_if_none_match_header({Host, DbName}) ->
-    {"COUCHDB-1697",
-     ?_assertEqual(304,
-         begin
-             Url = Host ++ "/" ++ DbName,
-             {ok, Code0, Headers0, _} = test_request:put(
-                 Url ++ "/doc", [{"Content-Type", "application/json"}], "{}"),
-             ?assert(Code0 =:= 201),
-             ETag = proplists:get_value("ETag", Headers0),
-             {ok, Code, _, _} = test_request:get(
-                 Url ++ "/doc", [{"Origin", "http://example.com"},
-                                 {"If-None-Match", ETag}]),
-             Code
-        end)}.
-
-
-maybe_append_vhost(true) ->
-    [{"Host", "http://example.com"}];
-maybe_append_vhost(false) ->
-    [].

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_csp_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_csp_tests.erl b/test/couchdb/couchdb_csp_tests.erl
deleted file mode 100644
index adb0e6d..0000000
--- a/test/couchdb/couchdb_csp_tests.erl
+++ /dev/null
@@ -1,96 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_csp_tests).
-
--include("couch_eunit.hrl").
-
--define(TIMEOUT, 1000).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    couch_server_sup:stop(),
-    erlang:monitor(process, Pid),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-setup() ->
-    ok = couch_config:set("csp", "enable", "true", false),
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
-    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
-    lists:concat(["http://", Addr, ":", Port, "/_utils/"]).
-
-teardown(_) ->
-    ok.
-
-
-csp_test_() ->
-    {
-        "Content Security Policy tests",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            {
-                foreach,
-                fun setup/0, fun teardown/1,
-                [
-                    fun should_not_return_any_csp_headers_when_disabled/1,
-                    fun should_apply_default_policy/1,
-                    fun should_return_custom_policy/1,
-                    fun should_only_enable_csp_when_true/1
-                ]
-            }
-        }
-    }.
-
-
-should_not_return_any_csp_headers_when_disabled(Url) ->
-    ?_assertEqual(undefined,
-        begin
-            ok = couch_config:set("csp", "enable", "false", false),
-            {ok, _, Headers, _} = test_request:get(Url),
-            proplists:get_value("Content-Security-Policy", Headers)
-        end).
-
-should_apply_default_policy(Url) ->
-    ?_assertEqual(
-        "default-src 'self'; img-src 'self'; font-src 'self'; "
-        "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';",
-        begin
-            {ok, _, Headers, _} = test_request:get(Url),
-            proplists:get_value("Content-Security-Policy", Headers)
-        end).
-
-should_return_custom_policy(Url) ->
-    ?_assertEqual("default-src 'http://example.com';",
-        begin
-            ok = couch_config:set("csp", "header_value",
-                                  "default-src 'http://example.com';", false),
-            {ok, _, Headers, _} = test_request:get(Url),
-            proplists:get_value("Content-Security-Policy", Headers)
-        end).
-
-should_only_enable_csp_when_true(Url) ->
-    ?_assertEqual(undefined,
-        begin
-            ok = couch_config:set("csp", "enable", "tru", false),
-            {ok, _, Headers, _} = test_request:get(Url),
-            proplists:get_value("Content-Security-Policy", Headers)
-        end).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_file_compression_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_file_compression_tests.erl b/test/couchdb/couchdb_file_compression_tests.erl
deleted file mode 100644
index fd3f513..0000000
--- a/test/couchdb/couchdb_file_compression_tests.erl
+++ /dev/null
@@ -1,239 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_file_compression_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
--define(DDOC_ID, <<"_design/test">>).
--define(DOCS_COUNT, 5000).
--define(TIMEOUT, 30000).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-setup() ->
-    couch_config:set("couchdb", "file_compression", "none", false),
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
-    ok = populate_db(Db, ?DOCS_COUNT),
-    DDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, ?DDOC_ID},
-        {<<"language">>, <<"javascript">>},
-        {<<"views">>, {[
-                {<<"by_id">>, {[
-                    {<<"map">>, <<"function(doc){emit(doc._id, doc.string);}">>}
-                ]}}
-            ]}
-        }
-    ]}),
-    {ok, _} = couch_db:update_doc(Db, DDoc, []),
-    refresh_index(DbName),
-    ok = couch_db:close(Db),
-    DbName.
-
-teardown(DbName) ->
-    ok = couch_server:delete(DbName, [?ADMIN_USER]),
-    ok.
-
-
-couch_auth_cache_test_() ->
-    {
-        "CouchDB file compression tests",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            {
-                foreach,
-                fun setup/0, fun teardown/1,
-                [
-                    fun should_use_none/1,
-                    fun should_use_deflate_1/1,
-                    fun should_use_deflate_9/1,
-                    fun should_use_snappy/1,
-                    fun should_compare_compression_methods/1
-                ]
-            }
-        }
-    }.
-
-
-should_use_none(DbName) ->
-    couch_config:set("couchdb", "file_compression", "none", false),
-    {
-        "Use no compression",
-        [
-            {"compact database", ?_test(compact_db(DbName))},
-            {"compact view", ?_test(compact_view(DbName))}
-        ]
-    }.
-
-should_use_deflate_1(DbName) ->
-    couch_config:set("couchdb", "file_compression", "deflate_1", false),
-    {
-        "Use deflate compression at level 1",
-        [
-            {"compact database", ?_test(compact_db(DbName))},
-            {"compact view", ?_test(compact_view(DbName))}
-        ]
-    }.
-
-should_use_deflate_9(DbName) ->
-    couch_config:set("couchdb", "file_compression", "deflate_9", false),
-    {
-        "Use deflate compression at level 9",
-        [
-            {"compact database", ?_test(compact_db(DbName))},
-            {"compact view", ?_test(compact_view(DbName))}
-        ]
-    }.
-
-should_use_snappy(DbName) ->
-    couch_config:set("couchdb", "file_compression", "snappy", false),
-    {
-        "Use snappy compression",
-        [
-            {"compact database", ?_test(compact_db(DbName))},
-            {"compact view", ?_test(compact_view(DbName))}
-        ]
-    }.
-
-should_compare_compression_methods(DbName) ->
-    {"none > snappy > deflate_1 > deflate_9",
-     {timeout, ?TIMEOUT div 1000, ?_test(compare_compression_methods(DbName))}}.
-
-compare_compression_methods(DbName) ->
-    couch_config:set("couchdb", "file_compression", "none", false),
-    compact_db(DbName),
-    compact_view(DbName),
-    DbSizeNone = db_disk_size(DbName),
-    ViewSizeNone = view_disk_size(DbName),
-
-    couch_config:set("couchdb", "file_compression", "snappy", false),
-    compact_db(DbName),
-    compact_view(DbName),
-    DbSizeSnappy = db_disk_size(DbName),
-    ViewSizeSnappy = view_disk_size(DbName),
-
-    ?assert(DbSizeNone > DbSizeSnappy),
-    ?assert(ViewSizeNone > ViewSizeSnappy),
-
-    couch_config:set("couchdb", "file_compression", "deflate_1", false),
-    compact_db(DbName),
-    compact_view(DbName),
-    DbSizeDeflate1 = db_disk_size(DbName),
-    ViewSizeDeflate1 = view_disk_size(DbName),
-
-    ?assert(DbSizeSnappy > DbSizeDeflate1),
-    ?assert(ViewSizeSnappy > ViewSizeDeflate1),
-
-    couch_config:set("couchdb", "file_compression", "deflate_9", false),
-    compact_db(DbName),
-    compact_view(DbName),
-    DbSizeDeflate9 = db_disk_size(DbName),
-    ViewSizeDeflate9 = view_disk_size(DbName),
-
-    ?assert(DbSizeDeflate1 > DbSizeDeflate9),
-    ?assert(ViewSizeDeflate1 > ViewSizeDeflate9).
-
-
-populate_db(_Db, NumDocs) when NumDocs =< 0 ->
-    ok;
-populate_db(Db, NumDocs) ->
-    Docs = lists:map(
-        fun(_) ->
-            couch_doc:from_json_obj({[
-                {<<"_id">>, couch_uuids:random()},
-                {<<"string">>, ?l2b(lists:duplicate(1000, $X))}
-            ]})
-        end,
-        lists:seq(1, 500)),
-    {ok, _} = couch_db:update_docs(Db, Docs, []),
-    populate_db(Db, NumDocs - 500).
-
-refresh_index(DbName) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    {ok, DDoc} = couch_db:open_doc(Db, ?DDOC_ID, [ejson_body]),
-    couch_mrview:query_view(Db, DDoc, <<"by_id">>, [{stale, false}]),
-    ok = couch_db:close(Db).
-
-compact_db(DbName) ->
-    DiskSizeBefore = db_disk_size(DbName),
-    {ok, Db} = couch_db:open_int(DbName, []),
-    {ok, CompactPid} = couch_db:start_compact(Db),
-    MonRef = erlang:monitor(process, CompactPid),
-    receive
-        {'DOWN', MonRef, process, CompactPid, normal} ->
-            ok;
-        {'DOWN', MonRef, process, CompactPid, Reason} ->
-            erlang:error({assertion_failed,
-                          [{module, ?MODULE},
-                           {line, ?LINE},
-                           {reason, "Error compacting database: "
-                                    ++ couch_util:to_list(Reason)}]})
-    after ?TIMEOUT ->
-        erlang:error({assertion_failed,
-                      [{module, ?MODULE},
-                       {line, ?LINE},
-                       {reason, "Timeout waiting for database compaction"}]})
-    end,
-    ok = couch_db:close(Db),
-    DiskSizeAfter = db_disk_size(DbName),
-    ?assert(DiskSizeBefore > DiskSizeAfter).
-
-compact_view(DbName) ->
-    DiskSizeBefore = view_disk_size(DbName),
-    {ok, MonRef} = couch_mrview:compact(DbName, ?DDOC_ID, [monitor]),
-    receive
-        {'DOWN', MonRef, process, _CompactPid, normal} ->
-            ok;
-        {'DOWN', MonRef, process, _CompactPid, Reason} ->
-            erlang:error({assertion_failed,
-                          [{module, ?MODULE},
-                           {line, ?LINE},
-                           {reason, "Error compacting view group: "
-                                    ++ couch_util:to_list(Reason)}]})
-    after ?TIMEOUT ->
-        erlang:error({assertion_failed,
-                      [{module, ?MODULE},
-                       {line, ?LINE},
-                       {reason, "Timeout waiting for view group compaction"}]})
-    end,
-    DiskSizeAfter = view_disk_size(DbName),
-    ?assert(DiskSizeBefore > DiskSizeAfter).
-
-db_disk_size(DbName) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    {ok, Info} = couch_db:get_db_info(Db),
-    ok = couch_db:close(Db),
-    couch_util:get_value(disk_size, Info).
-
-view_disk_size(DbName) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    {ok, DDoc} = couch_db:open_doc(Db, ?DDOC_ID, [ejson_body]),
-    {ok, Info} = couch_mrview:get_info(Db, DDoc),
-    ok = couch_db:close(Db),
-    couch_util:get_value(disk_size, Info).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_http_proxy_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_http_proxy_tests.erl b/test/couchdb/couchdb_http_proxy_tests.erl
deleted file mode 100644
index acb1974..0000000
--- a/test/couchdb/couchdb_http_proxy_tests.erl
+++ /dev/null
@@ -1,462 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_http_proxy_tests).
-
--include("couch_eunit.hrl").
-
--record(req, {method=get, path="", headers=[], body="", opts=[]}).
-
--define(CONFIG_FIXTURE_TEMP,
-    begin
-        FileName = filename:join([?TEMPDIR, ?tempfile() ++ ".ini"]),
-        {ok, Fd} = file:open(FileName, write),
-        ok = file:truncate(Fd),
-        ok = file:close(Fd),
-        FileName
-    end).
--define(TIMEOUT, 5000).
-
-
-start() ->
-    % we have to write any config changes to temp ini file to not loose them
-    % when supervisor will kill all children due to reaching restart threshold
-    % (each httpd_global_handlers changes causes couch_httpd restart)
-    couch_server_sup:start_link(?CONFIG_CHAIN ++ [?CONFIG_FIXTURE_TEMP]),
-    % 49151 is IANA Reserved, let's assume no one is listening there
-    couch_config:set("httpd_global_handlers", "_error",
-        "{couch_httpd_proxy, handle_proxy_req, <<\"http://127.0.0.1:49151/\">>}"
-    ),
-    ok.
-
-stop(_) ->
-    couch_server_sup:stop(),
-    ok.
-
-setup() ->
-    {ok, Pid} = test_web:start_link(),
-    Value = lists:flatten(io_lib:format(
-        "{couch_httpd_proxy, handle_proxy_req, ~p}",
-        [list_to_binary(proxy_url())])),
-    couch_config:set("httpd_global_handlers", "_test", Value),
-    % let couch_httpd restart
-    timer:sleep(100),
-    Pid.
-
-teardown(Pid) ->
-    erlang:monitor(process, Pid),
-    test_web:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, test_web_stop})
-    end.
-
-
-http_proxy_test_() ->
-    {
-        "HTTP Proxy handler tests",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            {
-                foreach,
-                fun setup/0, fun teardown/1,
-                [
-                    fun should_proxy_basic_request/1,
-                    fun should_return_alternative_status/1,
-                    fun should_respect_trailing_slash/1,
-                    fun should_proxy_headers/1,
-                    fun should_proxy_host_header/1,
-                    fun should_pass_headers_back/1,
-                    fun should_use_same_protocol_version/1,
-                    fun should_proxy_body/1,
-                    fun should_proxy_body_back/1,
-                    fun should_proxy_chunked_body/1,
-                    fun should_proxy_chunked_body_back/1,
-                    fun should_rewrite_location_header/1,
-                    fun should_not_rewrite_external_locations/1,
-                    fun should_rewrite_relative_location/1,
-                    fun should_refuse_connection_to_backend/1
-                ]
-            }
-
-        }
-    }.
-
-
-should_proxy_basic_request(_) ->
-    Remote = fun(Req) ->
-        'GET' = Req:get(method),
-        "/" = Req:get(path),
-        0 = Req:get(body_length),
-        <<>> = Req:recv_body(),
-        {ok, {200, [{"Content-Type", "text/plain"}], "ok"}}
-    end,
-    Local = fun
-        ({ok, "200", _, "ok"}) ->
-            true;
-        (_) ->
-            false
-    end,
-    ?_test(check_request(#req{}, Remote, Local)).
-
-should_return_alternative_status(_) ->
-    Remote = fun(Req) ->
-        "/alternate_status" = Req:get(path),
-        {ok, {201, [], "ok"}}
-    end,
-    Local = fun
-        ({ok, "201", _, "ok"}) ->
-            true;
-        (_) ->
-            false
-    end,
-    Req = #req{path = "/alternate_status"},
-    ?_test(check_request(Req, Remote, Local)).
-
-should_respect_trailing_slash(_) ->
-    Remote = fun(Req) ->
-        "/trailing_slash/" = Req:get(path),
-        {ok, {200, [], "ok"}}
-    end,
-    Local = fun
-        ({ok, "200", _, "ok"}) ->
-            true;
-        (_) ->
-            false
-    end,
-    Req = #req{path="/trailing_slash/"},
-    ?_test(check_request(Req, Remote, Local)).
-
-should_proxy_headers(_) ->
-    Remote = fun(Req) ->
-        "/passes_header" = Req:get(path),
-        "plankton" = Req:get_header_value("X-CouchDB-Ralph"),
-        {ok, {200, [], "ok"}}
-    end,
-    Local = fun
-        ({ok, "200", _, "ok"}) ->
-            true;
-        (_) ->
-            false
-    end,
-    Req = #req{
-        path="/passes_header",
-        headers=[{"X-CouchDB-Ralph", "plankton"}]
-    },
-    ?_test(check_request(Req, Remote, Local)).
-
-should_proxy_host_header(_) ->
-    Remote = fun(Req) ->
-        "/passes_host_header" = Req:get(path),
-        "www.google.com" = Req:get_header_value("Host"),
-        {ok, {200, [], "ok"}}
-    end,
-    Local = fun
-        ({ok, "200", _, "ok"}) ->
-            true;
-        (_) ->
-            false
-    end,
-    Req = #req{
-        path="/passes_host_header",
-        headers=[{"Host", "www.google.com"}]
-    },
-    ?_test(check_request(Req, Remote, Local)).
-
-should_pass_headers_back(_) ->
-    Remote = fun(Req) ->
-        "/passes_header_back" = Req:get(path),
-        {ok, {200, [{"X-CouchDB-Plankton", "ralph"}], "ok"}}
-    end,
-    Local = fun
-        ({ok, "200", Headers, "ok"}) ->
-            lists:member({"X-CouchDB-Plankton", "ralph"}, Headers);
-        (_) ->
-            false
-    end,
-    Req = #req{path="/passes_header_back"},
-    ?_test(check_request(Req, Remote, Local)).
-
-should_use_same_protocol_version(_) ->
-    Remote = fun(Req) ->
-        "/uses_same_version" = Req:get(path),
-        {1, 0} = Req:get(version),
-        {ok, {200, [], "ok"}}
-    end,
-    Local = fun
-        ({ok, "200", _, "ok"}) ->
-            true;
-        (_) ->
-            false
-    end,
-    Req = #req{
-        path="/uses_same_version",
-        opts=[{http_vsn, {1, 0}}]
-    },
-    ?_test(check_request(Req, Remote, Local)).
-
-should_proxy_body(_) ->
-    Remote = fun(Req) ->
-        'PUT' = Req:get(method),
-        "/passes_body" = Req:get(path),
-        <<"Hooray!">> = Req:recv_body(),
-        {ok, {201, [], "ok"}}
-    end,
-    Local = fun
-        ({ok, "201", _, "ok"}) ->
-            true;
-        (_) ->
-            false
-    end,
-    Req = #req{
-        method=put,
-        path="/passes_body",
-        body="Hooray!"
-    },
-    ?_test(check_request(Req, Remote, Local)).
-
-should_proxy_body_back(_) ->
-    BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
-    Remote = fun(Req) ->
-        'GET' = Req:get(method),
-        "/passes_eof_body" = Req:get(path),
-        {raw, {200, [{"Connection", "close"}], BodyChunks}}
-    end,
-    Local = fun
-        ({ok, "200", _, "foobarbazinga"}) ->
-            true;
-        (_) ->
-            false
-    end,
-    Req = #req{path="/passes_eof_body"},
-    ?_test(check_request(Req, Remote, Local)).
-
-should_proxy_chunked_body(_) ->
-    BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
-    Remote = fun(Req) ->
-        'POST' = Req:get(method),
-        "/passes_chunked_body" = Req:get(path),
-        RecvBody = fun
-            ({Length, Chunk}, [Chunk | Rest]) ->
-                Length = size(Chunk),
-                Rest;
-            ({0, []}, []) ->
-                ok
-        end,
-        ok = Req:stream_body(1024 * 1024, RecvBody, BodyChunks),
-        {ok, {201, [], "ok"}}
-    end,
-    Local = fun
-        ({ok, "201", _, "ok"}) ->
-            true;
-        (_) ->
-            false
-    end,
-    Req = #req{
-        method=post,
-        path="/passes_chunked_body",
-        headers=[{"Transfer-Encoding", "chunked"}],
-        body=chunked_body(BodyChunks)
-    },
-    ?_test(check_request(Req, Remote, Local)).
-
-should_proxy_chunked_body_back(_) ->
-    ?_test(begin
-        Remote = fun(Req) ->
-            'GET' = Req:get(method),
-            "/passes_chunked_body_back" = Req:get(path),
-            BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
-            {chunked, {200, [{"Transfer-Encoding", "chunked"}], BodyChunks}}
-        end,
-        Req = #req{
-            path="/passes_chunked_body_back",
-            opts=[{stream_to, self()}]
-        },
-
-        Resp = check_request(Req, Remote, no_local),
-        ?assertMatch({ibrowse_req_id, _}, Resp),
-        {_, ReqId} = Resp,
-
-        % Grab headers from response
-        receive
-            {ibrowse_async_headers, ReqId, "200", Headers} ->
-                ?assertEqual("chunked",
-                             proplists:get_value("Transfer-Encoding", Headers)),
-            ibrowse:stream_next(ReqId)
-        after 1000 ->
-            throw({error, timeout})
-        end,
-
-        ?assertEqual(<<"foobarbazinga">>, recv_body(ReqId, [])),
-        ?assertEqual(was_ok, test_web:check_last())
-    end).
-
-should_refuse_connection_to_backend(_) ->
-    Local = fun
-        ({ok, "500", _, _}) ->
-            true;
-        (_) ->
-            false
-    end,
-    Req = #req{opts=[{url, server_url("/_error")}]},
-    ?_test(check_request(Req, no_remote, Local)).
-
-should_rewrite_location_header(_) ->
-    {
-        "Testing location header rewrites",
-        do_rewrite_tests([
-            {"Location", proxy_url() ++ "/foo/bar",
-                         server_url() ++ "/foo/bar"},
-            {"Content-Location", proxy_url() ++ "/bing?q=2",
-                                 server_url() ++ "/bing?q=2"},
-            {"Uri", proxy_url() ++ "/zip#frag",
-                    server_url() ++ "/zip#frag"},
-            {"Destination", proxy_url(),
-                            server_url() ++ "/"}
-        ])
-    }.
-
-should_not_rewrite_external_locations(_) ->
-    {
-        "Testing no rewrite of external locations",
-        do_rewrite_tests([
-            {"Location", external_url() ++ "/search",
-                         external_url() ++ "/search"},
-            {"Content-Location", external_url() ++ "/s?q=2",
-                                 external_url() ++ "/s?q=2"},
-            {"Uri", external_url() ++ "/f#f",
-                    external_url() ++ "/f#f"},
-            {"Destination", external_url() ++ "/f?q=2#f",
-                            external_url() ++ "/f?q=2#f"}
-        ])
-    }.
-
-should_rewrite_relative_location(_) ->
-    {
-        "Testing relative rewrites",
-        do_rewrite_tests([
-            {"Location", "/foo",
-                         server_url() ++ "/foo"},
-            {"Content-Location", "bar",
-                                 server_url() ++ "/bar"},
-            {"Uri", "/zing?q=3",
-                    server_url() ++ "/zing?q=3"},
-            {"Destination", "bing?q=stuff#yay",
-                            server_url() ++ "/bing?q=stuff#yay"}
-        ])
-    }.
-
-
-do_rewrite_tests(Tests) ->
-    lists:map(fun({Header, Location, Url}) ->
-        should_rewrite_header(Header, Location, Url)
-    end, Tests).
-
-should_rewrite_header(Header, Location, Url) ->
-    Remote = fun(Req) ->
-        "/rewrite_test" = Req:get(path),
-        {ok, {302, [{Header, Location}], "ok"}}
-    end,
-    Local = fun
-        ({ok, "302", Headers, "ok"}) ->
-            ?assertEqual(Url, couch_util:get_value(Header, Headers)),
-            true;
-        (E) ->
-            ?debugFmt("~p", [E]),
-            false
-    end,
-    Req = #req{path="/rewrite_test"},
-    {Header, ?_test(check_request(Req, Remote, Local))}.
-
-
-server_url() ->
-    server_url("/_test").
-
-server_url(Resource) ->
-    Addr = couch_config:get("httpd", "bind_address"),
-    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
-    lists:concat(["http://", Addr, ":", Port, Resource]).
-
-proxy_url() ->
-    "http://127.0.0.1:" ++ integer_to_list(test_web:get_port()).
-
-external_url() ->
-    "https://google.com".
-
-check_request(Req, Remote, Local) ->
-    case Remote of
-        no_remote ->
-            ok;
-        _ ->
-            test_web:set_assert(Remote)
-    end,
-    Url = case proplists:lookup(url, Req#req.opts) of
-        none ->
-            server_url() ++ Req#req.path;
-        {url, DestUrl} ->
-            DestUrl
-    end,
-    Opts = [{headers_as_is, true} | Req#req.opts],
-    Resp =ibrowse:send_req(
-        Url, Req#req.headers, Req#req.method, Req#req.body, Opts
-    ),
-    %?debugFmt("ibrowse response: ~p", [Resp]),
-    case Local of
-        no_local ->
-            ok;
-        _ ->
-            ?assert(Local(Resp))
-    end,
-    case {Remote, Local} of
-        {no_remote, _} ->
-            ok;
-        {_, no_local} ->
-            ok;
-        _ ->
-            ?assertEqual(was_ok, test_web:check_last())
-    end,
-    Resp.
-
-chunked_body(Chunks) ->
-    chunked_body(Chunks, []).
-
-chunked_body([], Acc) ->
-    iolist_to_binary(lists:reverse(Acc, "0\r\n\r\n"));
-chunked_body([Chunk | Rest], Acc) ->
-    Size = to_hex(size(Chunk)),
-    chunked_body(Rest, ["\r\n", Chunk, "\r\n", Size | Acc]).
-
-to_hex(Val) ->
-    to_hex(Val, []).
-
-to_hex(0, Acc) ->
-    Acc;
-to_hex(Val, Acc) ->
-    to_hex(Val div 16, [hex_char(Val rem 16) | Acc]).
-
-hex_char(V) when V < 10 -> $0 + V;
-hex_char(V) -> $A + V - 10.
-
-recv_body(ReqId, Acc) ->
-    receive
-        {ibrowse_async_response, ReqId, Data} ->
-            recv_body(ReqId, [Data | Acc]);
-        {ibrowse_async_response_end, ReqId} ->
-            iolist_to_binary(lists:reverse(Acc));
-        Else ->
-            throw({error, unexpected_mesg, Else})
-    after ?TIMEOUT ->
-        throw({error, timeout})
-    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_modules_load_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_modules_load_tests.erl b/test/couchdb/couchdb_modules_load_tests.erl
deleted file mode 100644
index 4eaa42b..0000000
--- a/test/couchdb/couchdb_modules_load_tests.erl
+++ /dev/null
@@ -1,68 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_modules_load_tests).
-
--include("couch_eunit.hrl").
-
-
-modules_load_test_() ->
-    {
-        "Verify that all modules loads",
-        should_load_modules()
-    }.
-
-
-should_load_modules() ->
-    Modules = [
-        couch_auth_cache,
-        couch_btree,
-        couch_changes,
-        couch_compress,
-        couch_config,
-        couch_config_writer,
-        couch_db,
-        couch_db_update_notifier,
-        couch_db_update_notifier_sup,
-        couch_db_updater,
-        couch_doc,
-        % Fails unless couch_config gen_server is started.
-        % couch_ejson_compare,
-        couch_event_sup,
-        couch_external_manager,
-        couch_external_server,
-        couch_file,
-        couch_httpd,
-        couch_httpd_db,
-        couch_httpd_external,
-        couch_httpd_misc_handlers,
-        couch_httpd_rewrite,
-        couch_httpd_stats_handlers,
-        couch_key_tree,
-        couch_log,
-        couch_os_process,
-        couch_query_servers,
-        couch_ref_counter,
-        couch_server,
-        couch_server_sup,
-        couch_stats_aggregator,
-        couch_stats_collector,
-        couch_stream,
-        couch_task_status,
-        couch_util,
-        couch_work_queue,
-        json_stream_parse
-    ],
-    [should_load_module(Mod) || Mod <- Modules].
-
-should_load_module(Mod) ->
-    {atom_to_list(Mod), ?_assertMatch({module, _}, code:load_file(Mod))}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_os_daemons_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_os_daemons_tests.erl b/test/couchdb/couchdb_os_daemons_tests.erl
deleted file mode 100644
index aa949c9..0000000
--- a/test/couchdb/couchdb_os_daemons_tests.erl
+++ /dev/null
@@ -1,228 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_os_daemons_tests).
-
--include("couch_eunit.hrl").
-
-%% keep in sync with couchdb/couch_os_daemons.erl
--record(daemon, {
-    port,
-    name,
-    cmd,
-    kill,
-    status=running,
-    cfg_patterns=[],
-    errors=[],
-    buf=[]
-}).
-
--define(DAEMON_CONFIGER, "os_daemon_configer.escript").
--define(DAEMON_LOOPER, "os_daemon_looper.escript").
--define(DAEMON_BAD_PERM, "os_daemon_bad_perm.sh").
--define(DAEMON_CAN_REBOOT, "os_daemon_can_reboot.sh").
--define(DAEMON_DIE_ON_BOOT, "os_daemon_die_on_boot.sh").
--define(DAEMON_DIE_QUICKLY, "os_daemon_die_quickly.sh").
--define(DELAY, 100).
--define(TIMEOUT, 1000).
-
-
-setup(DName) ->
-    {ok, CfgPid} = couch_config:start_link(?CONFIG_CHAIN),
-    {ok, OsDPid} = couch_os_daemons:start_link(),
-    couch_config:set("os_daemons", DName,
-                     filename:join([?FIXTURESDIR, DName]), false),
-    timer:sleep(?DELAY),  % sleep a bit to let daemon set kill flag
-    {CfgPid, OsDPid}.
-
-teardown(_, {CfgPid, OsDPid}) ->
-    erlang:monitor(process, CfgPid),
-    couch_config:stop(),
-    receive
-        {'DOWN', _, _, CfgPid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, config_stop})
-    end,
-
-    erlang:monitor(process, OsDPid),
-    exit(OsDPid, normal),
-    receive
-        {'DOWN', _, _, OsDPid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, os_daemon_stop})
-    end.
-
-
-os_daemons_test_() ->
-    {
-        "OS Daemons tests",
-        {
-            foreachx,
-            fun setup/1, fun teardown/2,
-            [{?DAEMON_LOOPER, Fun} || Fun <- [
-                fun should_check_daemon/2,
-                fun should_check_daemon_table_form/2,
-                fun should_clean_tables_on_daemon_remove/2,
-                fun should_spawn_multiple_daemons/2,
-                fun should_keep_alive_one_daemon_on_killing_other/2
-            ]]
-        }
-    }.
-
-configuration_reader_test_() ->
-    {
-        "OS Daemon requests CouchDB configuration",
-        {
-            foreachx,
-            fun setup/1, fun teardown/2,
-            [{?DAEMON_CONFIGER,
-              fun should_read_write_config_settings_by_daemon/2}]
-
-        }
-    }.
-
-error_test_() ->
-    {
-        "OS Daemon process error tests",
-        {
-            foreachx,
-            fun setup/1, fun teardown/2,
-            [{?DAEMON_BAD_PERM, fun should_fail_due_to_lack_of_permissions/2},
-             {?DAEMON_DIE_ON_BOOT, fun should_die_on_boot/2},
-             {?DAEMON_DIE_QUICKLY, fun should_die_quickly/2},
-             {?DAEMON_CAN_REBOOT, fun should_not_being_halted/2}]
-        }
-    }.
-
-
-should_check_daemon(DName, _) ->
-    ?_test(begin
-        {ok, [D]} = couch_os_daemons:info([table]),
-        check_daemon(D, DName)
-    end).
-
-should_check_daemon_table_form(DName, _) ->
-    ?_test(begin
-        {ok, Tab} = couch_os_daemons:info(),
-        [D] = ets:tab2list(Tab),
-        check_daemon(D, DName)
-    end).
-
-should_clean_tables_on_daemon_remove(DName, _) ->
-    ?_test(begin
-        couch_config:delete("os_daemons", DName, false),
-        {ok, Tab2} = couch_os_daemons:info(),
-        ?_assertEqual([], ets:tab2list(Tab2))
-    end).
-
-should_spawn_multiple_daemons(DName, _) ->
-    ?_test(begin
-        couch_config:set("os_daemons", "bar",
-                         filename:join([?FIXTURESDIR, DName]), false),
-        couch_config:set("os_daemons", "baz",
-                         filename:join([?FIXTURESDIR, DName]), false),
-        timer:sleep(?DELAY),
-        {ok, Daemons} = couch_os_daemons:info([table]),
-        lists:foreach(fun(D) ->
-            check_daemon(D)
-        end, Daemons),
-        {ok, Tab} = couch_os_daemons:info(),
-        lists:foreach(fun(D) ->
-            check_daemon(D)
-        end, ets:tab2list(Tab))
-    end).
-
-should_keep_alive_one_daemon_on_killing_other(DName, _) ->
-    ?_test(begin
-        couch_config:set("os_daemons", "bar",
-                         filename:join([?FIXTURESDIR, DName]), false),
-        timer:sleep(?DELAY),
-        {ok, Daemons} = couch_os_daemons:info([table]),
-        lists:foreach(fun(D) ->
-            check_daemon(D)
-        end, Daemons),
-
-        couch_config:delete("os_daemons", "bar", false),
-        timer:sleep(?DELAY),
-        {ok, [D2]} = couch_os_daemons:info([table]),
-        check_daemon(D2, DName),
-
-        {ok, Tab} = couch_os_daemons:info(),
-        [T] = ets:tab2list(Tab),
-        check_daemon(T, DName)
-    end).
-
-should_read_write_config_settings_by_daemon(DName, _) ->
-    ?_test(begin
-        % have to wait till daemon run all his tests
-        % see daemon's script for more info
-        timer:sleep(?TIMEOUT),
-        {ok, [D]} = couch_os_daemons:info([table]),
-        check_daemon(D, DName)
-    end).
-
-should_fail_due_to_lack_of_permissions(DName, _) ->
-    ?_test(should_halts(DName, 1000)).
-
-should_die_on_boot(DName, _) ->
-    ?_test(should_halts(DName, 1000)).
-
-should_die_quickly(DName, _) ->
-    ?_test(should_halts(DName, 4000)).
-
-should_not_being_halted(DName, _) ->
-    ?_test(begin
-        timer:sleep(1000),
-        {ok, [D1]} = couch_os_daemons:info([table]),
-        check_daemon(D1, DName, 0),
-
-        % Should reboot every two seconds. We're at 1s, so wait
-        % until 3s to be in the middle of the next invocation's
-        % life span.
-
-        timer:sleep(2000),
-        {ok, [D2]} = couch_os_daemons:info([table]),
-        check_daemon(D2, DName, 1),
-
-        % If the kill command changed, that means we rebooted the process.
-        ?assertNotEqual(D1#daemon.kill, D2#daemon.kill)
-    end).
-
-should_halts(DName, Time) ->
-    timer:sleep(Time),
-    {ok, [D]} = couch_os_daemons:info([table]),
-    check_dead(D, DName),
-    couch_config:delete("os_daemons", DName, false).
-
-check_daemon(D) ->
-    check_daemon(D, D#daemon.name).
-
-check_daemon(D, Name) ->
-    check_daemon(D, Name, 0).
-
-check_daemon(D, Name, Errs) ->
-    ?assert(is_port(D#daemon.port)),
-    ?assertEqual(Name, D#daemon.name),
-    ?assertNotEqual(undefined, D#daemon.kill),
-    ?assertEqual(running, D#daemon.status),
-    ?assertEqual(Errs, length(D#daemon.errors)),
-    ?assertEqual([], D#daemon.buf).
-
-check_dead(D, Name) ->
-    ?assert(is_port(D#daemon.port)),
-    ?assertEqual(Name, D#daemon.name),
-    ?assertNotEqual(undefined, D#daemon.kill),
-    ?assertEqual(halted, D#daemon.status),
-    ?assertEqual(nil, D#daemon.errors),
-    ?assertEqual(nil, D#daemon.buf).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_os_proc_pool.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_os_proc_pool.erl b/test/couchdb/couchdb_os_proc_pool.erl
deleted file mode 100644
index 1bb266e..0000000
--- a/test/couchdb/couchdb_os_proc_pool.erl
+++ /dev/null
@@ -1,179 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_os_proc_pool).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(TIMEOUT, 3000).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    couch_config:set("query_server_config", "os_process_limit", "3", false),
-    Pid.
-
-stop(Pid) ->
-    couch_server_sup:stop(),
-    erlang:monitor(process, Pid),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-
-os_proc_pool_test_() ->
-    {
-        "OS processes pool tests",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            [
-                should_block_new_proc_on_full_pool(),
-                should_free_slot_on_proc_unexpected_exit()
-            ]
-        }
-    }.
-
-
-should_block_new_proc_on_full_pool() ->
-    ?_test(begin
-        Client1 = spawn_client(),
-        Client2 = spawn_client(),
-        Client3 = spawn_client(),
-
-        ?assertEqual(ok, ping_client(Client1)),
-        ?assertEqual(ok, ping_client(Client2)),
-        ?assertEqual(ok, ping_client(Client3)),
-
-        Proc1 = get_client_proc(Client1, "1"),
-        Proc2 = get_client_proc(Client2, "2"),
-        Proc3 = get_client_proc(Client3, "3"),
-
-        ?assertNotEqual(Proc1, Proc2),
-        ?assertNotEqual(Proc2, Proc3),
-        ?assertNotEqual(Proc3, Proc1),
-
-        Client4 = spawn_client(),
-        ?assertEqual(timeout, ping_client(Client4)),
-
-        ?assertEqual(ok, stop_client(Client1)),
-        ?assertEqual(ok, ping_client(Client4)),
-
-        Proc4 = get_client_proc(Client4, "4"),
-        ?assertEqual(Proc1, Proc4),
-
-        lists:map(fun(C) ->
-            ?assertEqual(ok, stop_client(C))
-        end, [Client2, Client3, Client4])
-    end).
-
-should_free_slot_on_proc_unexpected_exit() ->
-    ?_test(begin
-        Client1 = spawn_client(),
-        Client2 = spawn_client(),
-        Client3 = spawn_client(),
-
-        ?assertEqual(ok, ping_client(Client1)),
-        ?assertEqual(ok, ping_client(Client2)),
-        ?assertEqual(ok, ping_client(Client3)),
-
-        Proc1 = get_client_proc(Client1, "1"),
-        Proc2 = get_client_proc(Client2, "2"),
-        Proc3 = get_client_proc(Client3, "3"),
-
-        ?assertNotEqual(Proc1, Proc2),
-        ?assertNotEqual(Proc2, Proc3),
-        ?assertNotEqual(Proc3, Proc1),
-
-        ?assertEqual(ok, kill_client(Client1)),
-
-        Client4 = spawn_client(),
-        ?assertEqual(ok, ping_client(Client4)),
-
-        Proc4 = get_client_proc(Client4, "4"),
-        ?assertNotEqual(Proc4, Proc1),
-        ?assertNotEqual(Proc2, Proc4),
-        ?assertNotEqual(Proc3, Proc4),
-
-        lists:map(fun(C) ->
-            ?assertEqual(ok, stop_client(C))
-        end, [Client2, Client3, Client4])
-    end).
-
-
-spawn_client() ->
-    Parent = self(),
-    Ref = make_ref(),
-    Pid = spawn(fun() ->
-        Proc = couch_query_servers:get_os_process(<<"javascript">>),
-        loop(Parent, Ref, Proc)
-    end),
-    {Pid, Ref}.
-
-ping_client({Pid, Ref}) ->
-    Pid ! ping,
-    receive
-        {pong, Ref} ->
-            ok
-    after ?TIMEOUT ->
-        timeout
-    end.
-
-get_client_proc({Pid, Ref}, ClientName) ->
-    Pid ! get_proc,
-    receive
-        {proc, Ref, Proc} -> Proc
-    after ?TIMEOUT ->
-        erlang:error({assertion_failed,
-                     [{module, ?MODULE},
-                      {line, ?LINE},
-                      {reason, "Timeout getting client "
-                               ++ ClientName ++ " proc"}]})
-    end.
-
-stop_client({Pid, Ref}) ->
-    Pid ! stop,
-    receive
-        {stop, Ref} ->
-            ok
-    after ?TIMEOUT ->
-        timeout
-    end.
-
-kill_client({Pid, Ref}) ->
-    Pid ! die,
-    receive
-        {die, Ref} ->
-            ok
-    after ?TIMEOUT ->
-        timeout
-    end.
-
-loop(Parent, Ref, Proc) ->
-    receive
-        ping ->
-            Parent ! {pong, Ref},
-            loop(Parent, Ref, Proc);
-        get_proc  ->
-            Parent ! {proc, Ref, Proc},
-            loop(Parent, Ref, Proc);
-        stop ->
-            couch_query_servers:ret_os_process(Proc),
-            Parent ! {stop, Ref};
-        die ->
-            Parent ! {die, Ref},
-            exit(some_error)
-    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_update_conflicts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_update_conflicts_tests.erl b/test/couchdb/couchdb_update_conflicts_tests.erl
deleted file mode 100644
index 7226860..0000000
--- a/test/couchdb/couchdb_update_conflicts_tests.erl
+++ /dev/null
@@ -1,243 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_update_conflicts_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(i2l(I), integer_to_list(I)).
--define(ADMIN_USER, {userctx, #user_ctx{roles=[<<"_admin">>]}}).
--define(DOC_ID, <<"foobar">>).
--define(NUM_CLIENTS, [100, 500, 1000, 2000, 5000, 10000]).
--define(TIMEOUT, 10000).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    couch_config:set("couchdb", "delayed_commits", "true", false),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-setup() ->
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER, overwrite]),
-    Doc = couch_doc:from_json_obj({[{<<"_id">>, ?DOC_ID},
-                                    {<<"value">>, 0}]}),
-    {ok, Rev} = couch_db:update_doc(Db, Doc, []),
-    ok = couch_db:close(Db),
-    RevStr = couch_doc:rev_to_str(Rev),
-    {DbName, RevStr}.
-setup(_) ->
-    setup().
-
-teardown({DbName, _}) ->
-    ok = couch_server:delete(DbName, []),
-    ok.
-teardown(_, {DbName, _RevStr}) ->
-    teardown({DbName, _RevStr}).
-
-
-view_indexes_cleanup_test_() ->
-    {
-        "Update conflicts",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            [
-                concurrent_updates(),
-                couchdb_188()
-            ]
-        }
-    }.
-
-concurrent_updates()->
-    {
-        "Concurrent updates",
-        {
-            foreachx,
-            fun setup/1, fun teardown/2,
-            [{NumClients, fun should_concurrently_update_doc/2}
-             || NumClients <- ?NUM_CLIENTS]
-        }
-    }.
-
-couchdb_188()->
-    {
-        "COUCHDB-188",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [fun should_bulk_create_delete_doc/1]
-        }
-    }.
-
-
-should_concurrently_update_doc(NumClients, {DbName, InitRev})->
-     {?i2l(NumClients) ++ " clients",
-      {inorder,
-       [{"update doc",
-         {timeout, ?TIMEOUT div 1000,
-          ?_test(concurrent_doc_update(NumClients, DbName, InitRev))}},
-        {"ensure in single leaf",
-         ?_test(ensure_in_single_revision_leaf(DbName))}]}}.
-
-should_bulk_create_delete_doc({DbName, InitRev})->
-    ?_test(bulk_delete_create(DbName, InitRev)).
-
-
-concurrent_doc_update(NumClients, DbName, InitRev) ->
-    Clients = lists:map(
-        fun(Value) ->
-            ClientDoc = couch_doc:from_json_obj({[
-                {<<"_id">>, ?DOC_ID},
-                {<<"_rev">>, InitRev},
-                {<<"value">>, Value}
-            ]}),
-            Pid = spawn_client(DbName, ClientDoc),
-            {Value, Pid, erlang:monitor(process, Pid)}
-        end,
-        lists:seq(1, NumClients)),
-
-    lists:foreach(fun({_, Pid, _}) -> Pid ! go end, Clients),
-
-    {NumConflicts, SavedValue} = lists:foldl(
-        fun({Value, Pid, MonRef}, {AccConflicts, AccValue}) ->
-            receive
-                {'DOWN', MonRef, process, Pid, {ok, _NewRev}} ->
-                    {AccConflicts, Value};
-                {'DOWN', MonRef, process, Pid, conflict} ->
-                    {AccConflicts + 1, AccValue};
-                {'DOWN', MonRef, process, Pid, Error} ->
-                    erlang:error({assertion_failed,
-                         [{module, ?MODULE},
-                          {line, ?LINE},
-                          {reason, "Client " ++ ?i2l(Value)
-                                             ++ " got update error: "
-                                             ++ couch_util:to_list(Error)}]})
-            after ?TIMEOUT div 2 ->
-                 erlang:error({assertion_failed,
-                         [{module, ?MODULE},
-                          {line, ?LINE},
-                          {reason, "Timeout waiting for client "
-                                   ++ ?i2l(Value) ++ " to die"}]})
-            end
-        end, {0, nil}, Clients),
-    ?assertEqual(NumClients - 1, NumConflicts),
-
-    {ok, Db} = couch_db:open_int(DbName, []),
-    {ok, Leaves} = couch_db:open_doc_revs(Db, ?DOC_ID, all, []),
-    ok = couch_db:close(Db),
-    ?assertEqual(1, length(Leaves)),
-
-    [{ok, Doc2}] = Leaves,
-    {JsonDoc} = couch_doc:to_json_obj(Doc2, []),
-    ?assertEqual(SavedValue, couch_util:get_value(<<"value">>, JsonDoc)).
-
-ensure_in_single_revision_leaf(DbName) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    {ok, Leaves} = couch_db:open_doc_revs(Db, ?DOC_ID, all, []),
-    ok = couch_db:close(Db),
-    [{ok, Doc}] = Leaves,
-
-    %% FIXME: server restart won't work from test side
-    %% stop(ok),
-    %% start(),
-
-    {ok, Db2} = couch_db:open_int(DbName, []),
-    {ok, Leaves2} = couch_db:open_doc_revs(Db2, ?DOC_ID, all, []),
-    ok = couch_db:close(Db2),
-    ?assertEqual(1, length(Leaves2)),
-
-    [{ok, Doc2}] = Leaves,
-    ?assertEqual(Doc, Doc2).
-    
-bulk_delete_create(DbName, InitRev) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    
-    DeletedDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, ?DOC_ID},
-        {<<"_rev">>, InitRev},
-        {<<"_deleted">>, true}
-    ]}),
-    NewDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, ?DOC_ID},
-        {<<"value">>, 666}
-    ]}),
-
-    {ok, Results} = couch_db:update_docs(Db, [DeletedDoc, NewDoc], []),
-    ok = couch_db:close(Db),
-
-    ?assertEqual(2, length([ok || {ok, _} <- Results])),
-    [{ok, Rev1}, {ok, Rev2}] = Results,
-    
-    {ok, Db2} = couch_db:open_int(DbName, []),
-    {ok, [{ok, Doc1}]} = couch_db:open_doc_revs(
-        Db2, ?DOC_ID, [Rev1], [conflicts, deleted_conflicts]),
-    {ok, [{ok, Doc2}]} = couch_db:open_doc_revs(
-        Db2, ?DOC_ID, [Rev2], [conflicts, deleted_conflicts]),
-    ok = couch_db:close(Db2),
-
-    {Doc1Props} = couch_doc:to_json_obj(Doc1, []),
-    {Doc2Props} = couch_doc:to_json_obj(Doc2, []),
-
-    %% Document was deleted
-    ?assert(couch_util:get_value(<<"_deleted">>, Doc1Props)),
-    %% New document not flagged as deleted
-    ?assertEqual(undefined, couch_util:get_value(<<"_deleted">>,
-                                                 Doc2Props)),
-    %% New leaf revision has the right value
-    ?assertEqual(666, couch_util:get_value(<<"value">>,
-                                           Doc2Props)),
-    %% Deleted document has no conflicts
-    ?assertEqual(undefined, couch_util:get_value(<<"_conflicts">>,
-                                                 Doc1Props)),
-    %% Deleted document has no deleted conflicts
-    ?assertEqual(undefined, couch_util:get_value(<<"_deleted_conflicts">>,
-                                                 Doc1Props)),
-    %% New leaf revision doesn't have conflicts
-    ?assertEqual(undefined, couch_util:get_value(<<"_conflicts">>,
-                                                 Doc1Props)),
-    %% New leaf revision doesn't have deleted conflicts
-    ?assertEqual(undefined, couch_util:get_value(<<"_deleted_conflicts">>,
-                                                 Doc1Props)),
-
-    %% Deleted revision has position 2
-    ?assertEqual(2, element(1, Rev1)),
-    %% New leaf revision has position 1
-    ?assertEqual(1, element(1, Rev2)).
-
-
-spawn_client(DbName, Doc) ->
-    spawn(fun() ->
-        {ok, Db} = couch_db:open_int(DbName, []),
-        receive
-            go -> ok
-        end,
-        erlang:yield(),
-        Result = try
-            couch_db:update_doc(Db, Doc, [])
-        catch _:Error ->
-            Error
-        end,
-        ok = couch_db:close(Db),
-        exit(Result)
-    end).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_vhosts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_vhosts_tests.erl b/test/couchdb/couchdb_vhosts_tests.erl
deleted file mode 100644
index 94b1957..0000000
--- a/test/couchdb/couchdb_vhosts_tests.erl
+++ /dev/null
@@ -1,441 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_vhosts_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
--define(TIMEOUT, 1000).
--define(iofmt(S, A), lists:flatten(io_lib:format(S, A))).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-setup() ->
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
-    Doc = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"doc1">>},
-        {<<"value">>, 666}
-    ]}),
-
-    Doc1 = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"_design/doc1">>},
-        {<<"shows">>, {[
-            {<<"test">>, <<"function(doc, req) {
-            return { json: {
-                    requested_path: '/' + req.requested_path.join('/'),
-                    path: '/' + req.path.join('/')}};}">>}
-        ]}},
-        {<<"rewrites">>, [
-            {[
-                {<<"from">>, <<"/">>},
-                {<<"to">>, <<"_show/test">>}
-            ]}
-        ]}
-    ]}),
-    {ok, _} = couch_db:update_docs(Db, [Doc, Doc1]),
-    couch_db:ensure_full_commit(Db),
-    couch_db:close(Db),
-
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
-    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
-    Url = "http://" ++ Addr ++ ":" ++ Port,
-    {Url, ?b2l(DbName)}.
-
-setup_oauth() ->
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
-
-    couch_config:set("couch_httpd_auth", "authentication_db",
-                     ?b2l(?tempdb()), false),
-    couch_config:set("oauth_token_users", "otoksec1", "joe", false),
-    couch_config:set("oauth_consumer_secrets", "consec1", "foo", false),
-    couch_config:set("oauth_token_secrets", "otoksec1", "foobar", false),
-    couch_config:set("couch_httpd_auth", "require_valid_user", "true", false),
-
-    ok = couch_config:set(
-        "vhosts", "oauth-example.com",
-        "/" ++ ?b2l(DbName) ++ "/_design/test/_rewrite/foobar", false),
-
-    DDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"_design/test">>},
-        {<<"language">>, <<"javascript">>},
-        {<<"rewrites">>, [
-            {[
-                {<<"from">>, <<"foobar">>},
-                {<<"to">>, <<"_info">>}
-            ]}
-        ]}
-    ]}),
-    {ok, _} = couch_db:update_doc(Db, DDoc, []),
-
-    couch_db:ensure_full_commit(Db),
-    couch_db:close(Db),
-
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
-    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
-    Url = "http://" ++ Addr ++ ":" ++ Port,
-    {Url, ?b2l(DbName)}.
-
-teardown({_, DbName}) ->
-    ok = couch_server:delete(?l2b(DbName), []),
-    ok.
-
-
-vhosts_test_() ->
-    {
-        "Virtual Hosts rewrite tests",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            {
-                foreach,
-                fun setup/0, fun teardown/1,
-                [
-                    fun should_return_database_info/1,
-                    fun should_return_revs_info/1,
-                    fun should_serve_utils_for_vhost/1,
-                    fun should_return_virtual_request_path_field_in_request/1,
-                    fun should_return_real_request_path_field_in_request/1,
-                    fun should_match_wildcard_vhost/1,
-                    fun should_return_db_info_for_wildcard_vhost_for_custom_db/1,
-                    fun should_replace_rewrite_variables_for_db_and_doc/1,
-                    fun should_return_db_info_for_vhost_with_resource/1,
-                    fun should_return_revs_info_for_vhost_with_resource/1,
-                    fun should_return_db_info_for_vhost_with_wildcard_resource/1,
-                    fun should_return_path_for_vhost_with_wildcard_host/1
-                ]
-            }
-        }
-    }.
-
-oauth_test_() ->
-    {
-        "Virtual Hosts OAuth tests",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            {
-                foreach,
-                fun setup_oauth/0, fun teardown/1,
-                [
-                    fun should_require_auth/1,
-                    fun should_succeed_oauth/1,
-                    fun should_fail_oauth_with_wrong_credentials/1
-                ]
-            }
-        }
-    }.
-
-
-should_return_database_info({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
-        case test_request:get(Url, [], [{host_header, "example.com"}]) of
-            {ok, _, _, Body} ->
-                {JsonBody} = ejson:decode(Body),
-                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_return_revs_info({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
-        case test_request:get(Url ++ "/doc1?revs_info=true", [],
-                              [{host_header, "example.com"}]) of
-            {ok, _, _, Body} ->
-                {JsonBody} = ejson:decode(Body),
-                ?assert(proplists:is_defined(<<"_revs_info">>, JsonBody));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_serve_utils_for_vhost({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
-        case test_request:get(Url ++ "/_utils/index.html", [],
-                              [{host_header, "example.com"}]) of
-            {ok, _, _, Body} ->
-                ?assertMatch(<<"<!DOCTYPE html>", _/binary>>, Body);
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_return_virtual_request_path_field_in_request({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts", "example1.com",
-                              "/" ++ DbName ++ "/_design/doc1/_rewrite/",
-                              false),
-        case test_request:get(Url, [], [{host_header, "example1.com"}]) of
-            {ok, _, _, Body} ->
-                {Json} = ejson:decode(Body),
-                ?assertEqual(<<"/">>,
-                             proplists:get_value(<<"requested_path">>, Json));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_return_real_request_path_field_in_request({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts", "example1.com",
-                              "/" ++ DbName ++ "/_design/doc1/_rewrite/",
-                              false),
-        case test_request:get(Url, [], [{host_header, "example1.com"}]) of
-            {ok, _, _, Body} ->
-                {Json} = ejson:decode(Body),
-                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
-                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_match_wildcard_vhost({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts", "*.example.com",
-                              "/" ++ DbName ++ "/_design/doc1/_rewrite", false),
-        case test_request:get(Url, [], [{host_header, "test.example.com"}]) of
-            {ok, _, _, Body} ->
-                {Json} = ejson:decode(Body),
-                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
-                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_return_db_info_for_wildcard_vhost_for_custom_db({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts", ":dbname.example1.com",
-                              "/:dbname", false),
-        Host = DbName ++ ".example1.com",
-        case test_request:get(Url, [], [{host_header, Host}]) of
-            {ok, _, _, Body} ->
-                {JsonBody} = ejson:decode(Body),
-                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_replace_rewrite_variables_for_db_and_doc({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts",":appname.:dbname.example1.com",
-                              "/:dbname/_design/:appname/_rewrite/", false),
-        Host = "doc1." ++ DbName ++ ".example1.com",
-        case test_request:get(Url, [], [{host_header, Host}]) of
-            {ok, _, _, Body} ->
-                {Json} = ejson:decode(Body),
-                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
-                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_return_db_info_for_vhost_with_resource({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts",
-                              "example.com/test", "/" ++ DbName, false),
-        ReqUrl = Url ++ "/test",
-        case test_request:get(ReqUrl, [], [{host_header, "example.com"}]) of
-            {ok, _, _, Body} ->
-                {JsonBody} = ejson:decode(Body),
-                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-
-should_return_revs_info_for_vhost_with_resource({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts",
-                              "example.com/test", "/" ++ DbName, false),
-        ReqUrl = Url ++ "/test/doc1?revs_info=true",
-        case test_request:get(ReqUrl, [], [{host_header, "example.com"}]) of
-            {ok, _, _, Body} ->
-                {JsonBody} = ejson:decode(Body),
-                ?assert(proplists:is_defined(<<"_revs_info">>, JsonBody));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_return_db_info_for_vhost_with_wildcard_resource({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts", "*.example2.com/test", "/*", false),
-        ReqUrl = Url ++ "/test",
-        Host = DbName ++ ".example2.com",
-        case test_request:get(ReqUrl, [], [{host_header, Host}]) of
-            {ok, _, _, Body} ->
-                {JsonBody} = ejson:decode(Body),
-                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_return_path_for_vhost_with_wildcard_host({Url, DbName}) ->
-    ?_test(begin
-        ok = couch_config:set("vhosts", "*/test1",
-                              "/" ++ DbName ++ "/_design/doc1/_show/test",
-                              false),
-        case test_request:get(Url ++ "/test1") of
-            {ok, _, _, Body} ->
-                {Json} = ejson:decode(Body),
-                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
-                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_require_auth({Url, _}) ->
-    ?_test(begin
-        case test_request:get(Url, [], [{host_header, "oauth-example.com"}]) of
-            {ok, Code, _, Body} ->
-                ?assertEqual(401, Code),
-                {JsonBody} = ejson:decode(Body),
-                ?assertEqual(<<"unauthorized">>,
-                             couch_util:get_value(<<"error">>, JsonBody));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_succeed_oauth({Url, _}) ->
-    ?_test(begin
-        AuthDbName = couch_config:get("couch_httpd_auth", "authentication_db"),
-        JoeDoc = couch_doc:from_json_obj({[
-            {<<"_id">>, <<"org.couchdb.user:joe">>},
-            {<<"type">>, <<"user">>},
-            {<<"name">>, <<"joe">>},
-            {<<"roles">>, []},
-            {<<"password_sha">>, <<"fe95df1ca59a9b567bdca5cbaf8412abd6e06121">>},
-            {<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4001a73">>}
-        ]}),
-        {ok, AuthDb} = couch_db:open_int(?l2b(AuthDbName), [?ADMIN_USER]),
-        {ok, _} = couch_db:update_doc(AuthDb, JoeDoc, [?ADMIN_USER]),
-
-        Host = "oauth-example.com",
-        Consumer = {"consec1", "foo", hmac_sha1},
-        SignedParams = oauth:sign(
-            "GET", "http://" ++ Host ++ "/", [], Consumer, "otoksec1", "foobar"),
-        OAuthUrl = oauth:uri(Url, SignedParams),
-
-        case test_request:get(OAuthUrl, [], [{host_header, Host}]) of
-            {ok, Code, _, Body} ->
-                ?assertEqual(200, Code),
-                {JsonBody} = ejson:decode(Body),
-                ?assertEqual(<<"test">>,
-                             couch_util:get_value(<<"name">>, JsonBody));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).
-
-should_fail_oauth_with_wrong_credentials({Url, _}) ->
-    ?_test(begin
-        AuthDbName = couch_config:get("couch_httpd_auth", "authentication_db"),
-        JoeDoc = couch_doc:from_json_obj({[
-            {<<"_id">>, <<"org.couchdb.user:joe">>},
-            {<<"type">>, <<"user">>},
-            {<<"name">>, <<"joe">>},
-            {<<"roles">>, []},
-            {<<"password_sha">>, <<"fe95df1ca59a9b567bdca5cbaf8412abd6e06121">>},
-            {<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4001a73">>}
-        ]}),
-        {ok, AuthDb} = couch_db:open_int(?l2b(AuthDbName), [?ADMIN_USER]),
-        {ok, _} = couch_db:update_doc(AuthDb, JoeDoc, [?ADMIN_USER]),
-
-        Host = "oauth-example.com",
-        Consumer = {"consec1", "bad_secret", hmac_sha1},
-        SignedParams = oauth:sign(
-            "GET", "http://" ++ Host ++ "/", [], Consumer, "otoksec1", "foobar"),
-        OAuthUrl = oauth:uri(Url, SignedParams),
-
-        case test_request:get(OAuthUrl, [], [{host_header, Host}]) of
-            {ok, Code, _, Body} ->
-                ?assertEqual(401, Code),
-                {JsonBody} = ejson:decode(Body),
-                ?assertEqual(<<"unauthorized">>,
-                             couch_util:get_value(<<"error">>, JsonBody));
-            Else ->
-                erlang:error({assertion_failed,
-                             [{module, ?MODULE},
-                              {line, ?LINE},
-                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
-        end
-    end).


[23/42] Move files out of test/couchdb into top level test/ folder

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_cors_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_cors_tests.erl b/test/couchdb_cors_tests.erl
new file mode 100644
index 0000000..4e88ae7
--- /dev/null
+++ b/test/couchdb_cors_tests.erl
@@ -0,0 +1,344 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_cors_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(SUPPORTED_METHODS,
+        "GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, COPY, OPTIONS").
+-define(TIMEOUT, 1000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    ok = couch_config:set("httpd", "enable_cors", "true", false),
+    ok = couch_config:set("vhosts", "example.com", "/", false),
+    Pid.
+
+stop(Pid) ->
+    couch_server_sup:stop(),
+    erlang:monitor(process, Pid),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    couch_db:close(Db),
+
+    couch_config:set("cors", "credentials", "false", false),
+    couch_config:set("cors", "origins", "http://example.com", false),
+
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    Host = "http://" ++ Addr ++ ":" ++ Port,
+    {Host, ?b2l(DbName)}.
+
+setup({Mod, VHost}) ->
+    {Host, DbName} = setup(),
+    Url = case Mod of
+        server ->
+            Host;
+        db ->
+            Host ++ "/" ++ DbName
+    end,
+    DefaultHeaders = [{"Origin", "http://example.com"}]
+                     ++ maybe_append_vhost(VHost),
+    {Host, DbName, Url, DefaultHeaders}.
+
+teardown(DbName) when is_list(DbName) ->
+    ok = couch_server:delete(?l2b(DbName), [?ADMIN_USER]),
+    ok;
+teardown({_, DbName}) ->
+    teardown(DbName).
+
+teardown(_, {_, DbName, _, _}) ->
+    teardown(DbName).
+
+
+cors_test_() ->
+    Funs = [
+        fun should_not_allow_origin/2,
+        fun should_not_allow_origin_with_port_mismatch/2,
+        fun should_not_allow_origin_with_scheme_mismatch/2,
+        fun should_not_all_origin_due_case_mismatch/2,
+        fun should_make_simple_request/2,
+        fun should_make_preflight_request/2,
+        fun should_make_prefligh_request_with_port/2,
+        fun should_make_prefligh_request_with_scheme/2,
+        fun should_make_prefligh_request_with_wildcard_origin/2,
+        fun should_make_request_with_credentials/2,
+        fun should_make_origin_request_with_auth/2,
+        fun should_make_preflight_request_with_auth/2
+    ],
+    {
+        "CORS (COUCHDB-431)",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [
+                cors_tests(Funs),
+                vhost_cors_tests(Funs),
+                headers_tests()
+            ]
+        }
+    }.
+
+headers_tests() ->
+    {
+        "Various headers tests",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_not_return_cors_headers_for_invalid_origin/1,
+                fun should_not_return_cors_headers_for_invalid_origin_preflight/1,
+                fun should_make_request_against_attachment/1,
+                fun should_make_range_request_against_attachment/1,
+                fun should_make_request_with_if_none_match_header/1
+            ]
+        }
+    }.
+
+cors_tests(Funs) ->
+    {
+        "CORS tests",
+        [
+            make_test_case(server, false, Funs),
+            make_test_case(db, false, Funs)
+        ]
+    }.
+
+vhost_cors_tests(Funs) ->
+    {
+        "Virtual Host CORS",
+        [
+            make_test_case(server, true, Funs),
+            make_test_case(db, true, Funs)
+        ]
+    }.
+
+make_test_case(Mod, UseVhost, Funs) ->
+    {
+        case Mod of server -> "Server"; db -> "Database" end,
+        {foreachx, fun setup/1, fun teardown/2, [{{Mod, UseVhost}, Fun}
+                                                 || Fun <- Funs]}
+    }.
+
+
+should_not_allow_origin(_, {_, _, Url, Headers0}) ->
+    ?_assertEqual(undefined,
+        begin
+            couch_config:delete("cors", "origins", false),
+            Headers1 = proplists:delete("Origin", Headers0),
+            Headers = [{"Origin", "http://127.0.0.1"}]
+                      ++ Headers1,
+            {ok, _, Resp, _} = test_request:get(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_not_allow_origin_with_port_mismatch({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual(undefined,
+        begin
+            Headers = [{"Origin", "http://example.com:5984"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_not_allow_origin_with_scheme_mismatch({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual(undefined,
+        begin
+            Headers = [{"Origin", "http://example.com:5984"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_not_all_origin_due_case_mismatch({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual(undefined,
+        begin
+            Headers = [{"Origin", "http://ExAmPlE.CoM"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_simple_request(_, {_, _, Url, DefaultHeaders}) ->
+    ?_test(begin
+        {ok, _, Resp, _} = test_request:get(Url, DefaultHeaders),
+        ?assertEqual(
+            undefined,
+            proplists:get_value("Access-Control-Allow-Credentials", Resp)),
+        ?assertEqual(
+            "http://example.com",
+            proplists:get_value("Access-Control-Allow-Origin", Resp)),
+        ?assertEqual(
+            "Cache-Control, Content-Type, Server",
+            proplists:get_value("Access-Control-Expose-Headers", Resp))
+    end).
+
+should_make_preflight_request(_, {_, _, Url, DefaultHeaders}) ->
+    ?_assertEqual(?SUPPORTED_METHODS,
+        begin
+            Headers = DefaultHeaders
+                      ++ [{"Access-Control-Request-Method", "GET"}],
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Methods", Resp)
+        end).
+
+should_make_prefligh_request_with_port({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual("http://example.com:5984",
+        begin
+            couch_config:set("cors", "origins", "http://example.com:5984",
+                             false),
+            Headers = [{"Origin", "http://example.com:5984"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_prefligh_request_with_scheme({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual("https://example.com:5984",
+        begin
+            couch_config:set("cors", "origins", "https://example.com:5984",
+                             false),
+            Headers = [{"Origin", "https://example.com:5984"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_prefligh_request_with_wildcard_origin({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual("https://example.com:5984",
+        begin
+            couch_config:set("cors", "origins", "*", false),
+            Headers = [{"Origin", "https://example.com:5984"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_request_with_credentials(_, {_, _, Url, DefaultHeaders}) ->
+    ?_assertEqual("true",
+        begin
+            ok = couch_config:set("cors", "credentials", "true", false),
+            {ok, _, Resp, _} = test_request:options(Url, DefaultHeaders),
+            proplists:get_value("Access-Control-Allow-Credentials", Resp)
+        end).
+
+should_make_origin_request_with_auth(_, {_, _, Url, DefaultHeaders}) ->
+    ?_assertEqual("http://example.com",
+        begin
+            Hashed = couch_passwords:hash_admin_password(<<"test">>),
+            couch_config:set("admins", "test", Hashed, false),
+            {ok, _, Resp, _} = test_request:get(
+                Url, DefaultHeaders, [{basic_auth, {"test", "test"}}]),
+            couch_config:delete("admins", "test", false),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_preflight_request_with_auth(_, {_, _, Url, DefaultHeaders}) ->
+    ?_assertEqual(?SUPPORTED_METHODS,
+        begin
+            Hashed = couch_passwords:hash_admin_password(<<"test">>),
+            couch_config:set("admins", "test", Hashed, false),
+            Headers = DefaultHeaders
+                      ++ [{"Access-Control-Request-Method", "GET"}],
+            {ok, _, Resp, _} = test_request:options(
+                Url, Headers, [{basic_auth, {"test", "test"}}]),
+            couch_config:delete("admins", "test", false),
+            proplists:get_value("Access-Control-Allow-Methods", Resp)
+        end).
+
+should_not_return_cors_headers_for_invalid_origin({Host, _}) ->
+    ?_assertEqual(undefined,
+        begin
+            Headers = [{"Origin", "http://127.0.0.1"}],
+            {ok, _, Resp, _} = test_request:get(Host, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_not_return_cors_headers_for_invalid_origin_preflight({Host, _}) ->
+    ?_assertEqual(undefined,
+        begin
+            Headers = [{"Origin", "http://127.0.0.1"},
+                       {"Access-Control-Request-Method", "GET"}],
+            {ok, _, Resp, _} = test_request:options(Host, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_request_against_attachment({Host, DbName}) ->
+    {"COUCHDB-1689",
+     ?_assertEqual(200,
+         begin
+             Url = Host ++ "/" ++ DbName,
+             {ok, Code0, _, _} = test_request:put(
+                 Url ++ "/doc/file.txt", [{"Content-Type", "text/plain"}],
+                 "hello, couch!"),
+             ?assert(Code0 =:= 201),
+             {ok, Code, _, _} = test_request:get(
+                 Url ++ "/doc?attachments=true",
+                 [{"Origin", "http://example.com"}]),
+             Code
+         end)}.
+
+should_make_range_request_against_attachment({Host, DbName}) ->
+    {"COUCHDB-1689",
+     ?_assertEqual(206,
+         begin
+             Url = Host ++ "/" ++ DbName,
+             {ok, Code0, _, _} = test_request:put(
+                 Url ++ "/doc/file.txt",
+                 [{"Content-Type", "application/octet-stream"}],
+                 "hello, couch!"),
+             ?assert(Code0 =:= 201),
+             {ok, Code, _, _} = test_request:get(
+                 Url ++ "/doc/file.txt", [{"Origin", "http://example.com"},
+                                          {"Range", "bytes=0-6"}]),
+             Code
+         end)}.
+
+should_make_request_with_if_none_match_header({Host, DbName}) ->
+    {"COUCHDB-1697",
+     ?_assertEqual(304,
+         begin
+             Url = Host ++ "/" ++ DbName,
+             {ok, Code0, Headers0, _} = test_request:put(
+                 Url ++ "/doc", [{"Content-Type", "application/json"}], "{}"),
+             ?assert(Code0 =:= 201),
+             ETag = proplists:get_value("ETag", Headers0),
+             {ok, Code, _, _} = test_request:get(
+                 Url ++ "/doc", [{"Origin", "http://example.com"},
+                                 {"If-None-Match", ETag}]),
+             Code
+        end)}.
+
+
+maybe_append_vhost(true) ->
+    [{"Host", "http://example.com"}];
+maybe_append_vhost(false) ->
+    [].

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_csp_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_csp_tests.erl b/test/couchdb_csp_tests.erl
new file mode 100644
index 0000000..adb0e6d
--- /dev/null
+++ b/test/couchdb_csp_tests.erl
@@ -0,0 +1,96 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_csp_tests).
+
+-include("couch_eunit.hrl").
+
+-define(TIMEOUT, 1000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    Pid.
+
+stop(Pid) ->
+    couch_server_sup:stop(),
+    erlang:monitor(process, Pid),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    ok = couch_config:set("csp", "enable", "true", false),
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    lists:concat(["http://", Addr, ":", Port, "/_utils/"]).
+
+teardown(_) ->
+    ok.
+
+
+csp_test_() ->
+    {
+        "Content Security Policy tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_not_return_any_csp_headers_when_disabled/1,
+                    fun should_apply_default_policy/1,
+                    fun should_return_custom_policy/1,
+                    fun should_only_enable_csp_when_true/1
+                ]
+            }
+        }
+    }.
+
+
+should_not_return_any_csp_headers_when_disabled(Url) ->
+    ?_assertEqual(undefined,
+        begin
+            ok = couch_config:set("csp", "enable", "false", false),
+            {ok, _, Headers, _} = test_request:get(Url),
+            proplists:get_value("Content-Security-Policy", Headers)
+        end).
+
+should_apply_default_policy(Url) ->
+    ?_assertEqual(
+        "default-src 'self'; img-src 'self'; font-src 'self'; "
+        "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';",
+        begin
+            {ok, _, Headers, _} = test_request:get(Url),
+            proplists:get_value("Content-Security-Policy", Headers)
+        end).
+
+should_return_custom_policy(Url) ->
+    ?_assertEqual("default-src 'http://example.com';",
+        begin
+            ok = couch_config:set("csp", "header_value",
+                                  "default-src 'http://example.com';", false),
+            {ok, _, Headers, _} = test_request:get(Url),
+            proplists:get_value("Content-Security-Policy", Headers)
+        end).
+
+should_only_enable_csp_when_true(Url) ->
+    ?_assertEqual(undefined,
+        begin
+            ok = couch_config:set("csp", "enable", "tru", false),
+            {ok, _, Headers, _} = test_request:get(Url),
+            proplists:get_value("Content-Security-Policy", Headers)
+        end).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_file_compression_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_file_compression_tests.erl b/test/couchdb_file_compression_tests.erl
new file mode 100644
index 0000000..fd3f513
--- /dev/null
+++ b/test/couchdb_file_compression_tests.erl
@@ -0,0 +1,239 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_file_compression_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(DDOC_ID, <<"_design/test">>).
+-define(DOCS_COUNT, 5000).
+-define(TIMEOUT, 30000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    couch_config:set("couchdb", "file_compression", "none", false),
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    ok = populate_db(Db, ?DOCS_COUNT),
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, ?DDOC_ID},
+        {<<"language">>, <<"javascript">>},
+        {<<"views">>, {[
+                {<<"by_id">>, {[
+                    {<<"map">>, <<"function(doc){emit(doc._id, doc.string);}">>}
+                ]}}
+            ]}
+        }
+    ]}),
+    {ok, _} = couch_db:update_doc(Db, DDoc, []),
+    refresh_index(DbName),
+    ok = couch_db:close(Db),
+    DbName.
+
+teardown(DbName) ->
+    ok = couch_server:delete(DbName, [?ADMIN_USER]),
+    ok.
+
+
+couch_auth_cache_test_() ->
+    {
+        "CouchDB file compression tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_use_none/1,
+                    fun should_use_deflate_1/1,
+                    fun should_use_deflate_9/1,
+                    fun should_use_snappy/1,
+                    fun should_compare_compression_methods/1
+                ]
+            }
+        }
+    }.
+
+
+should_use_none(DbName) ->
+    couch_config:set("couchdb", "file_compression", "none", false),
+    {
+        "Use no compression",
+        [
+            {"compact database", ?_test(compact_db(DbName))},
+            {"compact view", ?_test(compact_view(DbName))}
+        ]
+    }.
+
+should_use_deflate_1(DbName) ->
+    couch_config:set("couchdb", "file_compression", "deflate_1", false),
+    {
+        "Use deflate compression at level 1",
+        [
+            {"compact database", ?_test(compact_db(DbName))},
+            {"compact view", ?_test(compact_view(DbName))}
+        ]
+    }.
+
+should_use_deflate_9(DbName) ->
+    couch_config:set("couchdb", "file_compression", "deflate_9", false),
+    {
+        "Use deflate compression at level 9",
+        [
+            {"compact database", ?_test(compact_db(DbName))},
+            {"compact view", ?_test(compact_view(DbName))}
+        ]
+    }.
+
+should_use_snappy(DbName) ->
+    couch_config:set("couchdb", "file_compression", "snappy", false),
+    {
+        "Use snappy compression",
+        [
+            {"compact database", ?_test(compact_db(DbName))},
+            {"compact view", ?_test(compact_view(DbName))}
+        ]
+    }.
+
+should_compare_compression_methods(DbName) ->
+    {"none > snappy > deflate_1 > deflate_9",
+     {timeout, ?TIMEOUT div 1000, ?_test(compare_compression_methods(DbName))}}.
+
+compare_compression_methods(DbName) ->
+    couch_config:set("couchdb", "file_compression", "none", false),
+    compact_db(DbName),
+    compact_view(DbName),
+    DbSizeNone = db_disk_size(DbName),
+    ViewSizeNone = view_disk_size(DbName),
+
+    couch_config:set("couchdb", "file_compression", "snappy", false),
+    compact_db(DbName),
+    compact_view(DbName),
+    DbSizeSnappy = db_disk_size(DbName),
+    ViewSizeSnappy = view_disk_size(DbName),
+
+    ?assert(DbSizeNone > DbSizeSnappy),
+    ?assert(ViewSizeNone > ViewSizeSnappy),
+
+    couch_config:set("couchdb", "file_compression", "deflate_1", false),
+    compact_db(DbName),
+    compact_view(DbName),
+    DbSizeDeflate1 = db_disk_size(DbName),
+    ViewSizeDeflate1 = view_disk_size(DbName),
+
+    ?assert(DbSizeSnappy > DbSizeDeflate1),
+    ?assert(ViewSizeSnappy > ViewSizeDeflate1),
+
+    couch_config:set("couchdb", "file_compression", "deflate_9", false),
+    compact_db(DbName),
+    compact_view(DbName),
+    DbSizeDeflate9 = db_disk_size(DbName),
+    ViewSizeDeflate9 = view_disk_size(DbName),
+
+    ?assert(DbSizeDeflate1 > DbSizeDeflate9),
+    ?assert(ViewSizeDeflate1 > ViewSizeDeflate9).
+
+
+populate_db(_Db, NumDocs) when NumDocs =< 0 ->
+    ok;
+populate_db(Db, NumDocs) ->
+    Docs = lists:map(
+        fun(_) ->
+            couch_doc:from_json_obj({[
+                {<<"_id">>, couch_uuids:random()},
+                {<<"string">>, ?l2b(lists:duplicate(1000, $X))}
+            ]})
+        end,
+        lists:seq(1, 500)),
+    {ok, _} = couch_db:update_docs(Db, Docs, []),
+    populate_db(Db, NumDocs - 500).
+
+refresh_index(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, DDoc} = couch_db:open_doc(Db, ?DDOC_ID, [ejson_body]),
+    couch_mrview:query_view(Db, DDoc, <<"by_id">>, [{stale, false}]),
+    ok = couch_db:close(Db).
+
+compact_db(DbName) ->
+    DiskSizeBefore = db_disk_size(DbName),
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, CompactPid} = couch_db:start_compact(Db),
+    MonRef = erlang:monitor(process, CompactPid),
+    receive
+        {'DOWN', MonRef, process, CompactPid, normal} ->
+            ok;
+        {'DOWN', MonRef, process, CompactPid, Reason} ->
+            erlang:error({assertion_failed,
+                          [{module, ?MODULE},
+                           {line, ?LINE},
+                           {reason, "Error compacting database: "
+                                    ++ couch_util:to_list(Reason)}]})
+    after ?TIMEOUT ->
+        erlang:error({assertion_failed,
+                      [{module, ?MODULE},
+                       {line, ?LINE},
+                       {reason, "Timeout waiting for database compaction"}]})
+    end,
+    ok = couch_db:close(Db),
+    DiskSizeAfter = db_disk_size(DbName),
+    ?assert(DiskSizeBefore > DiskSizeAfter).
+
+compact_view(DbName) ->
+    DiskSizeBefore = view_disk_size(DbName),
+    {ok, MonRef} = couch_mrview:compact(DbName, ?DDOC_ID, [monitor]),
+    receive
+        {'DOWN', MonRef, process, _CompactPid, normal} ->
+            ok;
+        {'DOWN', MonRef, process, _CompactPid, Reason} ->
+            erlang:error({assertion_failed,
+                          [{module, ?MODULE},
+                           {line, ?LINE},
+                           {reason, "Error compacting view group: "
+                                    ++ couch_util:to_list(Reason)}]})
+    after ?TIMEOUT ->
+        erlang:error({assertion_failed,
+                      [{module, ?MODULE},
+                       {line, ?LINE},
+                       {reason, "Timeout waiting for view group compaction"}]})
+    end,
+    DiskSizeAfter = view_disk_size(DbName),
+    ?assert(DiskSizeBefore > DiskSizeAfter).
+
+db_disk_size(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, Info} = couch_db:get_db_info(Db),
+    ok = couch_db:close(Db),
+    couch_util:get_value(disk_size, Info).
+
+view_disk_size(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, DDoc} = couch_db:open_doc(Db, ?DDOC_ID, [ejson_body]),
+    {ok, Info} = couch_mrview:get_info(Db, DDoc),
+    ok = couch_db:close(Db),
+    couch_util:get_value(disk_size, Info).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_http_proxy_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_http_proxy_tests.erl b/test/couchdb_http_proxy_tests.erl
new file mode 100644
index 0000000..acb1974
--- /dev/null
+++ b/test/couchdb_http_proxy_tests.erl
@@ -0,0 +1,462 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_http_proxy_tests).
+
+-include("couch_eunit.hrl").
+
+-record(req, {method=get, path="", headers=[], body="", opts=[]}).
+
+-define(CONFIG_FIXTURE_TEMP,
+    begin
+        FileName = filename:join([?TEMPDIR, ?tempfile() ++ ".ini"]),
+        {ok, Fd} = file:open(FileName, write),
+        ok = file:truncate(Fd),
+        ok = file:close(Fd),
+        FileName
+    end).
+-define(TIMEOUT, 5000).
+
+
+start() ->
+    % we have to write any config changes to temp ini file to not loose them
+    % when supervisor will kill all children due to reaching restart threshold
+    % (each httpd_global_handlers changes causes couch_httpd restart)
+    couch_server_sup:start_link(?CONFIG_CHAIN ++ [?CONFIG_FIXTURE_TEMP]),
+    % 49151 is IANA Reserved, let's assume no one is listening there
+    couch_config:set("httpd_global_handlers", "_error",
+        "{couch_httpd_proxy, handle_proxy_req, <<\"http://127.0.0.1:49151/\">>}"
+    ),
+    ok.
+
+stop(_) ->
+    couch_server_sup:stop(),
+    ok.
+
+setup() ->
+    {ok, Pid} = test_web:start_link(),
+    Value = lists:flatten(io_lib:format(
+        "{couch_httpd_proxy, handle_proxy_req, ~p}",
+        [list_to_binary(proxy_url())])),
+    couch_config:set("httpd_global_handlers", "_test", Value),
+    % let couch_httpd restart
+    timer:sleep(100),
+    Pid.
+
+teardown(Pid) ->
+    erlang:monitor(process, Pid),
+    test_web:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, test_web_stop})
+    end.
+
+
+http_proxy_test_() ->
+    {
+        "HTTP Proxy handler tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_proxy_basic_request/1,
+                    fun should_return_alternative_status/1,
+                    fun should_respect_trailing_slash/1,
+                    fun should_proxy_headers/1,
+                    fun should_proxy_host_header/1,
+                    fun should_pass_headers_back/1,
+                    fun should_use_same_protocol_version/1,
+                    fun should_proxy_body/1,
+                    fun should_proxy_body_back/1,
+                    fun should_proxy_chunked_body/1,
+                    fun should_proxy_chunked_body_back/1,
+                    fun should_rewrite_location_header/1,
+                    fun should_not_rewrite_external_locations/1,
+                    fun should_rewrite_relative_location/1,
+                    fun should_refuse_connection_to_backend/1
+                ]
+            }
+
+        }
+    }.
+
+
+should_proxy_basic_request(_) ->
+    Remote = fun(Req) ->
+        'GET' = Req:get(method),
+        "/" = Req:get(path),
+        0 = Req:get(body_length),
+        <<>> = Req:recv_body(),
+        {ok, {200, [{"Content-Type", "text/plain"}], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    ?_test(check_request(#req{}, Remote, Local)).
+
+should_return_alternative_status(_) ->
+    Remote = fun(Req) ->
+        "/alternate_status" = Req:get(path),
+        {ok, {201, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "201", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{path = "/alternate_status"},
+    ?_test(check_request(Req, Remote, Local)).
+
+should_respect_trailing_slash(_) ->
+    Remote = fun(Req) ->
+        "/trailing_slash/" = Req:get(path),
+        {ok, {200, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{path="/trailing_slash/"},
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_headers(_) ->
+    Remote = fun(Req) ->
+        "/passes_header" = Req:get(path),
+        "plankton" = Req:get_header_value("X-CouchDB-Ralph"),
+        {ok, {200, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{
+        path="/passes_header",
+        headers=[{"X-CouchDB-Ralph", "plankton"}]
+    },
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_host_header(_) ->
+    Remote = fun(Req) ->
+        "/passes_host_header" = Req:get(path),
+        "www.google.com" = Req:get_header_value("Host"),
+        {ok, {200, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{
+        path="/passes_host_header",
+        headers=[{"Host", "www.google.com"}]
+    },
+    ?_test(check_request(Req, Remote, Local)).
+
+should_pass_headers_back(_) ->
+    Remote = fun(Req) ->
+        "/passes_header_back" = Req:get(path),
+        {ok, {200, [{"X-CouchDB-Plankton", "ralph"}], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", Headers, "ok"}) ->
+            lists:member({"X-CouchDB-Plankton", "ralph"}, Headers);
+        (_) ->
+            false
+    end,
+    Req = #req{path="/passes_header_back"},
+    ?_test(check_request(Req, Remote, Local)).
+
+should_use_same_protocol_version(_) ->
+    Remote = fun(Req) ->
+        "/uses_same_version" = Req:get(path),
+        {1, 0} = Req:get(version),
+        {ok, {200, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{
+        path="/uses_same_version",
+        opts=[{http_vsn, {1, 0}}]
+    },
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_body(_) ->
+    Remote = fun(Req) ->
+        'PUT' = Req:get(method),
+        "/passes_body" = Req:get(path),
+        <<"Hooray!">> = Req:recv_body(),
+        {ok, {201, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "201", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{
+        method=put,
+        path="/passes_body",
+        body="Hooray!"
+    },
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_body_back(_) ->
+    BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
+    Remote = fun(Req) ->
+        'GET' = Req:get(method),
+        "/passes_eof_body" = Req:get(path),
+        {raw, {200, [{"Connection", "close"}], BodyChunks}}
+    end,
+    Local = fun
+        ({ok, "200", _, "foobarbazinga"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{path="/passes_eof_body"},
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_chunked_body(_) ->
+    BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
+    Remote = fun(Req) ->
+        'POST' = Req:get(method),
+        "/passes_chunked_body" = Req:get(path),
+        RecvBody = fun
+            ({Length, Chunk}, [Chunk | Rest]) ->
+                Length = size(Chunk),
+                Rest;
+            ({0, []}, []) ->
+                ok
+        end,
+        ok = Req:stream_body(1024 * 1024, RecvBody, BodyChunks),
+        {ok, {201, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "201", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{
+        method=post,
+        path="/passes_chunked_body",
+        headers=[{"Transfer-Encoding", "chunked"}],
+        body=chunked_body(BodyChunks)
+    },
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_chunked_body_back(_) ->
+    ?_test(begin
+        Remote = fun(Req) ->
+            'GET' = Req:get(method),
+            "/passes_chunked_body_back" = Req:get(path),
+            BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
+            {chunked, {200, [{"Transfer-Encoding", "chunked"}], BodyChunks}}
+        end,
+        Req = #req{
+            path="/passes_chunked_body_back",
+            opts=[{stream_to, self()}]
+        },
+
+        Resp = check_request(Req, Remote, no_local),
+        ?assertMatch({ibrowse_req_id, _}, Resp),
+        {_, ReqId} = Resp,
+
+        % Grab headers from response
+        receive
+            {ibrowse_async_headers, ReqId, "200", Headers} ->
+                ?assertEqual("chunked",
+                             proplists:get_value("Transfer-Encoding", Headers)),
+            ibrowse:stream_next(ReqId)
+        after 1000 ->
+            throw({error, timeout})
+        end,
+
+        ?assertEqual(<<"foobarbazinga">>, recv_body(ReqId, [])),
+        ?assertEqual(was_ok, test_web:check_last())
+    end).
+
+should_refuse_connection_to_backend(_) ->
+    Local = fun
+        ({ok, "500", _, _}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{opts=[{url, server_url("/_error")}]},
+    ?_test(check_request(Req, no_remote, Local)).
+
+should_rewrite_location_header(_) ->
+    {
+        "Testing location header rewrites",
+        do_rewrite_tests([
+            {"Location", proxy_url() ++ "/foo/bar",
+                         server_url() ++ "/foo/bar"},
+            {"Content-Location", proxy_url() ++ "/bing?q=2",
+                                 server_url() ++ "/bing?q=2"},
+            {"Uri", proxy_url() ++ "/zip#frag",
+                    server_url() ++ "/zip#frag"},
+            {"Destination", proxy_url(),
+                            server_url() ++ "/"}
+        ])
+    }.
+
+should_not_rewrite_external_locations(_) ->
+    {
+        "Testing no rewrite of external locations",
+        do_rewrite_tests([
+            {"Location", external_url() ++ "/search",
+                         external_url() ++ "/search"},
+            {"Content-Location", external_url() ++ "/s?q=2",
+                                 external_url() ++ "/s?q=2"},
+            {"Uri", external_url() ++ "/f#f",
+                    external_url() ++ "/f#f"},
+            {"Destination", external_url() ++ "/f?q=2#f",
+                            external_url() ++ "/f?q=2#f"}
+        ])
+    }.
+
+should_rewrite_relative_location(_) ->
+    {
+        "Testing relative rewrites",
+        do_rewrite_tests([
+            {"Location", "/foo",
+                         server_url() ++ "/foo"},
+            {"Content-Location", "bar",
+                                 server_url() ++ "/bar"},
+            {"Uri", "/zing?q=3",
+                    server_url() ++ "/zing?q=3"},
+            {"Destination", "bing?q=stuff#yay",
+                            server_url() ++ "/bing?q=stuff#yay"}
+        ])
+    }.
+
+
+do_rewrite_tests(Tests) ->
+    lists:map(fun({Header, Location, Url}) ->
+        should_rewrite_header(Header, Location, Url)
+    end, Tests).
+
+should_rewrite_header(Header, Location, Url) ->
+    Remote = fun(Req) ->
+        "/rewrite_test" = Req:get(path),
+        {ok, {302, [{Header, Location}], "ok"}}
+    end,
+    Local = fun
+        ({ok, "302", Headers, "ok"}) ->
+            ?assertEqual(Url, couch_util:get_value(Header, Headers)),
+            true;
+        (E) ->
+            ?debugFmt("~p", [E]),
+            false
+    end,
+    Req = #req{path="/rewrite_test"},
+    {Header, ?_test(check_request(Req, Remote, Local))}.
+
+
+server_url() ->
+    server_url("/_test").
+
+server_url(Resource) ->
+    Addr = couch_config:get("httpd", "bind_address"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    lists:concat(["http://", Addr, ":", Port, Resource]).
+
+proxy_url() ->
+    "http://127.0.0.1:" ++ integer_to_list(test_web:get_port()).
+
+external_url() ->
+    "https://google.com".
+
+check_request(Req, Remote, Local) ->
+    case Remote of
+        no_remote ->
+            ok;
+        _ ->
+            test_web:set_assert(Remote)
+    end,
+    Url = case proplists:lookup(url, Req#req.opts) of
+        none ->
+            server_url() ++ Req#req.path;
+        {url, DestUrl} ->
+            DestUrl
+    end,
+    Opts = [{headers_as_is, true} | Req#req.opts],
+    Resp =ibrowse:send_req(
+        Url, Req#req.headers, Req#req.method, Req#req.body, Opts
+    ),
+    %?debugFmt("ibrowse response: ~p", [Resp]),
+    case Local of
+        no_local ->
+            ok;
+        _ ->
+            ?assert(Local(Resp))
+    end,
+    case {Remote, Local} of
+        {no_remote, _} ->
+            ok;
+        {_, no_local} ->
+            ok;
+        _ ->
+            ?assertEqual(was_ok, test_web:check_last())
+    end,
+    Resp.
+
+chunked_body(Chunks) ->
+    chunked_body(Chunks, []).
+
+chunked_body([], Acc) ->
+    iolist_to_binary(lists:reverse(Acc, "0\r\n\r\n"));
+chunked_body([Chunk | Rest], Acc) ->
+    Size = to_hex(size(Chunk)),
+    chunked_body(Rest, ["\r\n", Chunk, "\r\n", Size | Acc]).
+
+to_hex(Val) ->
+    to_hex(Val, []).
+
+to_hex(0, Acc) ->
+    Acc;
+to_hex(Val, Acc) ->
+    to_hex(Val div 16, [hex_char(Val rem 16) | Acc]).
+
+hex_char(V) when V < 10 -> $0 + V;
+hex_char(V) -> $A + V - 10.
+
+recv_body(ReqId, Acc) ->
+    receive
+        {ibrowse_async_response, ReqId, Data} ->
+            recv_body(ReqId, [Data | Acc]);
+        {ibrowse_async_response_end, ReqId} ->
+            iolist_to_binary(lists:reverse(Acc));
+        Else ->
+            throw({error, unexpected_mesg, Else})
+    after ?TIMEOUT ->
+        throw({error, timeout})
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_modules_load_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_modules_load_tests.erl b/test/couchdb_modules_load_tests.erl
new file mode 100644
index 0000000..4eaa42b
--- /dev/null
+++ b/test/couchdb_modules_load_tests.erl
@@ -0,0 +1,68 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_modules_load_tests).
+
+-include("couch_eunit.hrl").
+
+
+modules_load_test_() ->
+    {
+        "Verify that all modules loads",
+        should_load_modules()
+    }.
+
+
+should_load_modules() ->
+    Modules = [
+        couch_auth_cache,
+        couch_btree,
+        couch_changes,
+        couch_compress,
+        couch_config,
+        couch_config_writer,
+        couch_db,
+        couch_db_update_notifier,
+        couch_db_update_notifier_sup,
+        couch_db_updater,
+        couch_doc,
+        % Fails unless couch_config gen_server is started.
+        % couch_ejson_compare,
+        couch_event_sup,
+        couch_external_manager,
+        couch_external_server,
+        couch_file,
+        couch_httpd,
+        couch_httpd_db,
+        couch_httpd_external,
+        couch_httpd_misc_handlers,
+        couch_httpd_rewrite,
+        couch_httpd_stats_handlers,
+        couch_key_tree,
+        couch_log,
+        couch_os_process,
+        couch_query_servers,
+        couch_ref_counter,
+        couch_server,
+        couch_server_sup,
+        couch_stats_aggregator,
+        couch_stats_collector,
+        couch_stream,
+        couch_task_status,
+        couch_util,
+        couch_work_queue,
+        json_stream_parse
+    ],
+    [should_load_module(Mod) || Mod <- Modules].
+
+should_load_module(Mod) ->
+    {atom_to_list(Mod), ?_assertMatch({module, _}, code:load_file(Mod))}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_os_daemons_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_os_daemons_tests.erl b/test/couchdb_os_daemons_tests.erl
new file mode 100644
index 0000000..aa949c9
--- /dev/null
+++ b/test/couchdb_os_daemons_tests.erl
@@ -0,0 +1,228 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_os_daemons_tests).
+
+-include("couch_eunit.hrl").
+
+%% keep in sync with couchdb/couch_os_daemons.erl
+-record(daemon, {
+    port,
+    name,
+    cmd,
+    kill,
+    status=running,
+    cfg_patterns=[],
+    errors=[],
+    buf=[]
+}).
+
+-define(DAEMON_CONFIGER, "os_daemon_configer.escript").
+-define(DAEMON_LOOPER, "os_daemon_looper.escript").
+-define(DAEMON_BAD_PERM, "os_daemon_bad_perm.sh").
+-define(DAEMON_CAN_REBOOT, "os_daemon_can_reboot.sh").
+-define(DAEMON_DIE_ON_BOOT, "os_daemon_die_on_boot.sh").
+-define(DAEMON_DIE_QUICKLY, "os_daemon_die_quickly.sh").
+-define(DELAY, 100).
+-define(TIMEOUT, 1000).
+
+
+setup(DName) ->
+    {ok, CfgPid} = couch_config:start_link(?CONFIG_CHAIN),
+    {ok, OsDPid} = couch_os_daemons:start_link(),
+    couch_config:set("os_daemons", DName,
+                     filename:join([?FIXTURESDIR, DName]), false),
+    timer:sleep(?DELAY),  % sleep a bit to let daemon set kill flag
+    {CfgPid, OsDPid}.
+
+teardown(_, {CfgPid, OsDPid}) ->
+    erlang:monitor(process, CfgPid),
+    couch_config:stop(),
+    receive
+        {'DOWN', _, _, CfgPid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, config_stop})
+    end,
+
+    erlang:monitor(process, OsDPid),
+    exit(OsDPid, normal),
+    receive
+        {'DOWN', _, _, OsDPid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, os_daemon_stop})
+    end.
+
+
+os_daemons_test_() ->
+    {
+        "OS Daemons tests",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{?DAEMON_LOOPER, Fun} || Fun <- [
+                fun should_check_daemon/2,
+                fun should_check_daemon_table_form/2,
+                fun should_clean_tables_on_daemon_remove/2,
+                fun should_spawn_multiple_daemons/2,
+                fun should_keep_alive_one_daemon_on_killing_other/2
+            ]]
+        }
+    }.
+
+configuration_reader_test_() ->
+    {
+        "OS Daemon requests CouchDB configuration",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{?DAEMON_CONFIGER,
+              fun should_read_write_config_settings_by_daemon/2}]
+
+        }
+    }.
+
+error_test_() ->
+    {
+        "OS Daemon process error tests",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{?DAEMON_BAD_PERM, fun should_fail_due_to_lack_of_permissions/2},
+             {?DAEMON_DIE_ON_BOOT, fun should_die_on_boot/2},
+             {?DAEMON_DIE_QUICKLY, fun should_die_quickly/2},
+             {?DAEMON_CAN_REBOOT, fun should_not_being_halted/2}]
+        }
+    }.
+
+
+should_check_daemon(DName, _) ->
+    ?_test(begin
+        {ok, [D]} = couch_os_daemons:info([table]),
+        check_daemon(D, DName)
+    end).
+
+should_check_daemon_table_form(DName, _) ->
+    ?_test(begin
+        {ok, Tab} = couch_os_daemons:info(),
+        [D] = ets:tab2list(Tab),
+        check_daemon(D, DName)
+    end).
+
+should_clean_tables_on_daemon_remove(DName, _) ->
+    ?_test(begin
+        couch_config:delete("os_daemons", DName, false),
+        {ok, Tab2} = couch_os_daemons:info(),
+        ?_assertEqual([], ets:tab2list(Tab2))
+    end).
+
+should_spawn_multiple_daemons(DName, _) ->
+    ?_test(begin
+        couch_config:set("os_daemons", "bar",
+                         filename:join([?FIXTURESDIR, DName]), false),
+        couch_config:set("os_daemons", "baz",
+                         filename:join([?FIXTURESDIR, DName]), false),
+        timer:sleep(?DELAY),
+        {ok, Daemons} = couch_os_daemons:info([table]),
+        lists:foreach(fun(D) ->
+            check_daemon(D)
+        end, Daemons),
+        {ok, Tab} = couch_os_daemons:info(),
+        lists:foreach(fun(D) ->
+            check_daemon(D)
+        end, ets:tab2list(Tab))
+    end).
+
+should_keep_alive_one_daemon_on_killing_other(DName, _) ->
+    ?_test(begin
+        couch_config:set("os_daemons", "bar",
+                         filename:join([?FIXTURESDIR, DName]), false),
+        timer:sleep(?DELAY),
+        {ok, Daemons} = couch_os_daemons:info([table]),
+        lists:foreach(fun(D) ->
+            check_daemon(D)
+        end, Daemons),
+
+        couch_config:delete("os_daemons", "bar", false),
+        timer:sleep(?DELAY),
+        {ok, [D2]} = couch_os_daemons:info([table]),
+        check_daemon(D2, DName),
+
+        {ok, Tab} = couch_os_daemons:info(),
+        [T] = ets:tab2list(Tab),
+        check_daemon(T, DName)
+    end).
+
+should_read_write_config_settings_by_daemon(DName, _) ->
+    ?_test(begin
+        % have to wait till daemon run all his tests
+        % see daemon's script for more info
+        timer:sleep(?TIMEOUT),
+        {ok, [D]} = couch_os_daemons:info([table]),
+        check_daemon(D, DName)
+    end).
+
+should_fail_due_to_lack_of_permissions(DName, _) ->
+    ?_test(should_halts(DName, 1000)).
+
+should_die_on_boot(DName, _) ->
+    ?_test(should_halts(DName, 1000)).
+
+should_die_quickly(DName, _) ->
+    ?_test(should_halts(DName, 4000)).
+
+should_not_being_halted(DName, _) ->
+    ?_test(begin
+        timer:sleep(1000),
+        {ok, [D1]} = couch_os_daemons:info([table]),
+        check_daemon(D1, DName, 0),
+
+        % Should reboot every two seconds. We're at 1s, so wait
+        % until 3s to be in the middle of the next invocation's
+        % life span.
+
+        timer:sleep(2000),
+        {ok, [D2]} = couch_os_daemons:info([table]),
+        check_daemon(D2, DName, 1),
+
+        % If the kill command changed, that means we rebooted the process.
+        ?assertNotEqual(D1#daemon.kill, D2#daemon.kill)
+    end).
+
+should_halts(DName, Time) ->
+    timer:sleep(Time),
+    {ok, [D]} = couch_os_daemons:info([table]),
+    check_dead(D, DName),
+    couch_config:delete("os_daemons", DName, false).
+
+check_daemon(D) ->
+    check_daemon(D, D#daemon.name).
+
+check_daemon(D, Name) ->
+    check_daemon(D, Name, 0).
+
+check_daemon(D, Name, Errs) ->
+    ?assert(is_port(D#daemon.port)),
+    ?assertEqual(Name, D#daemon.name),
+    ?assertNotEqual(undefined, D#daemon.kill),
+    ?assertEqual(running, D#daemon.status),
+    ?assertEqual(Errs, length(D#daemon.errors)),
+    ?assertEqual([], D#daemon.buf).
+
+check_dead(D, Name) ->
+    ?assert(is_port(D#daemon.port)),
+    ?assertEqual(Name, D#daemon.name),
+    ?assertNotEqual(undefined, D#daemon.kill),
+    ?assertEqual(halted, D#daemon.status),
+    ?assertEqual(nil, D#daemon.errors),
+    ?assertEqual(nil, D#daemon.buf).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_os_proc_pool.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_os_proc_pool.erl b/test/couchdb_os_proc_pool.erl
new file mode 100644
index 0000000..1bb266e
--- /dev/null
+++ b/test/couchdb_os_proc_pool.erl
@@ -0,0 +1,179 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_os_proc_pool).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(TIMEOUT, 3000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    couch_config:set("query_server_config", "os_process_limit", "3", false),
+    Pid.
+
+stop(Pid) ->
+    couch_server_sup:stop(),
+    erlang:monitor(process, Pid),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+
+os_proc_pool_test_() ->
+    {
+        "OS processes pool tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [
+                should_block_new_proc_on_full_pool(),
+                should_free_slot_on_proc_unexpected_exit()
+            ]
+        }
+    }.
+
+
+should_block_new_proc_on_full_pool() ->
+    ?_test(begin
+        Client1 = spawn_client(),
+        Client2 = spawn_client(),
+        Client3 = spawn_client(),
+
+        ?assertEqual(ok, ping_client(Client1)),
+        ?assertEqual(ok, ping_client(Client2)),
+        ?assertEqual(ok, ping_client(Client3)),
+
+        Proc1 = get_client_proc(Client1, "1"),
+        Proc2 = get_client_proc(Client2, "2"),
+        Proc3 = get_client_proc(Client3, "3"),
+
+        ?assertNotEqual(Proc1, Proc2),
+        ?assertNotEqual(Proc2, Proc3),
+        ?assertNotEqual(Proc3, Proc1),
+
+        Client4 = spawn_client(),
+        ?assertEqual(timeout, ping_client(Client4)),
+
+        ?assertEqual(ok, stop_client(Client1)),
+        ?assertEqual(ok, ping_client(Client4)),
+
+        Proc4 = get_client_proc(Client4, "4"),
+        ?assertEqual(Proc1, Proc4),
+
+        lists:map(fun(C) ->
+            ?assertEqual(ok, stop_client(C))
+        end, [Client2, Client3, Client4])
+    end).
+
+should_free_slot_on_proc_unexpected_exit() ->
+    ?_test(begin
+        Client1 = spawn_client(),
+        Client2 = spawn_client(),
+        Client3 = spawn_client(),
+
+        ?assertEqual(ok, ping_client(Client1)),
+        ?assertEqual(ok, ping_client(Client2)),
+        ?assertEqual(ok, ping_client(Client3)),
+
+        Proc1 = get_client_proc(Client1, "1"),
+        Proc2 = get_client_proc(Client2, "2"),
+        Proc3 = get_client_proc(Client3, "3"),
+
+        ?assertNotEqual(Proc1, Proc2),
+        ?assertNotEqual(Proc2, Proc3),
+        ?assertNotEqual(Proc3, Proc1),
+
+        ?assertEqual(ok, kill_client(Client1)),
+
+        Client4 = spawn_client(),
+        ?assertEqual(ok, ping_client(Client4)),
+
+        Proc4 = get_client_proc(Client4, "4"),
+        ?assertNotEqual(Proc4, Proc1),
+        ?assertNotEqual(Proc2, Proc4),
+        ?assertNotEqual(Proc3, Proc4),
+
+        lists:map(fun(C) ->
+            ?assertEqual(ok, stop_client(C))
+        end, [Client2, Client3, Client4])
+    end).
+
+
+spawn_client() ->
+    Parent = self(),
+    Ref = make_ref(),
+    Pid = spawn(fun() ->
+        Proc = couch_query_servers:get_os_process(<<"javascript">>),
+        loop(Parent, Ref, Proc)
+    end),
+    {Pid, Ref}.
+
+ping_client({Pid, Ref}) ->
+    Pid ! ping,
+    receive
+        {pong, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+get_client_proc({Pid, Ref}, ClientName) ->
+    Pid ! get_proc,
+    receive
+        {proc, Ref, Proc} -> Proc
+    after ?TIMEOUT ->
+        erlang:error({assertion_failed,
+                     [{module, ?MODULE},
+                      {line, ?LINE},
+                      {reason, "Timeout getting client "
+                               ++ ClientName ++ " proc"}]})
+    end.
+
+stop_client({Pid, Ref}) ->
+    Pid ! stop,
+    receive
+        {stop, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+kill_client({Pid, Ref}) ->
+    Pid ! die,
+    receive
+        {die, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+loop(Parent, Ref, Proc) ->
+    receive
+        ping ->
+            Parent ! {pong, Ref},
+            loop(Parent, Ref, Proc);
+        get_proc  ->
+            Parent ! {proc, Ref, Proc},
+            loop(Parent, Ref, Proc);
+        stop ->
+            couch_query_servers:ret_os_process(Proc),
+            Parent ! {stop, Ref};
+        die ->
+            Parent ! {die, Ref},
+            exit(some_error)
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_update_conflicts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_update_conflicts_tests.erl b/test/couchdb_update_conflicts_tests.erl
new file mode 100644
index 0000000..7226860
--- /dev/null
+++ b/test/couchdb_update_conflicts_tests.erl
@@ -0,0 +1,243 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_update_conflicts_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(i2l(I), integer_to_list(I)).
+-define(ADMIN_USER, {userctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(DOC_ID, <<"foobar">>).
+-define(NUM_CLIENTS, [100, 500, 1000, 2000, 5000, 10000]).
+-define(TIMEOUT, 10000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    couch_config:set("couchdb", "delayed_commits", "true", false),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER, overwrite]),
+    Doc = couch_doc:from_json_obj({[{<<"_id">>, ?DOC_ID},
+                                    {<<"value">>, 0}]}),
+    {ok, Rev} = couch_db:update_doc(Db, Doc, []),
+    ok = couch_db:close(Db),
+    RevStr = couch_doc:rev_to_str(Rev),
+    {DbName, RevStr}.
+setup(_) ->
+    setup().
+
+teardown({DbName, _}) ->
+    ok = couch_server:delete(DbName, []),
+    ok.
+teardown(_, {DbName, _RevStr}) ->
+    teardown({DbName, _RevStr}).
+
+
+view_indexes_cleanup_test_() ->
+    {
+        "Update conflicts",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [
+                concurrent_updates(),
+                couchdb_188()
+            ]
+        }
+    }.
+
+concurrent_updates()->
+    {
+        "Concurrent updates",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{NumClients, fun should_concurrently_update_doc/2}
+             || NumClients <- ?NUM_CLIENTS]
+        }
+    }.
+
+couchdb_188()->
+    {
+        "COUCHDB-188",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [fun should_bulk_create_delete_doc/1]
+        }
+    }.
+
+
+should_concurrently_update_doc(NumClients, {DbName, InitRev})->
+     {?i2l(NumClients) ++ " clients",
+      {inorder,
+       [{"update doc",
+         {timeout, ?TIMEOUT div 1000,
+          ?_test(concurrent_doc_update(NumClients, DbName, InitRev))}},
+        {"ensure in single leaf",
+         ?_test(ensure_in_single_revision_leaf(DbName))}]}}.
+
+should_bulk_create_delete_doc({DbName, InitRev})->
+    ?_test(bulk_delete_create(DbName, InitRev)).
+
+
+concurrent_doc_update(NumClients, DbName, InitRev) ->
+    Clients = lists:map(
+        fun(Value) ->
+            ClientDoc = couch_doc:from_json_obj({[
+                {<<"_id">>, ?DOC_ID},
+                {<<"_rev">>, InitRev},
+                {<<"value">>, Value}
+            ]}),
+            Pid = spawn_client(DbName, ClientDoc),
+            {Value, Pid, erlang:monitor(process, Pid)}
+        end,
+        lists:seq(1, NumClients)),
+
+    lists:foreach(fun({_, Pid, _}) -> Pid ! go end, Clients),
+
+    {NumConflicts, SavedValue} = lists:foldl(
+        fun({Value, Pid, MonRef}, {AccConflicts, AccValue}) ->
+            receive
+                {'DOWN', MonRef, process, Pid, {ok, _NewRev}} ->
+                    {AccConflicts, Value};
+                {'DOWN', MonRef, process, Pid, conflict} ->
+                    {AccConflicts + 1, AccValue};
+                {'DOWN', MonRef, process, Pid, Error} ->
+                    erlang:error({assertion_failed,
+                         [{module, ?MODULE},
+                          {line, ?LINE},
+                          {reason, "Client " ++ ?i2l(Value)
+                                             ++ " got update error: "
+                                             ++ couch_util:to_list(Error)}]})
+            after ?TIMEOUT div 2 ->
+                 erlang:error({assertion_failed,
+                         [{module, ?MODULE},
+                          {line, ?LINE},
+                          {reason, "Timeout waiting for client "
+                                   ++ ?i2l(Value) ++ " to die"}]})
+            end
+        end, {0, nil}, Clients),
+    ?assertEqual(NumClients - 1, NumConflicts),
+
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, Leaves} = couch_db:open_doc_revs(Db, ?DOC_ID, all, []),
+    ok = couch_db:close(Db),
+    ?assertEqual(1, length(Leaves)),
+
+    [{ok, Doc2}] = Leaves,
+    {JsonDoc} = couch_doc:to_json_obj(Doc2, []),
+    ?assertEqual(SavedValue, couch_util:get_value(<<"value">>, JsonDoc)).
+
+ensure_in_single_revision_leaf(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, Leaves} = couch_db:open_doc_revs(Db, ?DOC_ID, all, []),
+    ok = couch_db:close(Db),
+    [{ok, Doc}] = Leaves,
+
+    %% FIXME: server restart won't work from test side
+    %% stop(ok),
+    %% start(),
+
+    {ok, Db2} = couch_db:open_int(DbName, []),
+    {ok, Leaves2} = couch_db:open_doc_revs(Db2, ?DOC_ID, all, []),
+    ok = couch_db:close(Db2),
+    ?assertEqual(1, length(Leaves2)),
+
+    [{ok, Doc2}] = Leaves,
+    ?assertEqual(Doc, Doc2).
+    
+bulk_delete_create(DbName, InitRev) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    
+    DeletedDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, ?DOC_ID},
+        {<<"_rev">>, InitRev},
+        {<<"_deleted">>, true}
+    ]}),
+    NewDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, ?DOC_ID},
+        {<<"value">>, 666}
+    ]}),
+
+    {ok, Results} = couch_db:update_docs(Db, [DeletedDoc, NewDoc], []),
+    ok = couch_db:close(Db),
+
+    ?assertEqual(2, length([ok || {ok, _} <- Results])),
+    [{ok, Rev1}, {ok, Rev2}] = Results,
+    
+    {ok, Db2} = couch_db:open_int(DbName, []),
+    {ok, [{ok, Doc1}]} = couch_db:open_doc_revs(
+        Db2, ?DOC_ID, [Rev1], [conflicts, deleted_conflicts]),
+    {ok, [{ok, Doc2}]} = couch_db:open_doc_revs(
+        Db2, ?DOC_ID, [Rev2], [conflicts, deleted_conflicts]),
+    ok = couch_db:close(Db2),
+
+    {Doc1Props} = couch_doc:to_json_obj(Doc1, []),
+    {Doc2Props} = couch_doc:to_json_obj(Doc2, []),
+
+    %% Document was deleted
+    ?assert(couch_util:get_value(<<"_deleted">>, Doc1Props)),
+    %% New document not flagged as deleted
+    ?assertEqual(undefined, couch_util:get_value(<<"_deleted">>,
+                                                 Doc2Props)),
+    %% New leaf revision has the right value
+    ?assertEqual(666, couch_util:get_value(<<"value">>,
+                                           Doc2Props)),
+    %% Deleted document has no conflicts
+    ?assertEqual(undefined, couch_util:get_value(<<"_conflicts">>,
+                                                 Doc1Props)),
+    %% Deleted document has no deleted conflicts
+    ?assertEqual(undefined, couch_util:get_value(<<"_deleted_conflicts">>,
+                                                 Doc1Props)),
+    %% New leaf revision doesn't have conflicts
+    ?assertEqual(undefined, couch_util:get_value(<<"_conflicts">>,
+                                                 Doc1Props)),
+    %% New leaf revision doesn't have deleted conflicts
+    ?assertEqual(undefined, couch_util:get_value(<<"_deleted_conflicts">>,
+                                                 Doc1Props)),
+
+    %% Deleted revision has position 2
+    ?assertEqual(2, element(1, Rev1)),
+    %% New leaf revision has position 1
+    ?assertEqual(1, element(1, Rev2)).
+
+
+spawn_client(DbName, Doc) ->
+    spawn(fun() ->
+        {ok, Db} = couch_db:open_int(DbName, []),
+        receive
+            go -> ok
+        end,
+        erlang:yield(),
+        Result = try
+            couch_db:update_doc(Db, Doc, [])
+        catch _:Error ->
+            Error
+        end,
+        ok = couch_db:close(Db),
+        exit(Result)
+    end).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_vhosts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_vhosts_tests.erl b/test/couchdb_vhosts_tests.erl
new file mode 100644
index 0000000..94b1957
--- /dev/null
+++ b/test/couchdb_vhosts_tests.erl
@@ -0,0 +1,441 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_vhosts_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(TIMEOUT, 1000).
+-define(iofmt(S, A), lists:flatten(io_lib:format(S, A))).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    Doc = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"doc1">>},
+        {<<"value">>, 666}
+    ]}),
+
+    Doc1 = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"_design/doc1">>},
+        {<<"shows">>, {[
+            {<<"test">>, <<"function(doc, req) {
+            return { json: {
+                    requested_path: '/' + req.requested_path.join('/'),
+                    path: '/' + req.path.join('/')}};}">>}
+        ]}},
+        {<<"rewrites">>, [
+            {[
+                {<<"from">>, <<"/">>},
+                {<<"to">>, <<"_show/test">>}
+            ]}
+        ]}
+    ]}),
+    {ok, _} = couch_db:update_docs(Db, [Doc, Doc1]),
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db),
+
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    Url = "http://" ++ Addr ++ ":" ++ Port,
+    {Url, ?b2l(DbName)}.
+
+setup_oauth() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+
+    couch_config:set("couch_httpd_auth", "authentication_db",
+                     ?b2l(?tempdb()), false),
+    couch_config:set("oauth_token_users", "otoksec1", "joe", false),
+    couch_config:set("oauth_consumer_secrets", "consec1", "foo", false),
+    couch_config:set("oauth_token_secrets", "otoksec1", "foobar", false),
+    couch_config:set("couch_httpd_auth", "require_valid_user", "true", false),
+
+    ok = couch_config:set(
+        "vhosts", "oauth-example.com",
+        "/" ++ ?b2l(DbName) ++ "/_design/test/_rewrite/foobar", false),
+
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"_design/test">>},
+        {<<"language">>, <<"javascript">>},
+        {<<"rewrites">>, [
+            {[
+                {<<"from">>, <<"foobar">>},
+                {<<"to">>, <<"_info">>}
+            ]}
+        ]}
+    ]}),
+    {ok, _} = couch_db:update_doc(Db, DDoc, []),
+
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db),
+
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    Url = "http://" ++ Addr ++ ":" ++ Port,
+    {Url, ?b2l(DbName)}.
+
+teardown({_, DbName}) ->
+    ok = couch_server:delete(?l2b(DbName), []),
+    ok.
+
+
+vhosts_test_() ->
+    {
+        "Virtual Hosts rewrite tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_return_database_info/1,
+                    fun should_return_revs_info/1,
+                    fun should_serve_utils_for_vhost/1,
+                    fun should_return_virtual_request_path_field_in_request/1,
+                    fun should_return_real_request_path_field_in_request/1,
+                    fun should_match_wildcard_vhost/1,
+                    fun should_return_db_info_for_wildcard_vhost_for_custom_db/1,
+                    fun should_replace_rewrite_variables_for_db_and_doc/1,
+                    fun should_return_db_info_for_vhost_with_resource/1,
+                    fun should_return_revs_info_for_vhost_with_resource/1,
+                    fun should_return_db_info_for_vhost_with_wildcard_resource/1,
+                    fun should_return_path_for_vhost_with_wildcard_host/1
+                ]
+            }
+        }
+    }.
+
+oauth_test_() ->
+    {
+        "Virtual Hosts OAuth tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup_oauth/0, fun teardown/1,
+                [
+                    fun should_require_auth/1,
+                    fun should_succeed_oauth/1,
+                    fun should_fail_oauth_with_wrong_credentials/1
+                ]
+            }
+        }
+    }.
+
+
+should_return_database_info({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
+        case test_request:get(Url, [], [{host_header, "example.com"}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_revs_info({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
+        case test_request:get(Url ++ "/doc1?revs_info=true", [],
+                              [{host_header, "example.com"}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"_revs_info">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_serve_utils_for_vhost({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
+        case test_request:get(Url ++ "/_utils/index.html", [],
+                              [{host_header, "example.com"}]) of
+            {ok, _, _, Body} ->
+                ?assertMatch(<<"<!DOCTYPE html>", _/binary>>, Body);
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_virtual_request_path_field_in_request({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "example1.com",
+                              "/" ++ DbName ++ "/_design/doc1/_rewrite/",
+                              false),
+        case test_request:get(Url, [], [{host_header, "example1.com"}]) of
+            {ok, _, _, Body} ->
+                {Json} = ejson:decode(Body),
+                ?assertEqual(<<"/">>,
+                             proplists:get_value(<<"requested_path">>, Json));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_real_request_path_field_in_request({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "example1.com",
+                              "/" ++ DbName ++ "/_design/doc1/_rewrite/",
+                              false),
+        case test_request:get(Url, [], [{host_header, "example1.com"}]) of
+            {ok, _, _, Body} ->
+                {Json} = ejson:decode(Body),
+                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
+                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_match_wildcard_vhost({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "*.example.com",
+                              "/" ++ DbName ++ "/_design/doc1/_rewrite", false),
+        case test_request:get(Url, [], [{host_header, "test.example.com"}]) of
+            {ok, _, _, Body} ->
+                {Json} = ejson:decode(Body),
+                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
+                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_db_info_for_wildcard_vhost_for_custom_db({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", ":dbname.example1.com",
+                              "/:dbname", false),
+        Host = DbName ++ ".example1.com",
+        case test_request:get(Url, [], [{host_header, Host}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_replace_rewrite_variables_for_db_and_doc({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts",":appname.:dbname.example1.com",
+                              "/:dbname/_design/:appname/_rewrite/", false),
+        Host = "doc1." ++ DbName ++ ".example1.com",
+        case test_request:get(Url, [], [{host_header, Host}]) of
+            {ok, _, _, Body} ->
+                {Json} = ejson:decode(Body),
+                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
+                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_db_info_for_vhost_with_resource({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts",
+                              "example.com/test", "/" ++ DbName, false),
+        ReqUrl = Url ++ "/test",
+        case test_request:get(ReqUrl, [], [{host_header, "example.com"}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+
+should_return_revs_info_for_vhost_with_resource({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts",
+                              "example.com/test", "/" ++ DbName, false),
+        ReqUrl = Url ++ "/test/doc1?revs_info=true",
+        case test_request:get(ReqUrl, [], [{host_header, "example.com"}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"_revs_info">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_db_info_for_vhost_with_wildcard_resource({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "*.example2.com/test", "/*", false),
+        ReqUrl = Url ++ "/test",
+        Host = DbName ++ ".example2.com",
+        case test_request:get(ReqUrl, [], [{host_header, Host}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_path_for_vhost_with_wildcard_host({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "*/test1",
+                              "/" ++ DbName ++ "/_design/doc1/_show/test",
+                              false),
+        case test_request:get(Url ++ "/test1") of
+            {ok, _, _, Body} ->
+                {Json} = ejson:decode(Body),
+                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
+                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_require_auth({Url, _}) ->
+    ?_test(begin
+        case test_request:get(Url, [], [{host_header, "oauth-example.com"}]) of
+            {ok, Code, _, Body} ->
+                ?assertEqual(401, Code),
+                {JsonBody} = ejson:decode(Body),
+                ?assertEqual(<<"unauthorized">>,
+                             couch_util:get_value(<<"error">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_succeed_oauth({Url, _}) ->
+    ?_test(begin
+        AuthDbName = couch_config:get("couch_httpd_auth", "authentication_db"),
+        JoeDoc = couch_doc:from_json_obj({[
+            {<<"_id">>, <<"org.couchdb.user:joe">>},
+            {<<"type">>, <<"user">>},
+            {<<"name">>, <<"joe">>},
+            {<<"roles">>, []},
+            {<<"password_sha">>, <<"fe95df1ca59a9b567bdca5cbaf8412abd6e06121">>},
+            {<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4001a73">>}
+        ]}),
+        {ok, AuthDb} = couch_db:open_int(?l2b(AuthDbName), [?ADMIN_USER]),
+        {ok, _} = couch_db:update_doc(AuthDb, JoeDoc, [?ADMIN_USER]),
+
+        Host = "oauth-example.com",
+        Consumer = {"consec1", "foo", hmac_sha1},
+        SignedParams = oauth:sign(
+            "GET", "http://" ++ Host ++ "/", [], Consumer, "otoksec1", "foobar"),
+        OAuthUrl = oauth:uri(Url, SignedParams),
+
+        case test_request:get(OAuthUrl, [], [{host_header, Host}]) of
+            {ok, Code, _, Body} ->
+                ?assertEqual(200, Code),
+                {JsonBody} = ejson:decode(Body),
+                ?assertEqual(<<"test">>,
+                             couch_util:get_value(<<"name">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_fail_oauth_with_wrong_credentials({Url, _}) ->
+    ?_test(begin
+        AuthDbName = couch_config:get("couch_httpd_auth", "authentication_db"),
+        JoeDoc = couch_doc:from_json_obj({[
+            {<<"_id">>, <<"org.couchdb.user:joe">>},
+            {<<"type">>, <<"user">>},
+            {<<"name">>, <<"joe">>},
+            {<<"roles">>, []},
+            {<<"password_sha">>, <<"fe95df1ca59a9b567bdca5cbaf8412abd6e06121">>},
+            {<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4001a73">>}
+        ]}),
+        {ok, AuthDb} = couch_db:open_int(?l2b(AuthDbName), [?ADMIN_USER]),
+        {ok, _} = couch_db:update_doc(AuthDb, JoeDoc, [?ADMIN_USER]),
+
+        Host = "oauth-example.com",
+        Consumer = {"consec1", "bad_secret", hmac_sha1},
+        SignedParams = oauth:sign(
+            "GET", "http://" ++ Host ++ "/", [], Consumer, "otoksec1", "foobar"),
+        OAuthUrl = oauth:uri(Url, SignedParams),
+
+        case test_request:get(OAuthUrl, [], [{host_header, Host}]) of
+            {ok, Code, _, Body} ->
+                ?assertEqual(401, Code),
+                {JsonBody} = ejson:decode(Body),
+                ?assertEqual(<<"unauthorized">>,
+                             couch_util:get_value(<<"error">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).


[13/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 121-stats-aggregates.t etap test suite to eunit

Merged into couch_stats_tests suite.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/069dfb7e
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/069dfb7e
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/069dfb7e

Branch: refs/heads/1963-eunit-bigcouch
Commit: 069dfb7eecb3c6739ca17d6f025af4a4fa6f7590
Parents: b3abe4d
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue May 27 18:27:53 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couch_stats_tests.erl              | 225 ++++++++++++++++++-
 .../couchdb/fixtures/couch_stats_aggregates.cfg |  19 ++
 .../couchdb/fixtures/couch_stats_aggregates.ini |  20 ++
 3 files changed, 261 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/069dfb7e/test/couchdb/couch_stats_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_stats_tests.erl b/test/couchdb/couch_stats_tests.erl
index aaaa687..d156449 100644
--- a/test/couchdb/couch_stats_tests.erl
+++ b/test/couchdb/couch_stats_tests.erl
@@ -15,18 +15,41 @@
 -include("couch_eunit.hrl").
 -include_lib("couchdb/couch_db.hrl").
 
+-define(STATS_CFG_FIXTURE,
+    filename:join([?FIXTURESDIR, "couch_stats_aggregates.cfg"])).
+-define(STATS_INI_FIXTURE,
+    filename:join([?FIXTURESDIR, "couch_stats_aggregates.ini"])).
 -define(TIMEOUT, 1000).
--define(SLEEPTIME, 100).
+-define(TIMEWAIT, 500).
 
 
 setup_collector() ->
     couch_stats_collector:start(),
     ok.
 
+setup_aggregator(_) ->
+    {ok, Pid} = couch_config:start_link([?STATS_INI_FIXTURE]),
+    {ok, _} = couch_stats_collector:start(),
+    {ok, _} = couch_stats_aggregator:start(?STATS_CFG_FIXTURE),
+    Pid.
+
 teardown_collector(_) ->
     couch_stats_collector:stop(),
     ok.
 
+teardown_aggregator(_, Pid) ->
+    couch_stats_aggregator:stop(),
+    couch_stats_collector:stop(),
+    erlang:monitor(process, Pid),
+    couch_config:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, config_stop})
+    end,
+    ok.
+
 
 couch_stats_collector_test_() ->
     {
@@ -51,6 +74,39 @@ couch_stats_collector_test_() ->
         }
     }.
 
+couch_stats_aggregator_test_() ->
+    Funs = [
+        fun should_init_empty_aggregate/2,
+        fun should_get_empty_aggregate/2,
+        fun should_change_stats_on_values_add/2,
+        fun should_change_stats_for_all_times_on_values_add/2,
+        fun should_change_stats_on_values_change/2,
+        fun should_change_stats_for_all_times_on_values_change/2,
+        fun should_not_remove_data_after_some_time_for_0_sample/2,
+        fun should_remove_data_after_some_time_for_other_samples/2
+    ],
+    {
+        "CouchDB stats aggregator tests",
+        [
+            {
+                "Absolute values",
+                {
+                    foreachx,
+                    fun setup_aggregator/1, fun teardown_aggregator/2,
+                    [{absolute, Fun} || Fun <- Funs]
+                }
+            },
+            {
+                "Counters",
+                {
+                    foreachx,
+                    fun setup_aggregator/1, fun teardown_aggregator/2,
+                    [{counter, Fun} || Fun <- Funs]
+                }
+            }
+        ]
+    }.
+
 
 should_increment_counter() ->
     ?_assertEqual(100,
@@ -122,7 +178,7 @@ should_decrement_counter_on_process_exit() ->
             end,
             % sleep for awhile to let collector handle the updates
             % suddenly, it couldn't notice process death instantly
-            timer:sleep(?SLEEPTIME),
+            timer:sleep(?TIMEWAIT),
             couch_stats_collector:get(hoopla)
         end).
 
@@ -138,7 +194,7 @@ should_decrement_for_each_track_process_count_call_on_exit() ->
             after ?TIMEOUT ->
                 throw(timeout)
             end,
-            timer:sleep(?SLEEPTIME),
+            timer:sleep(?TIMEWAIT),
             couch_stats_collector:get(hoopla)
         end).
 
@@ -170,6 +226,158 @@ should_return_absolute_values() ->
             lists:sort(couch_stats_collector:all(absolute))
         end).
 
+should_init_empty_aggregate(absolute, _) ->
+    {Aggs} = couch_stats_aggregator:all(),
+    ?_assertEqual({[{'11', make_agg(<<"randomosity">>,
+                                    null, null, null, null, null)}]},
+                  couch_util:get_value(number, Aggs));
+should_init_empty_aggregate(counter, _) ->
+    {Aggs} = couch_stats_aggregator:all(),
+    ?_assertEqual({[{stuff, make_agg(<<"yay description">>,
+                                     null, null, null, null, null)}]},
+                  couch_util:get_value(testing, Aggs)).
+
+should_get_empty_aggregate(absolute, _) ->
+    ?_assertEqual(make_agg(<<"randomosity">>, null, null, null, null, null),
+             couch_stats_aggregator:get_json({number, '11'}));
+should_get_empty_aggregate(counter, _) ->
+    ?_assertEqual(make_agg(<<"yay description">>, null, null, null, null, null),
+             couch_stats_aggregator:get_json({testing, stuff})).
+
+should_change_stats_on_values_add(absolute, _) ->
+    lists:foreach(fun(X) ->
+        couch_stats_collector:record({number, 11}, X)
+    end, lists:seq(0, 10)),
+    couch_stats_aggregator:collect_sample(),
+    ?_assertEqual(make_agg(<<"randomosity">>, 5.0, 5.0, null, 5.0, 5.0),
+                  couch_stats_aggregator:get_json({number, 11}));
+should_change_stats_on_values_add(counter, _) ->
+    lists:foreach(fun(_) ->
+        couch_stats_collector:increment({testing, stuff})
+    end, lists:seq(1, 100)),
+    couch_stats_aggregator:collect_sample(),
+    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 100.0, null, 100, 100),
+                  couch_stats_aggregator:get_json({testing, stuff})).
+
+should_change_stats_for_all_times_on_values_add(absolute, _) ->
+    lists:foreach(fun(X) ->
+        couch_stats_collector:record({number, 11}, X)
+    end, lists:seq(0, 10)),
+    couch_stats_aggregator:collect_sample(),
+    ?_assertEqual(make_agg(<<"randomosity">>, 5.0, 5.0, null, 5.0, 5.0),
+                  couch_stats_aggregator:get_json({number, 11}, 1));
+should_change_stats_for_all_times_on_values_add(counter, _) ->
+    lists:foreach(fun(_) ->
+        couch_stats_collector:increment({testing, stuff})
+    end, lists:seq(1, 100)),
+    couch_stats_aggregator:collect_sample(),
+    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 100.0, null, 100, 100),
+                  couch_stats_aggregator:get_json({testing, stuff}, 1)).
+
+should_change_stats_on_values_change(absolute, _) ->
+    ?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
+        begin
+            lists:foreach(fun(X) ->
+                couch_stats_collector:record({number, 11}, X)
+            end, lists:seq(0, 10)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_collector:record({number, 11}, 15),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({number, 11})
+        end);
+should_change_stats_on_values_change(counter, _) ->
+    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 50.0, 70.711, 0, 100),
+        begin
+            lists:foreach(fun(_) ->
+                couch_stats_collector:increment({testing, stuff})
+            end, lists:seq(1, 100)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({testing, stuff})
+        end).
+
+should_change_stats_for_all_times_on_values_change(absolute, _) ->
+    ?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
+        begin
+            lists:foreach(fun(X) ->
+                couch_stats_collector:record({number, 11}, X)
+            end, lists:seq(0, 10)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_collector:record({number, 11}, 15),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({number, 11}, 1)
+        end);
+should_change_stats_for_all_times_on_values_change(counter, _) ->
+    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 50.0, 70.711, 0, 100),
+        begin
+            lists:foreach(fun(_) ->
+                couch_stats_collector:increment({testing, stuff})
+            end, lists:seq(1, 100)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({testing, stuff}, 1)
+        end).
+
+should_not_remove_data_after_some_time_for_0_sample(absolute, _) ->
+    ?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
+        begin
+            lists:foreach(fun(X) ->
+                couch_stats_collector:record({number, 11}, X)
+            end, lists:seq(0, 10)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_collector:record({number, 11}, 15),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({number, 11})
+        end);
+should_not_remove_data_after_some_time_for_0_sample(counter, _) ->
+    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 33.333, 57.735, 0, 100),
+        begin
+            lists:foreach(fun(_) ->
+                couch_stats_collector:increment({testing, stuff})
+            end, lists:seq(1, 100)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({testing, stuff})
+        end).
+
+should_remove_data_after_some_time_for_other_samples(absolute, _) ->
+    ?_assertEqual(make_agg(<<"randomosity">>, 15.0, 15.0, null, 15.0, 15.0),
+        begin
+            lists:foreach(fun(X) ->
+                couch_stats_collector:record({number, 11}, X)
+            end, lists:seq(0, 10)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_collector:record({number, 11}, 15),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({number, 11}, 1)
+        end);
+should_remove_data_after_some_time_for_other_samples(counter, _) ->
+    ?_assertEqual(make_agg(<<"yay description">>, 0, 0.0, 0.0, 0, 0),
+        begin
+            lists:foreach(fun(_) ->
+                couch_stats_collector:increment({testing, stuff})
+            end, lists:seq(1, 100)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({testing, stuff}, 1)
+        end).
+
 
 spawn_and_count(N) ->
     Self = self(),
@@ -191,3 +399,14 @@ repeat(_, 0) ->
 repeat(Fun, Count) ->
     Fun(),
     repeat(Fun, Count-1).
+
+make_agg(Desc, Sum, Mean, StdDev, Min, Max) ->
+    {[
+        {description, Desc},
+        {current, Sum},
+        {sum, Sum},
+        {mean, Mean},
+        {stddev, StdDev},
+        {min, Min},
+        {max, Max}
+    ]}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/069dfb7e/test/couchdb/fixtures/couch_stats_aggregates.cfg
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/couch_stats_aggregates.cfg b/test/couchdb/fixtures/couch_stats_aggregates.cfg
new file mode 100644
index 0000000..30e475d
--- /dev/null
+++ b/test/couchdb/fixtures/couch_stats_aggregates.cfg
@@ -0,0 +1,19 @@
+% Licensed to the Apache Software Foundation (ASF) under one
+% or more contributor license agreements.  See the NOTICE file
+% distributed with this work for additional information
+% regarding copyright ownership.  The ASF licenses this file
+% to you under the Apache License, Version 2.0 (the
+% "License"); you may not use this file except in compliance
+% with the License.  You may obtain a copy of the License at
+% 
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing,
+% software distributed under the License is distributed on an
+% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+% KIND, either express or implied.  See the License for the
+% specific language governing permissions and limitations
+% under the License.
+
+{testing, stuff, "yay description"}.
+{number, '11', "randomosity"}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/069dfb7e/test/couchdb/fixtures/couch_stats_aggregates.ini
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/couch_stats_aggregates.ini b/test/couchdb/fixtures/couch_stats_aggregates.ini
new file mode 100644
index 0000000..cc5cd21
--- /dev/null
+++ b/test/couchdb/fixtures/couch_stats_aggregates.ini
@@ -0,0 +1,20 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements.  See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership.  The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License.  You may obtain a copy of the License at
+; 
+;   http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied.  See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+[stats]
+rate = 10000000 ; We call collect_sample in testing
+samples = [0, 1]


[21/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 231-cors.t etap test suite to eunit

Extend vhost and subresource testing for more generic code.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/a8b74605
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/a8b74605
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/a8b74605

Branch: refs/heads/1963-eunit-bigcouch
Commit: a8b746051947bd26fbfc1158c97bcd0181f61103
Parents: 3d30015
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Jun 10 19:11:45 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:40 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_cors_tests.erl | 344 +++++++++++++++++++++++++++++++
 test/couchdb/test_request.erl       |  14 +-
 2 files changed, 357 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a8b74605/test/couchdb/couchdb_cors_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_cors_tests.erl b/test/couchdb/couchdb_cors_tests.erl
new file mode 100644
index 0000000..4e88ae7
--- /dev/null
+++ b/test/couchdb/couchdb_cors_tests.erl
@@ -0,0 +1,344 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_cors_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(SUPPORTED_METHODS,
+        "GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, COPY, OPTIONS").
+-define(TIMEOUT, 1000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    ok = couch_config:set("httpd", "enable_cors", "true", false),
+    ok = couch_config:set("vhosts", "example.com", "/", false),
+    Pid.
+
+stop(Pid) ->
+    couch_server_sup:stop(),
+    erlang:monitor(process, Pid),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    couch_db:close(Db),
+
+    couch_config:set("cors", "credentials", "false", false),
+    couch_config:set("cors", "origins", "http://example.com", false),
+
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    Host = "http://" ++ Addr ++ ":" ++ Port,
+    {Host, ?b2l(DbName)}.
+
+setup({Mod, VHost}) ->
+    {Host, DbName} = setup(),
+    Url = case Mod of
+        server ->
+            Host;
+        db ->
+            Host ++ "/" ++ DbName
+    end,
+    DefaultHeaders = [{"Origin", "http://example.com"}]
+                     ++ maybe_append_vhost(VHost),
+    {Host, DbName, Url, DefaultHeaders}.
+
+teardown(DbName) when is_list(DbName) ->
+    ok = couch_server:delete(?l2b(DbName), [?ADMIN_USER]),
+    ok;
+teardown({_, DbName}) ->
+    teardown(DbName).
+
+teardown(_, {_, DbName, _, _}) ->
+    teardown(DbName).
+
+
+cors_test_() ->
+    Funs = [
+        fun should_not_allow_origin/2,
+        fun should_not_allow_origin_with_port_mismatch/2,
+        fun should_not_allow_origin_with_scheme_mismatch/2,
+        fun should_not_all_origin_due_case_mismatch/2,
+        fun should_make_simple_request/2,
+        fun should_make_preflight_request/2,
+        fun should_make_prefligh_request_with_port/2,
+        fun should_make_prefligh_request_with_scheme/2,
+        fun should_make_prefligh_request_with_wildcard_origin/2,
+        fun should_make_request_with_credentials/2,
+        fun should_make_origin_request_with_auth/2,
+        fun should_make_preflight_request_with_auth/2
+    ],
+    {
+        "CORS (COUCHDB-431)",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [
+                cors_tests(Funs),
+                vhost_cors_tests(Funs),
+                headers_tests()
+            ]
+        }
+    }.
+
+headers_tests() ->
+    {
+        "Various headers tests",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_not_return_cors_headers_for_invalid_origin/1,
+                fun should_not_return_cors_headers_for_invalid_origin_preflight/1,
+                fun should_make_request_against_attachment/1,
+                fun should_make_range_request_against_attachment/1,
+                fun should_make_request_with_if_none_match_header/1
+            ]
+        }
+    }.
+
+cors_tests(Funs) ->
+    {
+        "CORS tests",
+        [
+            make_test_case(server, false, Funs),
+            make_test_case(db, false, Funs)
+        ]
+    }.
+
+vhost_cors_tests(Funs) ->
+    {
+        "Virtual Host CORS",
+        [
+            make_test_case(server, true, Funs),
+            make_test_case(db, true, Funs)
+        ]
+    }.
+
+make_test_case(Mod, UseVhost, Funs) ->
+    {
+        case Mod of server -> "Server"; db -> "Database" end,
+        {foreachx, fun setup/1, fun teardown/2, [{{Mod, UseVhost}, Fun}
+                                                 || Fun <- Funs]}
+    }.
+
+
+should_not_allow_origin(_, {_, _, Url, Headers0}) ->
+    ?_assertEqual(undefined,
+        begin
+            couch_config:delete("cors", "origins", false),
+            Headers1 = proplists:delete("Origin", Headers0),
+            Headers = [{"Origin", "http://127.0.0.1"}]
+                      ++ Headers1,
+            {ok, _, Resp, _} = test_request:get(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_not_allow_origin_with_port_mismatch({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual(undefined,
+        begin
+            Headers = [{"Origin", "http://example.com:5984"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_not_allow_origin_with_scheme_mismatch({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual(undefined,
+        begin
+            Headers = [{"Origin", "http://example.com:5984"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_not_all_origin_due_case_mismatch({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual(undefined,
+        begin
+            Headers = [{"Origin", "http://ExAmPlE.CoM"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_simple_request(_, {_, _, Url, DefaultHeaders}) ->
+    ?_test(begin
+        {ok, _, Resp, _} = test_request:get(Url, DefaultHeaders),
+        ?assertEqual(
+            undefined,
+            proplists:get_value("Access-Control-Allow-Credentials", Resp)),
+        ?assertEqual(
+            "http://example.com",
+            proplists:get_value("Access-Control-Allow-Origin", Resp)),
+        ?assertEqual(
+            "Cache-Control, Content-Type, Server",
+            proplists:get_value("Access-Control-Expose-Headers", Resp))
+    end).
+
+should_make_preflight_request(_, {_, _, Url, DefaultHeaders}) ->
+    ?_assertEqual(?SUPPORTED_METHODS,
+        begin
+            Headers = DefaultHeaders
+                      ++ [{"Access-Control-Request-Method", "GET"}],
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Methods", Resp)
+        end).
+
+should_make_prefligh_request_with_port({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual("http://example.com:5984",
+        begin
+            couch_config:set("cors", "origins", "http://example.com:5984",
+                             false),
+            Headers = [{"Origin", "http://example.com:5984"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_prefligh_request_with_scheme({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual("https://example.com:5984",
+        begin
+            couch_config:set("cors", "origins", "https://example.com:5984",
+                             false),
+            Headers = [{"Origin", "https://example.com:5984"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_prefligh_request_with_wildcard_origin({_, VHost}, {_, _, Url, _}) ->
+    ?_assertEqual("https://example.com:5984",
+        begin
+            couch_config:set("cors", "origins", "*", false),
+            Headers = [{"Origin", "https://example.com:5984"},
+                       {"Access-Control-Request-Method", "GET"}]
+                      ++ maybe_append_vhost(VHost),
+            {ok, _, Resp, _} = test_request:options(Url, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_request_with_credentials(_, {_, _, Url, DefaultHeaders}) ->
+    ?_assertEqual("true",
+        begin
+            ok = couch_config:set("cors", "credentials", "true", false),
+            {ok, _, Resp, _} = test_request:options(Url, DefaultHeaders),
+            proplists:get_value("Access-Control-Allow-Credentials", Resp)
+        end).
+
+should_make_origin_request_with_auth(_, {_, _, Url, DefaultHeaders}) ->
+    ?_assertEqual("http://example.com",
+        begin
+            Hashed = couch_passwords:hash_admin_password(<<"test">>),
+            couch_config:set("admins", "test", Hashed, false),
+            {ok, _, Resp, _} = test_request:get(
+                Url, DefaultHeaders, [{basic_auth, {"test", "test"}}]),
+            couch_config:delete("admins", "test", false),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_preflight_request_with_auth(_, {_, _, Url, DefaultHeaders}) ->
+    ?_assertEqual(?SUPPORTED_METHODS,
+        begin
+            Hashed = couch_passwords:hash_admin_password(<<"test">>),
+            couch_config:set("admins", "test", Hashed, false),
+            Headers = DefaultHeaders
+                      ++ [{"Access-Control-Request-Method", "GET"}],
+            {ok, _, Resp, _} = test_request:options(
+                Url, Headers, [{basic_auth, {"test", "test"}}]),
+            couch_config:delete("admins", "test", false),
+            proplists:get_value("Access-Control-Allow-Methods", Resp)
+        end).
+
+should_not_return_cors_headers_for_invalid_origin({Host, _}) ->
+    ?_assertEqual(undefined,
+        begin
+            Headers = [{"Origin", "http://127.0.0.1"}],
+            {ok, _, Resp, _} = test_request:get(Host, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_not_return_cors_headers_for_invalid_origin_preflight({Host, _}) ->
+    ?_assertEqual(undefined,
+        begin
+            Headers = [{"Origin", "http://127.0.0.1"},
+                       {"Access-Control-Request-Method", "GET"}],
+            {ok, _, Resp, _} = test_request:options(Host, Headers),
+            proplists:get_value("Access-Control-Allow-Origin", Resp)
+        end).
+
+should_make_request_against_attachment({Host, DbName}) ->
+    {"COUCHDB-1689",
+     ?_assertEqual(200,
+         begin
+             Url = Host ++ "/" ++ DbName,
+             {ok, Code0, _, _} = test_request:put(
+                 Url ++ "/doc/file.txt", [{"Content-Type", "text/plain"}],
+                 "hello, couch!"),
+             ?assert(Code0 =:= 201),
+             {ok, Code, _, _} = test_request:get(
+                 Url ++ "/doc?attachments=true",
+                 [{"Origin", "http://example.com"}]),
+             Code
+         end)}.
+
+should_make_range_request_against_attachment({Host, DbName}) ->
+    {"COUCHDB-1689",
+     ?_assertEqual(206,
+         begin
+             Url = Host ++ "/" ++ DbName,
+             {ok, Code0, _, _} = test_request:put(
+                 Url ++ "/doc/file.txt",
+                 [{"Content-Type", "application/octet-stream"}],
+                 "hello, couch!"),
+             ?assert(Code0 =:= 201),
+             {ok, Code, _, _} = test_request:get(
+                 Url ++ "/doc/file.txt", [{"Origin", "http://example.com"},
+                                          {"Range", "bytes=0-6"}]),
+             Code
+         end)}.
+
+should_make_request_with_if_none_match_header({Host, DbName}) ->
+    {"COUCHDB-1697",
+     ?_assertEqual(304,
+         begin
+             Url = Host ++ "/" ++ DbName,
+             {ok, Code0, Headers0, _} = test_request:put(
+                 Url ++ "/doc", [{"Content-Type", "application/json"}], "{}"),
+             ?assert(Code0 =:= 201),
+             ETag = proplists:get_value("ETag", Headers0),
+             {ok, Code, _, _} = test_request:get(
+                 Url ++ "/doc", [{"Origin", "http://example.com"},
+                                 {"If-None-Match", ETag}]),
+             Code
+        end)}.
+
+
+maybe_append_vhost(true) ->
+    [{"Host", "http://example.com"}];
+maybe_append_vhost(false) ->
+    [].

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a8b74605/test/couchdb/test_request.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/test_request.erl b/test/couchdb/test_request.erl
index 36871b4..68e4956 100644
--- a/test/couchdb/test_request.erl
+++ b/test/couchdb/test_request.erl
@@ -12,7 +12,9 @@
 
 -module(test_request).
 
--export([get/1, get/2, get/3, put/2, put/3]).
+-export([get/1, get/2, get/3]).
+-export([put/2, put/3]).
+-export([options/1, options/2, options/3]).
 -export([request/3, request/4]).
 
 get(Url) ->
@@ -31,6 +33,16 @@ put(Url, Headers, Body) ->
     request(put, Url, Headers, Body).
 
 
+options(Url) ->
+    request(options, Url, []).
+
+options(Url, Headers) ->
+    request(options, Url, Headers).
+
+options(Url, Headers, Opts) ->
+    request(options, Url, Headers, [], Opts).
+
+
 request(Method, Url, Headers) ->
     request(Method, Url, Headers, []).
 


[41/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Remove couch_ref_counter from couch module tests


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/863ee8f1
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/863ee8f1
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/863ee8f1

Branch: refs/heads/1963-eunit-bigcouch
Commit: 863ee8f1a7958f91f80e742468cba64fdb5b4e9b
Parents: de1812d
Author: Russell Branca <ch...@apache.org>
Authored: Thu Aug 21 12:53:27 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 13:49:08 2014 -0700

----------------------------------------------------------------------
 test/couchdb_modules_load_tests.erl | 1 -
 1 file changed, 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/863ee8f1/test/couchdb_modules_load_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_modules_load_tests.erl b/test/couchdb_modules_load_tests.erl
index 23cb375..c4fa40f 100644
--- a/test/couchdb_modules_load_tests.erl
+++ b/test/couchdb_modules_load_tests.erl
@@ -51,7 +51,6 @@ should_load_modules() ->
         couch_log,
         couch_os_process,
         couch_query_servers,
-        couch_ref_counter,
         couch_server,
         couch_server_sup,
         couch_stats_aggregator,


[10/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 170-os-daemons.t etap test suite to eunit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/a3eecf29
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/a3eecf29
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/a3eecf29

Branch: refs/heads/1963-eunit-bigcouch
Commit: a3eecf294ffc578ae30dead0fa5d734e64acc315
Parents: 7b3ecbe
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Jun 3 12:32:02 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_os_daemons_tests.erl      | 145 ++++++++++++++++++++
 test/couchdb/fixtures/os_daemon_looper.escript |  26 ++++
 2 files changed, 171 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a3eecf29/test/couchdb/couchdb_os_daemons_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_os_daemons_tests.erl b/test/couchdb/couchdb_os_daemons_tests.erl
new file mode 100644
index 0000000..1f9f6be
--- /dev/null
+++ b/test/couchdb/couchdb_os_daemons_tests.erl
@@ -0,0 +1,145 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_os_daemons_tests).
+
+-include("couch_eunit.hrl").
+
+%% keep in sync with couchdb/couch_os_daemons.erl
+-record(daemon, {
+    port,
+    name,
+    cmd,
+    kill,
+    status=running,
+    cfg_patterns=[],
+    errors=[],
+    buf=[]
+}).
+
+-define(DAEMON_LOOPER, "os_daemon_looper.escript").
+-define(DELAY, 100).
+-define(TIMEOUT, 1000).
+
+
+setup(DName) ->
+    {ok, CfgPid} = couch_config:start_link(?CONFIG_CHAIN),
+    {ok, OsDPid} = couch_os_daemons:start_link(),
+    couch_config:set("os_daemons", DName,
+                     filename:join([?FIXTURESDIR, DName]), false),
+    timer:sleep(?DELAY),  % sleep a bit to let daemon set kill flag
+    {CfgPid, OsDPid}.
+
+teardown(_, {CfgPid, OsDPid}) ->
+    erlang:monitor(process, CfgPid),
+    couch_config:stop(),
+    receive
+        {'DOWN', _, _, CfgPid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, config_stop})
+    end,
+
+    erlang:monitor(process, OsDPid),
+    exit(OsDPid, normal),
+    receive
+        {'DOWN', _, _, OsDPid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, os_daemon_stop})
+    end.
+
+
+os_daemons_test_() ->
+    {
+        "OS Daemons tests",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{?DAEMON_LOOPER, Fun} || Fun <- [
+                fun should_check_daemon/2,
+                fun should_check_daemon_table_form/2,
+                fun should_clean_tables_on_daemon_remove/2,
+                fun should_spawn_multiple_daemons/2,
+                fun should_keep_alive_one_daemon_on_killing_other/2
+            ]]
+        }
+    }.
+
+
+should_check_daemon(DName, _) ->
+    ?_test(begin
+        {ok, [D]} = couch_os_daemons:info([table]),
+        check_daemon(D, DName)
+    end).
+
+should_check_daemon_table_form(DName, _) ->
+    ?_test(begin
+        {ok, Tab} = couch_os_daemons:info(),
+        [D] = ets:tab2list(Tab),
+        check_daemon(D, DName)
+    end).
+
+should_clean_tables_on_daemon_remove(DName, _) ->
+    ?_test(begin
+        couch_config:delete("os_daemons", DName, false),
+        {ok, Tab2} = couch_os_daemons:info(),
+        ?_assertEqual([], ets:tab2list(Tab2))
+    end).
+
+should_spawn_multiple_daemons(DName, _) ->
+    ?_test(begin
+        couch_config:set("os_daemons", "bar",
+                         filename:join([?FIXTURESDIR, DName]), false),
+        couch_config:set("os_daemons", "baz",
+                         filename:join([?FIXTURESDIR, DName]), false),
+        timer:sleep(?DELAY),
+        {ok, Daemons} = couch_os_daemons:info([table]),
+        lists:foreach(fun(D) ->
+            check_daemon(D)
+        end, Daemons),
+        {ok, Tab} = couch_os_daemons:info(),
+        lists:foreach(fun(D) ->
+            check_daemon(D)
+        end, ets:tab2list(Tab))
+    end).
+
+should_keep_alive_one_daemon_on_killing_other(DName, _) ->
+    ?_test(begin
+        couch_config:set("os_daemons", "bar",
+                         filename:join([?FIXTURESDIR, DName]), false),
+        timer:sleep(?DELAY),
+        {ok, Daemons} = couch_os_daemons:info([table]),
+        lists:foreach(fun(D) ->
+            check_daemon(D)
+        end, Daemons),
+
+        couch_config:delete("os_daemons", "bar", false),
+        timer:sleep(?DELAY),
+        {ok, [D2]} = couch_os_daemons:info([table]),
+        check_daemon(D2, DName),
+
+        {ok, Tab} = couch_os_daemons:info(),
+        [T] = ets:tab2list(Tab),
+        check_daemon(T, DName)
+    end).
+
+
+check_daemon(D) ->
+    check_daemon(D, D#daemon.name).
+
+check_daemon(D, Name) ->
+    ?assert(is_port(D#daemon.port)),
+    ?assertEqual(Name, D#daemon.name),
+    ?assertNotEqual(undefined, D#daemon.kill),
+    ?assertEqual([], D#daemon.errors),
+    ?assertEqual([], D#daemon.buf).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a3eecf29/test/couchdb/fixtures/os_daemon_looper.escript
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_looper.escript b/test/couchdb/fixtures/os_daemon_looper.escript
new file mode 100755
index 0000000..73974e9
--- /dev/null
+++ b/test/couchdb/fixtures/os_daemon_looper.escript
@@ -0,0 +1,26 @@
+#! /usr/bin/env escript
+
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+loop() ->
+    loop(io:read("")).
+
+loop({ok, _}) ->
+    loop(io:read(""));
+loop(eof) ->
+    stop;
+loop({error, Reason}) ->
+    throw({error, Reason}).
+
+main([]) ->
+    loop().


[26/42] Move files out of test/couchdb into top level test/ folder

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_stats_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_stats_tests.erl b/test/couchdb/couch_stats_tests.erl
deleted file mode 100644
index d156449..0000000
--- a/test/couchdb/couch_stats_tests.erl
+++ /dev/null
@@ -1,412 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_stats_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(STATS_CFG_FIXTURE,
-    filename:join([?FIXTURESDIR, "couch_stats_aggregates.cfg"])).
--define(STATS_INI_FIXTURE,
-    filename:join([?FIXTURESDIR, "couch_stats_aggregates.ini"])).
--define(TIMEOUT, 1000).
--define(TIMEWAIT, 500).
-
-
-setup_collector() ->
-    couch_stats_collector:start(),
-    ok.
-
-setup_aggregator(_) ->
-    {ok, Pid} = couch_config:start_link([?STATS_INI_FIXTURE]),
-    {ok, _} = couch_stats_collector:start(),
-    {ok, _} = couch_stats_aggregator:start(?STATS_CFG_FIXTURE),
-    Pid.
-
-teardown_collector(_) ->
-    couch_stats_collector:stop(),
-    ok.
-
-teardown_aggregator(_, Pid) ->
-    couch_stats_aggregator:stop(),
-    couch_stats_collector:stop(),
-    erlang:monitor(process, Pid),
-    couch_config:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, config_stop})
-    end,
-    ok.
-
-
-couch_stats_collector_test_() ->
-    {
-        "CouchDB stats collector tests",
-        {
-            foreach,
-            fun setup_collector/0, fun teardown_collector/1,
-            [
-                should_increment_counter(),
-                should_decrement_counter(),
-                should_increment_and_decrement_counter(),
-                should_record_absolute_values(),
-                should_clear_absolute_values(),
-                should_track_process_count(),
-                should_increment_counter_multiple_times_per_pid(),
-                should_decrement_counter_on_process_exit(),
-                should_decrement_for_each_track_process_count_call_on_exit(),
-                should_return_all_counters_and_absolute_values(),
-                should_return_incremental_counters(),
-                should_return_absolute_values()
-            ]
-        }
-    }.
-
-couch_stats_aggregator_test_() ->
-    Funs = [
-        fun should_init_empty_aggregate/2,
-        fun should_get_empty_aggregate/2,
-        fun should_change_stats_on_values_add/2,
-        fun should_change_stats_for_all_times_on_values_add/2,
-        fun should_change_stats_on_values_change/2,
-        fun should_change_stats_for_all_times_on_values_change/2,
-        fun should_not_remove_data_after_some_time_for_0_sample/2,
-        fun should_remove_data_after_some_time_for_other_samples/2
-    ],
-    {
-        "CouchDB stats aggregator tests",
-        [
-            {
-                "Absolute values",
-                {
-                    foreachx,
-                    fun setup_aggregator/1, fun teardown_aggregator/2,
-                    [{absolute, Fun} || Fun <- Funs]
-                }
-            },
-            {
-                "Counters",
-                {
-                    foreachx,
-                    fun setup_aggregator/1, fun teardown_aggregator/2,
-                    [{counter, Fun} || Fun <- Funs]
-                }
-            }
-        ]
-    }.
-
-
-should_increment_counter() ->
-    ?_assertEqual(100,
-        begin
-            AddCount = fun() -> couch_stats_collector:increment(foo) end,
-            repeat(AddCount, 100),
-            couch_stats_collector:get(foo)
-        end).
-
-should_decrement_counter() ->
-    ?_assertEqual(67,
-        begin
-            AddCount = fun() -> couch_stats_collector:increment(foo) end,
-            RemCount = fun() -> couch_stats_collector:decrement(foo) end,
-            repeat(AddCount, 100),
-            repeat(RemCount, 33),
-            couch_stats_collector:get(foo)
-        end).
-
-should_increment_and_decrement_counter() ->
-    ?_assertEqual(0,
-        begin
-            AddCount = fun() -> couch_stats_collector:increment(foo) end,
-            RemCount = fun() -> couch_stats_collector:decrement(foo) end,
-            repeat(AddCount, 100),
-            repeat(RemCount, 25),
-            repeat(AddCount, 10),
-            repeat(RemCount, 5),
-            repeat(RemCount, 80),
-            couch_stats_collector:get(foo)
-        end).
-
-should_record_absolute_values() ->
-    ?_assertEqual(lists:seq(1, 15),
-        begin
-            lists:map(fun(Val) ->
-                couch_stats_collector:record(bar, Val)
-            end, lists:seq(1, 15)),
-            couch_stats_collector:get(bar)
-        end).
-
-should_clear_absolute_values() ->
-    ?_assertEqual(nil,
-        begin
-            lists:map(fun(Val) ->
-                couch_stats_collector:record(bar, Val)
-            end, lists:seq(1, 15)),
-            couch_stats_collector:clear(bar),
-            couch_stats_collector:get(bar)
-        end).
-
-should_track_process_count() ->
-    ?_assertMatch({_, 1}, spawn_and_count(1)).
-
-should_increment_counter_multiple_times_per_pid() ->
-    ?_assertMatch({_, 3}, spawn_and_count(3)).
-
-should_decrement_counter_on_process_exit() ->
-    ?_assertEqual(2,
-        begin
-            {Pid, 1} = spawn_and_count(1),
-            spawn_and_count(2),
-            RefMon = erlang:monitor(process, Pid),
-            Pid ! sepuku,
-            receive
-                {'DOWN', RefMon, _, _, _} -> ok
-            after ?TIMEOUT ->
-                throw(timeout)
-            end,
-            % sleep for awhile to let collector handle the updates
-            % suddenly, it couldn't notice process death instantly
-            timer:sleep(?TIMEWAIT),
-            couch_stats_collector:get(hoopla)
-        end).
-
-should_decrement_for_each_track_process_count_call_on_exit() ->
-    ?_assertEqual(2,
-        begin
-            {_, 2} = spawn_and_count(2),
-            {Pid, 6} = spawn_and_count(4),
-            RefMon = erlang:monitor(process, Pid),
-            Pid ! sepuku,
-            receive
-                {'DOWN', RefMon, _, _, _} -> ok
-            after ?TIMEOUT ->
-                throw(timeout)
-            end,
-            timer:sleep(?TIMEWAIT),
-            couch_stats_collector:get(hoopla)
-        end).
-
-should_return_all_counters_and_absolute_values() ->
-    ?_assertEqual([{bar,[1.0,0.0]}, {foo,1}],
-        begin
-            couch_stats_collector:record(bar, 0.0),
-            couch_stats_collector:record(bar, 1.0),
-            couch_stats_collector:increment(foo),
-            lists:sort(couch_stats_collector:all())
-        end).
-
-should_return_incremental_counters() ->
-    ?_assertEqual([{foo,1}],
-        begin
-            couch_stats_collector:record(bar, 0.0),
-            couch_stats_collector:record(bar, 1.0),
-            couch_stats_collector:increment(foo),
-            lists:sort(couch_stats_collector:all(incremental))
-        end).
-
-should_return_absolute_values() ->
-    ?_assertEqual([{bar,[1.0,0.0]}, {zing, "Z"}],
-        begin
-            couch_stats_collector:record(bar, 0.0),
-            couch_stats_collector:record(bar, 1.0),
-            couch_stats_collector:record(zing, 90),
-            couch_stats_collector:increment(foo),
-            lists:sort(couch_stats_collector:all(absolute))
-        end).
-
-should_init_empty_aggregate(absolute, _) ->
-    {Aggs} = couch_stats_aggregator:all(),
-    ?_assertEqual({[{'11', make_agg(<<"randomosity">>,
-                                    null, null, null, null, null)}]},
-                  couch_util:get_value(number, Aggs));
-should_init_empty_aggregate(counter, _) ->
-    {Aggs} = couch_stats_aggregator:all(),
-    ?_assertEqual({[{stuff, make_agg(<<"yay description">>,
-                                     null, null, null, null, null)}]},
-                  couch_util:get_value(testing, Aggs)).
-
-should_get_empty_aggregate(absolute, _) ->
-    ?_assertEqual(make_agg(<<"randomosity">>, null, null, null, null, null),
-             couch_stats_aggregator:get_json({number, '11'}));
-should_get_empty_aggregate(counter, _) ->
-    ?_assertEqual(make_agg(<<"yay description">>, null, null, null, null, null),
-             couch_stats_aggregator:get_json({testing, stuff})).
-
-should_change_stats_on_values_add(absolute, _) ->
-    lists:foreach(fun(X) ->
-        couch_stats_collector:record({number, 11}, X)
-    end, lists:seq(0, 10)),
-    couch_stats_aggregator:collect_sample(),
-    ?_assertEqual(make_agg(<<"randomosity">>, 5.0, 5.0, null, 5.0, 5.0),
-                  couch_stats_aggregator:get_json({number, 11}));
-should_change_stats_on_values_add(counter, _) ->
-    lists:foreach(fun(_) ->
-        couch_stats_collector:increment({testing, stuff})
-    end, lists:seq(1, 100)),
-    couch_stats_aggregator:collect_sample(),
-    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 100.0, null, 100, 100),
-                  couch_stats_aggregator:get_json({testing, stuff})).
-
-should_change_stats_for_all_times_on_values_add(absolute, _) ->
-    lists:foreach(fun(X) ->
-        couch_stats_collector:record({number, 11}, X)
-    end, lists:seq(0, 10)),
-    couch_stats_aggregator:collect_sample(),
-    ?_assertEqual(make_agg(<<"randomosity">>, 5.0, 5.0, null, 5.0, 5.0),
-                  couch_stats_aggregator:get_json({number, 11}, 1));
-should_change_stats_for_all_times_on_values_add(counter, _) ->
-    lists:foreach(fun(_) ->
-        couch_stats_collector:increment({testing, stuff})
-    end, lists:seq(1, 100)),
-    couch_stats_aggregator:collect_sample(),
-    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 100.0, null, 100, 100),
-                  couch_stats_aggregator:get_json({testing, stuff}, 1)).
-
-should_change_stats_on_values_change(absolute, _) ->
-    ?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
-        begin
-            lists:foreach(fun(X) ->
-                couch_stats_collector:record({number, 11}, X)
-            end, lists:seq(0, 10)),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_collector:record({number, 11}, 15),
-            couch_stats_aggregator:collect_sample(),
-            couch_stats_aggregator:get_json({number, 11})
-        end);
-should_change_stats_on_values_change(counter, _) ->
-    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 50.0, 70.711, 0, 100),
-        begin
-            lists:foreach(fun(_) ->
-                couch_stats_collector:increment({testing, stuff})
-            end, lists:seq(1, 100)),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_aggregator:collect_sample(),
-            couch_stats_aggregator:get_json({testing, stuff})
-        end).
-
-should_change_stats_for_all_times_on_values_change(absolute, _) ->
-    ?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
-        begin
-            lists:foreach(fun(X) ->
-                couch_stats_collector:record({number, 11}, X)
-            end, lists:seq(0, 10)),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_collector:record({number, 11}, 15),
-            couch_stats_aggregator:collect_sample(),
-            couch_stats_aggregator:get_json({number, 11}, 1)
-        end);
-should_change_stats_for_all_times_on_values_change(counter, _) ->
-    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 50.0, 70.711, 0, 100),
-        begin
-            lists:foreach(fun(_) ->
-                couch_stats_collector:increment({testing, stuff})
-            end, lists:seq(1, 100)),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_aggregator:collect_sample(),
-            couch_stats_aggregator:get_json({testing, stuff}, 1)
-        end).
-
-should_not_remove_data_after_some_time_for_0_sample(absolute, _) ->
-    ?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
-        begin
-            lists:foreach(fun(X) ->
-                couch_stats_collector:record({number, 11}, X)
-            end, lists:seq(0, 10)),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_collector:record({number, 11}, 15),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_aggregator:collect_sample(),
-            couch_stats_aggregator:get_json({number, 11})
-        end);
-should_not_remove_data_after_some_time_for_0_sample(counter, _) ->
-    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 33.333, 57.735, 0, 100),
-        begin
-            lists:foreach(fun(_) ->
-                couch_stats_collector:increment({testing, stuff})
-            end, lists:seq(1, 100)),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_aggregator:collect_sample(),
-            couch_stats_aggregator:get_json({testing, stuff})
-        end).
-
-should_remove_data_after_some_time_for_other_samples(absolute, _) ->
-    ?_assertEqual(make_agg(<<"randomosity">>, 15.0, 15.0, null, 15.0, 15.0),
-        begin
-            lists:foreach(fun(X) ->
-                couch_stats_collector:record({number, 11}, X)
-            end, lists:seq(0, 10)),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_collector:record({number, 11}, 15),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_aggregator:collect_sample(),
-            couch_stats_aggregator:get_json({number, 11}, 1)
-        end);
-should_remove_data_after_some_time_for_other_samples(counter, _) ->
-    ?_assertEqual(make_agg(<<"yay description">>, 0, 0.0, 0.0, 0, 0),
-        begin
-            lists:foreach(fun(_) ->
-                couch_stats_collector:increment({testing, stuff})
-            end, lists:seq(1, 100)),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_aggregator:collect_sample(),
-            timer:sleep(?TIMEWAIT),
-            couch_stats_aggregator:collect_sample(),
-            couch_stats_aggregator:get_json({testing, stuff}, 1)
-        end).
-
-
-spawn_and_count(N) ->
-    Self = self(),
-    Pid = spawn(fun() ->
-        lists:foreach(
-            fun(_) ->
-                couch_stats_collector:track_process_count(hoopla)
-            end, lists:seq(1,N)),
-        Self ! reporting,
-        receive
-            sepuku -> ok
-        end
-    end),
-    receive reporting -> ok end,
-    {Pid, couch_stats_collector:get(hoopla)}.
-
-repeat(_, 0) ->
-    ok;
-repeat(Fun, Count) ->
-    Fun(),
-    repeat(Fun, Count-1).
-
-make_agg(Desc, Sum, Mean, StdDev, Min, Max) ->
-    {[
-        {description, Desc},
-        {current, Sum},
-        {sum, Sum},
-        {mean, Mean},
-        {stddev, StdDev},
-        {min, Min},
-        {max, Max}
-    ]}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_stream_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_stream_tests.erl b/test/couchdb/couch_stream_tests.erl
deleted file mode 100644
index 335a2fe..0000000
--- a/test/couchdb/couch_stream_tests.erl
+++ /dev/null
@@ -1,100 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_stream_tests).
-
--include("couch_eunit.hrl").
-
-
-setup() ->
-    {ok, Fd} = couch_file:open(?tempfile(), [create, overwrite]),
-    {ok, Stream} = couch_stream:open(Fd),
-    {Fd, Stream}.
-
-teardown({Fd, _}) ->
-    ok = couch_file:close(Fd).
-
-
-stream_test_() ->
-    {
-        "CouchDB stream tests",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [
-                fun should_write/1,
-                fun should_write_consecutive/1,
-                fun should_write_empty_binary/1,
-                fun should_return_file_pointers_on_close/1,
-                fun should_return_stream_size_on_close/1,
-                fun should_return_valid_pointers/1,
-                fun should_recall_last_pointer_position/1,
-                fun should_stream_more_with_4K_chunk_size/1
-            ]
-        }
-    }.
-
-
-should_write({_, Stream}) ->
-    ?_assertEqual(ok, couch_stream:write(Stream, <<"food">>)).
-
-should_write_consecutive({_, Stream}) ->
-    couch_stream:write(Stream, <<"food">>),
-    ?_assertEqual(ok, couch_stream:write(Stream, <<"foob">>)).
-
-should_write_empty_binary({_, Stream}) ->
-    ?_assertEqual(ok, couch_stream:write(Stream, <<>>)).
-
-should_return_file_pointers_on_close({_, Stream}) ->
-    couch_stream:write(Stream, <<"foodfoob">>),
-    {Ptrs, _, _, _, _} = couch_stream:close(Stream),
-    ?_assertEqual([{0, 8}], Ptrs).
-
-should_return_stream_size_on_close({_, Stream}) ->
-    couch_stream:write(Stream, <<"foodfoob">>),
-    {_, Length, _, _, _} = couch_stream:close(Stream),
-    ?_assertEqual(8, Length).
-
-should_return_valid_pointers({Fd, Stream}) ->
-    couch_stream:write(Stream, <<"foodfoob">>),
-    {Ptrs, _, _, _, _} = couch_stream:close(Stream),
-    ?_assertEqual(<<"foodfoob">>, read_all(Fd, Ptrs)).
-
-should_recall_last_pointer_position({Fd, Stream}) ->
-    couch_stream:write(Stream, <<"foodfoob">>),
-    {_, _, _, _, _} = couch_stream:close(Stream),
-    {ok, ExpPtr} = couch_file:bytes(Fd),
-    {ok, Stream2} = couch_stream:open(Fd),
-    ZeroBits = <<0:(8 * 10)>>,
-    OneBits = <<1:(8 * 10)>>,
-    ok = couch_stream:write(Stream2, OneBits),
-    ok = couch_stream:write(Stream2, ZeroBits),
-    {Ptrs, 20, _, _, _} = couch_stream:close(Stream2),
-    [{ExpPtr, 20}] = Ptrs,
-    AllBits = iolist_to_binary([OneBits, ZeroBits]),
-    ?_assertEqual(AllBits, read_all(Fd, Ptrs)).
-
-should_stream_more_with_4K_chunk_size({Fd, _}) ->
-    {ok, Stream} = couch_stream:open(Fd, [{buffer_size, 4096}]),
-    lists:foldl(
-        fun(_, Acc) ->
-            Data = <<"a1b2c">>,
-            couch_stream:write(Stream, Data),
-            [Data | Acc]
-        end, [], lists:seq(1, 1024)),
-    ?_assertMatch({[{0, 4100}, {4106, 1020}], 5120, _, _, _},
-                  couch_stream:close(Stream)).
-
-
-read_all(Fd, PosList) ->
-    Data = couch_stream:foldl(Fd, PosList, fun(Bin, Acc) -> [Bin, Acc] end, []),
-    iolist_to_binary(Data).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_task_status_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_task_status_tests.erl b/test/couchdb/couch_task_status_tests.erl
deleted file mode 100644
index f71ad2b..0000000
--- a/test/couchdb/couch_task_status_tests.erl
+++ /dev/null
@@ -1,225 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_task_status_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(TIMEOUT, 1000).
-
-
-setup() ->
-    {ok, TaskStatusPid} = couch_task_status:start_link(),
-    TaskUpdaterPid = spawn(fun() -> loop() end),
-    {TaskStatusPid, TaskUpdaterPid}.
-
-teardown({TaskStatusPid, _}) ->
-    erlang:monitor(process, TaskStatusPid),
-    couch_task_status:stop(),
-    receive
-        {'DOWN', _, _, TaskStatusPid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw(timeout_error)
-    end.
-
-
-couch_task_status_test_() ->
-    {
-        "CouchDB task status updates",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [
-                fun should_register_task/1,
-                fun should_set_task_startup_time/1,
-                fun should_have_update_time_as_startup_before_any_progress/1,
-                fun should_set_task_type/1,
-                fun should_not_register_multiple_tasks_for_same_pid/1,
-                fun should_set_task_progress/1,
-                fun should_update_task_progress/1,
-                fun should_update_time_changes_on_task_progress/1,
-                fun should_control_update_frequency/1,
-                fun should_reset_control_update_frequency/1,
-                fun should_track_multiple_tasks/1,
-                fun should_finish_task/1
-
-            ]
-        }
-    }.
-
-
-should_register_task({_, Pid}) ->
-    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-    ?_assertEqual(1, length(couch_task_status:all())).
-
-should_set_task_startup_time({_, Pid}) ->
-    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-    ?_assert(is_integer(get_task_prop(Pid, started_on))).
-
-should_have_update_time_as_startup_before_any_progress({_, Pid}) ->
-    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-    StartTime = get_task_prop(Pid, started_on),
-    ?_assertEqual(StartTime, get_task_prop(Pid, updated_on)).
-
-should_set_task_type({_, Pid}) ->
-    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-    ?_assertEqual(replication, get_task_prop(Pid, type)).
-
-should_not_register_multiple_tasks_for_same_pid({_, Pid}) ->
-    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-    ?_assertEqual({add_task_error, already_registered},
-                  call(Pid, add, [{type, compaction}, {progress, 0}])).
-
-should_set_task_progress({_, Pid}) ->
-    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-    ?_assertEqual(0, get_task_prop(Pid, progress)).
-
-should_update_task_progress({_, Pid}) ->
-    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-    call(Pid, update, [{progress, 25}]),
-    ?_assertEqual(25, get_task_prop(Pid, progress)).
-
-should_update_time_changes_on_task_progress({_, Pid}) ->
-    ?_assert(
-        begin
-            ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-            ok = timer:sleep(1000),  % sleep awhile to customize update time
-            call(Pid, update, [{progress, 25}]),
-            get_task_prop(Pid, updated_on) > get_task_prop(Pid, started_on)
-        end).
-
-should_control_update_frequency({_, Pid}) ->
-    ?_assertEqual(66,
-        begin
-            ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-            call(Pid, update, [{progress, 50}]),
-            call(Pid, update_frequency, 500),
-            call(Pid, update, [{progress, 66}]),
-            call(Pid, update, [{progress, 77}]),
-            get_task_prop(Pid, progress)
-        end).
-
-should_reset_control_update_frequency({_, Pid}) ->
-    ?_assertEqual(87,
-        begin
-            ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-            call(Pid, update, [{progress, 50}]),
-            call(Pid, update_frequency, 500),
-            call(Pid, update, [{progress, 66}]),
-            call(Pid, update, [{progress, 77}]),
-            call(Pid, update_frequency, 0),
-            call(Pid, update, [{progress, 87}]),
-            get_task_prop(Pid, progress)
-        end).
-
-should_track_multiple_tasks(_) ->
-    ?_assert(run_multiple_tasks()).
-
-should_finish_task({_, Pid}) ->
-    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
-    ?assertEqual(1, length(couch_task_status:all())),
-    ok = call(Pid, done),
-    ?_assertEqual(0, length(couch_task_status:all())).
-
-
-run_multiple_tasks() ->
-    Pid1 = spawn(fun() -> loop() end),
-    Pid2 = spawn(fun() -> loop() end),
-    Pid3 = spawn(fun() -> loop() end),
-    call(Pid1, add, [{type, replication}, {progress, 0}]),
-    call(Pid2, add, [{type, compaction}, {progress, 0}]),
-    call(Pid3, add, [{type, indexer}, {progress, 0}]),
-
-    ?assertEqual(3, length(couch_task_status:all())),
-    ?assertEqual(replication, get_task_prop(Pid1, type)),
-    ?assertEqual(compaction, get_task_prop(Pid2, type)),
-    ?assertEqual(indexer, get_task_prop(Pid3, type)),
-
-    call(Pid2, update, [{progress, 33}]),
-    call(Pid3, update, [{progress, 42}]),
-    call(Pid1, update, [{progress, 11}]),
-    ?assertEqual(42, get_task_prop(Pid3, progress)),
-    call(Pid1, update, [{progress, 72}]),
-    ?assertEqual(72, get_task_prop(Pid1, progress)),
-    ?assertEqual(33, get_task_prop(Pid2, progress)),
-
-    call(Pid1, done),
-    ?assertEqual(2, length(couch_task_status:all())),
-    call(Pid3, done),
-    ?assertEqual(1, length(couch_task_status:all())),
-    call(Pid2, done),
-    ?assertEqual(0, length(couch_task_status:all())),
-
-    true.
-
-
-loop() ->
-    receive
-        {add, Props, From} ->
-            Resp = couch_task_status:add_task(Props),
-            From ! {ok, self(), Resp},
-            loop();
-        {update, Props, From} ->
-            Resp = couch_task_status:update(Props),
-            From ! {ok, self(), Resp},
-            loop();
-        {update_frequency, Msecs, From} ->
-            Resp = couch_task_status:set_update_frequency(Msecs),
-            From ! {ok, self(), Resp},
-            loop();
-        {done, From} ->
-            From ! {ok, self(), ok}
-    end.
-
-call(Pid, Command) ->
-    Pid ! {Command, self()},
-    wait(Pid).
-
-call(Pid, Command, Arg) ->
-    Pid ! {Command, Arg, self()},
-    wait(Pid).
-
-wait(Pid) ->
-    receive
-        {ok, Pid, Msg} ->
-            Msg
-    after ?TIMEOUT ->
-        throw(timeout_error)
-    end.
-
-get_task_prop(Pid, Prop) ->
-    From = list_to_binary(pid_to_list(Pid)),
-    Element = lists:foldl(
-        fun(PropList, Acc) ->
-            case couch_util:get_value(pid, PropList) of
-                From ->
-                    [PropList | Acc];
-                _ ->
-                    Acc
-            end
-        end,
-        [], couch_task_status:all()
-    ),
-    case couch_util:get_value(Prop, hd(Element), nil) of
-        nil ->
-            erlang:error({assertion_failed,
-                         [{module, ?MODULE},
-                          {line, ?LINE},
-                          {reason, "Could not get property '"
-                                   ++ couch_util:to_list(Prop)
-                                   ++ "' for task "
-                                   ++ pid_to_list(Pid)}]});
-        Value ->
-            Value
-    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_util_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_util_tests.erl b/test/couchdb/couch_util_tests.erl
deleted file mode 100644
index 8e24e72..0000000
--- a/test/couchdb/couch_util_tests.erl
+++ /dev/null
@@ -1,136 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_util_tests).
-
--include("couch_eunit.hrl").
-
-
-setup() ->
-    %% We cannot start driver from here since it becomes bounded to eunit
-    %% master process and the next couch_server_sup:start_link call will
-    %% fail because server couldn't load driver since it already is.
-    %%
-    %% On other hand, we cannot unload driver here due to
-    %% {error, not_loaded_by_this_process} while it is. Any ideas is welcome.
-    %%
-    couch_server_sup:start_link(?CONFIG_CHAIN),
-    %% couch_config:start_link(?CONFIG_CHAIN),
-    %% {ok, _} = couch_drv:start_link(),
-    ok.
-
-teardown(_) ->
-    couch_server_sup:stop(),
-    %% couch_config:stop(),
-    %% erl_ddll:unload_driver(couch_icu_driver),
-    ok.
-
-
-collation_test_() ->
-    {
-        "Collation tests",
-        [
-            {
-                setup,
-                fun setup/0, fun teardown/1,
-                [
-                    should_collate_ascii(),
-                    should_collate_non_ascii()
-                ]
-            }
-        ]
-    }.
-
-should_collate_ascii() ->
-    ?_assertEqual(1, couch_util:collate(<<"foo">>, <<"bar">>)).
-
-should_collate_non_ascii() ->
-    ?_assertEqual(-1, couch_util:collate(<<"A">>, <<"aa">>)).
-
-to_existed_atom_test() ->
-    ?assert(couch_util:to_existing_atom(true)),
-    ?assertMatch(foo, couch_util:to_existing_atom(<<"foo">>)),
-    ?assertMatch(foobarbaz, couch_util:to_existing_atom("foobarbaz")).
-
-implode_test() ->
-    ?assertEqual([1, 38, 2, 38, 3], couch_util:implode([1, 2, 3], "&")).
-
-trim_test() ->
-    lists:map(fun(S) -> ?assertEqual("foo", couch_util:trim(S)) end,
-              [" foo", "foo ", "\tfoo", " foo ", "foo\t", "foo\n", "\nfoo"]).
-
-abs_pathname_test() ->
-    {ok, Cwd} = file:get_cwd(),
-    ?assertEqual(Cwd ++ "/foo", couch_util:abs_pathname("./foo")).
-
-flush_test() ->
-    ?assertNot(couch_util:should_flush()),
-    AcquireMem = fun() ->
-        _IntsToAGazillion = lists:seq(1, 200000),
-        _LotsOfData = lists:map(fun(_) -> <<"foobar">> end,
-                                lists:seq(1, 500000)),
-        _BigBin = list_to_binary(_LotsOfData),
-
-        %% Allocation 200K tuples puts us above the memory threshold
-        %% Originally, there should be:
-        %%      ?assertNot(should_flush())
-        %% however, unlike for etap test, GC collects all allocated bits
-        %% making this conditions fail. So we have to invert the condition
-        %% since GC works, cleans the memory and everything is fine.
-        ?assertNot(couch_util:should_flush())
-    end,
-    AcquireMem(),
-
-    %% Checking to flush invokes GC
-    ?assertNot(couch_util:should_flush()).
-
-verify_test() ->
-    ?assert(couch_util:verify("It4Vooya", "It4Vooya")),
-    ?assertNot(couch_util:verify("It4VooyaX", "It4Vooya")),
-    ?assert(couch_util:verify(<<"ahBase3r">>, <<"ahBase3r">>)),
-    ?assertNot(couch_util:verify(<<"ahBase3rX">>, <<"ahBase3r">>)),
-    ?assertNot(couch_util:verify(nil, <<"ahBase3r">>)).
-
-find_in_binary_test_() ->
-    Cases = [
-        {<<"foo">>, <<"foobar">>, {exact, 0}},
-        {<<"foo">>, <<"foofoo">>, {exact, 0}},
-        {<<"foo">>, <<"barfoo">>, {exact, 3}},
-        {<<"foo">>, <<"barfo">>, {partial, 3}},
-        {<<"f">>, <<"fobarfff">>, {exact, 0}},
-        {<<"f">>, <<"obarfff">>, {exact, 4}},
-        {<<"f">>, <<"obarggf">>, {exact, 6}},
-        {<<"f">>, <<"f">>, {exact, 0}},
-        {<<"f">>, <<"g">>, not_found},
-        {<<"foo">>, <<"f">>, {partial, 0}},
-        {<<"foo">>, <<"g">>, not_found},
-        {<<"foo">>, <<"">>, not_found},
-        {<<"fofo">>, <<"foofo">>, {partial, 3}},
-        {<<"foo">>, <<"gfobarfo">>, {partial, 6}},
-        {<<"foo">>, <<"gfobarf">>, {partial, 6}},
-        {<<"foo">>, <<"gfobar">>, not_found},
-        {<<"fog">>, <<"gbarfogquiz">>, {exact, 4}},
-        {<<"ggg">>, <<"ggg">>, {exact, 0}},
-        {<<"ggg">>, <<"ggggg">>, {exact, 0}},
-        {<<"ggg">>, <<"bggg">>, {exact, 1}},
-        {<<"ggg">>, <<"bbgg">>, {partial, 2}},
-        {<<"ggg">>, <<"bbbg">>, {partial, 3}},
-        {<<"ggg">>, <<"bgbggbggg">>, {exact, 6}},
-        {<<"ggg">>, <<"bgbggb">>, not_found}
-    ],
-    lists:map(
-        fun({Needle, Haystack, Result}) ->
-            Msg = lists:flatten(io_lib:format("Looking for ~s in ~s",
-                                              [Needle, Haystack])),
-            {Msg, ?_assertMatch(Result,
-                                couch_util:find_in_binary(Needle, Haystack))}
-        end, Cases).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_uuids_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_uuids_tests.erl b/test/couchdb/couch_uuids_tests.erl
deleted file mode 100644
index ea1d034..0000000
--- a/test/couchdb/couch_uuids_tests.erl
+++ /dev/null
@@ -1,161 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_uuids_tests).
-
--include("couch_eunit.hrl").
-
--define(TIMEOUT_S, 20).
-
-
-setup() ->
-    {ok, Pid} = couch_config:start_link(?CONFIG_CHAIN),
-    erlang:monitor(process, Pid),
-    couch_uuids:start(),
-    Pid.
-
-setup(Opts) ->
-    Pid = setup(),
-    lists:foreach(
-        fun({Option, Value}) ->
-            couch_config:set("uuids", Option, Value, false)
-        end, Opts),
-    Pid.
-
-teardown(Pid) ->
-    couch_uuids:stop(),
-    couch_config:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} -> ok
-    after
-        1000 -> throw({timeout_error, config_stop})
-    end.
-
-teardown(_, Pid) ->
-    teardown(Pid).
-
-
-default_test_() ->
-    {
-        "Default UUID algorithm",
-        {
-            setup,
-            fun setup/0, fun teardown/1,
-            fun should_be_unique/1
-        }
-    }.
-
-sequential_test_() ->
-    Opts = [{"algorithm", "sequential"}],
-    Cases = [
-        fun should_be_unique/2,
-        fun should_increment_monotonically/2,
-        fun should_rollover/2
-    ],
-    {
-        "UUID algorithm: sequential",
-        {
-            foreachx,
-            fun setup/1, fun teardown/2,
-            [{Opts, Fun} || Fun <- Cases]
-        }
-    }.
-
-utc_test_() ->
-    Opts = [{"algorithm", "utc_random"}],
-    Cases = [
-        fun should_be_unique/2,
-        fun should_increment_monotonically/2
-    ],
-    {
-        "UUID algorithm: utc_random",
-        {
-            foreachx,
-            fun setup/1, fun teardown/2,
-            [{Opts, Fun} || Fun <- Cases]
-        }
-    }.
-
-utc_id_suffix_test_() ->
-    Opts = [{"algorithm", "utc_id"}, {"utc_id_suffix", "bozo"}],
-    Cases = [
-        fun should_be_unique/2,
-        fun should_increment_monotonically/2,
-        fun should_preserve_suffix/2
-    ],
-    {
-        "UUID algorithm: utc_id",
-        {
-            foreachx,
-            fun setup/1, fun teardown/2,
-            [{Opts, Fun} || Fun <- Cases]
-        }
-    }.
-
-
-should_be_unique() ->
-    %% this one may really runs for too long on slow hosts
-    {timeout, ?TIMEOUT_S, ?_assert(test_unique(10000, [couch_uuids:new()]))}.
-should_be_unique(_) ->
-    should_be_unique().
-should_be_unique(_, _) ->
-    should_be_unique().
-
-should_increment_monotonically(_, _) ->
-    ?_assert(couch_uuids:new() < couch_uuids:new()).
-
-should_rollover(_, _) ->
-    ?_test(begin
-        UUID = binary_to_list(couch_uuids:new()),
-        Prefix = element(1, lists:split(26, UUID)),
-        N = gen_until_pref_change(Prefix, 0),
-        ?assert(N >= 5000 andalso N =< 11000)
-    end).
-
-should_preserve_suffix(_, _) ->
-    ?_test(begin
-        UUID = binary_to_list(couch_uuids:new()),
-        Suffix = get_suffix(UUID),
-        ?assert(test_same_suffix(10000, Suffix))
-    end).
-
-
-test_unique(0, _) ->
-    true;
-test_unique(N, UUIDs) ->
-    UUID = couch_uuids:new(),
-    ?assertNot(lists:member(UUID, UUIDs)),
-    test_unique(N - 1, [UUID| UUIDs]).
-
-get_prefix(UUID) ->
-    element(1, lists:split(26, binary_to_list(UUID))).
-
-gen_until_pref_change(_, Count) when Count > 8251 ->
-    Count;
-gen_until_pref_change(Prefix, N) ->
-    case get_prefix(couch_uuids:new()) of
-        Prefix -> gen_until_pref_change(Prefix, N + 1);
-        _ -> N
-    end.
-
-get_suffix(UUID) when is_binary(UUID) ->
-    get_suffix(binary_to_list(UUID));
-get_suffix(UUID) ->
-    element(2, lists:split(14, UUID)).
-
-test_same_suffix(0, _) ->
-    true;
-test_same_suffix(N, Suffix) ->
-    case get_suffix(couch_uuids:new()) of
-        Suffix -> test_same_suffix(N - 1, Suffix);
-        _ -> false
-    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_work_queue_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_work_queue_tests.erl b/test/couchdb/couch_work_queue_tests.erl
deleted file mode 100644
index 8a463b5..0000000
--- a/test/couchdb/couch_work_queue_tests.erl
+++ /dev/null
@@ -1,393 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_work_queue_tests).
-
--include("couch_eunit.hrl").
-
--define(TIMEOUT, 100).
-
-
-setup(Opts) ->
-    {ok, Q} = couch_work_queue:new(Opts),
-    Producer = spawn_producer(Q),
-    Consumer = spawn_consumer(Q),
-    {Q, Producer, Consumer}.
-
-setup_max_items() ->
-    setup([{max_items, 3}]).
-
-setup_max_size() ->
-    setup([{max_size, 160}]).
-
-setup_max_items_and_size() ->
-    setup([{max_size, 160}, {max_items, 3}]).
-
-setup_multi_workers() ->
-    {Q, Producer, Consumer1} = setup([{max_size, 160},
-                                      {max_items, 3},
-                                      {multi_workers, true}]),
-    Consumer2 = spawn_consumer(Q),
-    Consumer3 = spawn_consumer(Q),
-    {Q, Producer, [Consumer1, Consumer2, Consumer3]}.
-
-teardown({Q, Producer, Consumers}) when is_list(Consumers) ->
-    % consume all to unblock and let producer/consumer stop without timeout
-    [consume(Consumer, all) || Consumer <- Consumers],
-
-    ok = close_queue(Q),
-    ok = stop(Producer, "producer"),
-    R = [stop(Consumer, "consumer") || Consumer <- Consumers],
-    R = [ok || _ <- Consumers],
-    ok;
-teardown({Q, Producer, Consumer}) ->
-    teardown({Q, Producer, [Consumer]}).
-
-
-single_consumer_test_() ->
-    {
-        "Single producer and consumer",
-        [
-            {
-                "Queue with 3 max items",
-                {
-                    foreach,
-                    fun setup_max_items/0, fun teardown/1,
-                    single_consumer_max_item_count() ++ common_cases()
-                }
-            },
-            {
-                "Queue with max size of 160 bytes",
-                {
-                    foreach,
-                    fun setup_max_size/0, fun teardown/1,
-                    single_consumer_max_size() ++ common_cases()
-                }
-            },
-            {
-                "Queue with max size of 160 bytes and 3 max items",
-                {
-                    foreach,
-                    fun setup_max_items_and_size/0, fun teardown/1,
-                    single_consumer_max_items_and_size() ++ common_cases()
-                }
-            }
-        ]
-    }.
-
-multiple_consumers_test_() ->
-    {
-        "Single producer and multiple consumers",
-        [
-            {
-                "Queue with max size of 160 bytes and 3 max items",
-                {
-                    foreach,
-                    fun setup_multi_workers/0, fun teardown/1,
-                    common_cases() ++ multiple_consumers()
-                }
-
-            }
-        ]
-    }.
-
-common_cases()->
-    [
-        fun should_block_consumer_on_dequeue_from_empty_queue/1,
-        fun should_consume_right_item/1,
-        fun should_timeout_on_close_non_empty_queue/1,
-        fun should_not_block_producer_for_non_empty_queue_after_close/1,
-        fun should_be_closed/1
-    ].
-
-single_consumer_max_item_count()->
-    [
-        fun should_have_no_items_for_new_queue/1,
-        fun should_block_producer_on_full_queue_count/1,
-        fun should_receive_first_queued_item/1,
-        fun should_consume_multiple_items/1,
-        fun should_consume_all/1
-    ].
-
-single_consumer_max_size()->
-    [
-        fun should_have_zero_size_for_new_queue/1,
-        fun should_block_producer_on_full_queue_size/1,
-        fun should_increase_queue_size_on_produce/1,
-        fun should_receive_first_queued_item/1,
-        fun should_consume_multiple_items/1,
-        fun should_consume_all/1
-    ].
-
-single_consumer_max_items_and_size() ->
-    single_consumer_max_item_count() ++ single_consumer_max_size().
-
-multiple_consumers() ->
-    [
-        fun should_have_zero_size_for_new_queue/1,
-        fun should_have_no_items_for_new_queue/1,
-        fun should_increase_queue_size_on_produce/1
-    ].
-
-
-should_have_no_items_for_new_queue({Q, _, _}) ->
-    ?_assertEqual(0, couch_work_queue:item_count(Q)).
-
-should_have_zero_size_for_new_queue({Q, _, _}) ->
-    ?_assertEqual(0, couch_work_queue:size(Q)).
-
-should_block_consumer_on_dequeue_from_empty_queue({_, _, Consumers}) when is_list(Consumers) ->
-    [consume(C, 2) || C <- Consumers],
-    Pongs = [ping(C) || C <- Consumers],
-    ?_assertEqual([timeout, timeout, timeout], Pongs);
-should_block_consumer_on_dequeue_from_empty_queue({_, _, Consumer}) ->
-    consume(Consumer, 1),
-    Pong = ping(Consumer),
-    ?_assertEqual(timeout, Pong).
-
-should_consume_right_item({Q, Producer, Consumers}) when is_list(Consumers) ->
-    [consume(C, 3) || C <- Consumers],
-
-    Item1 = produce(Producer, 10),
-    ok = ping(Producer),
-    ?assertEqual(0, couch_work_queue:item_count(Q)),
-    ?assertEqual(0, couch_work_queue:size(Q)),
-
-    Item2 = produce(Producer, 10),
-    ok = ping(Producer),
-    ?assertEqual(0, couch_work_queue:item_count(Q)),
-    ?assertEqual(0, couch_work_queue:size(Q)),
-
-    Item3 = produce(Producer, 10),
-    ok = ping(Producer),
-    ?assertEqual(0, couch_work_queue:item_count(Q)),
-    ?assertEqual(0, couch_work_queue:size(Q)),
-
-    R = [{ping(C), Item}
-         || {C, Item} <- lists:zip(Consumers, [Item1, Item2, Item3])],
-
-    ?_assertEqual([{ok, Item1}, {ok, Item2}, {ok, Item3}], R);
-should_consume_right_item({_, Producer, Consumer}) ->
-    consume(Consumer, 1),
-    Item = produce(Producer, 10),
-    produce(Producer, 20),
-    ok = ping(Producer),
-    ok = ping(Consumer),
-    {ok, Items} = last_consumer_items(Consumer),
-    ?_assertEqual([Item], Items).
-
-should_increase_queue_size_on_produce({Q, Producer, _}) ->
-    produce(Producer, 50),
-    ok = ping(Producer),
-    Count1 = couch_work_queue:item_count(Q),
-    Size1 = couch_work_queue:size(Q),
-
-    produce(Producer, 10),
-    Count2 = couch_work_queue:item_count(Q),
-    Size2 = couch_work_queue:size(Q),
-
-    ?_assertEqual([{Count1, Size1}, {Count2, Size2}], [{1, 50}, {2, 60}]).
-
-should_block_producer_on_full_queue_count({Q, Producer, _}) ->
-    produce(Producer, 10),
-    ?assertEqual(1, couch_work_queue:item_count(Q)),
-    ok = ping(Producer),
-
-    produce(Producer, 15),
-    ?assertEqual(2, couch_work_queue:item_count(Q)),
-    ok = ping(Producer),
-
-    produce(Producer, 20),
-    ?assertEqual(3, couch_work_queue:item_count(Q)),
-    Pong = ping(Producer),
-
-    ?_assertEqual(timeout, Pong).
-
-should_block_producer_on_full_queue_size({Q, Producer, _}) ->
-    produce(Producer, 100),
-    ok = ping(Producer),
-    ?assertEqual(1, couch_work_queue:item_count(Q)),
-    ?assertEqual(100, couch_work_queue:size(Q)),
-
-    produce(Producer, 110),
-    Pong = ping(Producer),
-    ?assertEqual(2, couch_work_queue:item_count(Q)),
-    ?assertEqual(210, couch_work_queue:size(Q)),
-
-    ?_assertEqual(timeout, Pong).
-
-should_consume_multiple_items({_, Producer, Consumer}) ->
-    Item1 = produce(Producer, 10),
-    ok = ping(Producer),
-
-    Item2 = produce(Producer, 15),
-    ok = ping(Producer),
-
-    consume(Consumer, 2),
-
-    {ok, Items} = last_consumer_items(Consumer),
-    ?_assertEqual([Item1, Item2], Items).
-
-should_receive_first_queued_item({Q, Producer, Consumer}) ->
-    consume(Consumer, 100),
-    timeout = ping(Consumer),
-
-    Item = produce(Producer, 11),
-    ok = ping(Producer),
-
-    ok = ping(Consumer),
-    ?assertEqual(0, couch_work_queue:item_count(Q)),
-
-    {ok, Items} = last_consumer_items(Consumer),
-    ?_assertEqual([Item], Items).
-
-should_consume_all({_, Producer, Consumer}) ->
-    Item1 = produce(Producer, 10),
-    Item2 = produce(Producer, 15),
-    Item3 = produce(Producer, 20),
-
-    consume(Consumer, all),
-
-    {ok, Items} = last_consumer_items(Consumer),
-    ?_assertEqual([Item1, Item2, Item3], Items).
-
-should_timeout_on_close_non_empty_queue({Q, Producer, _}) ->
-    produce(Producer, 1),
-    Status = close_queue(Q),
-
-    ?_assertEqual(timeout, Status).
-
-should_not_block_producer_for_non_empty_queue_after_close({Q, Producer, _}) ->
-    produce(Producer, 1),
-    close_queue(Q),
-    Pong = ping(Producer),
-    Size = couch_work_queue:size(Q),
-    Count = couch_work_queue:item_count(Q),
-
-    ?_assertEqual({ok, 1, 1}, {Pong, Size, Count}).
-
-should_be_closed({Q, _, Consumers}) when is_list(Consumers) ->
-    ok = close_queue(Q),
-
-    [consume(C, 1) || C <- Consumers],
-
-    LastConsumerItems = [last_consumer_items(C) || C <- Consumers],
-    ItemsCount = couch_work_queue:item_count(Q),
-    Size = couch_work_queue:size(Q),
-
-    ?_assertEqual({[closed, closed, closed], closed, closed},
-                  {LastConsumerItems, ItemsCount, Size});
-should_be_closed({Q, _, Consumer}) ->
-    ok = close_queue(Q),
-
-    consume(Consumer, 1),
-
-    LastConsumerItems = last_consumer_items(Consumer),
-    ItemsCount = couch_work_queue:item_count(Q),
-    Size = couch_work_queue:size(Q),
-
-    ?_assertEqual({closed, closed, closed},
-                  {LastConsumerItems, ItemsCount, Size}).
-
-
-close_queue(Q) ->
-    ok = couch_work_queue:close(Q),
-    MonRef = erlang:monitor(process, Q),
-    receive
-        {'DOWN', MonRef, process, Q, _Reason} -> ok
-    after ?TIMEOUT ->
-        erlang:demonitor(MonRef),
-        timeout
-    end.
-
-spawn_consumer(Q) ->
-    Parent = self(),
-    spawn(fun() -> consumer_loop(Parent, Q, nil) end).
-
-consumer_loop(Parent, Q, PrevItem) ->
-    receive
-        {stop, Ref} ->
-            Parent ! {ok, Ref};
-        {ping, Ref} ->
-            Parent ! {pong, Ref},
-            consumer_loop(Parent, Q, PrevItem);
-        {last_item, Ref} ->
-            Parent ! {item, Ref, PrevItem},
-            consumer_loop(Parent, Q, PrevItem);
-        {consume, N} ->
-            Result = couch_work_queue:dequeue(Q, N),
-            consumer_loop(Parent, Q, Result)
-    end.
-
-spawn_producer(Q) ->
-    Parent = self(),
-    spawn(fun() -> producer_loop(Parent, Q) end).
-
-producer_loop(Parent, Q) ->
-    receive
-        {stop, Ref} ->
-            Parent ! {ok, Ref};
-        {ping, Ref} ->
-            Parent ! {pong, Ref},
-            producer_loop(Parent, Q);
-        {produce, Ref, Size} ->
-            Item = crypto:rand_bytes(Size),
-            Parent ! {item, Ref, Item},
-            ok = couch_work_queue:queue(Q, Item),
-            producer_loop(Parent, Q)
-    end.
-
-consume(Consumer, N) ->
-    Consumer ! {consume, N}.
-
-last_consumer_items(Consumer) ->
-    Ref = make_ref(),
-    Consumer ! {last_item, Ref},
-    receive
-        {item, Ref, Items} ->
-            Items
-    after ?TIMEOUT ->
-        timeout
-    end.
-
-produce(Producer, Size) ->
-    Ref = make_ref(),
-    Producer ! {produce, Ref, Size},
-    receive
-        {item, Ref, Item} ->
-            Item
-    after ?TIMEOUT ->
-        erlang:error({assertion_failed,
-                      [{module, ?MODULE},
-                       {line, ?LINE},
-                       {reason, "Timeout asking producer to produce an item"}]})
-    end.
-
-ping(Pid) ->
-    Ref = make_ref(),
-    Pid ! {ping, Ref},
-    receive
-        {pong, Ref} ->
-            ok
-    after ?TIMEOUT ->
-        timeout
-    end.
-
-stop(Pid, Name) ->
-    Ref = make_ref(),
-    Pid ! {stop, Ref},
-    receive
-        {ok, Ref} -> ok
-    after ?TIMEOUT ->
-        ?debugMsg("Timeout stopping " ++ Name),
-        timeout
-    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_attachments_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_attachments_tests.erl b/test/couchdb/couchdb_attachments_tests.erl
deleted file mode 100644
index cf59785..0000000
--- a/test/couchdb/couchdb_attachments_tests.erl
+++ /dev/null
@@ -1,638 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_attachments_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(COMPRESSION_LEVEL, 8).
--define(ATT_BIN_NAME, <<"logo.png">>).
--define(ATT_TXT_NAME, <<"file.erl">>).
--define(FIXTURE_PNG, filename:join([?FIXTURESDIR, "logo.png"])).
--define(FIXTURE_TXT, ?FILE).
--define(TIMEOUT, 1000).
--define(TIMEOUT_EUNIT, 10).
--define(TIMEWAIT, 100).
--define(i2l(I), integer_to_list(I)).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    % ensure in default compression settings for attachments_compression_tests
-    couch_config:set("attachments", "compression_level",
-                     ?i2l(?COMPRESSION_LEVEL), false),
-    couch_config:set("attachments", "compressible_types", "text/*", false),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-setup() ->
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, []),
-    ok = couch_db:close(Db),
-    Addr = couch_config:get("httpd", "bind_address", any),
-    Port = mochiweb_socket_server:get(couch_httpd, port),
-    Host = Addr ++ ":" ++ ?i2l(Port),
-    {Host, ?b2l(DbName)}.
-
-setup({binary, standalone}) ->
-    {Host, DbName} = setup(),
-        setup_att(fun create_standalone_png_att/2, Host, DbName, ?FIXTURE_PNG);
-setup({text, standalone}) ->
-    {Host, DbName} = setup(),
-    setup_att(fun create_standalone_text_att/2, Host, DbName, ?FIXTURE_TXT);
-setup({binary, inline}) ->
-    {Host, DbName} = setup(),
-    setup_att(fun create_inline_png_att/2, Host, DbName, ?FIXTURE_PNG);
-setup({text, inline}) ->
-    {Host, DbName} = setup(),
-    setup_att(fun create_inline_text_att/2, Host, DbName, ?FIXTURE_TXT);
-setup(compressed) ->
-    {Host, DbName} = setup(),
-    setup_att(fun create_already_compressed_att/2, Host, DbName, ?FIXTURE_TXT).
-setup_att(Fun, Host, DbName, File) ->
-    HttpHost = "http://" ++ Host,
-    AttUrl = Fun(HttpHost, DbName),
-    {ok, Data} = file:read_file(File),
-    DocUrl = string:join([HttpHost, DbName, "doc"], "/"),
-    Helpers = {DbName, DocUrl, AttUrl},
-    {Data, Helpers}.
-
-teardown(_, {_, {DbName, _, _}}) ->
-    teardown(DbName).
-
-teardown({_, DbName}) ->
-    teardown(DbName);
-teardown(DbName) ->
-    ok = couch_server:delete(?l2b(DbName), []),
-    ok.
-
-
-attachments_test_() ->
-    {
-        "Attachments tests",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            [
-                attachments_md5_tests(),
-                attachments_compression_tests()
-            ]
-        }
-    }.
-
-attachments_md5_tests() ->
-    {
-        "Attachments MD5 tests",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [
-                fun should_upload_attachment_without_md5/1,
-                fun should_upload_attachment_by_chunks_without_md5/1,
-                fun should_upload_attachment_with_valid_md5_header/1,
-                fun should_upload_attachment_by_chunks_with_valid_md5_header/1,
-                fun should_upload_attachment_by_chunks_with_valid_md5_trailer/1,
-                fun should_reject_attachment_with_invalid_md5/1,
-                fun should_reject_chunked_attachment_with_invalid_md5/1,
-                fun should_reject_chunked_attachment_with_invalid_md5_trailer/1
-            ]
-        }
-    }.
-
-attachments_compression_tests() ->
-    Funs = [
-         fun should_get_att_without_accept_gzip_encoding/2,
-         fun should_get_att_with_accept_gzip_encoding/2,
-         fun should_get_att_with_accept_deflate_encoding/2,
-         fun should_return_406_response_on_unsupported_encoding/2,
-         fun should_get_doc_with_att_data/2,
-         fun should_get_doc_with_att_data_stub/2
-    ],
-    {
-        "Attachments compression tests",
-        [
-            {
-                "Created via Attachments API",
-                created_attachments_compression_tests(standalone, Funs)
-            },
-            {
-                "Created inline via Document API",
-                created_attachments_compression_tests(inline, Funs)
-            },
-            {
-                "Created already been compressed via Attachments API",
-                {
-                    foreachx,
-                    fun setup/1, fun teardown/2,
-                    [{compressed, Fun} || Fun <- Funs]
-                }
-            },
-            {
-                foreach,
-                fun setup/0, fun teardown/1,
-                [
-                    fun should_not_create_compressed_att_with_deflate_encoding/1,
-                    fun should_not_create_compressed_att_with_compress_encoding/1,
-                    fun should_create_compressible_att_with_ctype_params/1
-                ]
-            }
-        ]
-    }.
-
-created_attachments_compression_tests(Mod, Funs) ->
-    [
-        {
-            "Compressiable attachments",
-            {
-                foreachx,
-                fun setup/1, fun teardown/2,
-                [{{text, Mod}, Fun} || Fun <- Funs]
-            }
-        },
-        {
-            "Uncompressiable attachments",
-            {
-                foreachx,
-                fun setup/1, fun teardown/2,
-                [{{binary, Mod}, Fun} || Fun <- Funs]
-            }
-        }
-    ].
-
-
-
-should_upload_attachment_without_md5({Host, DbName}) ->
-    ?_test(begin
-        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
-        Body = "We all live in a yellow submarine!",
-        Headers = [
-            {"Content-Length", "34"},
-            {"Content-Type", "text/plain"},
-            {"Host", Host}
-        ],
-        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
-        ?assertEqual(201, Code),
-        ?assertEqual(true, get_json(Json, [<<"ok">>]))
-    end).
-
-should_upload_attachment_by_chunks_without_md5({Host, DbName}) ->
-    ?_test(begin
-        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
-        AttData = <<"We all live in a yellow submarine!">>,
-        <<Part1:21/binary, Part2:13/binary>> = AttData,
-        Body = chunked_body([Part1, Part2]),
-        Headers = [
-            {"Content-Type", "text/plain"},
-            {"Transfer-Encoding", "chunked"},
-            {"Host", Host}
-        ],
-        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
-        ?assertEqual(201, Code),
-        ?assertEqual(true, get_json(Json, [<<"ok">>]))
-    end).
-
-should_upload_attachment_with_valid_md5_header({Host, DbName}) ->
-    ?_test(begin
-        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
-        Body = "We all live in a yellow submarine!",
-        Headers = [
-            {"Content-Length", "34"},
-            {"Content-Type", "text/plain"},
-            {"Content-MD5", ?b2l(base64:encode(couch_util:md5(Body)))},
-            {"Host", Host}
-        ],
-        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
-        ?assertEqual(201, Code),
-        ?assertEqual(true, get_json(Json, [<<"ok">>]))
-    end).
-
-should_upload_attachment_by_chunks_with_valid_md5_header({Host, DbName}) ->
-    ?_test(begin
-        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
-        AttData = <<"We all live in a yellow submarine!">>,
-        <<Part1:21/binary, Part2:13/binary>> = AttData,
-        Body = chunked_body([Part1, Part2]),
-        Headers = [
-            {"Content-Type", "text/plain"},
-            {"Content-MD5", ?b2l(base64:encode(couch_util:md5(AttData)))},
-            {"Host", Host},
-            {"Transfer-Encoding", "chunked"}
-        ],
-        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
-        ?assertEqual(201, Code),
-        ?assertEqual(true, get_json(Json, [<<"ok">>]))
-    end).
-
-should_upload_attachment_by_chunks_with_valid_md5_trailer({Host, DbName}) ->
-    ?_test(begin
-        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
-        AttData = <<"We all live in a yellow submarine!">>,
-        <<Part1:21/binary, Part2:13/binary>> = AttData,
-        Body = [chunked_body([Part1, Part2]),
-                "Content-MD5: ", base64:encode(couch_util:md5(AttData)),
-                "\r\n"],
-        Headers = [
-            {"Content-Type", "text/plain"},
-            {"Host", Host},
-            {"Trailer", "Content-MD5"},
-            {"Transfer-Encoding", "chunked"}
-        ],
-        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
-        ?assertEqual(201, Code),
-        ?assertEqual(true, get_json(Json, [<<"ok">>]))
-    end).
-
-should_reject_attachment_with_invalid_md5({Host, DbName}) ->
-    ?_test(begin
-        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
-        Body = "We all live in a yellow submarine!",
-        Headers = [
-            {"Content-Length", "34"},
-            {"Content-Type", "text/plain"},
-            {"Content-MD5", ?b2l(base64:encode(<<"foobar!">>))},
-            {"Host", Host}
-        ],
-        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
-        ?assertEqual(400, Code),
-        ?assertEqual(<<"content_md5_mismatch">>,
-                     get_json(Json, [<<"error">>]))
-    end).
-
-
-should_reject_chunked_attachment_with_invalid_md5({Host, DbName}) ->
-    ?_test(begin
-        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
-        AttData = <<"We all live in a yellow submarine!">>,
-        <<Part1:21/binary, Part2:13/binary>> = AttData,
-        Body = chunked_body([Part1, Part2]),
-        Headers = [
-            {"Content-Type", "text/plain"},
-            {"Content-MD5", ?b2l(base64:encode(<<"foobar!">>))},
-            {"Host", Host},
-            {"Transfer-Encoding", "chunked"}
-        ],
-        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
-        ?assertEqual(400, Code),
-        ?assertEqual(<<"content_md5_mismatch">>,
-                     get_json(Json, [<<"error">>]))
-    end).
-
-should_reject_chunked_attachment_with_invalid_md5_trailer({Host, DbName}) ->
-    ?_test(begin
-        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
-        AttData = <<"We all live in a yellow submarine!">>,
-        <<Part1:21/binary, Part2:13/binary>> = AttData,
-        Body = [chunked_body([Part1, Part2]),
-                "Content-MD5: ", base64:encode(<<"foobar!">>),
-                "\r\n"],
-        Headers = [
-            {"Content-Type", "text/plain"},
-            {"Host", Host},
-            {"Trailer", "Content-MD5"},
-            {"Transfer-Encoding", "chunked"}
-        ],
-        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
-        ?assertEqual(400, Code),
-        ?assertEqual(<<"content_md5_mismatch">>, get_json(Json, [<<"error">>]))
-    end).
-
-should_get_att_without_accept_gzip_encoding(_, {Data, {_, _, AttUrl}}) ->
-    ?_test(begin
-        {ok, Code, Headers, Body} = test_request:get(AttUrl),
-        ?assertEqual(200, Code),
-        ?assertNot(lists:member({"Content-Encoding", "gzip"}, Headers)),
-        ?assertEqual(Data, iolist_to_binary(Body))
-    end).
-
-should_get_att_with_accept_gzip_encoding(compressed, {Data, {_, _, AttUrl}}) ->
-    ?_test(begin
-        {ok, Code, Headers, Body} = test_request:get(
-            AttUrl, [{"Accept-Encoding", "gzip"}]),
-        ?assertEqual(200, Code),
-        ?assert(lists:member({"Content-Encoding", "gzip"}, Headers)),
-        ?assertEqual(Data, zlib:gunzip(iolist_to_binary(Body)))
-    end);
-should_get_att_with_accept_gzip_encoding({text, _}, {Data, {_, _, AttUrl}}) ->
-    ?_test(begin
-        {ok, Code, Headers, Body} = test_request:get(
-            AttUrl, [{"Accept-Encoding", "gzip"}]),
-        ?assertEqual(200, Code),
-        ?assert(lists:member({"Content-Encoding", "gzip"}, Headers)),
-        ?assertEqual(Data, zlib:gunzip(iolist_to_binary(Body)))
-    end);
-should_get_att_with_accept_gzip_encoding({binary, _}, {Data, {_, _, AttUrl}}) ->
-    ?_test(begin
-        {ok, Code, Headers, Body} = test_request:get(
-            AttUrl, [{"Accept-Encoding", "gzip"}]),
-        ?assertEqual(200, Code),
-        ?assertEqual(undefined,
-                     couch_util:get_value("Content-Encoding", Headers)),
-        ?assertEqual(Data, iolist_to_binary(Body))
-    end).
-
-should_get_att_with_accept_deflate_encoding(_, {Data, {_, _, AttUrl}}) ->
-    ?_test(begin
-        {ok, Code, Headers, Body} = test_request:get(
-            AttUrl, [{"Accept-Encoding", "deflate"}]),
-        ?assertEqual(200, Code),
-        ?assertEqual(undefined,
-                     couch_util:get_value("Content-Encoding", Headers)),
-        ?assertEqual(Data, iolist_to_binary(Body))
-    end).
-
-should_return_406_response_on_unsupported_encoding(_, {_, {_, _, AttUrl}}) ->
-    ?_assertEqual(406,
-        begin
-            {ok, Code, _, _} = test_request:get(
-                AttUrl, [{"Accept-Encoding", "deflate, *;q=0"}]),
-            Code
-        end).
-
-should_get_doc_with_att_data(compressed, {Data, {_, DocUrl, _}}) ->
-    ?_test(begin
-        Url = DocUrl ++ "?attachments=true",
-        {ok, Code, _, Body} = test_request:get(
-            Url, [{"Accept", "application/json"}]),
-        ?assertEqual(200, Code),
-        Json = ejson:decode(Body),
-        AttJson = couch_util:get_nested_json_value(
-            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
-        AttData = couch_util:get_nested_json_value(
-            AttJson, [<<"data">>]),
-        ?assertEqual(
-            <<"text/plain">>,
-            couch_util:get_nested_json_value(AttJson,[<<"content_type">>])),
-        ?assertEqual(Data, base64:decode(AttData))
-    end);
-should_get_doc_with_att_data({text, _}, {Data, {_, DocUrl, _}}) ->
-    ?_test(begin
-        Url = DocUrl ++ "?attachments=true",
-        {ok, Code, _, Body} = test_request:get(
-            Url, [{"Accept", "application/json"}]),
-        ?assertEqual(200, Code),
-        Json = ejson:decode(Body),
-        AttJson = couch_util:get_nested_json_value(
-            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
-        AttData = couch_util:get_nested_json_value(
-            AttJson, [<<"data">>]),
-        ?assertEqual(
-            <<"text/plain">>,
-            couch_util:get_nested_json_value(AttJson,[<<"content_type">>])),
-        ?assertEqual(Data, base64:decode(AttData))
-    end);
-should_get_doc_with_att_data({binary, _}, {Data, {_, DocUrl, _}}) ->
-    ?_test(begin
-        Url = DocUrl ++ "?attachments=true",
-        {ok, Code, _, Body} = test_request:get(
-            Url, [{"Accept", "application/json"}]),
-        ?assertEqual(200, Code),
-        Json = ejson:decode(Body),
-        AttJson = couch_util:get_nested_json_value(
-            Json, [<<"_attachments">>, ?ATT_BIN_NAME]),
-        AttData = couch_util:get_nested_json_value(
-            AttJson, [<<"data">>]),
-        ?assertEqual(
-            <<"image/png">>,
-            couch_util:get_nested_json_value(AttJson,[<<"content_type">>])),
-        ?assertEqual(Data, base64:decode(AttData))
-    end).
-
-should_get_doc_with_att_data_stub(compressed, {Data, {_, DocUrl, _}}) ->
-    ?_test(begin
-        Url = DocUrl ++ "?att_encoding_info=true",
-        {ok, Code, _, Body} = test_request:get(
-            Url, [{"Accept", "application/json"}]),
-        ?assertEqual(200, Code),
-        Json = ejson:decode(Body),
-        {AttJson} = couch_util:get_nested_json_value(
-            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
-        ?assertEqual(<<"gzip">>,
-                     couch_util:get_value(<<"encoding">>, AttJson)),
-        AttLength = couch_util:get_value(<<"length">>, AttJson),
-        EncLength = couch_util:get_value(<<"encoded_length">>, AttJson),
-        ?assertEqual(AttLength, EncLength),
-        ?assertEqual(iolist_size(zlib:gzip(Data)), AttLength)
-    end);
-should_get_doc_with_att_data_stub({text, _}, {Data, {_, DocUrl, _}}) ->
-    ?_test(begin
-        Url = DocUrl ++ "?att_encoding_info=true",
-        {ok, Code, _, Body} = test_request:get(
-            Url, [{"Accept", "application/json"}]),
-        ?assertEqual(200, Code),
-        Json = ejson:decode(Body),
-        {AttJson} = couch_util:get_nested_json_value(
-            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
-        ?assertEqual(<<"gzip">>,
-                     couch_util:get_value(<<"encoding">>, AttJson)),
-        AttEncLength = iolist_size(gzip(Data)),
-        ?assertEqual(AttEncLength,
-                     couch_util:get_value(<<"encoded_length">>, AttJson)),
-        ?assertEqual(byte_size(Data),
-                     couch_util:get_value(<<"length">>, AttJson))
-    end);
-should_get_doc_with_att_data_stub({binary, _}, {Data, {_, DocUrl, _}}) ->
-    ?_test(begin
-        Url = DocUrl ++ "?att_encoding_info=true",
-        {ok, Code, _, Body} = test_request:get(
-            Url, [{"Accept", "application/json"}]),
-        ?assertEqual(200, Code),
-        Json = ejson:decode(Body),
-        {AttJson} = couch_util:get_nested_json_value(
-            Json, [<<"_attachments">>, ?ATT_BIN_NAME]),
-        ?assertEqual(undefined,
-                     couch_util:get_value(<<"encoding">>, AttJson)),
-        ?assertEqual(undefined,
-                     couch_util:get_value(<<"encoded_length">>, AttJson)),
-        ?assertEqual(byte_size(Data),
-                     couch_util:get_value(<<"length">>, AttJson))
-    end).
-
-should_not_create_compressed_att_with_deflate_encoding({Host, DbName}) ->
-    ?_assertEqual(415,
-        begin
-            HttpHost = "http://" ++ Host,
-            AttUrl = string:join([HttpHost, DbName, ?docid(), "file.txt"], "/"),
-            {ok, Data} = file:read_file(?FIXTURE_TXT),
-            Body = zlib:compress(Data),
-            Headers = [
-                {"Content-Encoding", "deflate"},
-                {"Content-Type", "text/plain"}
-            ],
-            {ok, Code, _, _} = test_request:put(AttUrl, Headers, Body),
-            Code
-        end).
-
-should_not_create_compressed_att_with_compress_encoding({Host, DbName}) ->
-    % Note: As of OTP R13B04, it seems there's no LZW compression
-    % (i.e. UNIX compress utility implementation) lib in OTP.
-    % However there's a simple working Erlang implementation at:
-    % http://scienceblogs.com/goodmath/2008/01/simple_lempelziv_compression_i.php
-    ?_assertEqual(415,
-        begin
-            HttpHost = "http://" ++ Host,
-            AttUrl = string:join([HttpHost, DbName, ?docid(), "file.txt"], "/"),
-            {ok, Data} = file:read_file(?FIXTURE_TXT),
-            Headers = [
-                {"Content-Encoding", "compress"},
-                {"Content-Type", "text/plain"}
-            ],
-            {ok, Code, _, _} = test_request:put(AttUrl, Headers, Data),
-            Code
-        end).
-
-should_create_compressible_att_with_ctype_params({Host, DbName}) ->
-    {timeout, ?TIMEOUT_EUNIT, ?_test(begin
-        HttpHost = "http://" ++ Host,
-        DocUrl = string:join([HttpHost, DbName, ?docid()], "/"),
-        AttUrl = string:join([DocUrl, ?b2l(?ATT_TXT_NAME)], "/"),
-        {ok, Data} = file:read_file(?FIXTURE_TXT),
-        Headers = [{"Content-Type", "text/plain; charset=UTF-8"}],
-        {ok, Code0, _, _} = test_request:put(AttUrl, Headers, Data),
-        ?assertEqual(201, Code0),
-
-        {ok, Code1, _, Body} = test_request:get(
-            DocUrl ++ "?att_encoding_info=true"),
-        ?assertEqual(200, Code1),
-        Json = ejson:decode(Body),
-        {AttJson} = couch_util:get_nested_json_value(
-            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
-        ?assertEqual(<<"gzip">>,
-                     couch_util:get_value(<<"encoding">>, AttJson)),
-        AttEncLength = iolist_size(gzip(Data)),
-        ?assertEqual(AttEncLength,
-                     couch_util:get_value(<<"encoded_length">>, AttJson)),
-        ?assertEqual(byte_size(Data),
-                     couch_util:get_value(<<"length">>, AttJson))
-    end)}.
-
-
-get_json(Json, Path) ->
-    couch_util:get_nested_json_value(Json, Path).
-
-to_hex(Val) ->
-    to_hex(Val, []).
-
-to_hex(0, Acc) ->
-    Acc;
-to_hex(Val, Acc) ->
-    to_hex(Val div 16, [hex_char(Val rem 16) | Acc]).
-
-hex_char(V) when V < 10 -> $0 + V;
-hex_char(V) -> $A + V - 10.
-
-chunked_body(Chunks) ->
-    chunked_body(Chunks, []).
-
-chunked_body([], Acc) ->
-    iolist_to_binary(lists:reverse(Acc, "0\r\n"));
-chunked_body([Chunk | Rest], Acc) ->
-    Size = to_hex(size(Chunk)),
-    chunked_body(Rest, ["\r\n", Chunk, "\r\n", Size | Acc]).
-
-get_socket() ->
-    Options = [binary, {packet, 0}, {active, false}],
-    Addr = couch_config:get("httpd", "bind_address", any),
-    Port = mochiweb_socket_server:get(couch_httpd, port),
-    {ok, Sock} = gen_tcp:connect(Addr, Port, Options),
-    Sock.
-
-request(Method, Url, Headers, Body) ->
-    RequestHead = [Method, " ", Url, " HTTP/1.1"],
-    RequestHeaders = [[string:join([Key, Value], ": "), "\r\n"]
-                      || {Key, Value} <- Headers],
-    Request = [RequestHead, "\r\n", RequestHeaders, "\r\n", Body, "\r\n"],
-    Sock = get_socket(),
-    gen_tcp:send(Sock, list_to_binary(lists:flatten(Request))),
-    timer:sleep(?TIMEWAIT),  % must wait to receive complete response
-    {ok, R} = gen_tcp:recv(Sock, 0),
-    gen_tcp:close(Sock),
-    [Header, Body1] = re:split(R, "\r\n\r\n", [{return, binary}]),
-    {ok, {http_response, _, Code, _}, _} =
-        erlang:decode_packet(http, Header, []),
-    Json = ejson:decode(Body1),
-    {ok, Code, Json}.
-
-create_standalone_text_att(Host, DbName) ->
-    {ok, Data} = file:read_file(?FIXTURE_TXT),
-    Url = string:join([Host, DbName, "doc", ?b2l(?ATT_TXT_NAME)], "/"),
-    {ok, Code, _Headers, _Body} = test_request:put(
-        Url, [{"Content-Type", "text/plain"}], Data),
-    ?assertEqual(201, Code),
-    Url.
-
-create_standalone_png_att(Host, DbName) ->
-    {ok, Data} = file:read_file(?FIXTURE_PNG),
-    Url = string:join([Host, DbName, "doc", ?b2l(?ATT_BIN_NAME)], "/"),
-    {ok, Code, _Headers, _Body} = test_request:put(
-        Url, [{"Content-Type", "image/png"}], Data),
-    ?assertEqual(201, Code),
-    Url.
-
-create_inline_text_att(Host, DbName) ->
-    {ok, Data} = file:read_file(?FIXTURE_TXT),
-    Url = string:join([Host, DbName, "doc"], "/"),
-    Doc = {[
-        {<<"_attachments">>, {[
-            {?ATT_TXT_NAME, {[
-                {<<"content_type">>, <<"text/plain">>},
-                {<<"data">>, base64:encode(Data)}
-            ]}
-        }]}}
-    ]},
-    {ok, Code, _Headers, _Body} = test_request:put(
-        Url, [{"Content-Type", "application/json"}], ejson:encode(Doc)),
-    ?assertEqual(201, Code),
-    string:join([Url, ?b2l(?ATT_TXT_NAME)], "/").
-
-create_inline_png_att(Host, DbName) ->
-    {ok, Data} = file:read_file(?FIXTURE_PNG),
-    Url = string:join([Host, DbName, "doc"], "/"),
-    Doc = {[
-        {<<"_attachments">>, {[
-            {?ATT_BIN_NAME, {[
-                {<<"content_type">>, <<"image/png">>},
-                {<<"data">>, base64:encode(Data)}
-            ]}
-        }]}}
-    ]},
-    {ok, Code, _Headers, _Body} = test_request:put(
-        Url, [{"Content-Type", "application/json"}], ejson:encode(Doc)),
-    ?assertEqual(201, Code),
-    string:join([Url, ?b2l(?ATT_BIN_NAME)], "/").
-
-create_already_compressed_att(Host, DbName) ->
-    {ok, Data} = file:read_file(?FIXTURE_TXT),
-    Url = string:join([Host, DbName, "doc", ?b2l(?ATT_TXT_NAME)], "/"),
-    {ok, Code, _Headers, _Body} = test_request:put(
-        Url, [{"Content-Type", "text/plain"}, {"Content-Encoding", "gzip"}],
-        zlib:gzip(Data)),
-    ?assertEqual(201, Code),
-    Url.
-
-gzip(Data) ->
-    Z = zlib:open(),
-    ok = zlib:deflateInit(Z, ?COMPRESSION_LEVEL, deflated, 16 + 15, 8, default),
-    zlib:deflate(Z, Data),
-    Last = zlib:deflate(Z, [], finish),
-    ok = zlib:deflateEnd(Z),
-    ok = zlib:close(Z),
-    Last.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_compaction_daemon.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_compaction_daemon.erl b/test/couchdb/couchdb_compaction_daemon.erl
deleted file mode 100644
index 725a97b..0000000
--- a/test/couchdb/couchdb_compaction_daemon.erl
+++ /dev/null
@@ -1,231 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_compaction_daemon).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
--define(DELAY, 100).
--define(TIMEOUT, 30000).
--define(TIMEOUT_S, ?TIMEOUT div 1000).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    couch_config:set("compaction_daemon", "check_interval", "3", false),
-    couch_config:set("compaction_daemon", "min_file_size", "100000", false),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-setup() ->
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
-    create_design_doc(Db),
-    ok = couch_db:close(Db),
-    DbName.
-
-teardown(DbName) ->
-    Configs = couch_config:get("compactions"),
-    lists:foreach(
-        fun({Key, _}) ->
-            ok = couch_config:delete("compactions", Key, false)
-        end,
-        Configs),
-    couch_server:delete(DbName, [?ADMIN_USER]),
-    ok.
-
-
-compaction_daemon_test_() ->
-    {
-        "Compaction daemon tests",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            {
-                foreach,
-                fun setup/0, fun teardown/1,
-                [
-                    fun should_compact_by_default_rule/1,
-                    fun should_compact_by_dbname_rule/1
-                ]
-            }
-        }
-    }.
-
-
-should_compact_by_default_rule(DbName) ->
-    {timeout, ?TIMEOUT_S, ?_test(begin
-        {ok, Db} = couch_db:open_int(DbName, []),
-        populate(DbName, 70, 70, 200 * 1024),
-
-        {_, DbFileSize} = get_db_frag(DbName),
-        {_, ViewFileSize} = get_view_frag(DbName),
-
-        ok = couch_config:set("compactions", "_default",
-            "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"70%\"}]",
-            false),
-
-        ok = timer:sleep(4000), % something >= check_interval
-        wait_compaction_finished(DbName),
-        ok = couch_config:delete("compactions", "_default", false),
-
-        {DbFrag2, DbFileSize2} = get_db_frag(DbName),
-        {ViewFrag2, ViewFileSize2} = get_view_frag(DbName),
-
-        ?assert(DbFrag2 < 70),
-        ?assert(ViewFrag2 < 70),
-
-        ?assert(DbFileSize > DbFileSize2),
-        ?assert(ViewFileSize > ViewFileSize2),
-
-        ?assert(couch_db:is_idle(Db)),
-        ok = couch_db:close(Db)
-    end)}.
-
-should_compact_by_dbname_rule(DbName) ->
-    {timeout, ?TIMEOUT_S, ?_test(begin
-        {ok, Db} = couch_db:open_int(DbName, []),
-        populate(DbName, 70, 70, 200 * 1024),
-
-        {_, DbFileSize} = get_db_frag(DbName),
-        {_, ViewFileSize} = get_view_frag(DbName),
-
-        ok = couch_config:set("compactions", ?b2l(DbName),
-            "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"70%\"}]",
-            false),
-
-        ok = timer:sleep(4000), % something >= check_interval
-        wait_compaction_finished(DbName),
-        ok = couch_config:delete("compactions", ?b2l(DbName), false),
-
-        {DbFrag2, DbFileSize2} = get_db_frag(DbName),
-        {ViewFrag2, ViewFileSize2} = get_view_frag(DbName),
-
-        ?assert(DbFrag2 < 70),
-        ?assert(ViewFrag2 < 70),
-
-        ?assert(DbFileSize > DbFileSize2),
-        ?assert(ViewFileSize > ViewFileSize2),
-
-        ?assert(couch_db:is_idle(Db)),
-        ok = couch_db:close(Db)
-    end)}.
-
-
-create_design_doc(Db) ->
-    DDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"_design/foo">>},
-        {<<"language">>, <<"javascript">>},
-        {<<"views">>, {[
-            {<<"foo">>, {[
-                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
-            ]}},
-            {<<"foo2">>, {[
-                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
-            ]}},
-            {<<"foo3">>, {[
-                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
-            ]}}
-        ]}}
-    ]}),
-    {ok, _} = couch_db:update_docs(Db, [DDoc]),
-    {ok, _} = couch_db:ensure_full_commit(Db),
-    ok.
-
-populate(DbName, DbFrag, ViewFrag, MinFileSize) ->
-    {CurDbFrag, DbFileSize} = get_db_frag(DbName),
-    {CurViewFrag, ViewFileSize} = get_view_frag(DbName),
-    populate(DbName, DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag,
-             lists:min([DbFileSize, ViewFileSize])).
-
-populate(_Db, DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag, FileSize)
-    when CurDbFrag >= DbFrag, CurViewFrag >= ViewFrag, FileSize >= MinFileSize ->
-    ok;
-populate(DbName, DbFrag, ViewFrag, MinFileSize, _, _, _) ->
-    update(DbName),
-    {CurDbFrag, DbFileSize} = get_db_frag(DbName),
-    {CurViewFrag, ViewFileSize} = get_view_frag(DbName),
-    populate(DbName, DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag,
-             lists:min([DbFileSize, ViewFileSize])).
-
-update(DbName) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    lists:foreach(fun(_) ->
-        Doc = couch_doc:from_json_obj({[{<<"_id">>, couch_uuids:new()}]}),
-        {ok, _} = couch_db:update_docs(Db, [Doc]),
-        query_view(Db#db.name)
-    end, lists:seq(1, 200)),
-    couch_db:close(Db).
-
-db_url(DbName) ->
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
-    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
-    "http://" ++ Addr ++ ":" ++ Port ++ "/" ++ ?b2l(DbName).
-
-query_view(DbName) ->
-    {ok, Code, _Headers, _Body} = test_request:get(
-        db_url(DbName) ++ "/_design/foo/_view/foo"),
-    ?assertEqual(200, Code).
-
-get_db_frag(DbName) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    {ok, Info} = couch_db:get_db_info(Db),
-    couch_db:close(Db),
-    FileSize = couch_util:get_value(disk_size, Info),
-    DataSize = couch_util:get_value(data_size, Info),
-    {round((FileSize - DataSize) / FileSize * 100), FileSize}.
-
-get_view_frag(DbName) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    {ok, Info} = couch_mrview:get_info(Db, <<"_design/foo">>),
-    couch_db:close(Db),
-    FileSize = couch_util:get_value(disk_size, Info),
-    DataSize = couch_util:get_value(data_size, Info),
-    {round((FileSize - DataSize) / FileSize * 100), FileSize}.
-
-wait_compaction_finished(DbName) ->
-    Parent = self(),
-    Loop = spawn_link(fun() -> wait_loop(DbName, Parent) end),
-    receive
-        {done, Loop} ->
-            ok
-    after ?TIMEOUT ->
-        erlang:error(
-            {assertion_failed,
-             [{module, ?MODULE}, {line, ?LINE},
-              {reason, "Compaction timeout"}]})
-    end.
-
-wait_loop(DbName, Parent) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    {ok, DbInfo} = couch_db:get_db_info(Db),
-    {ok, ViewInfo} = couch_mrview:get_info(Db, <<"_design/foo">>),
-    couch_db:close(Db),
-    case (couch_util:get_value(compact_running, ViewInfo) =:= true) orelse
-        (couch_util:get_value(compact_running, DbInfo) =:= true) of
-        false ->
-            Parent ! {done, self()};
-        true ->
-            ok = timer:sleep(?DELAY),
-            wait_loop(DbName, Parent)
-    end.


[14/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 120-stats-collect.t etap test suite to eunit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/b3abe4de
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/b3abe4de
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/b3abe4de

Branch: refs/heads/1963-eunit-bigcouch
Commit: b3abe4ded16075865da5f4878dd28b361031935a
Parents: 7d7a742
Author: Alexander Shorin <kx...@apache.org>
Authored: Mon May 26 23:57:38 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couch_stats_tests.erl | 193 ++++++++++++++++++++++++++++++++
 1 file changed, 193 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/b3abe4de/test/couchdb/couch_stats_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_stats_tests.erl b/test/couchdb/couch_stats_tests.erl
new file mode 100644
index 0000000..aaaa687
--- /dev/null
+++ b/test/couchdb/couch_stats_tests.erl
@@ -0,0 +1,193 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_stats_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(TIMEOUT, 1000).
+-define(SLEEPTIME, 100).
+
+
+setup_collector() ->
+    couch_stats_collector:start(),
+    ok.
+
+teardown_collector(_) ->
+    couch_stats_collector:stop(),
+    ok.
+
+
+couch_stats_collector_test_() ->
+    {
+        "CouchDB stats collector tests",
+        {
+            foreach,
+            fun setup_collector/0, fun teardown_collector/1,
+            [
+                should_increment_counter(),
+                should_decrement_counter(),
+                should_increment_and_decrement_counter(),
+                should_record_absolute_values(),
+                should_clear_absolute_values(),
+                should_track_process_count(),
+                should_increment_counter_multiple_times_per_pid(),
+                should_decrement_counter_on_process_exit(),
+                should_decrement_for_each_track_process_count_call_on_exit(),
+                should_return_all_counters_and_absolute_values(),
+                should_return_incremental_counters(),
+                should_return_absolute_values()
+            ]
+        }
+    }.
+
+
+should_increment_counter() ->
+    ?_assertEqual(100,
+        begin
+            AddCount = fun() -> couch_stats_collector:increment(foo) end,
+            repeat(AddCount, 100),
+            couch_stats_collector:get(foo)
+        end).
+
+should_decrement_counter() ->
+    ?_assertEqual(67,
+        begin
+            AddCount = fun() -> couch_stats_collector:increment(foo) end,
+            RemCount = fun() -> couch_stats_collector:decrement(foo) end,
+            repeat(AddCount, 100),
+            repeat(RemCount, 33),
+            couch_stats_collector:get(foo)
+        end).
+
+should_increment_and_decrement_counter() ->
+    ?_assertEqual(0,
+        begin
+            AddCount = fun() -> couch_stats_collector:increment(foo) end,
+            RemCount = fun() -> couch_stats_collector:decrement(foo) end,
+            repeat(AddCount, 100),
+            repeat(RemCount, 25),
+            repeat(AddCount, 10),
+            repeat(RemCount, 5),
+            repeat(RemCount, 80),
+            couch_stats_collector:get(foo)
+        end).
+
+should_record_absolute_values() ->
+    ?_assertEqual(lists:seq(1, 15),
+        begin
+            lists:map(fun(Val) ->
+                couch_stats_collector:record(bar, Val)
+            end, lists:seq(1, 15)),
+            couch_stats_collector:get(bar)
+        end).
+
+should_clear_absolute_values() ->
+    ?_assertEqual(nil,
+        begin
+            lists:map(fun(Val) ->
+                couch_stats_collector:record(bar, Val)
+            end, lists:seq(1, 15)),
+            couch_stats_collector:clear(bar),
+            couch_stats_collector:get(bar)
+        end).
+
+should_track_process_count() ->
+    ?_assertMatch({_, 1}, spawn_and_count(1)).
+
+should_increment_counter_multiple_times_per_pid() ->
+    ?_assertMatch({_, 3}, spawn_and_count(3)).
+
+should_decrement_counter_on_process_exit() ->
+    ?_assertEqual(2,
+        begin
+            {Pid, 1} = spawn_and_count(1),
+            spawn_and_count(2),
+            RefMon = erlang:monitor(process, Pid),
+            Pid ! sepuku,
+            receive
+                {'DOWN', RefMon, _, _, _} -> ok
+            after ?TIMEOUT ->
+                throw(timeout)
+            end,
+            % sleep for awhile to let collector handle the updates
+            % suddenly, it couldn't notice process death instantly
+            timer:sleep(?SLEEPTIME),
+            couch_stats_collector:get(hoopla)
+        end).
+
+should_decrement_for_each_track_process_count_call_on_exit() ->
+    ?_assertEqual(2,
+        begin
+            {_, 2} = spawn_and_count(2),
+            {Pid, 6} = spawn_and_count(4),
+            RefMon = erlang:monitor(process, Pid),
+            Pid ! sepuku,
+            receive
+                {'DOWN', RefMon, _, _, _} -> ok
+            after ?TIMEOUT ->
+                throw(timeout)
+            end,
+            timer:sleep(?SLEEPTIME),
+            couch_stats_collector:get(hoopla)
+        end).
+
+should_return_all_counters_and_absolute_values() ->
+    ?_assertEqual([{bar,[1.0,0.0]}, {foo,1}],
+        begin
+            couch_stats_collector:record(bar, 0.0),
+            couch_stats_collector:record(bar, 1.0),
+            couch_stats_collector:increment(foo),
+            lists:sort(couch_stats_collector:all())
+        end).
+
+should_return_incremental_counters() ->
+    ?_assertEqual([{foo,1}],
+        begin
+            couch_stats_collector:record(bar, 0.0),
+            couch_stats_collector:record(bar, 1.0),
+            couch_stats_collector:increment(foo),
+            lists:sort(couch_stats_collector:all(incremental))
+        end).
+
+should_return_absolute_values() ->
+    ?_assertEqual([{bar,[1.0,0.0]}, {zing, "Z"}],
+        begin
+            couch_stats_collector:record(bar, 0.0),
+            couch_stats_collector:record(bar, 1.0),
+            couch_stats_collector:record(zing, 90),
+            couch_stats_collector:increment(foo),
+            lists:sort(couch_stats_collector:all(absolute))
+        end).
+
+
+spawn_and_count(N) ->
+    Self = self(),
+    Pid = spawn(fun() ->
+        lists:foreach(
+            fun(_) ->
+                couch_stats_collector:track_process_count(hoopla)
+            end, lists:seq(1,N)),
+        Self ! reporting,
+        receive
+            sepuku -> ok
+        end
+    end),
+    receive reporting -> ok end,
+    {Pid, couch_stats_collector:get(hoopla)}.
+
+repeat(_, 0) ->
+    ok;
+repeat(Fun, Count) ->
+    Fun(),
+    repeat(Fun, Count-1).


[38/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Add couch_eunit.hrl


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/de1812d8
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/de1812d8
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/de1812d8

Branch: refs/heads/1963-eunit-bigcouch
Commit: de1812d860cdecc5ff09517f4e727bb3cfc58c2c
Parents: d444897
Author: Russell Branca <ch...@apache.org>
Authored: Fri Aug 15 13:12:06 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 13:49:02 2014 -0700

----------------------------------------------------------------------
 include/couch_eunit.hrl                 | 53 ++++++++++++++++++++++++++++
 test/couch_auth_cache_tests.erl         |  1 -
 test/couch_changes_tests.erl            |  1 -
 test/couchdb_compaction_daemon.erl      |  1 -
 test/couchdb_cors_tests.erl             |  1 -
 test/couchdb_file_compression_tests.erl |  1 -
 test/couchdb_update_conflicts_tests.erl |  1 -
 test/couchdb_vhosts_tests.erl           |  1 -
 test/couchdb_views_tests.erl            |  1 -
 9 files changed, 53 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/de1812d8/include/couch_eunit.hrl
----------------------------------------------------------------------
diff --git a/include/couch_eunit.hrl b/include/couch_eunit.hrl
new file mode 100644
index 0000000..f2e6eac
--- /dev/null
+++ b/include/couch_eunit.hrl
@@ -0,0 +1,53 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-include_lib("eunit/include/eunit.hrl").
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+
+-define(BUILDDIR,
+    fun() ->
+        case os:getenv("BUILDDIR") of
+            false ->
+                throw("BUILDDIR environment variable must be set");
+            Dir ->
+                Dir
+        end
+    end).
+-define(CONFIG_CHAIN, [
+    filename:join([?BUILDDIR(), "etc", "couchdb", "default_dev.ini"]),
+    filename:join([?BUILDDIR(), "etc", "couchdb", "local_dev.ini"]),
+    filename:join([?BUILDDIR(), "etc", "couchdb", "eunit.ini"])]).
+-define(FIXTURESDIR,
+    filename:join([?SOURCEDIR, "src", "couch", "test", "fixtures"])).
+-define(TEMPDIR,
+    filename:join([?BUILDDIR(), "test", "temp"])).
+
+-define(tempfile,
+    fun() ->
+        {A, B, C} = erlang:now(),
+        N = node(),
+        FileName = lists:flatten(io_lib:format("~p-~p.~p.~p", [N, A, B, C])),
+        filename:join([?TEMPDIR, FileName])
+    end).
+-define(tempdb,
+    fun() ->
+            Nums = tuple_to_list(erlang:now()),
+            Prefix = "eunit-test-db",
+            Suffix = lists:concat([integer_to_list(Num) || Num <- Nums]),
+            list_to_binary(Prefix ++ "-" ++ Suffix)
+    end).
+-define(docid,
+    fun() ->
+        {A, B, C} = erlang:now(),
+        lists:flatten(io_lib:format("~p~p~p", [A, B, C]))
+    end).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/de1812d8/test/couch_auth_cache_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_auth_cache_tests.erl b/test/couch_auth_cache_tests.erl
index 83531ff..afad552 100644
--- a/test/couch_auth_cache_tests.erl
+++ b/test/couch_auth_cache_tests.erl
@@ -15,7 +15,6 @@
 -include_lib("couch/include/couch_eunit.hrl").
 -include_lib("couch/include/couch_db.hrl").
 
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(SALT, <<"SALT">>).
 -define(TIMEOUT, 1000).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/de1812d8/test/couch_changes_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_changes_tests.erl b/test/couch_changes_tests.erl
index 3ae1b52..64754fa 100644
--- a/test/couch_changes_tests.erl
+++ b/test/couch_changes_tests.erl
@@ -15,7 +15,6 @@
 -include_lib("couch/include/couch_eunit.hrl").
 -include_lib("couch/include/couch_db.hrl").
 
--define(ADMIN_USER, {user_ctx, #user_ctx{roles = [<<"_admin">>]}}).
 -define(TIMEOUT, 3000).
 -define(TEST_TIMEOUT, 10000).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/de1812d8/test/couchdb_compaction_daemon.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_compaction_daemon.erl b/test/couchdb_compaction_daemon.erl
index 24c5d7b..0d2195b 100644
--- a/test/couchdb_compaction_daemon.erl
+++ b/test/couchdb_compaction_daemon.erl
@@ -15,7 +15,6 @@
 -include_lib("couch/include/couch_eunit.hrl").
 -include_lib("couch/include/couch_db.hrl").
 
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(DELAY, 100).
 -define(TIMEOUT, 30000).
 -define(TIMEOUT_S, ?TIMEOUT div 1000).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/de1812d8/test/couchdb_cors_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_cors_tests.erl b/test/couchdb_cors_tests.erl
index 52d1797..cfdbd63 100644
--- a/test/couchdb_cors_tests.erl
+++ b/test/couchdb_cors_tests.erl
@@ -16,7 +16,6 @@
 -include_lib("couch/include/couch_db.hrl").
 
 
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(SUPPORTED_METHODS,
         "GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, COPY, OPTIONS").
 -define(TIMEOUT, 1000).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/de1812d8/test/couchdb_file_compression_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_file_compression_tests.erl b/test/couchdb_file_compression_tests.erl
index 5b12882..8f590b9 100644
--- a/test/couchdb_file_compression_tests.erl
+++ b/test/couchdb_file_compression_tests.erl
@@ -15,7 +15,6 @@
 -include_lib("couch/include/couch_eunit.hrl").
 -include_lib("couch/include/couch_db.hrl").
 
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(DDOC_ID, <<"_design/test">>).
 -define(DOCS_COUNT, 5000).
 -define(TIMEOUT, 30000).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/de1812d8/test/couchdb_update_conflicts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_update_conflicts_tests.erl b/test/couchdb_update_conflicts_tests.erl
index 7ac1187..58d6014 100644
--- a/test/couchdb_update_conflicts_tests.erl
+++ b/test/couchdb_update_conflicts_tests.erl
@@ -16,7 +16,6 @@
 -include_lib("couch/include/couch_db.hrl").
 
 -define(i2l(I), integer_to_list(I)).
--define(ADMIN_USER, {userctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(DOC_ID, <<"foobar">>).
 -define(NUM_CLIENTS, [100, 500, 1000, 2000, 5000, 10000]).
 -define(TIMEOUT, 10000).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/de1812d8/test/couchdb_vhosts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_vhosts_tests.erl b/test/couchdb_vhosts_tests.erl
index 321012f..5eb266c 100644
--- a/test/couchdb_vhosts_tests.erl
+++ b/test/couchdb_vhosts_tests.erl
@@ -15,7 +15,6 @@
 -include_lib("couch/include/couch_eunit.hrl").
 -include_lib("couch/include/couch_db.hrl").
 
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(TIMEOUT, 1000).
 -define(iofmt(S, A), lists:flatten(io_lib:format(S, A))).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/de1812d8/test/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_views_tests.erl b/test/couchdb_views_tests.erl
index 03fb9cc..012d12e 100644
--- a/test/couchdb_views_tests.erl
+++ b/test/couchdb_views_tests.erl
@@ -16,7 +16,6 @@
 -include_lib("couch/include/couch_db.hrl").
 -include_lib("couch_mrview/include/couch_mrview.hrl").
 
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(DELAY, 100).
 -define(TIMEOUT, 1000).
 


[20/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 220-compaction-daemon.t etap test suite to eunit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/a701f76b
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/a701f76b
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/a701f76b

Branch: refs/heads/1963-eunit-bigcouch
Commit: a701f76bcd0d500ede7e1f3c781e2e5553accfd2
Parents: 7a6d78e
Author: Alexander Shorin <kx...@apache.org>
Authored: Mon Jun 9 20:15:37 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:40 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_compaction_daemon.erl | 231 ++++++++++++++++++++++++
 1 file changed, 231 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a701f76b/test/couchdb/couchdb_compaction_daemon.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_compaction_daemon.erl b/test/couchdb/couchdb_compaction_daemon.erl
new file mode 100644
index 0000000..725a97b
--- /dev/null
+++ b/test/couchdb/couchdb_compaction_daemon.erl
@@ -0,0 +1,231 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_compaction_daemon).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(DELAY, 100).
+-define(TIMEOUT, 30000).
+-define(TIMEOUT_S, ?TIMEOUT div 1000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    couch_config:set("compaction_daemon", "check_interval", "3", false),
+    couch_config:set("compaction_daemon", "min_file_size", "100000", false),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    create_design_doc(Db),
+    ok = couch_db:close(Db),
+    DbName.
+
+teardown(DbName) ->
+    Configs = couch_config:get("compactions"),
+    lists:foreach(
+        fun({Key, _}) ->
+            ok = couch_config:delete("compactions", Key, false)
+        end,
+        Configs),
+    couch_server:delete(DbName, [?ADMIN_USER]),
+    ok.
+
+
+compaction_daemon_test_() ->
+    {
+        "Compaction daemon tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_compact_by_default_rule/1,
+                    fun should_compact_by_dbname_rule/1
+                ]
+            }
+        }
+    }.
+
+
+should_compact_by_default_rule(DbName) ->
+    {timeout, ?TIMEOUT_S, ?_test(begin
+        {ok, Db} = couch_db:open_int(DbName, []),
+        populate(DbName, 70, 70, 200 * 1024),
+
+        {_, DbFileSize} = get_db_frag(DbName),
+        {_, ViewFileSize} = get_view_frag(DbName),
+
+        ok = couch_config:set("compactions", "_default",
+            "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"70%\"}]",
+            false),
+
+        ok = timer:sleep(4000), % something >= check_interval
+        wait_compaction_finished(DbName),
+        ok = couch_config:delete("compactions", "_default", false),
+
+        {DbFrag2, DbFileSize2} = get_db_frag(DbName),
+        {ViewFrag2, ViewFileSize2} = get_view_frag(DbName),
+
+        ?assert(DbFrag2 < 70),
+        ?assert(ViewFrag2 < 70),
+
+        ?assert(DbFileSize > DbFileSize2),
+        ?assert(ViewFileSize > ViewFileSize2),
+
+        ?assert(couch_db:is_idle(Db)),
+        ok = couch_db:close(Db)
+    end)}.
+
+should_compact_by_dbname_rule(DbName) ->
+    {timeout, ?TIMEOUT_S, ?_test(begin
+        {ok, Db} = couch_db:open_int(DbName, []),
+        populate(DbName, 70, 70, 200 * 1024),
+
+        {_, DbFileSize} = get_db_frag(DbName),
+        {_, ViewFileSize} = get_view_frag(DbName),
+
+        ok = couch_config:set("compactions", ?b2l(DbName),
+            "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"70%\"}]",
+            false),
+
+        ok = timer:sleep(4000), % something >= check_interval
+        wait_compaction_finished(DbName),
+        ok = couch_config:delete("compactions", ?b2l(DbName), false),
+
+        {DbFrag2, DbFileSize2} = get_db_frag(DbName),
+        {ViewFrag2, ViewFileSize2} = get_view_frag(DbName),
+
+        ?assert(DbFrag2 < 70),
+        ?assert(ViewFrag2 < 70),
+
+        ?assert(DbFileSize > DbFileSize2),
+        ?assert(ViewFileSize > ViewFileSize2),
+
+        ?assert(couch_db:is_idle(Db)),
+        ok = couch_db:close(Db)
+    end)}.
+
+
+create_design_doc(Db) ->
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"_design/foo">>},
+        {<<"language">>, <<"javascript">>},
+        {<<"views">>, {[
+            {<<"foo">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}},
+            {<<"foo2">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}},
+            {<<"foo3">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}}
+        ]}}
+    ]}),
+    {ok, _} = couch_db:update_docs(Db, [DDoc]),
+    {ok, _} = couch_db:ensure_full_commit(Db),
+    ok.
+
+populate(DbName, DbFrag, ViewFrag, MinFileSize) ->
+    {CurDbFrag, DbFileSize} = get_db_frag(DbName),
+    {CurViewFrag, ViewFileSize} = get_view_frag(DbName),
+    populate(DbName, DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag,
+             lists:min([DbFileSize, ViewFileSize])).
+
+populate(_Db, DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag, FileSize)
+    when CurDbFrag >= DbFrag, CurViewFrag >= ViewFrag, FileSize >= MinFileSize ->
+    ok;
+populate(DbName, DbFrag, ViewFrag, MinFileSize, _, _, _) ->
+    update(DbName),
+    {CurDbFrag, DbFileSize} = get_db_frag(DbName),
+    {CurViewFrag, ViewFileSize} = get_view_frag(DbName),
+    populate(DbName, DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag,
+             lists:min([DbFileSize, ViewFileSize])).
+
+update(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    lists:foreach(fun(_) ->
+        Doc = couch_doc:from_json_obj({[{<<"_id">>, couch_uuids:new()}]}),
+        {ok, _} = couch_db:update_docs(Db, [Doc]),
+        query_view(Db#db.name)
+    end, lists:seq(1, 200)),
+    couch_db:close(Db).
+
+db_url(DbName) ->
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    "http://" ++ Addr ++ ":" ++ Port ++ "/" ++ ?b2l(DbName).
+
+query_view(DbName) ->
+    {ok, Code, _Headers, _Body} = test_request:get(
+        db_url(DbName) ++ "/_design/foo/_view/foo"),
+    ?assertEqual(200, Code).
+
+get_db_frag(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, Info} = couch_db:get_db_info(Db),
+    couch_db:close(Db),
+    FileSize = couch_util:get_value(disk_size, Info),
+    DataSize = couch_util:get_value(data_size, Info),
+    {round((FileSize - DataSize) / FileSize * 100), FileSize}.
+
+get_view_frag(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, Info} = couch_mrview:get_info(Db, <<"_design/foo">>),
+    couch_db:close(Db),
+    FileSize = couch_util:get_value(disk_size, Info),
+    DataSize = couch_util:get_value(data_size, Info),
+    {round((FileSize - DataSize) / FileSize * 100), FileSize}.
+
+wait_compaction_finished(DbName) ->
+    Parent = self(),
+    Loop = spawn_link(fun() -> wait_loop(DbName, Parent) end),
+    receive
+        {done, Loop} ->
+            ok
+    after ?TIMEOUT ->
+        erlang:error(
+            {assertion_failed,
+             [{module, ?MODULE}, {line, ?LINE},
+              {reason, "Compaction timeout"}]})
+    end.
+
+wait_loop(DbName, Parent) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, DbInfo} = couch_db:get_db_info(Db),
+    {ok, ViewInfo} = couch_mrview:get_info(Db, <<"_design/foo">>),
+    couch_db:close(Db),
+    case (couch_util:get_value(compact_running, ViewInfo) =:= true) orelse
+        (couch_util:get_value(compact_running, DbInfo) =:= true) of
+        false ->
+            Parent ! {done, self()};
+        true ->
+            ok = timer:sleep(?DELAY),
+            wait_loop(DbName, Parent)
+    end.


[29/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Move files out of test/couchdb into top level test/ folder


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/e40ca37c
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/e40ca37c
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/e40ca37c

Branch: refs/heads/1963-eunit-bigcouch
Commit: e40ca37c8c09bc8c893529808abdc327bf47ca4e
Parents: 8c955aa
Author: Russell Branca <ch...@apache.org>
Authored: Mon Aug 11 13:19:06 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:22:57 2014 -0700

----------------------------------------------------------------------
 test/couch_auth_cache_tests.erl                 | 238 +++++++
 test/couch_btree_tests.erl                      | 551 +++++++++++++++
 test/couch_changes_tests.erl                    | 612 +++++++++++++++++
 test/couch_db_tests.erl                         | 114 ++++
 test/couch_doc_json_tests.erl                   | 391 +++++++++++
 test/couch_file_tests.erl                       | 265 ++++++++
 test/couch_key_tree_tests.erl                   | 380 +++++++++++
 test/couch_passwords_tests.erl                  |  54 ++
 test/couch_ref_counter_tests.erl                | 107 +++
 test/couch_stats_tests.erl                      | 412 ++++++++++++
 test/couch_stream_tests.erl                     | 100 +++
 test/couch_task_status_tests.erl                | 225 +++++++
 test/couch_util_tests.erl                       | 136 ++++
 test/couch_uuids_tests.erl                      | 161 +++++
 test/couch_work_queue_tests.erl                 | 393 +++++++++++
 test/couchdb/couch_auth_cache_tests.erl         | 238 -------
 test/couchdb/couch_btree_tests.erl              | 551 ---------------
 test/couchdb/couch_changes_tests.erl            | 612 -----------------
 test/couchdb/couch_db_tests.erl                 | 114 ----
 test/couchdb/couch_doc_json_tests.erl           | 391 -----------
 test/couchdb/couch_file_tests.erl               | 265 --------
 test/couchdb/couch_key_tree_tests.erl           | 380 -----------
 test/couchdb/couch_passwords_tests.erl          |  54 --
 test/couchdb/couch_ref_counter_tests.erl        | 107 ---
 test/couchdb/couch_stats_tests.erl              | 412 ------------
 test/couchdb/couch_stream_tests.erl             | 100 ---
 test/couchdb/couch_task_status_tests.erl        | 225 -------
 test/couchdb/couch_util_tests.erl               | 136 ----
 test/couchdb/couch_uuids_tests.erl              | 161 -----
 test/couchdb/couch_work_queue_tests.erl         | 393 -----------
 test/couchdb/couchdb_attachments_tests.erl      | 638 ------------------
 test/couchdb/couchdb_compaction_daemon.erl      | 231 -------
 test/couchdb/couchdb_cors_tests.erl             | 344 ----------
 test/couchdb/couchdb_csp_tests.erl              |  96 ---
 test/couchdb/couchdb_file_compression_tests.erl | 239 -------
 test/couchdb/couchdb_http_proxy_tests.erl       | 462 -------------
 test/couchdb/couchdb_modules_load_tests.erl     |  68 --
 test/couchdb/couchdb_os_daemons_tests.erl       | 228 -------
 test/couchdb/couchdb_os_proc_pool.erl           | 179 -----
 test/couchdb/couchdb_update_conflicts_tests.erl | 243 -------
 test/couchdb/couchdb_vhosts_tests.erl           | 441 ------------
 test/couchdb/couchdb_views_tests.erl            | 669 -------------------
 .../3b835456c235b1827e012e25666152f3.view       | Bin 4192 -> 0 bytes
 .../couchdb/fixtures/couch_stats_aggregates.cfg |  19 -
 .../couchdb/fixtures/couch_stats_aggregates.ini |  20 -
 test/couchdb/fixtures/logo.png                  | Bin 3010 -> 0 bytes
 test/couchdb/fixtures/os_daemon_bad_perm.sh     |  17 -
 test/couchdb/fixtures/os_daemon_can_reboot.sh   |  15 -
 .../couchdb/fixtures/os_daemon_configer.escript | 101 ---
 test/couchdb/fixtures/os_daemon_die_on_boot.sh  |  15 -
 test/couchdb/fixtures/os_daemon_die_quickly.sh  |  15 -
 test/couchdb/fixtures/os_daemon_looper.escript  |  26 -
 test/couchdb/fixtures/test.couch                | Bin 16482 -> 0 bytes
 test/couchdb/json_stream_parse_tests.erl        | 151 -----
 test/couchdb/test_request.erl                   |  75 ---
 test/couchdb/test_web.erl                       | 112 ----
 test/couchdb_attachments_tests.erl              | 638 ++++++++++++++++++
 test/couchdb_compaction_daemon.erl              | 231 +++++++
 test/couchdb_cors_tests.erl                     | 344 ++++++++++
 test/couchdb_csp_tests.erl                      |  96 +++
 test/couchdb_file_compression_tests.erl         | 239 +++++++
 test/couchdb_http_proxy_tests.erl               | 462 +++++++++++++
 test/couchdb_modules_load_tests.erl             |  68 ++
 test/couchdb_os_daemons_tests.erl               | 228 +++++++
 test/couchdb_os_proc_pool.erl                   | 179 +++++
 test/couchdb_update_conflicts_tests.erl         | 243 +++++++
 test/couchdb_vhosts_tests.erl                   | 441 ++++++++++++
 test/couchdb_views_tests.erl                    | 669 +++++++++++++++++++
 .../3b835456c235b1827e012e25666152f3.view       | Bin 0 -> 4192 bytes
 test/fixtures/couch_stats_aggregates.cfg        |  19 +
 test/fixtures/couch_stats_aggregates.ini        |  20 +
 test/fixtures/logo.png                          | Bin 0 -> 3010 bytes
 test/fixtures/os_daemon_bad_perm.sh             |  17 +
 test/fixtures/os_daemon_can_reboot.sh           |  15 +
 test/fixtures/os_daemon_configer.escript        | 101 +++
 test/fixtures/os_daemon_die_on_boot.sh          |  15 +
 test/fixtures/os_daemon_die_quickly.sh          |  15 +
 test/fixtures/os_daemon_looper.escript          |  26 +
 test/fixtures/test.couch                        | Bin 0 -> 16482 bytes
 test/json_stream_parse_tests.erl                | 151 +++++
 test/test_request.erl                           |  75 +++
 test/test_web.erl                               | 112 ++++
 82 files changed, 8543 insertions(+), 8543 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_auth_cache_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_auth_cache_tests.erl b/test/couch_auth_cache_tests.erl
new file mode 100644
index 0000000..3b2321c
--- /dev/null
+++ b/test/couch_auth_cache_tests.erl
@@ -0,0 +1,238 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_auth_cache_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(SALT, <<"SALT">>).
+-define(TIMEOUT, 1000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    couch_config:set("couch_httpd_auth", "authentication_db",
+                     ?b2l(DbName), false),
+    DbName.
+
+teardown(DbName) ->
+    ok = couch_server:delete(DbName, [?ADMIN_USER]),
+    ok.
+
+
+couch_auth_cache_test_() ->
+    {
+        "CouchDB auth cache tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_get_nil_on_missed_cache/1,
+                    fun should_get_right_password_hash/1,
+                    fun should_ensure_doc_hash_equals_cached_one/1,
+                    fun should_update_password/1,
+                    fun should_cleanup_cache_after_userdoc_deletion/1,
+                    fun should_restore_cache_after_userdoc_recreation/1,
+                    fun should_drop_cache_on_auth_db_change/1,
+                    fun should_restore_cache_on_auth_db_change/1,
+                    fun should_recover_cache_after_shutdown/1
+                ]
+            }
+        }
+    }.
+
+
+should_get_nil_on_missed_cache(_) ->
+    ?_assertEqual(nil, couch_auth_cache:get_user_creds("joe")).
+
+should_get_right_password_hash(DbName) ->
+    ?_test(begin
+        PasswordHash = hash_password("pass1"),
+        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
+        Creds = couch_auth_cache:get_user_creds("joe"),
+        ?assertEqual(PasswordHash,
+                      couch_util:get_value(<<"password_sha">>, Creds))
+    end).
+
+should_ensure_doc_hash_equals_cached_one(DbName) ->
+    ?_test(begin
+        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
+        Creds = couch_auth_cache:get_user_creds("joe"),
+
+        CachedHash = couch_util:get_value(<<"password_sha">>, Creds),
+        StoredHash = get_user_doc_password_sha(DbName, "joe"),
+        ?assertEqual(StoredHash, CachedHash)
+    end).
+
+should_update_password(DbName) ->
+    ?_test(begin
+        PasswordHash = hash_password("pass2"),
+        {ok, Rev} = update_user_doc(DbName, "joe", "pass1"),
+        {ok, _} = update_user_doc(DbName, "joe", "pass2", Rev),
+        Creds = couch_auth_cache:get_user_creds("joe"),
+        ?assertEqual(PasswordHash,
+                      couch_util:get_value(<<"password_sha">>, Creds))
+    end).
+
+should_cleanup_cache_after_userdoc_deletion(DbName) ->
+    ?_test(begin
+        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
+        delete_user_doc(DbName, "joe"),
+        ?assertEqual(nil, couch_auth_cache:get_user_creds("joe"))
+    end).
+
+should_restore_cache_after_userdoc_recreation(DbName) ->
+    ?_test(begin
+        PasswordHash = hash_password("pass5"),
+        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
+        delete_user_doc(DbName, "joe"),
+        ?assertEqual(nil, couch_auth_cache:get_user_creds("joe")),
+
+        {ok, _} = update_user_doc(DbName, "joe", "pass5"),
+        Creds = couch_auth_cache:get_user_creds("joe"),
+
+        ?assertEqual(PasswordHash,
+                      couch_util:get_value(<<"password_sha">>, Creds))
+    end).
+
+should_drop_cache_on_auth_db_change(DbName) ->
+    ?_test(begin
+        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
+        full_commit(DbName),
+        couch_config:set("couch_httpd_auth", "authentication_db",
+                         ?b2l(?tempdb()), false),
+        ?assertEqual(nil, couch_auth_cache:get_user_creds("joe"))
+    end).
+
+should_restore_cache_on_auth_db_change(DbName) ->
+    ?_test(begin
+        PasswordHash = hash_password("pass1"),
+        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
+        Creds = couch_auth_cache:get_user_creds("joe"),
+        full_commit(DbName),
+
+        DbName1 = ?tempdb(),
+        couch_config:set("couch_httpd_auth", "authentication_db",
+                         ?b2l(DbName1), false),
+
+        {ok, _} = update_user_doc(DbName1, "joe", "pass5"),
+        full_commit(DbName1),
+
+        couch_config:set("couch_httpd_auth", "authentication_db",
+                         ?b2l(DbName), false),
+
+        Creds = couch_auth_cache:get_user_creds("joe"),
+        ?assertEqual(PasswordHash,
+                      couch_util:get_value(<<"password_sha">>, Creds))
+    end).
+
+should_recover_cache_after_shutdown(DbName) ->
+    ?_test(begin
+        PasswordHash = hash_password("pass2"),
+        {ok, Rev0} = update_user_doc(DbName, "joe", "pass1"),
+        {ok, Rev1} = update_user_doc(DbName, "joe", "pass2", Rev0),
+        full_commit(DbName),
+        shutdown_db(DbName),
+        {ok, Rev1} = get_doc_rev(DbName, "joe"),
+        ?assertEqual(PasswordHash, get_user_doc_password_sha(DbName, "joe"))
+    end).
+
+
+update_user_doc(DbName, UserName, Password) ->
+    update_user_doc(DbName, UserName, Password, nil).
+
+update_user_doc(DbName, UserName, Password, Rev) ->
+    User = iolist_to_binary(UserName),
+    Doc = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"org.couchdb.user:", User/binary>>},
+        {<<"name">>, User},
+        {<<"type">>, <<"user">>},
+        {<<"salt">>, ?SALT},
+        {<<"password_sha">>, hash_password(Password)},
+        {<<"roles">>, []}
+    ] ++ case Rev of
+            nil -> [];
+            _ ->   [{<<"_rev">>, Rev}]
+         end
+    }),
+    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
+    {ok, NewRev} = couch_db:update_doc(AuthDb, Doc, []),
+    ok = couch_db:close(AuthDb),
+    {ok, couch_doc:rev_to_str(NewRev)}.
+
+hash_password(Password) ->
+    ?l2b(couch_util:to_hex(crypto:sha(iolist_to_binary([Password, ?SALT])))).
+
+shutdown_db(DbName) ->
+    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
+    ok = couch_db:close(AuthDb),
+    couch_util:shutdown_sync(AuthDb#db.main_pid),
+    ok = timer:sleep(1000).
+
+get_doc_rev(DbName, UserName) ->
+    DocId = iolist_to_binary([<<"org.couchdb.user:">>, UserName]),
+    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
+    UpdateRev =
+    case couch_db:open_doc(AuthDb, DocId, []) of
+    {ok, Doc} ->
+        {Props} = couch_doc:to_json_obj(Doc, []),
+        couch_util:get_value(<<"_rev">>, Props);
+    {not_found, missing} ->
+        nil
+    end,
+    ok = couch_db:close(AuthDb),
+    {ok, UpdateRev}.
+
+get_user_doc_password_sha(DbName, UserName) ->
+    DocId = iolist_to_binary([<<"org.couchdb.user:">>, UserName]),
+    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
+    {ok, Doc} = couch_db:open_doc(AuthDb, DocId, []),
+    ok = couch_db:close(AuthDb),
+    {Props} = couch_doc:to_json_obj(Doc, []),
+    couch_util:get_value(<<"password_sha">>, Props).
+
+delete_user_doc(DbName, UserName) ->
+    DocId = iolist_to_binary([<<"org.couchdb.user:">>, UserName]),
+    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
+    {ok, Doc} = couch_db:open_doc(AuthDb, DocId, []),
+    {Props} = couch_doc:to_json_obj(Doc, []),
+    DeletedDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, DocId},
+        {<<"_rev">>, couch_util:get_value(<<"_rev">>, Props)},
+        {<<"_deleted">>, true}
+    ]}),
+    {ok, _} = couch_db:update_doc(AuthDb, DeletedDoc, []),
+    ok = couch_db:close(AuthDb).
+
+full_commit(DbName) ->
+    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
+    {ok, _} = couch_db:ensure_full_commit(AuthDb),
+    ok = couch_db:close(AuthDb).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_btree_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_btree_tests.erl b/test/couch_btree_tests.erl
new file mode 100644
index 0000000..911640f
--- /dev/null
+++ b/test/couch_btree_tests.erl
@@ -0,0 +1,551 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_btree_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(ROWS, 1000).
+
+
+setup() ->
+    {ok, Fd} = couch_file:open(?tempfile(), [create, overwrite]),
+    {ok, Btree} = couch_btree:open(nil, Fd, [{compression, none},
+                                             {reduce, fun reduce_fun/2}]),
+    {Fd, Btree}.
+
+setup_kvs(_) ->
+    setup().
+
+setup_red() ->
+    {_, EvenOddKVs} = lists:foldl(
+        fun(Idx, {Key, Acc}) ->
+            case Key of
+                "even" -> {"odd", [{{Key, Idx}, 1} | Acc]};
+                _ -> {"even", [{{Key, Idx}, 1} | Acc]}
+            end
+        end, {"odd", []}, lists:seq(1, ?ROWS)),
+    {Fd, Btree} = setup(),
+    {ok, Btree1} = couch_btree:add_remove(Btree, EvenOddKVs, []),
+    {Fd, Btree1}.
+setup_red(_) ->
+    setup_red().
+
+teardown(Fd) when is_pid(Fd) ->
+    ok = couch_file:close(Fd);
+teardown({Fd, _}) ->
+    teardown(Fd).
+teardown(_, {Fd, _}) ->
+    teardown(Fd).
+
+
+kvs_test_funs() ->
+    [
+        fun should_set_fd_correctly/2,
+        fun should_set_root_correctly/2,
+        fun should_create_zero_sized_btree/2,
+        fun should_set_reduce_option/2,
+        fun should_fold_over_empty_btree/2,
+        fun should_add_all_keys/2,
+        fun should_continuously_add_new_kv/2,
+        fun should_continuously_remove_keys/2,
+        fun should_insert_keys_in_reversed_order/2,
+        fun should_add_every_odd_key_remove_every_even/2,
+        fun should_add_every_even_key_remove_every_old/2
+    ].
+
+red_test_funs() ->
+    [
+        fun should_reduce_whole_range/2,
+        fun should_reduce_first_half/2,
+        fun should_reduce_second_half/2
+    ].
+
+
+btree_open_test_() ->
+    {ok, Fd} = couch_file:open(?tempfile(), [create, overwrite]),
+    {ok, Btree} = couch_btree:open(nil, Fd, [{compression, none}]),
+    {
+        "Ensure that created btree is really a btree record",
+        ?_assert(is_record(Btree, btree))
+    }.
+
+sorted_kvs_test_() ->
+    Funs = kvs_test_funs(),
+    Sorted = [{Seq, random:uniform()} || Seq <- lists:seq(1, ?ROWS)],
+    {
+        "BTree with sorted keys",
+        {
+            foreachx,
+            fun setup_kvs/1, fun teardown/2,
+            [{Sorted, Fun} || Fun <- Funs]
+        }
+    }.
+
+rsorted_kvs_test_() ->
+    Sorted = [{Seq, random:uniform()} || Seq <- lists:seq(1, ?ROWS)],
+    Funs = kvs_test_funs(),
+    Reversed = Sorted,
+    {
+        "BTree with backward sorted keys",
+        {
+            foreachx,
+            fun setup_kvs/1, fun teardown/2,
+            [{Reversed, Fun} || Fun <- Funs]
+        }
+    }.
+
+shuffled_kvs_test_() ->
+    Funs = kvs_test_funs(),
+    Sorted = [{Seq, random:uniform()} || Seq <- lists:seq(1, ?ROWS)],
+    Shuffled = shuffle(Sorted),
+    {
+        "BTree with shuffled keys",
+        {
+            foreachx,
+            fun setup_kvs/1, fun teardown/2,
+            [{Shuffled, Fun} || Fun <- Funs]
+        }
+    }.
+
+reductions_test_() ->
+    {
+        "BTree reductions",
+        [
+            {
+                "Common tests",
+                {
+                    foreach,
+                    fun setup_red/0, fun teardown/1,
+                    [
+                        fun should_reduce_without_specified_direction/1,
+                        fun should_reduce_forward/1,
+                        fun should_reduce_backward/1
+                    ]
+                }
+            },
+            {
+                "Range requests",
+                [
+                    {
+                        "Forward direction",
+                        {
+                            foreachx,
+                            fun setup_red/1, fun teardown/2,
+                            [{fwd, F} || F <- red_test_funs()]
+                        }
+                    },
+                    {
+                        "Backward direction",
+                        {
+                            foreachx,
+                            fun setup_red/1, fun teardown/2,
+                            [{rev, F} || F <- red_test_funs()]
+                        }
+                    }
+                ]
+            }
+        ]
+    }.
+
+
+should_set_fd_correctly(_, {Fd, Btree}) ->
+    ?_assertMatch(Fd, Btree#btree.fd).
+
+should_set_root_correctly(_, {_, Btree}) ->
+    ?_assertMatch(nil, Btree#btree.root).
+
+should_create_zero_sized_btree(_, {_, Btree}) ->
+    ?_assertMatch(0, couch_btree:size(Btree)).
+
+should_set_reduce_option(_, {_, Btree}) ->
+    ReduceFun = fun reduce_fun/2,
+    Btree1 = couch_btree:set_options(Btree, [{reduce, ReduceFun}]),
+    ?_assertMatch(ReduceFun, Btree1#btree.reduce).
+
+should_fold_over_empty_btree(_, {_, Btree}) ->
+    {ok, _, EmptyRes} = couch_btree:foldl(Btree, fun(_, X) -> {ok, X+1} end, 0),
+    ?_assertEqual(EmptyRes, 0).
+
+should_add_all_keys(KeyValues, {Fd, Btree}) ->
+    {ok, Btree1} = couch_btree:add_remove(Btree, KeyValues, []),
+    [
+        should_return_complete_btree_on_adding_all_keys(KeyValues, Btree1),
+        should_have_non_zero_size(Btree1),
+        should_have_lesser_size_than_file(Fd, Btree1),
+        should_keep_root_pointer_to_kp_node(Fd, Btree1),
+        should_remove_all_keys(KeyValues, Btree1)
+    ].
+
+should_return_complete_btree_on_adding_all_keys(KeyValues, Btree) ->
+    ?_assert(test_btree(Btree, KeyValues)).
+
+should_have_non_zero_size(Btree) ->
+    ?_assert(couch_btree:size(Btree) > 0).
+
+should_have_lesser_size_than_file(Fd, Btree) ->
+    ?_assert((couch_btree:size(Btree) =< couch_file:bytes(Fd))).
+
+should_keep_root_pointer_to_kp_node(Fd, Btree) ->
+    ?_assertMatch({ok, {kp_node, _}},
+                  couch_file:pread_term(Fd, element(1, Btree#btree.root))).
+
+should_remove_all_keys(KeyValues, Btree) ->
+    Keys = keys(KeyValues),
+    {ok, Btree1} = couch_btree:add_remove(Btree, [], Keys),
+    {
+        "Should remove all the keys",
+        [
+            should_produce_valid_btree(Btree1, []),
+            should_be_empty(Btree1)
+        ]
+    }.
+
+should_continuously_add_new_kv(KeyValues, {_, Btree}) ->
+    {Btree1, _} = lists:foldl(
+        fun(KV, {BtAcc, PrevSize}) ->
+            {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [KV], []),
+            ?assert(couch_btree:size(BtAcc2) > PrevSize),
+            {BtAcc2, couch_btree:size(BtAcc2)}
+        end, {Btree, couch_btree:size(Btree)}, KeyValues),
+    {
+        "Should continuously add key-values to btree",
+        [
+            should_produce_valid_btree(Btree1, KeyValues),
+            should_not_be_empty(Btree1)
+        ]
+    }.
+
+should_continuously_remove_keys(KeyValues, {_, Btree}) ->
+    {ok, Btree1} = couch_btree:add_remove(Btree, KeyValues, []),
+    {Btree2, _} = lists:foldl(
+        fun({K, _}, {BtAcc, PrevSize}) ->
+            {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [], [K]),
+            ?assert(couch_btree:size(BtAcc2) < PrevSize),
+            {BtAcc2, couch_btree:size(BtAcc2)}
+        end, {Btree1, couch_btree:size(Btree1)}, KeyValues),
+    {
+        "Should continuously remove keys from btree",
+        [
+            should_produce_valid_btree(Btree2, []),
+            should_be_empty(Btree2)
+        ]
+    }.
+
+should_insert_keys_in_reversed_order(KeyValues, {_, Btree}) ->
+    KeyValuesRev = lists:reverse(KeyValues),
+    {Btree1, _} = lists:foldl(
+        fun(KV, {BtAcc, PrevSize}) ->
+            {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [KV], []),
+            ?assert(couch_btree:size(BtAcc2) > PrevSize),
+            {BtAcc2, couch_btree:size(BtAcc2)}
+        end, {Btree, couch_btree:size(Btree)}, KeyValuesRev),
+    should_produce_valid_btree(Btree1, KeyValues).
+
+should_add_every_odd_key_remove_every_even(KeyValues, {_, Btree}) ->
+    {ok, Btree1} = couch_btree:add_remove(Btree, KeyValues, []),
+    {_, Rem2Keys0, Rem2Keys1} = lists:foldl(fun(X, {Count, Left, Right}) ->
+        case Count rem 2 == 0 of
+            true -> {Count + 1, [X | Left], Right};
+            false -> {Count + 1, Left, [X | Right]}
+        end
+                                            end, {0, [], []}, KeyValues),
+    ?_assert(test_add_remove(Btree1, Rem2Keys0, Rem2Keys1)).
+
+should_add_every_even_key_remove_every_old(KeyValues, {_, Btree}) ->
+    {ok, Btree1} = couch_btree:add_remove(Btree, KeyValues, []),
+    {_, Rem2Keys0, Rem2Keys1} = lists:foldl(fun(X, {Count, Left, Right}) ->
+        case Count rem 2 == 0 of
+            true -> {Count + 1, [X | Left], Right};
+            false -> {Count + 1, Left, [X | Right]}
+        end
+                                            end, {0, [], []}, KeyValues),
+    ?_assert(test_add_remove(Btree1, Rem2Keys1, Rem2Keys0)).
+
+
+should_reduce_without_specified_direction({_, Btree}) ->
+    ?_assertMatch(
+        {ok, [{{"odd", _}, ?ROWS div 2}, {{"even", _}, ?ROWS div 2}]},
+        fold_reduce(Btree, [])).
+
+should_reduce_forward({_, Btree}) ->
+    ?_assertMatch(
+        {ok, [{{"odd", _}, ?ROWS div 2}, {{"even", _}, ?ROWS div 2}]},
+        fold_reduce(Btree, [{dir, fwd}])).
+
+should_reduce_backward({_, Btree}) ->
+    ?_assertMatch(
+        {ok, [{{"even", _}, ?ROWS div 2}, {{"odd", _}, ?ROWS div 2}]},
+        fold_reduce(Btree, [{dir, rev}])).
+
+should_reduce_whole_range(fwd, {_, Btree}) ->
+    {SK, EK} = {{"even", 0}, {"odd", ?ROWS - 1}},
+    [
+        {
+            "include endkey",
+            ?_assertMatch(
+                {ok, [{{"odd", 1}, ?ROWS div 2},
+                      {{"even", 2}, ?ROWS div 2}]},
+                fold_reduce(Btree, [{dir, fwd},
+                                    {start_key, SK},
+                                    {end_key, EK}]))
+        },
+        {
+            "exclude endkey",
+            ?_assertMatch(
+                {ok, [{{"odd", 1}, (?ROWS div 2) - 1},
+                      {{"even", 2}, ?ROWS div 2}]},
+                fold_reduce(Btree, [{dir, fwd},
+                                    {start_key, SK},
+                                    {end_key_gt, EK}]))
+        }
+    ];
+should_reduce_whole_range(rev, {_, Btree}) ->
+    {SK, EK} = {{"odd", ?ROWS - 1}, {"even", 2}},
+    [
+        {
+            "include endkey",
+            ?_assertMatch(
+                {ok, [{{"even", ?ROWS}, ?ROWS div 2},
+                      {{"odd", ?ROWS - 1}, ?ROWS div 2}]},
+                fold_reduce(Btree, [{dir, rev},
+                                    {start_key, SK},
+                                    {end_key, EK}]))
+        },
+        {
+            "exclude endkey",
+            ?_assertMatch(
+                {ok, [{{"even", ?ROWS}, (?ROWS div 2) - 1},
+                      {{"odd", ?ROWS - 1}, ?ROWS div 2}]},
+                fold_reduce(Btree, [{dir, rev},
+                                    {start_key, SK},
+                                    {end_key_gt, EK}]))
+        }
+    ].
+
+should_reduce_first_half(fwd, {_, Btree}) ->
+    {SK, EK} = {{"even", 0}, {"odd", (?ROWS div 2) - 1}},
+    [
+        {
+            "include endkey",
+            ?_assertMatch(
+                {ok, [{{"odd", 1}, ?ROWS div 4},
+                      {{"even", 2}, ?ROWS div 2}]},
+                fold_reduce(Btree, [{dir, fwd},
+                                    {start_key, SK}, {end_key, EK}]))
+        },
+        {
+            "exclude endkey",
+            ?_assertMatch(
+                {ok, [{{"odd", 1}, (?ROWS div 4) - 1},
+                      {{"even", 2}, ?ROWS div 2}]},
+                fold_reduce(Btree, [{dir, fwd},
+                                    {start_key, SK},
+                                    {end_key_gt, EK}]))
+        }
+    ];
+should_reduce_first_half(rev, {_, Btree}) ->
+    {SK, EK} = {{"odd", ?ROWS - 1}, {"even", ?ROWS div 2}},
+    [
+        {
+            "include endkey",
+            ?_assertMatch(
+                {ok, [{{"even", ?ROWS}, (?ROWS div 4) + 1},
+                      {{"odd", ?ROWS - 1}, ?ROWS div 2}]},
+                fold_reduce(Btree, [{dir, rev},
+                                    {start_key, SK},
+                                    {end_key, EK}]))
+        },
+        {
+            "exclude endkey",
+            ?_assertMatch(
+                {ok, [{{"even", ?ROWS}, ?ROWS div 4},
+                      {{"odd", ?ROWS - 1}, ?ROWS div 2}]},
+                fold_reduce(Btree, [{dir, rev},
+                                    {start_key, SK},
+                                    {end_key_gt, EK}]))
+        }
+    ].
+
+should_reduce_second_half(fwd, {_, Btree}) ->
+    {SK, EK} = {{"even", ?ROWS div 2}, {"odd", ?ROWS - 1}},
+    [
+        {
+            "include endkey",
+            ?_assertMatch(
+                {ok, [{{"odd", 1}, ?ROWS div 2},
+                      {{"even", ?ROWS div 2}, (?ROWS div 4) + 1}]},
+                fold_reduce(Btree, [{dir, fwd},
+                                    {start_key, SK},
+                                    {end_key, EK}]))
+        },
+        {
+            "exclude endkey",
+            ?_assertMatch(
+                {ok, [{{"odd", 1}, (?ROWS div 2) - 1},
+                      {{"even", ?ROWS div 2}, (?ROWS div 4) + 1}]},
+                fold_reduce(Btree, [{dir, fwd},
+                                    {start_key, SK},
+                                    {end_key_gt, EK}]))
+        }
+    ];
+should_reduce_second_half(rev, {_, Btree}) ->
+    {SK, EK} = {{"odd", (?ROWS div 2) + 1}, {"even", 2}},
+    [
+        {
+            "include endkey",
+            ?_assertMatch(
+                {ok, [{{"even", ?ROWS}, ?ROWS div 2},
+                      {{"odd", (?ROWS div 2) + 1}, (?ROWS div 4) + 1}]},
+                fold_reduce(Btree, [{dir, rev},
+                                    {start_key, SK},
+                                    {end_key, EK}]))
+        },
+        {
+            "exclude endkey",
+            ?_assertMatch(
+                {ok, [{{"even", ?ROWS}, (?ROWS div 2) - 1},
+                      {{"odd", (?ROWS div 2) + 1}, (?ROWS div 4) + 1}]},
+                fold_reduce(Btree, [{dir, rev},
+                                    {start_key, SK},
+                                    {end_key_gt, EK}]))
+        }
+    ].
+
+should_produce_valid_btree(Btree, KeyValues) ->
+    ?_assert(test_btree(Btree, KeyValues)).
+
+should_be_empty(Btree) ->
+    ?_assertEqual(couch_btree:size(Btree), 0).
+
+should_not_be_empty(Btree) ->
+    ?_assert(couch_btree:size(Btree) > 0).
+
+fold_reduce(Btree, Opts) ->
+    GroupFun = fun({K1, _}, {K2, _}) ->
+        K1 == K2
+    end,
+    FoldFun = fun(GroupedKey, Unreduced, Acc) ->
+        {ok, [{GroupedKey, couch_btree:final_reduce(Btree, Unreduced)} | Acc]}
+    end,
+    couch_btree:fold_reduce(Btree, FoldFun, [],
+                            [{key_group_fun, GroupFun}] ++ Opts).
+
+
+keys(KVs) ->
+    [K || {K, _} <- KVs].
+
+reduce_fun(reduce, KVs) ->
+    length(KVs);
+reduce_fun(rereduce, Reds) ->
+    lists:sum(Reds).
+
+
+shuffle(List) ->
+    randomize(round(math:log(length(List)) + 0.5), List).
+
+randomize(1, List) ->
+    randomize(List);
+randomize(T, List) ->
+    lists:foldl(
+        fun(_E, Acc) ->
+            randomize(Acc)
+        end, randomize(List), lists:seq(1, (T - 1))).
+
+randomize(List) ->
+    D = lists:map(fun(A) -> {random:uniform(), A} end, List),
+    {_, D1} = lists:unzip(lists:keysort(1, D)),
+    D1.
+
+test_btree(Btree, KeyValues) ->
+    ok = test_key_access(Btree, KeyValues),
+    ok = test_lookup_access(Btree, KeyValues),
+    ok = test_final_reductions(Btree, KeyValues),
+    ok = test_traversal_callbacks(Btree, KeyValues),
+    true.
+
+test_add_remove(Btree, OutKeyValues, RemainingKeyValues) ->
+    Btree2 = lists:foldl(
+        fun({K, _}, BtAcc) ->
+            {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [], [K]),
+            BtAcc2
+        end, Btree, OutKeyValues),
+    true = test_btree(Btree2, RemainingKeyValues),
+
+    Btree3 = lists:foldl(
+        fun(KV, BtAcc) ->
+            {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [KV], []),
+            BtAcc2
+        end, Btree2, OutKeyValues),
+    true = test_btree(Btree3, OutKeyValues ++ RemainingKeyValues).
+
+test_key_access(Btree, List) ->
+    FoldFun = fun(Element, {[HAcc|TAcc], Count}) ->
+        case Element == HAcc of
+            true -> {ok, {TAcc, Count + 1}};
+            _ -> {ok, {TAcc, Count + 1}}
+        end
+    end,
+    Length = length(List),
+    Sorted = lists:sort(List),
+    {ok, _, {[], Length}} = couch_btree:foldl(Btree, FoldFun, {Sorted, 0}),
+    {ok, _, {[], Length}} = couch_btree:fold(Btree, FoldFun,
+                                             {Sorted, 0}, [{dir, rev}]),
+    ok.
+
+test_lookup_access(Btree, KeyValues) ->
+    FoldFun = fun({Key, Value}, {Key, Value}) -> {stop, true} end,
+    lists:foreach(
+        fun({Key, Value}) ->
+            [{ok, {Key, Value}}] = couch_btree:lookup(Btree, [Key]),
+            {ok, _, true} = couch_btree:foldl(Btree, FoldFun,
+                                              {Key, Value}, [{start_key, Key}])
+        end, KeyValues).
+
+test_final_reductions(Btree, KeyValues) ->
+    KVLen = length(KeyValues),
+    FoldLFun = fun(_X, LeadingReds, Acc) ->
+        CountToStart = KVLen div 3 + Acc,
+        CountToStart = couch_btree:final_reduce(Btree, LeadingReds),
+        {ok, Acc + 1}
+    end,
+    FoldRFun = fun(_X, LeadingReds, Acc) ->
+        CountToEnd = KVLen - KVLen div 3 + Acc,
+        CountToEnd = couch_btree:final_reduce(Btree, LeadingReds),
+        {ok, Acc + 1}
+    end,
+    {LStartKey, _} = case KVLen of
+        0 -> {nil, nil};
+        _ -> lists:nth(KVLen div 3 + 1, lists:sort(KeyValues))
+    end,
+    {RStartKey, _} = case KVLen of
+        0 -> {nil, nil};
+        _ -> lists:nth(KVLen div 3, lists:sort(KeyValues))
+    end,
+    {ok, _, FoldLRed} = couch_btree:foldl(Btree, FoldLFun, 0,
+                                          [{start_key, LStartKey}]),
+    {ok, _, FoldRRed} = couch_btree:fold(Btree, FoldRFun, 0,
+                                         [{dir, rev}, {start_key, RStartKey}]),
+    KVLen = FoldLRed + FoldRRed,
+    ok.
+
+test_traversal_callbacks(Btree, _KeyValues) ->
+    FoldFun = fun
+        (visit, _GroupedKey, _Unreduced, Acc) ->
+            {ok, Acc andalso false};
+        (traverse, _LK, _Red, Acc) ->
+            {skip, Acc andalso true}
+    end,
+    % With 250 items the root is a kp. Always skipping should reduce to true.
+    {ok, _, true} = couch_btree:fold(Btree, FoldFun, true, [{dir, fwd}]),
+    ok.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_changes_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_changes_tests.erl b/test/couch_changes_tests.erl
new file mode 100644
index 0000000..a129ba2
--- /dev/null
+++ b/test/couch_changes_tests.erl
@@ -0,0 +1,612 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_changes_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles = [<<"_admin">>]}}).
+-define(TIMEOUT, 3000).
+-define(TEST_TIMEOUT, 10000).
+
+-record(row, {
+    id,
+    seq,
+    deleted = false
+}).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = create_db(DbName),
+    Revs = [R || {ok, R} <- [
+        save_doc(Db, {[{<<"_id">>, <<"doc1">>}]}),
+        save_doc(Db, {[{<<"_id">>, <<"doc2">>}]}),
+        save_doc(Db, {[{<<"_id">>, <<"doc3">>}]}),
+        save_doc(Db, {[{<<"_id">>, <<"doc4">>}]}),
+        save_doc(Db, {[{<<"_id">>, <<"doc5">>}]})
+    ]],
+    Rev = lists:nth(3, Revs),
+    {ok, Rev1} = save_doc(Db, {[{<<"_id">>, <<"doc3">>}, {<<"_rev">>, Rev}]}),
+    Revs1 = Revs ++ [Rev1],
+    Revs2 = Revs1 ++ [R || {ok, R} <- [
+        save_doc(Db, {[{<<"_id">>, <<"doc6">>}]}),
+        save_doc(Db, {[{<<"_id">>, <<"_design/foo">>}]}),
+        save_doc(Db, {[{<<"_id">>, <<"doc7">>}]}),
+        save_doc(Db, {[{<<"_id">>, <<"doc8">>}]})
+    ]],
+    {DbName, list_to_tuple(Revs2)}.
+
+teardown({DbName, _}) ->
+    delete_db(DbName),
+    ok.
+
+
+changes_test_() ->
+    {
+        "Changes feeed",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [
+                filter_by_doc_id(),
+                filter_by_design(),
+                continuous_feed(),
+                filter_by_custom_function()
+            ]
+        }
+    }.
+
+filter_by_doc_id() ->
+    {
+        "Filter _doc_id",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_filter_by_specific_doc_ids/1,
+                fun should_filter_by_specific_doc_ids_descending/1,
+                fun should_filter_by_specific_doc_ids_with_since/1,
+                fun should_filter_by_specific_doc_ids_no_result/1,
+                fun should_handle_deleted_docs/1
+            ]
+        }
+    }.
+
+filter_by_design() ->
+    {
+        "Filter _design",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_emit_only_design_documents/1
+            ]
+        }
+    }.
+
+filter_by_custom_function() ->
+    {
+        "Filter function",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_receive_heartbeats/1
+            ]
+        }
+    }.
+
+continuous_feed() ->
+    {
+        "Continuous Feed",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_filter_continuous_feed_by_specific_doc_ids/1
+            ]
+        }
+    }.
+
+
+should_filter_by_specific_doc_ids({DbName, _}) ->
+    ?_test(
+        begin
+            ChangesArgs = #changes_args{
+                filter = "_doc_ids"
+            },
+            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
+            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
+            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
+
+            {Rows, LastSeq} = wait_finished(Consumer),
+            {ok, Db} = couch_db:open_int(DbName, []),
+            UpSeq = couch_db:get_update_seq(Db),
+            couch_db:close(Db),
+            stop_consumer(Consumer),
+
+            ?assertEqual(2, length(Rows)),
+            [#row{seq = Seq1, id = Id1}, #row{seq = Seq2, id = Id2}] = Rows,
+            ?assertEqual(<<"doc4">>, Id1),
+            ?assertEqual(4, Seq1),
+            ?assertEqual(<<"doc3">>, Id2),
+            ?assertEqual(6, Seq2),
+            ?assertEqual(UpSeq, LastSeq)
+        end).
+
+should_filter_by_specific_doc_ids_descending({DbName, _}) ->
+    ?_test(
+        begin
+            ChangesArgs = #changes_args{
+                filter = "_doc_ids",
+                dir = rev
+            },
+            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
+            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
+            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
+
+            {Rows, LastSeq} = wait_finished(Consumer),
+            {ok, Db} = couch_db:open_int(DbName, []),
+            couch_db:close(Db),
+            stop_consumer(Consumer),
+
+            ?assertEqual(2, length(Rows)),
+            [#row{seq = Seq1, id = Id1}, #row{seq = Seq2, id = Id2}] = Rows,
+            ?assertEqual(<<"doc3">>, Id1),
+            ?assertEqual(6, Seq1),
+            ?assertEqual(<<"doc4">>, Id2),
+            ?assertEqual(4, Seq2),
+            ?assertEqual(4, LastSeq)
+        end).
+
+should_filter_by_specific_doc_ids_with_since({DbName, _}) ->
+    ?_test(
+        begin
+            ChangesArgs = #changes_args{
+                filter = "_doc_ids",
+                since = 5
+            },
+            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
+            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
+            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
+
+            {Rows, LastSeq} = wait_finished(Consumer),
+            {ok, Db} = couch_db:open_int(DbName, []),
+            UpSeq = couch_db:get_update_seq(Db),
+            couch_db:close(Db),
+            stop_consumer(Consumer),
+
+            ?assertEqual(1, length(Rows)),
+            [#row{seq = Seq1, id = Id1}] = Rows,
+            ?assertEqual(<<"doc3">>, Id1),
+            ?assertEqual(6, Seq1),
+            ?assertEqual(UpSeq, LastSeq)
+        end).
+
+should_filter_by_specific_doc_ids_no_result({DbName, _}) ->
+    ?_test(
+        begin
+            ChangesArgs = #changes_args{
+                filter = "_doc_ids",
+                since = 6
+            },
+            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
+            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
+            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
+
+            {Rows, LastSeq} = wait_finished(Consumer),
+            {ok, Db} = couch_db:open_int(DbName, []),
+            UpSeq = couch_db:get_update_seq(Db),
+            couch_db:close(Db),
+            stop_consumer(Consumer),
+
+            ?assertEqual(0, length(Rows)),
+            ?assertEqual(UpSeq, LastSeq)
+        end).
+
+should_handle_deleted_docs({DbName, Revs}) ->
+    ?_test(
+        begin
+            Rev3_2 = element(6, Revs),
+            {ok, Db} = couch_db:open_int(DbName, []),
+            {ok, _} = save_doc(
+                Db,
+                {[{<<"_id">>, <<"doc3">>},
+                  {<<"_deleted">>, true},
+                  {<<"_rev">>, Rev3_2}]}),
+
+            ChangesArgs = #changes_args{
+                filter = "_doc_ids",
+                since = 9
+            },
+            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
+            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
+            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
+
+            {Rows, LastSeq} = wait_finished(Consumer),
+            couch_db:close(Db),
+            stop_consumer(Consumer),
+
+            ?assertEqual(1, length(Rows)),
+            ?assertMatch(
+                [#row{seq = LastSeq, id = <<"doc3">>, deleted = true}],
+                Rows
+            ),
+            ?assertEqual(11, LastSeq)
+        end).
+
+should_filter_continuous_feed_by_specific_doc_ids({DbName, Revs}) ->
+    ?_test(
+        begin
+            {ok, Db} = couch_db:open_int(DbName, []),
+            ChangesArgs = #changes_args{
+                filter = "_doc_ids",
+                feed = "continuous"
+            },
+            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
+            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
+            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
+            pause(Consumer),
+
+            Rows = get_rows(Consumer),
+            ?assertEqual(2, length(Rows)),
+            [#row{seq = Seq1, id = Id1}, #row{seq = Seq2, id = Id2}] = Rows,
+            ?assertEqual(<<"doc4">>, Id1),
+            ?assertEqual(4, Seq1),
+            ?assertEqual(<<"doc3">>, Id2),
+            ?assertEqual(6, Seq2),
+
+            clear_rows(Consumer),
+            {ok, _Rev9} = save_doc(Db, {[{<<"_id">>, <<"doc9">>}]}),
+            {ok, _Rev10} = save_doc(Db, {[{<<"_id">>, <<"doc10">>}]}),
+            unpause(Consumer),
+            pause(Consumer),
+            ?assertEqual([], get_rows(Consumer)),
+
+            Rev4 = element(4, Revs),
+            Rev3_2 = element(6, Revs),
+            {ok, Rev4_2} = save_doc(Db, {[{<<"_id">>, <<"doc4">>},
+                                          {<<"_rev">>, Rev4}]}),
+            {ok, _} = save_doc(Db, {[{<<"_id">>, <<"doc11">>}]}),
+            {ok, _} = save_doc(Db, {[{<<"_id">>, <<"doc4">>},
+                                     {<<"_rev">>, Rev4_2}]}),
+            {ok, _} = save_doc(Db, {[{<<"_id">>, <<"doc12">>}]}),
+            {ok, Rev3_3} = save_doc(Db, {[{<<"_id">>, <<"doc3">>},
+                                          {<<"_rev">>, Rev3_2}]}),
+            unpause(Consumer),
+            pause(Consumer),
+
+            NewRows = get_rows(Consumer),
+            ?assertEqual(2, length(NewRows)),
+            [Row14, Row16] = NewRows,
+            ?assertEqual(<<"doc4">>, Row14#row.id),
+            ?assertEqual(15, Row14#row.seq),
+            ?assertEqual(<<"doc3">>, Row16#row.id),
+            ?assertEqual(17, Row16#row.seq),
+
+            clear_rows(Consumer),
+            {ok, _Rev3_4} = save_doc(Db, {[{<<"_id">>, <<"doc3">>},
+                                           {<<"_rev">>, Rev3_3}]}),
+            unpause(Consumer),
+            pause(Consumer),
+
+            FinalRows = get_rows(Consumer),
+
+            unpause(Consumer),
+            stop_consumer(Consumer),
+
+            ?assertMatch([#row{seq = 18, id = <<"doc3">>}], FinalRows)
+        end).
+
+should_emit_only_design_documents({DbName, Revs}) ->
+    ?_test(
+        begin
+            ChangesArgs = #changes_args{
+                filter = "_design"
+            },
+            Consumer = spawn_consumer(DbName, ChangesArgs, {json_req, null}),
+
+            {Rows, LastSeq} = wait_finished(Consumer),
+            {ok, Db} = couch_db:open_int(DbName, []),
+            UpSeq = couch_db:get_update_seq(Db),
+            couch_db:close(Db),
+
+            ?assertEqual(1, length(Rows)),
+            ?assertEqual(UpSeq, LastSeq),
+            ?assertEqual([#row{seq = 8, id = <<"_design/foo">>}], Rows),
+
+            stop_consumer(Consumer),
+
+            {ok, Db2} = couch_db:open_int(DbName, [?ADMIN_USER]),
+            {ok, _} = save_doc(Db2, {[{<<"_id">>, <<"_design/foo">>},
+                                      {<<"_rev">>, element(8, Revs)},
+                                      {<<"_deleted">>, true}]}),
+
+            Consumer2 = spawn_consumer(DbName, ChangesArgs, {json_req, null}),
+
+            {Rows2, LastSeq2} = wait_finished(Consumer2),
+            UpSeq2 = UpSeq + 1,
+            couch_db:close(Db2),
+
+            ?assertEqual(1, length(Rows2)),
+            ?assertEqual(UpSeq2, LastSeq2),
+            ?assertEqual([#row{seq = 11,
+                               id = <<"_design/foo">>,
+                               deleted = true}],
+                          Rows2)
+        end).
+
+should_receive_heartbeats(_) ->
+    {timeout, ?TEST_TIMEOUT div 1000,
+     ?_test(
+         begin
+             DbName = ?tempdb(),
+             Timeout = 100,
+             {ok, Db} = create_db(DbName),
+
+             {ok, _} = save_doc(Db, {[
+                 {<<"_id">>, <<"_design/filtered">>},
+                 {<<"language">>, <<"javascript">>},
+                     {<<"filters">>, {[
+                         {<<"foo">>, <<"function(doc) {
+                             return ['doc10', 'doc11', 'doc12'].indexOf(doc._id) != -1;}">>
+                     }]}}
+             ]}),
+
+             ChangesArgs = #changes_args{
+                 filter = "filtered/foo",
+                 feed = "continuous",
+                 timeout = 10000,
+                 heartbeat = 1000
+             },
+             Consumer = spawn_consumer(DbName, ChangesArgs, {json_req, null}),
+
+             {ok, _Rev1} = save_doc(Db, {[{<<"_id">>, <<"doc1">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev2} = save_doc(Db, {[{<<"_id">>, <<"doc2">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev3} = save_doc(Db, {[{<<"_id">>, <<"doc3">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev4} = save_doc(Db, {[{<<"_id">>, <<"doc4">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev5} = save_doc(Db, {[{<<"_id">>, <<"doc5">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev6} = save_doc(Db, {[{<<"_id">>, <<"doc6">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev7} = save_doc(Db, {[{<<"_id">>, <<"doc7">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev8} = save_doc(Db, {[{<<"_id">>, <<"doc8">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev9} = save_doc(Db, {[{<<"_id">>, <<"doc9">>}]}),
+
+             Heartbeats = get_heartbeats(Consumer),
+             ?assert(Heartbeats > 0),
+
+             {ok, _Rev10} = save_doc(Db, {[{<<"_id">>, <<"doc10">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev11} = save_doc(Db, {[{<<"_id">>, <<"doc11">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev12} = save_doc(Db, {[{<<"_id">>, <<"doc12">>}]}),
+
+             Heartbeats2 = get_heartbeats(Consumer),
+             ?assert(Heartbeats2 > Heartbeats),
+
+             Rows = get_rows(Consumer),
+             ?assertEqual(3, length(Rows)),
+
+             {ok, _Rev13} = save_doc(Db, {[{<<"_id">>, <<"doc13">>}]}),
+             timer:sleep(Timeout),
+             {ok, _Rev14} = save_doc(Db, {[{<<"_id">>, <<"doc14">>}]}),
+             timer:sleep(Timeout),
+
+             Heartbeats3 = get_heartbeats(Consumer),
+             ?assert(Heartbeats3 > Heartbeats2)
+        end)}.
+
+
+save_doc(Db, Json) ->
+    Doc = couch_doc:from_json_obj(Json),
+    {ok, Rev} = couch_db:update_doc(Db, Doc, []),
+    {ok, couch_doc:rev_to_str(Rev)}.
+
+get_rows(Consumer) ->
+    Ref = make_ref(),
+    Consumer ! {get_rows, Ref},
+    Resp = receive
+        {rows, Ref, Rows} ->
+            Rows
+    after ?TIMEOUT ->
+        timeout
+    end,
+    ?assertNotEqual(timeout, Resp),
+    Resp.
+
+get_heartbeats(Consumer) ->
+    Ref = make_ref(),
+    Consumer ! {get_heartbeats, Ref},
+    Resp = receive
+        {hearthbeats, Ref, HeartBeats} ->
+            HeartBeats
+    after ?TIMEOUT ->
+        timeout
+    end,
+    ?assertNotEqual(timeout, Resp),
+    Resp.
+
+clear_rows(Consumer) ->
+    Ref = make_ref(),
+    Consumer ! {reset, Ref},
+    Resp = receive
+        {ok, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end,
+    ?assertNotEqual(timeout, Resp),
+    Resp.
+
+stop_consumer(Consumer) ->
+    Ref = make_ref(),
+    Consumer ! {stop, Ref},
+    Resp = receive
+        {ok, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end,
+    ?assertNotEqual(timeout, Resp),
+    Resp.
+
+pause(Consumer) ->
+    Ref = make_ref(),
+    Consumer ! {pause, Ref},
+    Resp = receive
+        {paused, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end,
+    ?assertNotEqual(timeout, Resp),
+    Resp.
+
+unpause(Consumer) ->
+    Ref = make_ref(),
+    Consumer ! {continue, Ref},
+    Resp = receive
+        {ok, Ref} ->
+            ok
+    after ?TIMEOUT ->
+       timeout
+    end,
+    ?assertNotEqual(timeout, Resp),
+    Resp.
+
+wait_finished(_Consumer) ->
+    Resp = receive
+        {consumer_finished, Rows, LastSeq} ->
+            {Rows, LastSeq}
+    after ?TIMEOUT ->
+        timeout
+    end,
+    ?assertNotEqual(timeout, Resp),
+    Resp.
+
+spawn_consumer(DbName, ChangesArgs0, Req) ->
+    Parent = self(),
+    spawn(fun() ->
+        put(heartbeat_count, 0),
+        Callback = fun
+            ({change, {Change}, _}, _, Acc) ->
+                Id = couch_util:get_value(<<"id">>, Change),
+                Seq = couch_util:get_value(<<"seq">>, Change),
+                Del = couch_util:get_value(<<"deleted">>, Change, false),
+                [#row{id = Id, seq = Seq, deleted = Del} | Acc];
+            ({stop, LastSeq}, _, Acc) ->
+                Parent ! {consumer_finished, lists:reverse(Acc), LastSeq},
+                stop_loop(Parent, Acc);
+            (timeout, _, Acc) ->
+                put(heartbeat_count, get(heartbeat_count) + 1),
+                maybe_pause(Parent, Acc);
+            (_, _, Acc) ->
+                maybe_pause(Parent, Acc)
+        end,
+        {ok, Db} = couch_db:open_int(DbName, []),
+        ChangesArgs = case (ChangesArgs0#changes_args.timeout =:= undefined)
+            andalso (ChangesArgs0#changes_args.heartbeat =:= undefined) of
+            true ->
+                ChangesArgs0#changes_args{timeout = 10, heartbeat = 10};
+            false ->
+                ChangesArgs0
+        end,
+        FeedFun = couch_changes:handle_changes(ChangesArgs, Req, Db),
+        try
+            FeedFun({Callback, []})
+        catch throw:{stop, _} ->
+            ok
+        end,
+        catch couch_db:close(Db)
+    end).
+
+maybe_pause(Parent, Acc) ->
+    receive
+        {get_rows, Ref} ->
+            Parent ! {rows, Ref, lists:reverse(Acc)},
+            maybe_pause(Parent, Acc);
+        {get_heartbeats, Ref} ->
+            Parent ! {hearthbeats, Ref, get(heartbeat_count)},
+            maybe_pause(Parent, Acc);
+        {reset, Ref} ->
+            Parent ! {ok, Ref},
+            maybe_pause(Parent, []);
+        {pause, Ref} ->
+            Parent ! {paused, Ref},
+            pause_loop(Parent, Acc);
+        {stop, Ref} ->
+            Parent ! {ok, Ref},
+            throw({stop, Acc});
+        V ->
+            erlang:error({assertion_failed,
+                      [{module, ?MODULE},
+                       {line, ?LINE},
+                       {value, V},
+                       {reason, "Received unexpected message"}]})
+    after 0 ->
+        Acc
+    end.
+
+pause_loop(Parent, Acc) ->
+    receive
+        {stop, Ref} ->
+            Parent ! {ok, Ref},
+            throw({stop, Acc});
+        {reset, Ref} ->
+            Parent ! {ok, Ref},
+            pause_loop(Parent, []);
+        {continue, Ref} ->
+            Parent ! {ok, Ref},
+            Acc;
+        {get_rows, Ref} ->
+            Parent ! {rows, Ref, lists:reverse(Acc)},
+            pause_loop(Parent, Acc)
+    end.
+
+stop_loop(Parent, Acc) ->
+    receive
+        {get_rows, Ref} ->
+            Parent ! {rows, Ref, lists:reverse(Acc)},
+            stop_loop(Parent, Acc);
+        {stop, Ref} ->
+            Parent ! {ok, Ref},
+            Acc
+    end.
+
+create_db(DbName) ->
+    couch_db:create(DbName, [?ADMIN_USER, overwrite]).
+
+delete_db(DbName) ->
+    ok = couch_server:delete(DbName, [?ADMIN_USER]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_db_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_db_tests.erl b/test/couch_db_tests.erl
new file mode 100644
index 0000000..3089714
--- /dev/null
+++ b/test/couch_db_tests.erl
@@ -0,0 +1,114 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_db_tests).
+
+-include("couch_eunit.hrl").
+
+-define(TIMEOUT, 120).
+
+
+setup() ->
+    {ok, _} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    couch_config:set("log", "include_sasl", "false", false),
+    ok.
+
+teardown(_) ->
+    couch_server_sup:stop().
+
+
+create_delete_db_test_()->
+    {
+        "Database create/delete tests",
+        {
+            setup,
+            fun setup/0, fun teardown/1,
+            fun(_) ->
+                [should_create_db(),
+                 should_delete_db(),
+                 should_create_multiple_dbs(),
+                 should_delete_multiple_dbs(),
+                 should_create_delete_database_continuously()]
+            end
+        }
+    }.
+
+
+should_create_db() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, []),
+    ok = couch_db:close(Db),
+    {ok, AllDbs} = couch_server:all_databases(),
+    ?_assert(lists:member(DbName, AllDbs)).
+
+should_delete_db() ->
+    DbName = ?tempdb(),
+    couch_db:create(DbName, []),
+    couch_server:delete(DbName, []),
+    {ok, AllDbs} = couch_server:all_databases(),
+    ?_assertNot(lists:member(DbName, AllDbs)).
+
+should_create_multiple_dbs() ->
+    gen_server:call(couch_server, {set_max_dbs_open, 3}),
+
+    DbNames = [?tempdb() || _ <- lists:seq(1, 6)],
+    lists:foreach(fun(DbName) ->
+        {ok, Db} = couch_db:create(DbName, []),
+        ok = couch_db:close(Db)
+    end, DbNames),
+
+    {ok, AllDbs} = couch_server:all_databases(),
+    NumCreated = lists:foldl(fun(DbName, Acc) ->
+        ?assert(lists:member(DbName, AllDbs)),
+        Acc+1
+    end, 0, DbNames),
+
+    ?_assertEqual(NumCreated, 6).
+
+should_delete_multiple_dbs() ->
+    DbNames = [?tempdb() || _ <- lists:seq(1, 6)],
+    lists:foreach(fun(DbName) ->
+        {ok, Db} = couch_db:create(DbName, []),
+        ok = couch_db:close(Db)
+    end, DbNames),
+
+    lists:foreach(fun(DbName) ->
+        ok = couch_server:delete(DbName, [])
+    end, DbNames),
+
+    {ok, AllDbs} = couch_server:all_databases(),
+    NumDeleted = lists:foldl(fun(DbName, Acc) ->
+        ?assertNot(lists:member(DbName, AllDbs)),
+        Acc + 1
+    end, 0, DbNames),
+
+    ?_assertEqual(NumDeleted, 6).
+
+should_create_delete_database_continuously() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, []),
+    couch_db:close(Db),
+    [{timeout, ?TIMEOUT, {integer_to_list(N) ++ " times",
+                           ?_assert(loop(DbName, N))}}
+     || N <- [10, 100, 1000]].
+
+loop(_, 0) ->
+    true;
+loop(DbName, N) ->
+    ok = cycle(DbName),
+    loop(DbName, N - 1).
+
+cycle(DbName) ->
+    ok = couch_server:delete(DbName, []),
+    {ok, Db} = couch_db:create(DbName, []),
+    couch_db:close(Db),
+    ok.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_doc_json_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_doc_json_tests.erl b/test/couch_doc_json_tests.erl
new file mode 100644
index 0000000..1592b6b
--- /dev/null
+++ b/test/couch_doc_json_tests.erl
@@ -0,0 +1,391 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_doc_json_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+
+setup() ->
+    couch_config:start_link(?CONFIG_CHAIN),
+    couch_config:set("attachments", "compression_level", "0", false),
+    ok.
+
+teardown(_) ->
+    couch_config:stop().
+
+
+json_doc_test_() ->
+    {
+        setup,
+        fun setup/0, fun teardown/1,
+        [
+            {
+                "Document from JSON",
+                [
+                    from_json_success_cases(),
+                    from_json_error_cases()
+                ]
+            },
+            {
+                "Document to JSON",
+                [
+                    to_json_success_cases()
+                ]
+            }
+        ]
+    }.
+
+from_json_success_cases() ->
+    Cases = [
+        {
+            {[]},
+            #doc{},
+            "Return an empty document for an empty JSON object."
+        },
+        {
+            {[{<<"_id">>, <<"zing!">>}]},
+            #doc{id = <<"zing!">>},
+            "Parses document ids."
+        },
+        {
+            {[{<<"_id">>, <<"_design/foo">>}]},
+            #doc{id = <<"_design/foo">>},
+            "_design/document ids."
+        },
+        {
+            {[{<<"_id">>, <<"_local/bam">>}]},
+            #doc{id = <<"_local/bam">>},
+            "_local/document ids."
+        },
+        {
+            {[{<<"_rev">>, <<"4-230234">>}]},
+            #doc{revs = {4, [<<"230234">>]}},
+            "_rev stored in revs."
+        },
+        {
+            {[{<<"soap">>, 35}]},
+            #doc{body = {[{<<"soap">>, 35}]}},
+            "Non underscore prefixed fields stored in body."
+        },
+        {
+            {[{<<"_attachments">>, {[
+                {<<"my_attachment.fu">>, {[
+                    {<<"stub">>, true},
+                    {<<"content_type">>, <<"application/awesome">>},
+                    {<<"length">>, 45}
+                ]}},
+                {<<"noahs_private_key.gpg">>, {[
+                    {<<"data">>, <<"SSBoYXZlIGEgcGV0IGZpc2gh">>},
+                    {<<"content_type">>, <<"application/pgp-signature">>}
+                ]}}
+            ]}}]},
+            #doc{atts = [
+                #att{
+                    name = <<"my_attachment.fu">>,
+                    data = stub,
+                    type = <<"application/awesome">>,
+                    att_len = 45,
+                    disk_len = 45,
+                    revpos = nil
+                },
+                #att{
+                    name = <<"noahs_private_key.gpg">>,
+                    data = <<"I have a pet fish!">>,
+                    type = <<"application/pgp-signature">>,
+                    att_len = 18,
+                    disk_len = 18,
+                    revpos = 0
+                }
+            ]},
+            "Attachments are parsed correctly."
+        },
+        {
+            {[{<<"_deleted">>, true}]},
+            #doc{deleted = true},
+            "_deleted controls the deleted field."
+        },
+        {
+            {[{<<"_deleted">>, false}]},
+            #doc{},
+            "{\"_deleted\": false} is ok."
+        },
+        {
+            {[
+                 {<<"_revisions">>,
+                  {[{<<"start">>, 4},
+                    {<<"ids">>, [<<"foo1">>, <<"phi3">>, <<"omega">>]}]}},
+                 {<<"_rev">>, <<"6-something">>}
+             ]},
+            #doc{revs = {4, [<<"foo1">>, <<"phi3">>, <<"omega">>]}},
+            "_revisions attribute are preferred to _rev."
+        },
+        {
+            {[{<<"_revs_info">>, dropping}]},
+            #doc{},
+            "Drops _revs_info."
+        },
+        {
+            {[{<<"_local_seq">>, dropping}]},
+            #doc{},
+            "Drops _local_seq."
+        },
+        {
+            {[{<<"_conflicts">>, dropping}]},
+            #doc{},
+            "Drops _conflicts."
+        },
+        {
+            {[{<<"_deleted_conflicts">>, dropping}]},
+            #doc{},
+            "Drops _deleted_conflicts."
+        }
+    ],
+    lists:map(
+        fun({EJson, Expect, Msg}) ->
+            {Msg, ?_assertMatch(Expect, couch_doc:from_json_obj(EJson))}
+        end,
+        Cases).
+
+from_json_error_cases() ->
+    Cases = [
+        {
+            [],
+            {bad_request, "Document must be a JSON object"},
+            "arrays are invalid"
+        },
+        {
+            4,
+            {bad_request, "Document must be a JSON object"},
+            "integers are invalid"
+        },
+        {
+            true,
+            {bad_request, "Document must be a JSON object"},
+            "literals are invalid"
+        },
+        {
+            {[{<<"_id">>, {[{<<"foo">>, 5}]}}]},
+            {bad_request, <<"Document id must be a string">>},
+            "Document id must be a string."
+        },
+        {
+            {[{<<"_id">>, <<"_random">>}]},
+            {bad_request,
+             <<"Only reserved document ids may start with underscore.">>},
+            "Disallow arbitrary underscore prefixed docids."
+        },
+        {
+            {[{<<"_rev">>, 5}]},
+            {bad_request, <<"Invalid rev format">>},
+            "_rev must be a string"
+        },
+        {
+            {[{<<"_rev">>, "foobar"}]},
+            {bad_request, <<"Invalid rev format">>},
+            "_rev must be %d-%s"
+        },
+        {
+            {[{<<"_rev">>, "foo-bar"}]},
+            "Error if _rev's integer expection is broken."
+        },
+        {
+            {[{<<"_revisions">>, {[{<<"start">>, true}]}}]},
+            {doc_validation, "_revisions.start isn't an integer."},
+            "_revisions.start must be an integer."
+        },
+        {
+            {[{<<"_revisions">>, {[{<<"start">>, 0}, {<<"ids">>, 5}]}}]},
+            {doc_validation, "_revisions.ids isn't a array."},
+            "_revions.ids must be a list."
+        },
+        {
+            {[{<<"_revisions">>, {[{<<"start">>, 0}, {<<"ids">>, [5]}]}}]},
+            {doc_validation, "RevId isn't a string"},
+            "Revision ids must be strings."
+        },
+        {
+            {[{<<"_something">>, 5}]},
+            {doc_validation, <<"Bad special document member: _something">>},
+            "Underscore prefix fields are reserved."
+        }
+    ],
+
+    lists:map(fun
+        ({EJson, Expect, Msg}) ->
+            Error = (catch couch_doc:from_json_obj(EJson)),
+            {Msg, ?_assertMatch(Expect, Error)};
+        ({EJson, Msg}) ->
+            try
+                couch_doc:from_json_obj(EJson),
+                {"Conversion failed to raise an exception", ?_assert(false)}
+            catch
+                _:_ -> {Msg, ?_assert(true)}
+            end
+    end, Cases).
+
+to_json_success_cases() ->
+    Cases = [
+        {
+            #doc{},
+            {[{<<"_id">>, <<"">>}]},
+            "Empty docs are {\"_id\": \"\"}"
+        },
+        {
+            #doc{id = <<"foo">>},
+            {[{<<"_id">>, <<"foo">>}]},
+            "_id is added."
+        },
+        {
+            #doc{revs = {5, ["foo"]}},
+            {[{<<"_id">>, <<>>}, {<<"_rev">>, <<"5-foo">>}]},
+            "_rev is added."
+        },
+        {
+            [revs],
+            #doc{revs = {5, [<<"first">>, <<"second">>]}},
+            {[
+                 {<<"_id">>, <<>>},
+                 {<<"_rev">>, <<"5-first">>},
+                 {<<"_revisions">>, {[
+                     {<<"start">>, 5},
+                     {<<"ids">>, [<<"first">>, <<"second">>]}
+                 ]}}
+             ]},
+            "_revisions include with revs option"
+        },
+        {
+            #doc{body = {[{<<"foo">>, <<"bar">>}]}},
+            {[{<<"_id">>, <<>>}, {<<"foo">>, <<"bar">>}]},
+            "Arbitrary fields are added."
+        },
+        {
+            #doc{deleted = true, body = {[{<<"foo">>, <<"bar">>}]}},
+            {[{<<"_id">>, <<>>}, {<<"foo">>, <<"bar">>}, {<<"_deleted">>, true}]},
+            "Deleted docs no longer drop body members."
+        },
+        {
+            #doc{meta = [
+                {revs_info, 4, [{<<"fin">>, deleted}, {<<"zim">>, missing}]}
+            ]},
+            {[
+                 {<<"_id">>, <<>>},
+                 {<<"_revs_info">>, [
+                     {[{<<"rev">>, <<"4-fin">>}, {<<"status">>, <<"deleted">>}]},
+                     {[{<<"rev">>, <<"3-zim">>}, {<<"status">>, <<"missing">>}]}
+                 ]}
+             ]},
+            "_revs_info field is added correctly."
+        },
+        {
+            #doc{meta = [{local_seq, 5}]},
+            {[{<<"_id">>, <<>>}, {<<"_local_seq">>, 5}]},
+            "_local_seq is added as an integer."
+        },
+        {
+            #doc{meta = [{conflicts, [{3, <<"yep">>}, {1, <<"snow">>}]}]},
+            {[
+                {<<"_id">>, <<>>},
+                {<<"_conflicts">>, [<<"3-yep">>, <<"1-snow">>]}
+            ]},
+            "_conflicts is added as an array of strings."
+        },
+        {
+            #doc{meta = [{deleted_conflicts, [{10923, <<"big_cowboy_hat">>}]}]},
+            {[
+                 {<<"_id">>, <<>>},
+                 {<<"_deleted_conflicts">>, [<<"10923-big_cowboy_hat">>]}
+             ]},
+            "_deleted_conflicsts is added as an array of strings."
+        },
+        {
+            #doc{atts = [
+                #att{
+                    name = <<"big.xml">>,
+                    type = <<"xml/sucks">>,
+                    data = fun() -> ok end,
+                    revpos = 1,
+                    att_len = 400,
+                    disk_len = 400
+                },
+                #att{
+                    name = <<"fast.json">>,
+                    type = <<"json/ftw">>,
+                    data = <<"{\"so\": \"there!\"}">>,
+                    revpos = 1,
+                    att_len = 16,
+                    disk_len = 16
+                }
+            ]},
+            {[
+                 {<<"_id">>, <<>>},
+                 {<<"_attachments">>, {[
+                       {<<"big.xml">>, {[
+                           {<<"content_type">>, <<"xml/sucks">>},
+                           {<<"revpos">>, 1},
+                           {<<"length">>, 400},
+                           {<<"stub">>, true}
+                       ]}},
+                       {<<"fast.json">>, {[
+                           {<<"content_type">>, <<"json/ftw">>},
+                           {<<"revpos">>, 1},
+                           {<<"length">>, 16},
+                           {<<"stub">>, true}
+                       ]}}
+                ]}}
+            ]},
+            "Attachments attached as stubs only include a length."
+        },
+        {
+            [attachments],
+            #doc{atts = [
+                #att{
+                    name = <<"stuff.txt">>,
+                    type = <<"text/plain">>,
+                    data = fun() -> <<"diet pepsi">> end,
+                    revpos = 1,
+                    att_len = 10,
+                    disk_len = 10
+                },
+                #att{
+                    name = <<"food.now">>,
+                    type = <<"application/food">>,
+                    revpos = 1,
+                    data = <<"sammich">>
+                }
+            ]},
+            {[
+                {<<"_id">>, <<>>},
+                {<<"_attachments">>, {[
+                   {<<"stuff.txt">>, {[
+                       {<<"content_type">>, <<"text/plain">>},
+                       {<<"revpos">>, 1},
+                       {<<"data">>, <<"ZGlldCBwZXBzaQ==">>}
+                   ]}},
+                   {<<"food.now">>, {[
+                       {<<"content_type">>, <<"application/food">>},
+                       {<<"revpos">>, 1},
+                       {<<"data">>, <<"c2FtbWljaA==">>}
+                   ]}}
+                ]}}
+            ]},
+            "Attachments included inline with attachments option."
+        }
+    ],
+
+    lists:map(fun
+        ({Doc, EJson, Msg}) ->
+            {Msg, ?_assertMatch(EJson, couch_doc:to_json_obj(Doc, []))};
+        ({Options, Doc, EJson, Msg}) ->
+            {Msg, ?_assertMatch(EJson, couch_doc:to_json_obj(Doc, Options))}
+    end, Cases).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_file_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_file_tests.erl b/test/couch_file_tests.erl
new file mode 100644
index 0000000..ad13383
--- /dev/null
+++ b/test/couch_file_tests.erl
@@ -0,0 +1,265 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_file_tests).
+
+-include("couch_eunit.hrl").
+
+-define(BLOCK_SIZE, 4096).
+-define(setup(F), {setup, fun setup/0, fun teardown/1, F}).
+-define(foreach(Fs), {foreach, fun setup/0, fun teardown/1, Fs}).
+
+
+setup() ->
+    {ok, Fd} = couch_file:open(?tempfile(), [create, overwrite]),
+    Fd.
+
+teardown(Fd) ->
+    ok = couch_file:close(Fd).
+
+
+open_close_test_() ->
+    {
+        "Test for proper file open and close",
+        [
+            should_return_enoent_if_missed(),
+            should_ignore_invalid_flags_with_open(),
+            ?setup(fun should_return_pid_on_file_open/1),
+            should_close_file_properly(),
+            ?setup(fun should_create_empty_new_files/1)
+        ]
+    }.
+
+should_return_enoent_if_missed() ->
+    ?_assertEqual({error, enoent}, couch_file:open("not a real file")).
+
+should_ignore_invalid_flags_with_open() ->
+    ?_assertMatch({ok, _},
+                  couch_file:open(?tempfile(), [create, invalid_option])).
+
+should_return_pid_on_file_open(Fd) ->
+    ?_assert(is_pid(Fd)).
+
+should_close_file_properly() ->
+    {ok, Fd} = couch_file:open(?tempfile(), [create, overwrite]),
+    ok = couch_file:close(Fd),
+    ?_assert(true).
+
+should_create_empty_new_files(Fd) ->
+    ?_assertMatch({ok, 0}, couch_file:bytes(Fd)).
+
+
+read_write_test_() ->
+    {
+        "Common file read/write tests",
+        ?foreach([
+            fun should_increase_file_size_on_write/1,
+            fun should_return_current_file_size_on_write/1,
+            fun should_write_and_read_term/1,
+            fun should_write_and_read_binary/1,
+            fun should_write_and_read_large_binary/1,
+            fun should_return_term_as_binary_for_reading_binary/1,
+            fun should_read_term_written_as_binary/1,
+            fun should_read_iolist/1,
+            fun should_fsync/1,
+            fun should_not_read_beyond_eof/1,
+            fun should_truncate/1
+        ])
+    }.
+
+
+should_increase_file_size_on_write(Fd) ->
+    {ok, 0, _} = couch_file:append_term(Fd, foo),
+    {ok, Size} = couch_file:bytes(Fd),
+    ?_assert(Size > 0).
+
+should_return_current_file_size_on_write(Fd) ->
+    {ok, 0, _} = couch_file:append_term(Fd, foo),
+    {ok, Size} = couch_file:bytes(Fd),
+    ?_assertMatch({ok, Size, _}, couch_file:append_term(Fd, bar)).
+
+should_write_and_read_term(Fd) ->
+    {ok, Pos, _} = couch_file:append_term(Fd, foo),
+    ?_assertMatch({ok, foo}, couch_file:pread_term(Fd, Pos)).
+
+should_write_and_read_binary(Fd) ->
+    {ok, Pos, _} = couch_file:append_binary(Fd, <<"fancy!">>),
+    ?_assertMatch({ok, <<"fancy!">>}, couch_file:pread_binary(Fd, Pos)).
+
+should_return_term_as_binary_for_reading_binary(Fd) ->
+    {ok, Pos, _} = couch_file:append_term(Fd, foo),
+    Foo = couch_compress:compress(foo, snappy),
+    ?_assertMatch({ok, Foo}, couch_file:pread_binary(Fd, Pos)).
+
+should_read_term_written_as_binary(Fd) ->
+    {ok, Pos, _} = couch_file:append_binary(Fd, <<131,100,0,3,102,111,111>>),
+    ?_assertMatch({ok, foo}, couch_file:pread_term(Fd, Pos)).
+
+should_write_and_read_large_binary(Fd) ->
+    BigBin = list_to_binary(lists:duplicate(100000, 0)),
+    {ok, Pos, _} = couch_file:append_binary(Fd, BigBin),
+    ?_assertMatch({ok, BigBin}, couch_file:pread_binary(Fd, Pos)).
+
+should_read_iolist(Fd) ->
+    %% append_binary == append_iolist?
+    %% Possible bug in pread_iolist or iolist() -> append_binary
+    {ok, Pos, _} = couch_file:append_binary(Fd, ["foo", $m, <<"bam">>]),
+    {ok, IoList} = couch_file:pread_iolist(Fd, Pos),
+    ?_assertMatch(<<"foombam">>, iolist_to_binary(IoList)).
+
+should_fsync(Fd) ->
+    {"How does on test fsync?", ?_assertMatch(ok, couch_file:sync(Fd))}.
+
+should_not_read_beyond_eof(_) ->
+    {"No idea how to test reading beyond EOF", ?_assert(true)}.
+
+should_truncate(Fd) ->
+    {ok, 0, _} = couch_file:append_term(Fd, foo),
+    {ok, Size} = couch_file:bytes(Fd),
+    BigBin = list_to_binary(lists:duplicate(100000, 0)),
+    {ok, _, _} = couch_file:append_binary(Fd, BigBin),
+    ok = couch_file:truncate(Fd, Size),
+    ?_assertMatch({ok, foo}, couch_file:pread_term(Fd, 0)).
+
+
+header_test_() ->
+    {
+        "File header read/write tests",
+        [
+            ?foreach([
+                fun should_write_and_read_atom_header/1,
+                fun should_write_and_read_tuple_header/1,
+                fun should_write_and_read_second_header/1,
+                fun should_truncate_second_header/1,
+                fun should_produce_same_file_size_on_rewrite/1,
+                fun should_save_headers_larger_than_block_size/1
+            ]),
+            should_recover_header_marker_corruption(),
+            should_recover_header_size_corruption(),
+            should_recover_header_md5sig_corruption(),
+            should_recover_header_data_corruption()
+        ]
+    }.
+
+
+should_write_and_read_atom_header(Fd) ->
+    ok = couch_file:write_header(Fd, hello),
+    ?_assertMatch({ok, hello}, couch_file:read_header(Fd)).
+
+should_write_and_read_tuple_header(Fd) ->
+    ok = couch_file:write_header(Fd, {<<"some_data">>, 32}),
+    ?_assertMatch({ok, {<<"some_data">>, 32}}, couch_file:read_header(Fd)).
+
+should_write_and_read_second_header(Fd) ->
+    ok = couch_file:write_header(Fd, {<<"some_data">>, 32}),
+    ok = couch_file:write_header(Fd, [foo, <<"more">>]),
+    ?_assertMatch({ok, [foo, <<"more">>]}, couch_file:read_header(Fd)).
+
+should_truncate_second_header(Fd) ->
+    ok = couch_file:write_header(Fd, {<<"some_data">>, 32}),
+    {ok, Size} = couch_file:bytes(Fd),
+    ok = couch_file:write_header(Fd, [foo, <<"more">>]),
+    ok = couch_file:truncate(Fd, Size),
+    ?_assertMatch({ok, {<<"some_data">>, 32}}, couch_file:read_header(Fd)).
+
+should_produce_same_file_size_on_rewrite(Fd) ->
+    ok = couch_file:write_header(Fd, {<<"some_data">>, 32}),
+    {ok, Size1} = couch_file:bytes(Fd),
+    ok = couch_file:write_header(Fd, [foo, <<"more">>]),
+    {ok, Size2} = couch_file:bytes(Fd),
+    ok = couch_file:truncate(Fd, Size1),
+    ok = couch_file:write_header(Fd, [foo, <<"more">>]),
+    ?_assertMatch({ok, Size2}, couch_file:bytes(Fd)).
+
+should_save_headers_larger_than_block_size(Fd) ->
+    Header = erlang:make_tuple(5000, <<"CouchDB">>),
+    couch_file:write_header(Fd, Header),
+    {"COUCHDB-1319", ?_assertMatch({ok, Header}, couch_file:read_header(Fd))}.
+
+
+should_recover_header_marker_corruption() ->
+    ?_assertMatch(
+        ok,
+        check_header_recovery(
+            fun(CouchFd, RawFd, Expect, HeaderPos) ->
+                ?assertNotMatch(Expect, couch_file:read_header(CouchFd)),
+                file:pwrite(RawFd, HeaderPos, <<0>>),
+                ?assertMatch(Expect, couch_file:read_header(CouchFd))
+            end)
+    ).
+
+should_recover_header_size_corruption() ->
+    ?_assertMatch(
+        ok,
+        check_header_recovery(
+            fun(CouchFd, RawFd, Expect, HeaderPos) ->
+                ?assertNotMatch(Expect, couch_file:read_header(CouchFd)),
+                % +1 for 0x1 byte marker
+                file:pwrite(RawFd, HeaderPos + 1, <<10/integer>>),
+                ?assertMatch(Expect, couch_file:read_header(CouchFd))
+            end)
+    ).
+
+should_recover_header_md5sig_corruption() ->
+    ?_assertMatch(
+        ok,
+        check_header_recovery(
+            fun(CouchFd, RawFd, Expect, HeaderPos) ->
+                ?assertNotMatch(Expect, couch_file:read_header(CouchFd)),
+                % +5 = +1 for 0x1 byte and +4 for term size.
+                file:pwrite(RawFd, HeaderPos + 5, <<"F01034F88D320B22">>),
+                ?assertMatch(Expect, couch_file:read_header(CouchFd))
+            end)
+    ).
+
+should_recover_header_data_corruption() ->
+    ?_assertMatch(
+        ok,
+        check_header_recovery(
+            fun(CouchFd, RawFd, Expect, HeaderPos) ->
+                ?assertNotMatch(Expect, couch_file:read_header(CouchFd)),
+                % +21 = +1 for 0x1 byte, +4 for term size and +16 for MD5 sig
+                file:pwrite(RawFd, HeaderPos + 21, <<"some data goes here!">>),
+                ?assertMatch(Expect, couch_file:read_header(CouchFd))
+            end)
+    ).
+
+
+check_header_recovery(CheckFun) ->
+    Path = ?tempfile(),
+    {ok, Fd} = couch_file:open(Path, [create, overwrite]),
+    {ok, RawFd} = file:open(Path, [read, write, raw, binary]),
+
+    {ok, _} = write_random_data(Fd),
+    ExpectHeader = {some_atom, <<"a binary">>, 756},
+    ok = couch_file:write_header(Fd, ExpectHeader),
+
+    {ok, HeaderPos} = write_random_data(Fd),
+    ok = couch_file:write_header(Fd, {2342, <<"corruption! greed!">>}),
+
+    CheckFun(Fd, RawFd, {ok, ExpectHeader}, HeaderPos),
+
+    ok = file:close(RawFd),
+    ok = couch_file:close(Fd),
+    ok.
+
+write_random_data(Fd) ->
+    write_random_data(Fd, 100 + random:uniform(1000)).
+
+write_random_data(Fd, 0) ->
+    {ok, Bytes} = couch_file:bytes(Fd),
+    {ok, (1 + Bytes div ?BLOCK_SIZE) * ?BLOCK_SIZE};
+write_random_data(Fd, N) ->
+    Choices = [foo, bar, <<"bizzingle">>, "bank", ["rough", stuff]],
+    Term = lists:nth(random:uniform(4) + 1, Choices),
+    {ok, _, _} = couch_file:append_term(Fd, Term),
+    write_random_data(Fd, N - 1).


[31/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Add .eunit to .gitignore


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/67dd9f5b
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/67dd9f5b
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/67dd9f5b

Branch: refs/heads/1963-eunit-bigcouch
Commit: 67dd9f5ba8aee6b2ea321930d9dcd76c43e9be2f
Parents: 1db1396
Author: Russell Branca <ch...@apache.org>
Authored: Mon Aug 11 15:40:14 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:23:39 2014 -0700

----------------------------------------------------------------------
 .gitignore | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/67dd9f5b/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index e5bb133..1e91829 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ ebin/
 priv/couch_js/config.h
 priv/couchjs
 priv/couchspawnkillable
+
+.eunit
\ No newline at end of file


[16/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 232-csp.t etap test suite to eunit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/ec28de18
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/ec28de18
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/ec28de18

Branch: refs/heads/1963-eunit-bigcouch
Commit: ec28de184a5af0b1553a9b6f9a76059e9e02e684
Parents: a8b7460
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Jun 10 19:54:20 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:40 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_csp_tests.erl | 96 +++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/ec28de18/test/couchdb/couchdb_csp_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_csp_tests.erl b/test/couchdb/couchdb_csp_tests.erl
new file mode 100644
index 0000000..adb0e6d
--- /dev/null
+++ b/test/couchdb/couchdb_csp_tests.erl
@@ -0,0 +1,96 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_csp_tests).
+
+-include("couch_eunit.hrl").
+
+-define(TIMEOUT, 1000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    Pid.
+
+stop(Pid) ->
+    couch_server_sup:stop(),
+    erlang:monitor(process, Pid),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    ok = couch_config:set("csp", "enable", "true", false),
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    lists:concat(["http://", Addr, ":", Port, "/_utils/"]).
+
+teardown(_) ->
+    ok.
+
+
+csp_test_() ->
+    {
+        "Content Security Policy tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_not_return_any_csp_headers_when_disabled/1,
+                    fun should_apply_default_policy/1,
+                    fun should_return_custom_policy/1,
+                    fun should_only_enable_csp_when_true/1
+                ]
+            }
+        }
+    }.
+
+
+should_not_return_any_csp_headers_when_disabled(Url) ->
+    ?_assertEqual(undefined,
+        begin
+            ok = couch_config:set("csp", "enable", "false", false),
+            {ok, _, Headers, _} = test_request:get(Url),
+            proplists:get_value("Content-Security-Policy", Headers)
+        end).
+
+should_apply_default_policy(Url) ->
+    ?_assertEqual(
+        "default-src 'self'; img-src 'self'; font-src 'self'; "
+        "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';",
+        begin
+            {ok, _, Headers, _} = test_request:get(Url),
+            proplists:get_value("Content-Security-Policy", Headers)
+        end).
+
+should_return_custom_policy(Url) ->
+    ?_assertEqual("default-src 'http://example.com';",
+        begin
+            ok = couch_config:set("csp", "header_value",
+                                  "default-src 'http://example.com';", false),
+            {ok, _, Headers, _} = test_request:get(Url),
+            proplists:get_value("Content-Security-Policy", Headers)
+        end).
+
+should_only_enable_csp_when_true(Url) ->
+    ?_assertEqual(undefined,
+        begin
+            ok = couch_config:set("csp", "enable", "tru", false),
+            {ok, _, Headers, _} = test_request:get(Url),
+            proplists:get_value("Content-Security-Policy", Headers)
+        end).


[39/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Use mochiweb_http:stop instead of gen_server:cast(.., stop)

The version of mochiweb currently used does not support sending a cast
stop message, so use the builtin stop function.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/f49e1a68
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/f49e1a68
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/f49e1a68

Branch: refs/heads/1963-eunit-bigcouch
Commit: f49e1a687b4dc0e209a673ee6632ea9af1e5c0e0
Parents: 98e4a99
Author: Russell Branca <ch...@apache.org>
Authored: Thu Aug 21 13:40:08 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 13:49:08 2014 -0700

----------------------------------------------------------------------
 test/test_web.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/f49e1a68/test/test_web.erl
----------------------------------------------------------------------
diff --git a/test/test_web.erl b/test/test_web.erl
index 98172d0..d568b7e 100644
--- a/test/test_web.erl
+++ b/test/test_web.erl
@@ -69,7 +69,7 @@ terminate(_Reason, _State) ->
     ok.
 
 stop() ->
-    gen_server:cast(?SERVER, stop).
+    mochiweb_http:stop(?SERVER).
 
 
 handle_call({check_request, Req}, _From, State) when is_function(State, 1) ->


[34/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Delete tests for the no longer present couch_ref_counter.erl


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/7fba43af
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/7fba43af
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/7fba43af

Branch: refs/heads/1963-eunit-bigcouch
Commit: 7fba43afa8207cbc08980900f5ec0fb60208613c
Parents: 244f360
Author: Russell Branca <ch...@apache.org>
Authored: Wed Aug 13 14:24:28 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:24:50 2014 -0700

----------------------------------------------------------------------
 test/couch_ref_counter_tests.erl | 107 ----------------------------------
 1 file changed, 107 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/7fba43af/test/couch_ref_counter_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_ref_counter_tests.erl b/test/couch_ref_counter_tests.erl
deleted file mode 100644
index 0217623..0000000
--- a/test/couch_ref_counter_tests.erl
+++ /dev/null
@@ -1,107 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_ref_counter_tests).
-
--include_lib("couch/include/couch_eunit.hrl").
--include_lib("couch/include/couch_db.hrl").
-
--define(TIMEOUT, 1000).
-
-
-setup() ->
-    {ok, RefCtr} = couch_ref_counter:start([]),
-    ChildPid = spawn(fun() -> loop() end),
-    {RefCtr, ChildPid}.
-
-teardown({_, ChildPid}) ->
-    erlang:monitor(process, ChildPid),
-    ChildPid ! close,
-    wait().
-
-
-couch_ref_counter_test_() ->
-    {
-        "CouchDB reference counter tests",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [
-                fun should_initialize_with_calling_process_as_referrer/1,
-                fun should_ignore_unknown_pid/1,
-                fun should_increment_counter_on_pid_add/1,
-                fun should_not_increase_counter_on_readding_same_pid/1,
-                fun should_drop_ref_for_double_added_pid/1,
-                fun should_decrement_counter_on_pid_drop/1,
-                fun should_add_after_drop/1,
-                fun should_decrement_counter_on_process_exit/1
-
-            ]
-        }
-    }.
-
-
-should_initialize_with_calling_process_as_referrer({RefCtr, _}) ->
-    ?_assertEqual(1, couch_ref_counter:count(RefCtr)).
-
-should_ignore_unknown_pid({RefCtr, ChildPid}) ->
-    ?_assertEqual(ok, couch_ref_counter:drop(RefCtr, ChildPid)).
-
-should_increment_counter_on_pid_add({RefCtr, ChildPid}) ->
-    couch_ref_counter:add(RefCtr, ChildPid),
-    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
-
-should_not_increase_counter_on_readding_same_pid({RefCtr, ChildPid}) ->
-    couch_ref_counter:add(RefCtr, ChildPid),
-    couch_ref_counter:add(RefCtr, ChildPid),
-    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
-
-should_drop_ref_for_double_added_pid({RefCtr, ChildPid}) ->
-    couch_ref_counter:add(RefCtr, ChildPid),
-    couch_ref_counter:add(RefCtr, ChildPid),
-    couch_ref_counter:drop(RefCtr, ChildPid),
-    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
-
-should_decrement_counter_on_pid_drop({RefCtr, ChildPid}) ->
-    couch_ref_counter:add(RefCtr, ChildPid),
-    couch_ref_counter:drop(RefCtr, ChildPid),
-    ?_assertEqual(1, couch_ref_counter:count(RefCtr)).
-
-should_add_after_drop({RefCtr, ChildPid}) ->
-    couch_ref_counter:add(RefCtr, ChildPid),
-    couch_ref_counter:drop(RefCtr, ChildPid),
-    couch_ref_counter:add(RefCtr, ChildPid),
-    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
-
-should_decrement_counter_on_process_exit({RefCtr, ChildPid}) ->
-    ?_assertEqual(1,
-        begin
-            couch_ref_counter:add(RefCtr, ChildPid),
-            erlang:monitor(process, ChildPid),
-            ChildPid ! close,
-            wait(),
-            couch_ref_counter:count(RefCtr)
-        end).
-
-
-loop() ->
-    receive
-        close -> ok
-    end.
-
-wait() ->
-    receive
-        {'DOWN', _, _, _, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw(timeout_error)
-    end.


[30/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Update include paths


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/86f1d342
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/86f1d342
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/86f1d342

Branch: refs/heads/1963-eunit-bigcouch
Commit: 86f1d3421d9d89a8be44a7e19ee9b58396624fa7
Parents: e40ca37
Author: Russell Branca <ch...@apache.org>
Authored: Mon Aug 11 14:50:15 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:23:20 2014 -0700

----------------------------------------------------------------------
 test/couch_auth_cache_tests.erl         | 18 ++----------------
 test/couch_btree_tests.erl              |  4 ++--
 test/couch_changes_tests.erl            |  4 ++--
 test/couch_db_tests.erl                 |  2 +-
 test/couch_doc_json_tests.erl           |  4 ++--
 test/couch_file_tests.erl               |  2 +-
 test/couch_key_tree_tests.erl           |  2 +-
 test/couch_passwords_tests.erl          |  2 +-
 test/couch_ref_counter_tests.erl        |  4 ++--
 test/couch_stats_tests.erl              |  4 ++--
 test/couch_stream_tests.erl             |  2 +-
 test/couch_task_status_tests.erl        |  4 ++--
 test/couch_util_tests.erl               |  2 +-
 test/couch_uuids_tests.erl              |  2 +-
 test/couch_work_queue_tests.erl         |  2 +-
 test/couchdb_attachments_tests.erl      |  4 ++--
 test/couchdb_compaction_daemon.erl      |  4 ++--
 test/couchdb_cors_tests.erl             |  4 ++--
 test/couchdb_csp_tests.erl              |  2 +-
 test/couchdb_file_compression_tests.erl |  4 ++--
 test/couchdb_http_proxy_tests.erl       |  2 +-
 test/couchdb_os_daemons_tests.erl       |  2 +-
 test/couchdb_os_proc_pool.erl           |  4 ++--
 test/couchdb_update_conflicts_tests.erl |  4 ++--
 test/couchdb_vhosts_tests.erl           |  4 ++--
 test/couchdb_views_tests.erl            |  4 ++--
 test/json_stream_parse_tests.erl        |  2 +-
 test/test_web.erl                       |  2 +-
 28 files changed, 43 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_auth_cache_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_auth_cache_tests.erl b/test/couch_auth_cache_tests.erl
index 3b2321c..b2c43c3 100644
--- a/test/couch_auth_cache_tests.erl
+++ b/test/couch_auth_cache_tests.erl
@@ -12,28 +12,14 @@
 
 -module(couch_auth_cache_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(SALT, <<"SALT">>).
 -define(TIMEOUT, 1000).
 
 
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
 setup() ->
     DbName = ?tempdb(),
     couch_config:set("couch_httpd_auth", "authentication_db",

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_btree_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_btree_tests.erl b/test/couch_btree_tests.erl
index 911640f..60469cf 100644
--- a/test/couch_btree_tests.erl
+++ b/test/couch_btree_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couch_btree_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(ROWS, 1000).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_changes_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_changes_tests.erl b/test/couch_changes_tests.erl
index a129ba2..7fd4c55 100644
--- a/test/couch_changes_tests.erl
+++ b/test/couch_changes_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couch_changes_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(ADMIN_USER, {user_ctx, #user_ctx{roles = [<<"_admin">>]}}).
 -define(TIMEOUT, 3000).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_db_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_db_tests.erl b/test/couch_db_tests.erl
index 3089714..d1fdf06 100644
--- a/test/couch_db_tests.erl
+++ b/test/couch_db_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couch_db_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 -define(TIMEOUT, 120).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_doc_json_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_doc_json_tests.erl b/test/couch_doc_json_tests.erl
index 1592b6b..23fc961 100644
--- a/test/couch_doc_json_tests.erl
+++ b/test/couch_doc_json_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couch_doc_json_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 
 setup() ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_file_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_file_tests.erl b/test/couch_file_tests.erl
index ad13383..9b167ca 100644
--- a/test/couch_file_tests.erl
+++ b/test/couch_file_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couch_file_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 -define(BLOCK_SIZE, 4096).
 -define(setup(F), {setup, fun setup/0, fun teardown/1, F}).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_key_tree_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_key_tree_tests.erl b/test/couch_key_tree_tests.erl
index 753ecc4..7d68902 100644
--- a/test/couch_key_tree_tests.erl
+++ b/test/couch_key_tree_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couch_key_tree_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 -define(DEPTH, 10).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_passwords_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_passwords_tests.erl b/test/couch_passwords_tests.erl
index 116265c..dea6d6b 100644
--- a/test/couch_passwords_tests.erl
+++ b/test/couch_passwords_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couch_passwords_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 
 pbkdf2_test_()->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_ref_counter_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_ref_counter_tests.erl b/test/couch_ref_counter_tests.erl
index b7e97b4..0217623 100644
--- a/test/couch_ref_counter_tests.erl
+++ b/test/couch_ref_counter_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couch_ref_counter_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(TIMEOUT, 1000).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_stats_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_stats_tests.erl b/test/couch_stats_tests.erl
index d156449..6dbbb6a 100644
--- a/test/couch_stats_tests.erl
+++ b/test/couch_stats_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couch_stats_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(STATS_CFG_FIXTURE,
     filename:join([?FIXTURESDIR, "couch_stats_aggregates.cfg"])).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_stream_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_stream_tests.erl b/test/couch_stream_tests.erl
index 335a2fe..3e84ca0 100644
--- a/test/couch_stream_tests.erl
+++ b/test/couch_stream_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couch_stream_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 
 setup() ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_task_status_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_task_status_tests.erl b/test/couch_task_status_tests.erl
index f71ad2b..200cee1 100644
--- a/test/couch_task_status_tests.erl
+++ b/test/couch_task_status_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couch_task_status_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(TIMEOUT, 1000).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_util_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_util_tests.erl b/test/couch_util_tests.erl
index 8e24e72..17e6942 100644
--- a/test/couch_util_tests.erl
+++ b/test/couch_util_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couch_util_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 
 setup() ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_uuids_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_uuids_tests.erl b/test/couch_uuids_tests.erl
index ea1d034..754a49b 100644
--- a/test/couch_uuids_tests.erl
+++ b/test/couch_uuids_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couch_uuids_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 -define(TIMEOUT_S, 20).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couch_work_queue_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_work_queue_tests.erl b/test/couch_work_queue_tests.erl
index 8a463b5..c94dc00 100644
--- a/test/couch_work_queue_tests.erl
+++ b/test/couch_work_queue_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couch_work_queue_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 -define(TIMEOUT, 100).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_attachments_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_attachments_tests.erl b/test/couchdb_attachments_tests.erl
index cf59785..70587f8 100644
--- a/test/couchdb_attachments_tests.erl
+++ b/test/couchdb_attachments_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couchdb_attachments_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(COMPRESSION_LEVEL, 8).
 -define(ATT_BIN_NAME, <<"logo.png">>).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_compaction_daemon.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_compaction_daemon.erl b/test/couchdb_compaction_daemon.erl
index 725a97b..39e6118 100644
--- a/test/couchdb_compaction_daemon.erl
+++ b/test/couchdb_compaction_daemon.erl
@@ -12,8 +12,8 @@
 
 -module(couchdb_compaction_daemon).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(DELAY, 100).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_cors_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_cors_tests.erl b/test/couchdb_cors_tests.erl
index 4e88ae7..9f927c4 100644
--- a/test/couchdb_cors_tests.erl
+++ b/test/couchdb_cors_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couchdb_cors_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 
 -define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_csp_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_csp_tests.erl b/test/couchdb_csp_tests.erl
index adb0e6d..9733ad4 100644
--- a/test/couchdb_csp_tests.erl
+++ b/test/couchdb_csp_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couchdb_csp_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 -define(TIMEOUT, 1000).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_file_compression_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_file_compression_tests.erl b/test/couchdb_file_compression_tests.erl
index fd3f513..dc3f939 100644
--- a/test/couchdb_file_compression_tests.erl
+++ b/test/couchdb_file_compression_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couchdb_file_compression_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(DDOC_ID, <<"_design/test">>).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_http_proxy_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_http_proxy_tests.erl b/test/couchdb_http_proxy_tests.erl
index acb1974..262eb15 100644
--- a/test/couchdb_http_proxy_tests.erl
+++ b/test/couchdb_http_proxy_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couchdb_http_proxy_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 -record(req, {method=get, path="", headers=[], body="", opts=[]}).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_os_daemons_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_os_daemons_tests.erl b/test/couchdb_os_daemons_tests.erl
index aa949c9..ef21f58 100644
--- a/test/couchdb_os_daemons_tests.erl
+++ b/test/couchdb_os_daemons_tests.erl
@@ -12,7 +12,7 @@
 
 -module(couchdb_os_daemons_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 %% keep in sync with couchdb/couch_os_daemons.erl
 -record(daemon, {

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_os_proc_pool.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_os_proc_pool.erl b/test/couchdb_os_proc_pool.erl
index 1bb266e..1a37bd6 100644
--- a/test/couchdb_os_proc_pool.erl
+++ b/test/couchdb_os_proc_pool.erl
@@ -12,8 +12,8 @@
 
 -module(couchdb_os_proc_pool).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(TIMEOUT, 3000).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_update_conflicts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_update_conflicts_tests.erl b/test/couchdb_update_conflicts_tests.erl
index 7226860..adf1e5c 100644
--- a/test/couchdb_update_conflicts_tests.erl
+++ b/test/couchdb_update_conflicts_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couchdb_update_conflicts_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(i2l(I), integer_to_list(I)).
 -define(ADMIN_USER, {userctx, #user_ctx{roles=[<<"_admin">>]}}).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_vhosts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_vhosts_tests.erl b/test/couchdb_vhosts_tests.erl
index 94b1957..fc55c1c 100644
--- a/test/couchdb_vhosts_tests.erl
+++ b/test/couchdb_vhosts_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couchdb_vhosts_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
 -define(TIMEOUT, 1000).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_views_tests.erl b/test/couchdb_views_tests.erl
index 6d81f32..24b5e5b 100644
--- a/test/couchdb_views_tests.erl
+++ b/test/couchdb_views_tests.erl
@@ -12,8 +12,8 @@
 
 -module(couchdb_views_tests).
 
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
 -include_lib("couch_mrview/include/couch_mrview.hrl").
 
 -define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/json_stream_parse_tests.erl
----------------------------------------------------------------------
diff --git a/test/json_stream_parse_tests.erl b/test/json_stream_parse_tests.erl
index 92303b6..ffcf918 100644
--- a/test/json_stream_parse_tests.erl
+++ b/test/json_stream_parse_tests.erl
@@ -12,7 +12,7 @@
 
 -module(json_stream_parse_tests).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 -define(CASES,
     [

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/86f1d342/test/test_web.erl
----------------------------------------------------------------------
diff --git a/test/test_web.erl b/test/test_web.erl
index 1de2cd1..98172d0 100644
--- a/test/test_web.erl
+++ b/test/test_web.erl
@@ -13,7 +13,7 @@
 -module(test_web).
 -behaviour(gen_server).
 
--include("couch_eunit.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
 
 -export([start_link/0, stop/0, loop/1, get_port/0, set_assert/1, check_last/0]).
 -export([init/1, terminate/2, code_change/3]).


[33/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Switch to using config instead of couch_config


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/244f360a
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/244f360a
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/244f360a

Branch: refs/heads/1963-eunit-bigcouch
Commit: 244f360a40a154df5d0258d9074d4f7c41744eab
Parents: 67dd9f5
Author: Russell Branca <ch...@apache.org>
Authored: Mon Aug 11 15:52:57 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:24:47 2014 -0700

----------------------------------------------------------------------
 test/couch_auth_cache_tests.erl          |  8 ++---
 test/couch_db_tests.erl                  |  2 +-
 test/couch_doc_json_tests.erl            |  6 ++--
 test/couch_stats_tests.erl               |  4 +--
 test/couch_util_tests.erl                |  4 +--
 test/couch_uuids_tests.erl               |  6 ++--
 test/couchdb_attachments_tests.erl       |  8 ++---
 test/couchdb_compaction_daemon.erl       | 18 +++++------
 test/couchdb_cors_tests.erl              | 28 ++++++++---------
 test/couchdb_csp_tests.erl               | 10 +++---
 test/couchdb_file_compression_tests.erl  | 18 +++++------
 test/couchdb_http_proxy_tests.erl        |  6 ++--
 test/couchdb_modules_load_tests.erl      |  4 +--
 test/couchdb_os_daemons_tests.erl        | 18 +++++------
 test/couchdb_os_proc_pool.erl            |  2 +-
 test/couchdb_update_conflicts_tests.erl  |  2 +-
 test/couchdb_vhosts_tests.erl            | 44 +++++++++++++--------------
 test/couchdb_views_tests.erl             | 18 +++++------
 test/fixtures/os_daemon_configer.escript |  2 +-
 19 files changed, 104 insertions(+), 104 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couch_auth_cache_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_auth_cache_tests.erl b/test/couch_auth_cache_tests.erl
index 371cb63..83531ff 100644
--- a/test/couch_auth_cache_tests.erl
+++ b/test/couch_auth_cache_tests.erl
@@ -22,7 +22,7 @@
 
 setup() ->
     DbName = ?tempdb(),
-    couch_config:set("couch_httpd_auth", "authentication_db",
+    config:set("couch_httpd_auth", "authentication_db",
                      ?b2l(DbName), false),
     DbName.
 
@@ -113,7 +113,7 @@ should_drop_cache_on_auth_db_change(DbName) ->
     ?_test(begin
         {ok, _} = update_user_doc(DbName, "joe", "pass1"),
         full_commit(DbName),
-        couch_config:set("couch_httpd_auth", "authentication_db",
+        config:set("couch_httpd_auth", "authentication_db",
                          ?b2l(?tempdb()), false),
         ?assertEqual(nil, couch_auth_cache:get_user_creds("joe"))
     end).
@@ -126,13 +126,13 @@ should_restore_cache_on_auth_db_change(DbName) ->
         full_commit(DbName),
 
         DbName1 = ?tempdb(),
-        couch_config:set("couch_httpd_auth", "authentication_db",
+        config:set("couch_httpd_auth", "authentication_db",
                          ?b2l(DbName1), false),
 
         {ok, _} = update_user_doc(DbName1, "joe", "pass5"),
         full_commit(DbName1),
 
-        couch_config:set("couch_httpd_auth", "authentication_db",
+        config:set("couch_httpd_auth", "authentication_db",
                          ?b2l(DbName), false),
 
         Creds = couch_auth_cache:get_user_creds("joe"),

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couch_db_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_db_tests.erl b/test/couch_db_tests.erl
index 90f0537..83d6a81 100644
--- a/test/couch_db_tests.erl
+++ b/test/couch_db_tests.erl
@@ -19,7 +19,7 @@
 
 setup() ->
     ok = test_util:start_couch(),
-    couch_config:set("log", "include_sasl", "false", false),
+    config:set("log", "include_sasl", "false", false),
     ok.
 
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couch_doc_json_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_doc_json_tests.erl b/test/couch_doc_json_tests.erl
index 23fc961..ef5d0f2 100644
--- a/test/couch_doc_json_tests.erl
+++ b/test/couch_doc_json_tests.erl
@@ -17,12 +17,12 @@
 
 
 setup() ->
-    couch_config:start_link(?CONFIG_CHAIN),
-    couch_config:set("attachments", "compression_level", "0", false),
+    config:start_link(?CONFIG_CHAIN),
+    config:set("attachments", "compression_level", "0", false),
     ok.
 
 teardown(_) ->
-    couch_config:stop().
+    config:stop().
 
 
 json_doc_test_() ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couch_stats_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_stats_tests.erl b/test/couch_stats_tests.erl
index 6dbbb6a..d62afaf 100644
--- a/test/couch_stats_tests.erl
+++ b/test/couch_stats_tests.erl
@@ -28,7 +28,7 @@ setup_collector() ->
     ok.
 
 setup_aggregator(_) ->
-    {ok, Pid} = couch_config:start_link([?STATS_INI_FIXTURE]),
+    {ok, Pid} = config:start_link([?STATS_INI_FIXTURE]),
     {ok, _} = couch_stats_collector:start(),
     {ok, _} = couch_stats_aggregator:start(?STATS_CFG_FIXTURE),
     Pid.
@@ -41,7 +41,7 @@ teardown_aggregator(_, Pid) ->
     couch_stats_aggregator:stop(),
     couch_stats_collector:stop(),
     erlang:monitor(process, Pid),
-    couch_config:stop(),
+    config:stop(),
     receive
         {'DOWN', _, _, Pid, _} ->
             ok

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couch_util_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_util_tests.erl b/test/couch_util_tests.erl
index c5689af..4c313a2 100644
--- a/test/couch_util_tests.erl
+++ b/test/couch_util_tests.erl
@@ -24,13 +24,13 @@ setup() ->
     %% {error, not_loaded_by_this_process} while it is. Any ideas is welcome.
     %%
     ok = test_util:start_couch(),
-    %% couch_config:start_link(?CONFIG_CHAIN),
+    %% config:start_link(?CONFIG_CHAIN),
     %% {ok, _} = couch_drv:start_link(),
     ok.
 
 teardown(_) ->
     ok = test_util:stop_couch(),
-    %% couch_config:stop(),
+    %% config:stop(),
     %% erl_ddll:unload_driver(couch_icu_driver),
     ok.
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couch_uuids_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_uuids_tests.erl b/test/couch_uuids_tests.erl
index 754a49b..27c5cb8 100644
--- a/test/couch_uuids_tests.erl
+++ b/test/couch_uuids_tests.erl
@@ -18,7 +18,7 @@
 
 
 setup() ->
-    {ok, Pid} = couch_config:start_link(?CONFIG_CHAIN),
+    {ok, Pid} = config:start_link(?CONFIG_CHAIN),
     erlang:monitor(process, Pid),
     couch_uuids:start(),
     Pid.
@@ -27,13 +27,13 @@ setup(Opts) ->
     Pid = setup(),
     lists:foreach(
         fun({Option, Value}) ->
-            couch_config:set("uuids", Option, Value, false)
+            config:set("uuids", Option, Value, false)
         end, Opts),
     Pid.
 
 teardown(Pid) ->
     couch_uuids:stop(),
-    couch_config:stop(),
+    config:stop(),
     receive
         {'DOWN', _, _, Pid, _} -> ok
     after

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_attachments_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_attachments_tests.erl b/test/couchdb_attachments_tests.erl
index 0391822..b203a82 100644
--- a/test/couchdb_attachments_tests.erl
+++ b/test/couchdb_attachments_tests.erl
@@ -29,16 +29,16 @@
 start() ->
     ok = test_util:start_couch(),
     % ensure in default compression settings for attachments_compression_tests
-    couch_config:set("attachments", "compression_level",
+    config:set("attachments", "compression_level",
                      ?i2l(?COMPRESSION_LEVEL), false),
-    couch_config:set("attachments", "compressible_types", "text/*", false),
+    config:set("attachments", "compressible_types", "text/*", false),
     ok.
 
 setup() ->
     DbName = ?tempdb(),
     {ok, Db} = couch_db:create(DbName, []),
     ok = couch_db:close(Db),
-    Addr = couch_config:get("httpd", "bind_address", any),
+    Addr = config:get("httpd", "bind_address", any),
     Port = mochiweb_socket_server:get(couch_httpd, port),
     Host = Addr ++ ":" ++ ?i2l(Port),
     {Host, ?b2l(DbName)}.
@@ -540,7 +540,7 @@ chunked_body([Chunk | Rest], Acc) ->
 
 get_socket() ->
     Options = [binary, {packet, 0}, {active, false}],
-    Addr = couch_config:get("httpd", "bind_address", any),
+    Addr = config:get("httpd", "bind_address", any),
     Port = mochiweb_socket_server:get(couch_httpd, port),
     {ok, Sock} = gen_tcp:connect(Addr, Port, Options),
     Sock.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_compaction_daemon.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_compaction_daemon.erl b/test/couchdb_compaction_daemon.erl
index de166a4..24c5d7b 100644
--- a/test/couchdb_compaction_daemon.erl
+++ b/test/couchdb_compaction_daemon.erl
@@ -23,8 +23,8 @@
 
 start() ->
     ok = test_util:start_couch(),
-    couch_config:set("compaction_daemon", "check_interval", "3", false),
-    couch_config:set("compaction_daemon", "min_file_size", "100000", false),
+    config:set("compaction_daemon", "check_interval", "3", false),
+    config:set("compaction_daemon", "min_file_size", "100000", false),
     ok.
 
 setup() ->
@@ -35,10 +35,10 @@ setup() ->
     DbName.
 
 teardown(DbName) ->
-    Configs = couch_config:get("compactions"),
+    Configs = config:get("compactions"),
     lists:foreach(
         fun({Key, _}) ->
-            ok = couch_config:delete("compactions", Key, false)
+            ok = config:delete("compactions", Key, false)
         end,
         Configs),
     couch_server:delete(DbName, [?ADMIN_USER]),
@@ -71,13 +71,13 @@ should_compact_by_default_rule(DbName) ->
         {_, DbFileSize} = get_db_frag(DbName),
         {_, ViewFileSize} = get_view_frag(DbName),
 
-        ok = couch_config:set("compactions", "_default",
+        ok = config:set("compactions", "_default",
             "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"70%\"}]",
             false),
 
         ok = timer:sleep(4000), % something >= check_interval
         wait_compaction_finished(DbName),
-        ok = couch_config:delete("compactions", "_default", false),
+        ok = config:delete("compactions", "_default", false),
 
         {DbFrag2, DbFileSize2} = get_db_frag(DbName),
         {ViewFrag2, ViewFileSize2} = get_view_frag(DbName),
@@ -100,13 +100,13 @@ should_compact_by_dbname_rule(DbName) ->
         {_, DbFileSize} = get_db_frag(DbName),
         {_, ViewFileSize} = get_view_frag(DbName),
 
-        ok = couch_config:set("compactions", ?b2l(DbName),
+        ok = config:set("compactions", ?b2l(DbName),
             "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"70%\"}]",
             false),
 
         ok = timer:sleep(4000), % something >= check_interval
         wait_compaction_finished(DbName),
-        ok = couch_config:delete("compactions", ?b2l(DbName), false),
+        ok = config:delete("compactions", ?b2l(DbName), false),
 
         {DbFrag2, DbFileSize2} = get_db_frag(DbName),
         {ViewFrag2, ViewFileSize2} = get_view_frag(DbName),
@@ -168,7 +168,7 @@ update(DbName) ->
     couch_db:close(Db).
 
 db_url(DbName) ->
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Addr = config:get("httpd", "bind_address", "127.0.0.1"),
     Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
     "http://" ++ Addr ++ ":" ++ Port ++ "/" ++ ?b2l(DbName).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_cors_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_cors_tests.erl b/test/couchdb_cors_tests.erl
index 336d5ee..02f9da9 100644
--- a/test/couchdb_cors_tests.erl
+++ b/test/couchdb_cors_tests.erl
@@ -24,8 +24,8 @@
 
 start() ->
     ok = test_util:start_couch(),
-    ok = couch_config:set("httpd", "enable_cors", "true", false),
-    ok = couch_config:set("vhosts", "example.com", "/", false),
+    ok = config:set("httpd", "enable_cors", "true", false),
+    ok = config:set("vhosts", "example.com", "/", false),
     ok.
 
 setup() ->
@@ -33,10 +33,10 @@ setup() ->
     {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
     couch_db:close(Db),
 
-    couch_config:set("cors", "credentials", "false", false),
-    couch_config:set("cors", "origins", "http://example.com", false),
+    config:set("cors", "credentials", "false", false),
+    config:set("cors", "origins", "http://example.com", false),
 
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Addr = config:get("httpd", "bind_address", "127.0.0.1"),
     Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
     Host = "http://" ++ Addr ++ ":" ++ Port,
     {Host, ?b2l(DbName)}.
@@ -136,7 +136,7 @@ make_test_case(Mod, UseVhost, Funs) ->
 should_not_allow_origin(_, {_, _, Url, Headers0}) ->
     ?_assertEqual(undefined,
         begin
-            couch_config:delete("cors", "origins", false),
+            config:delete("cors", "origins", false),
             Headers1 = proplists:delete("Origin", Headers0),
             Headers = [{"Origin", "http://127.0.0.1"}]
                       ++ Headers1,
@@ -200,7 +200,7 @@ should_make_preflight_request(_, {_, _, Url, DefaultHeaders}) ->
 should_make_prefligh_request_with_port({_, VHost}, {_, _, Url, _}) ->
     ?_assertEqual("http://example.com:5984",
         begin
-            couch_config:set("cors", "origins", "http://example.com:5984",
+            config:set("cors", "origins", "http://example.com:5984",
                              false),
             Headers = [{"Origin", "http://example.com:5984"},
                        {"Access-Control-Request-Method", "GET"}]
@@ -212,7 +212,7 @@ should_make_prefligh_request_with_port({_, VHost}, {_, _, Url, _}) ->
 should_make_prefligh_request_with_scheme({_, VHost}, {_, _, Url, _}) ->
     ?_assertEqual("https://example.com:5984",
         begin
-            couch_config:set("cors", "origins", "https://example.com:5984",
+            config:set("cors", "origins", "https://example.com:5984",
                              false),
             Headers = [{"Origin", "https://example.com:5984"},
                        {"Access-Control-Request-Method", "GET"}]
@@ -224,7 +224,7 @@ should_make_prefligh_request_with_scheme({_, VHost}, {_, _, Url, _}) ->
 should_make_prefligh_request_with_wildcard_origin({_, VHost}, {_, _, Url, _}) ->
     ?_assertEqual("https://example.com:5984",
         begin
-            couch_config:set("cors", "origins", "*", false),
+            config:set("cors", "origins", "*", false),
             Headers = [{"Origin", "https://example.com:5984"},
                        {"Access-Control-Request-Method", "GET"}]
                       ++ maybe_append_vhost(VHost),
@@ -235,7 +235,7 @@ should_make_prefligh_request_with_wildcard_origin({_, VHost}, {_, _, Url, _}) ->
 should_make_request_with_credentials(_, {_, _, Url, DefaultHeaders}) ->
     ?_assertEqual("true",
         begin
-            ok = couch_config:set("cors", "credentials", "true", false),
+            ok = config:set("cors", "credentials", "true", false),
             {ok, _, Resp, _} = test_request:options(Url, DefaultHeaders),
             proplists:get_value("Access-Control-Allow-Credentials", Resp)
         end).
@@ -244,10 +244,10 @@ should_make_origin_request_with_auth(_, {_, _, Url, DefaultHeaders}) ->
     ?_assertEqual("http://example.com",
         begin
             Hashed = couch_passwords:hash_admin_password(<<"test">>),
-            couch_config:set("admins", "test", Hashed, false),
+            config:set("admins", "test", Hashed, false),
             {ok, _, Resp, _} = test_request:get(
                 Url, DefaultHeaders, [{basic_auth, {"test", "test"}}]),
-            couch_config:delete("admins", "test", false),
+            config:delete("admins", "test", false),
             proplists:get_value("Access-Control-Allow-Origin", Resp)
         end).
 
@@ -255,12 +255,12 @@ should_make_preflight_request_with_auth(_, {_, _, Url, DefaultHeaders}) ->
     ?_assertEqual(?SUPPORTED_METHODS,
         begin
             Hashed = couch_passwords:hash_admin_password(<<"test">>),
-            couch_config:set("admins", "test", Hashed, false),
+            config:set("admins", "test", Hashed, false),
             Headers = DefaultHeaders
                       ++ [{"Access-Control-Request-Method", "GET"}],
             {ok, _, Resp, _} = test_request:options(
                 Url, Headers, [{basic_auth, {"test", "test"}}]),
-            couch_config:delete("admins", "test", false),
+            config:delete("admins", "test", false),
             proplists:get_value("Access-Control-Allow-Methods", Resp)
         end).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_csp_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_csp_tests.erl b/test/couchdb_csp_tests.erl
index 0626a08..3dbe6e3 100644
--- a/test/couchdb_csp_tests.erl
+++ b/test/couchdb_csp_tests.erl
@@ -18,8 +18,8 @@
 
 
 setup() ->
-    ok = couch_config:set("csp", "enable", "true", false),
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    ok = config:set("csp", "enable", "true", false),
+    Addr = config:get("httpd", "bind_address", "127.0.0.1"),
     Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
     lists:concat(["http://", Addr, ":", Port, "/_utils/"]).
 
@@ -50,7 +50,7 @@ csp_test_() ->
 should_not_return_any_csp_headers_when_disabled(Url) ->
     ?_assertEqual(undefined,
         begin
-            ok = couch_config:set("csp", "enable", "false", false),
+            ok = config:set("csp", "enable", "false", false),
             {ok, _, Headers, _} = test_request:get(Url),
             proplists:get_value("Content-Security-Policy", Headers)
         end).
@@ -67,7 +67,7 @@ should_apply_default_policy(Url) ->
 should_return_custom_policy(Url) ->
     ?_assertEqual("default-src 'http://example.com';",
         begin
-            ok = couch_config:set("csp", "header_value",
+            ok = config:set("csp", "header_value",
                                   "default-src 'http://example.com';", false),
             {ok, _, Headers, _} = test_request:get(Url),
             proplists:get_value("Content-Security-Policy", Headers)
@@ -76,7 +76,7 @@ should_return_custom_policy(Url) ->
 should_only_enable_csp_when_true(Url) ->
     ?_assertEqual(undefined,
         begin
-            ok = couch_config:set("csp", "enable", "tru", false),
+            ok = config:set("csp", "enable", "tru", false),
             {ok, _, Headers, _} = test_request:get(Url),
             proplists:get_value("Content-Security-Policy", Headers)
         end).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_file_compression_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_file_compression_tests.erl b/test/couchdb_file_compression_tests.erl
index 5573dd8..5b12882 100644
--- a/test/couchdb_file_compression_tests.erl
+++ b/test/couchdb_file_compression_tests.erl
@@ -22,7 +22,7 @@
 
 
 setup() ->
-    couch_config:set("couchdb", "file_compression", "none", false),
+    config:set("couchdb", "file_compression", "none", false),
     DbName = ?tempdb(),
     {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
     ok = populate_db(Db, ?DOCS_COUNT),
@@ -68,7 +68,7 @@ couch_auth_cache_test_() ->
 
 
 should_use_none(DbName) ->
-    couch_config:set("couchdb", "file_compression", "none", false),
+    config:set("couchdb", "file_compression", "none", false),
     {
         "Use no compression",
         [
@@ -78,7 +78,7 @@ should_use_none(DbName) ->
     }.
 
 should_use_deflate_1(DbName) ->
-    couch_config:set("couchdb", "file_compression", "deflate_1", false),
+    config:set("couchdb", "file_compression", "deflate_1", false),
     {
         "Use deflate compression at level 1",
         [
@@ -88,7 +88,7 @@ should_use_deflate_1(DbName) ->
     }.
 
 should_use_deflate_9(DbName) ->
-    couch_config:set("couchdb", "file_compression", "deflate_9", false),
+    config:set("couchdb", "file_compression", "deflate_9", false),
     {
         "Use deflate compression at level 9",
         [
@@ -98,7 +98,7 @@ should_use_deflate_9(DbName) ->
     }.
 
 should_use_snappy(DbName) ->
-    couch_config:set("couchdb", "file_compression", "snappy", false),
+    config:set("couchdb", "file_compression", "snappy", false),
     {
         "Use snappy compression",
         [
@@ -112,13 +112,13 @@ should_compare_compression_methods(DbName) ->
      {timeout, ?TIMEOUT div 1000, ?_test(compare_compression_methods(DbName))}}.
 
 compare_compression_methods(DbName) ->
-    couch_config:set("couchdb", "file_compression", "none", false),
+    config:set("couchdb", "file_compression", "none", false),
     compact_db(DbName),
     compact_view(DbName),
     DbSizeNone = db_disk_size(DbName),
     ViewSizeNone = view_disk_size(DbName),
 
-    couch_config:set("couchdb", "file_compression", "snappy", false),
+    config:set("couchdb", "file_compression", "snappy", false),
     compact_db(DbName),
     compact_view(DbName),
     DbSizeSnappy = db_disk_size(DbName),
@@ -127,7 +127,7 @@ compare_compression_methods(DbName) ->
     ?assert(DbSizeNone > DbSizeSnappy),
     ?assert(ViewSizeNone > ViewSizeSnappy),
 
-    couch_config:set("couchdb", "file_compression", "deflate_1", false),
+    config:set("couchdb", "file_compression", "deflate_1", false),
     compact_db(DbName),
     compact_view(DbName),
     DbSizeDeflate1 = db_disk_size(DbName),
@@ -136,7 +136,7 @@ compare_compression_methods(DbName) ->
     ?assert(DbSizeSnappy > DbSizeDeflate1),
     ?assert(ViewSizeSnappy > ViewSizeDeflate1),
 
-    couch_config:set("couchdb", "file_compression", "deflate_9", false),
+    config:set("couchdb", "file_compression", "deflate_9", false),
     compact_db(DbName),
     compact_view(DbName),
     DbSizeDeflate9 = db_disk_size(DbName),

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_http_proxy_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_http_proxy_tests.erl b/test/couchdb_http_proxy_tests.erl
index 0c554ff..cee6a02 100644
--- a/test/couchdb_http_proxy_tests.erl
+++ b/test/couchdb_http_proxy_tests.erl
@@ -33,7 +33,7 @@ start() ->
     % (each httpd_global_handlers changes causes couch_httpd restart)
     ok = test_util:start_couch(test_util:config_files() ++ [?CONFIG_FIXTURE_TEMP]),
     % 49151 is IANA Reserved, let's assume no one is listening there
-    couch_config:set("httpd_global_handlers", "_error",
+    config:set("httpd_global_handlers", "_error",
         "{couch_httpd_proxy, handle_proxy_req, <<\"http://127.0.0.1:49151/\">>}"
     ),
     ok.
@@ -43,7 +43,7 @@ setup() ->
     Value = lists:flatten(io_lib:format(
         "{couch_httpd_proxy, handle_proxy_req, ~p}",
         [list_to_binary(proxy_url())])),
-    couch_config:set("httpd_global_handlers", "_test", Value),
+    config:set("httpd_global_handlers", "_test", Value),
     % let couch_httpd restart
     timer:sleep(100),
     Pid.
@@ -381,7 +381,7 @@ server_url() ->
     server_url("/_test").
 
 server_url(Resource) ->
-    Addr = couch_config:get("httpd", "bind_address"),
+    Addr = config:get("httpd", "bind_address"),
     Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
     lists:concat(["http://", Addr, ":", Port, Resource]).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_modules_load_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_modules_load_tests.erl b/test/couchdb_modules_load_tests.erl
index 4eaa42b..23cb375 100644
--- a/test/couchdb_modules_load_tests.erl
+++ b/test/couchdb_modules_load_tests.erl
@@ -28,8 +28,8 @@ should_load_modules() ->
         couch_btree,
         couch_changes,
         couch_compress,
-        couch_config,
-        couch_config_writer,
+        config,
+        config_writer,
         couch_db,
         couch_db_update_notifier,
         couch_db_update_notifier_sup,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_os_daemons_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_os_daemons_tests.erl b/test/couchdb_os_daemons_tests.erl
index ef21f58..3eeb7d7 100644
--- a/test/couchdb_os_daemons_tests.erl
+++ b/test/couchdb_os_daemons_tests.erl
@@ -37,16 +37,16 @@
 
 
 setup(DName) ->
-    {ok, CfgPid} = couch_config:start_link(?CONFIG_CHAIN),
+    {ok, CfgPid} = config:start_link(?CONFIG_CHAIN),
     {ok, OsDPid} = couch_os_daemons:start_link(),
-    couch_config:set("os_daemons", DName,
+    config:set("os_daemons", DName,
                      filename:join([?FIXTURESDIR, DName]), false),
     timer:sleep(?DELAY),  % sleep a bit to let daemon set kill flag
     {CfgPid, OsDPid}.
 
 teardown(_, {CfgPid, OsDPid}) ->
     erlang:monitor(process, CfgPid),
-    couch_config:stop(),
+    config:stop(),
     receive
         {'DOWN', _, _, CfgPid, _} ->
             ok
@@ -121,16 +121,16 @@ should_check_daemon_table_form(DName, _) ->
 
 should_clean_tables_on_daemon_remove(DName, _) ->
     ?_test(begin
-        couch_config:delete("os_daemons", DName, false),
+        config:delete("os_daemons", DName, false),
         {ok, Tab2} = couch_os_daemons:info(),
         ?_assertEqual([], ets:tab2list(Tab2))
     end).
 
 should_spawn_multiple_daemons(DName, _) ->
     ?_test(begin
-        couch_config:set("os_daemons", "bar",
+        config:set("os_daemons", "bar",
                          filename:join([?FIXTURESDIR, DName]), false),
-        couch_config:set("os_daemons", "baz",
+        config:set("os_daemons", "baz",
                          filename:join([?FIXTURESDIR, DName]), false),
         timer:sleep(?DELAY),
         {ok, Daemons} = couch_os_daemons:info([table]),
@@ -145,7 +145,7 @@ should_spawn_multiple_daemons(DName, _) ->
 
 should_keep_alive_one_daemon_on_killing_other(DName, _) ->
     ?_test(begin
-        couch_config:set("os_daemons", "bar",
+        config:set("os_daemons", "bar",
                          filename:join([?FIXTURESDIR, DName]), false),
         timer:sleep(?DELAY),
         {ok, Daemons} = couch_os_daemons:info([table]),
@@ -153,7 +153,7 @@ should_keep_alive_one_daemon_on_killing_other(DName, _) ->
             check_daemon(D)
         end, Daemons),
 
-        couch_config:delete("os_daemons", "bar", false),
+        config:delete("os_daemons", "bar", false),
         timer:sleep(?DELAY),
         {ok, [D2]} = couch_os_daemons:info([table]),
         check_daemon(D2, DName),
@@ -203,7 +203,7 @@ should_halts(DName, Time) ->
     timer:sleep(Time),
     {ok, [D]} = couch_os_daemons:info([table]),
     check_dead(D, DName),
-    couch_config:delete("os_daemons", DName, false).
+    config:delete("os_daemons", DName, false).
 
 check_daemon(D) ->
     check_daemon(D, D#daemon.name).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_os_proc_pool.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_os_proc_pool.erl b/test/couchdb_os_proc_pool.erl
index ae07d58..a8ab752 100644
--- a/test/couchdb_os_proc_pool.erl
+++ b/test/couchdb_os_proc_pool.erl
@@ -20,7 +20,7 @@
 
 start() ->
     ok = test_util:start_couch(),
-    couch_config:set("query_server_config", "os_process_limit", "3", false),
+    config:set("query_server_config", "os_process_limit", "3", false),
     ok.
 
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_update_conflicts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_update_conflicts_tests.erl b/test/couchdb_update_conflicts_tests.erl
index 2450313..7ac1187 100644
--- a/test/couchdb_update_conflicts_tests.erl
+++ b/test/couchdb_update_conflicts_tests.erl
@@ -24,7 +24,7 @@
 
 start() ->
     ok = test_util:start_couch(),
-    couch_config:set("couchdb", "delayed_commits", "true", false),
+    config:set("couchdb", "delayed_commits", "true", false),
     ok.
 
 setup() ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_vhosts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_vhosts_tests.erl b/test/couchdb_vhosts_tests.erl
index 7432022..321012f 100644
--- a/test/couchdb_vhosts_tests.erl
+++ b/test/couchdb_vhosts_tests.erl
@@ -47,7 +47,7 @@ setup() ->
     couch_db:ensure_full_commit(Db),
     couch_db:close(Db),
 
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Addr = config:get("httpd", "bind_address", "127.0.0.1"),
     Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
     Url = "http://" ++ Addr ++ ":" ++ Port,
     {Url, ?b2l(DbName)}.
@@ -56,14 +56,14 @@ setup_oauth() ->
     DbName = ?tempdb(),
     {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
 
-    couch_config:set("couch_httpd_auth", "authentication_db",
+    config:set("couch_httpd_auth", "authentication_db",
                      ?b2l(?tempdb()), false),
-    couch_config:set("oauth_token_users", "otoksec1", "joe", false),
-    couch_config:set("oauth_consumer_secrets", "consec1", "foo", false),
-    couch_config:set("oauth_token_secrets", "otoksec1", "foobar", false),
-    couch_config:set("couch_httpd_auth", "require_valid_user", "true", false),
+    config:set("oauth_token_users", "otoksec1", "joe", false),
+    config:set("oauth_consumer_secrets", "consec1", "foo", false),
+    config:set("oauth_token_secrets", "otoksec1", "foobar", false),
+    config:set("couch_httpd_auth", "require_valid_user", "true", false),
 
-    ok = couch_config:set(
+    ok = config:set(
         "vhosts", "oauth-example.com",
         "/" ++ ?b2l(DbName) ++ "/_design/test/_rewrite/foobar", false),
 
@@ -82,7 +82,7 @@ setup_oauth() ->
     couch_db:ensure_full_commit(Db),
     couch_db:close(Db),
 
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Addr = config:get("httpd", "bind_address", "127.0.0.1"),
     Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
     Url = "http://" ++ Addr ++ ":" ++ Port,
     {Url, ?b2l(DbName)}.
@@ -140,7 +140,7 @@ oauth_test_() ->
 
 should_return_database_info({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
+        ok = config:set("vhosts", "example.com", "/" ++ DbName, false),
         case test_request:get(Url, [], [{host_header, "example.com"}]) of
             {ok, _, _, Body} ->
                 {JsonBody} = ejson:decode(Body),
@@ -155,7 +155,7 @@ should_return_database_info({Url, DbName}) ->
 
 should_return_revs_info({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
+        ok = config:set("vhosts", "example.com", "/" ++ DbName, false),
         case test_request:get(Url ++ "/doc1?revs_info=true", [],
                               [{host_header, "example.com"}]) of
             {ok, _, _, Body} ->
@@ -171,7 +171,7 @@ should_return_revs_info({Url, DbName}) ->
 
 should_serve_utils_for_vhost({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
+        ok = config:set("vhosts", "example.com", "/" ++ DbName, false),
         case test_request:get(Url ++ "/_utils/index.html", [],
                               [{host_header, "example.com"}]) of
             {ok, _, _, Body} ->
@@ -186,7 +186,7 @@ should_serve_utils_for_vhost({Url, DbName}) ->
 
 should_return_virtual_request_path_field_in_request({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts", "example1.com",
+        ok = config:set("vhosts", "example1.com",
                               "/" ++ DbName ++ "/_design/doc1/_rewrite/",
                               false),
         case test_request:get(Url, [], [{host_header, "example1.com"}]) of
@@ -204,7 +204,7 @@ should_return_virtual_request_path_field_in_request({Url, DbName}) ->
 
 should_return_real_request_path_field_in_request({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts", "example1.com",
+        ok = config:set("vhosts", "example1.com",
                               "/" ++ DbName ++ "/_design/doc1/_rewrite/",
                               false),
         case test_request:get(Url, [], [{host_header, "example1.com"}]) of
@@ -222,7 +222,7 @@ should_return_real_request_path_field_in_request({Url, DbName}) ->
 
 should_match_wildcard_vhost({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts", "*.example.com",
+        ok = config:set("vhosts", "*.example.com",
                               "/" ++ DbName ++ "/_design/doc1/_rewrite", false),
         case test_request:get(Url, [], [{host_header, "test.example.com"}]) of
             {ok, _, _, Body} ->
@@ -239,7 +239,7 @@ should_match_wildcard_vhost({Url, DbName}) ->
 
 should_return_db_info_for_wildcard_vhost_for_custom_db({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts", ":dbname.example1.com",
+        ok = config:set("vhosts", ":dbname.example1.com",
                               "/:dbname", false),
         Host = DbName ++ ".example1.com",
         case test_request:get(Url, [], [{host_header, Host}]) of
@@ -256,7 +256,7 @@ should_return_db_info_for_wildcard_vhost_for_custom_db({Url, DbName}) ->
 
 should_replace_rewrite_variables_for_db_and_doc({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts",":appname.:dbname.example1.com",
+        ok = config:set("vhosts",":appname.:dbname.example1.com",
                               "/:dbname/_design/:appname/_rewrite/", false),
         Host = "doc1." ++ DbName ++ ".example1.com",
         case test_request:get(Url, [], [{host_header, Host}]) of
@@ -274,7 +274,7 @@ should_replace_rewrite_variables_for_db_and_doc({Url, DbName}) ->
 
 should_return_db_info_for_vhost_with_resource({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts",
+        ok = config:set("vhosts",
                               "example.com/test", "/" ++ DbName, false),
         ReqUrl = Url ++ "/test",
         case test_request:get(ReqUrl, [], [{host_header, "example.com"}]) of
@@ -292,7 +292,7 @@ should_return_db_info_for_vhost_with_resource({Url, DbName}) ->
 
 should_return_revs_info_for_vhost_with_resource({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts",
+        ok = config:set("vhosts",
                               "example.com/test", "/" ++ DbName, false),
         ReqUrl = Url ++ "/test/doc1?revs_info=true",
         case test_request:get(ReqUrl, [], [{host_header, "example.com"}]) of
@@ -309,7 +309,7 @@ should_return_revs_info_for_vhost_with_resource({Url, DbName}) ->
 
 should_return_db_info_for_vhost_with_wildcard_resource({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts", "*.example2.com/test", "/*", false),
+        ok = config:set("vhosts", "*.example2.com/test", "/*", false),
         ReqUrl = Url ++ "/test",
         Host = DbName ++ ".example2.com",
         case test_request:get(ReqUrl, [], [{host_header, Host}]) of
@@ -326,7 +326,7 @@ should_return_db_info_for_vhost_with_wildcard_resource({Url, DbName}) ->
 
 should_return_path_for_vhost_with_wildcard_host({Url, DbName}) ->
     ?_test(begin
-        ok = couch_config:set("vhosts", "*/test1",
+        ok = config:set("vhosts", "*/test1",
                               "/" ++ DbName ++ "/_design/doc1/_show/test",
                               false),
         case test_request:get(Url ++ "/test1") of
@@ -360,7 +360,7 @@ should_require_auth({Url, _}) ->
 
 should_succeed_oauth({Url, _}) ->
     ?_test(begin
-        AuthDbName = couch_config:get("couch_httpd_auth", "authentication_db"),
+        AuthDbName = config:get("couch_httpd_auth", "authentication_db"),
         JoeDoc = couch_doc:from_json_obj({[
             {<<"_id">>, <<"org.couchdb.user:joe">>},
             {<<"type">>, <<"user">>},
@@ -394,7 +394,7 @@ should_succeed_oauth({Url, _}) ->
 
 should_fail_oauth_with_wrong_credentials({Url, _}) ->
     ?_test(begin
-        AuthDbName = couch_config:get("couch_httpd_auth", "authentication_db"),
+        AuthDbName = config:get("couch_httpd_auth", "authentication_db"),
         JoeDoc = couch_doc:from_json_obj({[
             {<<"_id">>, <<"org.couchdb.user:joe">>},
             {<<"type">>, <<"user">>},

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_views_tests.erl b/test/couchdb_views_tests.erl
index 6efa7a1..03fb9cc 100644
--- a/test/couchdb_views_tests.erl
+++ b/test/couchdb_views_tests.erl
@@ -121,7 +121,7 @@ should_not_remember_docs_in_index_after_backup_restore_test() ->
 should_upgrade_legacy_view_files_test() ->
     start(),
 
-    ok = couch_config:set("query_server_config", "commit_freq", "0", false),
+    ok = config:set("query_server_config", "commit_freq", "0", false),
 
     DbName = <<"test">>,
     DbFileName = "test.couch",
@@ -130,8 +130,8 @@ should_upgrade_legacy_view_files_test() ->
     FixtureViewFilePath = filename:join([?FIXTURESDIR, OldViewName]),
     NewViewName = "a1c5929f912aca32f13446122cc6ce50.view",
 
-    DbDir = couch_config:get("couchdb", "database_dir"),
-    ViewDir = couch_config:get("couchdb", "view_index_dir"),
+    DbDir = config:get("couchdb", "database_dir"),
+    ViewDir = config:get("couchdb", "view_index_dir"),
     OldViewFilePath = filename:join([ViewDir, ".test_design", OldViewName]),
     NewViewFilePath = filename:join([ViewDir, ".test_design", "mrview",
                                      NewViewName]),
@@ -290,8 +290,8 @@ couchdb_1309(DbName) ->
 
 couchdb_1283() ->
     ?_test(begin
-        ok = couch_config:set("couchdb", "max_dbs_open", "3", false),
-        ok = couch_config:set("couchdb", "delayed_commits", "false", false),
+        ok = config:set("couchdb", "max_dbs_open", "3", false),
+        ok = config:set("couchdb", "delayed_commits", "false", false),
 
         {ok, MDb1} = couch_db:create(?tempdb(), [?ADMIN_USER]),
         DDoc = couch_doc:from_json_obj({[
@@ -463,7 +463,7 @@ delete_design_doc(DbName, DDName, Rev) ->
     couch_db:close(Db).
 
 db_url(DbName) ->
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Addr = config:get("httpd", "bind_address", "127.0.0.1"),
     Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
     "http://" ++ Addr ++ ":" ++ Port ++ "/" ++ ?b2l(DbName).
 
@@ -507,7 +507,7 @@ count_db_refs(DbName) ->
 
 count_index_files(DbName) ->
     % call server to fetch the index files
-    RootDir = couch_config:get("couchdb", "view_index_dir"),
+    RootDir = config:get("couchdb", "view_index_dir"),
     length(filelib:wildcard(RootDir ++ "/." ++
         binary_to_list(DbName) ++ "_design"++"/mrview/*")).
 
@@ -516,13 +516,13 @@ has_doc(DocId1, Rows) ->
     lists:any(fun({R}) -> lists:member({<<"id">>, DocId}, R) end, Rows).
 
 backup_db_file(DbName) ->
-    DbDir = couch_config:get("couchdb", "database_dir"),
+    DbDir = config:get("couchdb", "database_dir"),
     DbFile = filename:join([DbDir, ?b2l(DbName) ++ ".couch"]),
     {ok, _} = file:copy(DbFile, DbFile ++ ".backup"),
     ok.
 
 restore_backup_db_file(DbName) ->
-    DbDir = couch_config:get("couchdb", "database_dir"),
+    DbDir = config:get("couchdb", "database_dir"),
     stop(whereis(couch_server_sup)),
     DbFile = filename:join([DbDir, ?b2l(DbName) ++ ".couch"]),
     ok = file:delete(DbFile),

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/244f360a/test/fixtures/os_daemon_configer.escript
----------------------------------------------------------------------
diff --git a/test/fixtures/os_daemon_configer.escript b/test/fixtures/os_daemon_configer.escript
index d437423..0074fc0 100755
--- a/test/fixtures/os_daemon_configer.escript
+++ b/test/fixtures/os_daemon_configer.escript
@@ -83,7 +83,7 @@ loop({error, _Reason}) ->
 
 main([]) ->
     init_code_path(),
-    couch_config:start_link(?CONFIG_CHAIN),
+    config:start_link(?CONFIG_CHAIN),
     couch_drv:start_link(),
     do_tests().
 


[27/42] Move files out of test/couchdb into top level test/ folder

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_btree_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_btree_tests.erl b/test/couchdb/couch_btree_tests.erl
deleted file mode 100644
index 911640f..0000000
--- a/test/couchdb/couch_btree_tests.erl
+++ /dev/null
@@ -1,551 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_btree_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(ROWS, 1000).
-
-
-setup() ->
-    {ok, Fd} = couch_file:open(?tempfile(), [create, overwrite]),
-    {ok, Btree} = couch_btree:open(nil, Fd, [{compression, none},
-                                             {reduce, fun reduce_fun/2}]),
-    {Fd, Btree}.
-
-setup_kvs(_) ->
-    setup().
-
-setup_red() ->
-    {_, EvenOddKVs} = lists:foldl(
-        fun(Idx, {Key, Acc}) ->
-            case Key of
-                "even" -> {"odd", [{{Key, Idx}, 1} | Acc]};
-                _ -> {"even", [{{Key, Idx}, 1} | Acc]}
-            end
-        end, {"odd", []}, lists:seq(1, ?ROWS)),
-    {Fd, Btree} = setup(),
-    {ok, Btree1} = couch_btree:add_remove(Btree, EvenOddKVs, []),
-    {Fd, Btree1}.
-setup_red(_) ->
-    setup_red().
-
-teardown(Fd) when is_pid(Fd) ->
-    ok = couch_file:close(Fd);
-teardown({Fd, _}) ->
-    teardown(Fd).
-teardown(_, {Fd, _}) ->
-    teardown(Fd).
-
-
-kvs_test_funs() ->
-    [
-        fun should_set_fd_correctly/2,
-        fun should_set_root_correctly/2,
-        fun should_create_zero_sized_btree/2,
-        fun should_set_reduce_option/2,
-        fun should_fold_over_empty_btree/2,
-        fun should_add_all_keys/2,
-        fun should_continuously_add_new_kv/2,
-        fun should_continuously_remove_keys/2,
-        fun should_insert_keys_in_reversed_order/2,
-        fun should_add_every_odd_key_remove_every_even/2,
-        fun should_add_every_even_key_remove_every_old/2
-    ].
-
-red_test_funs() ->
-    [
-        fun should_reduce_whole_range/2,
-        fun should_reduce_first_half/2,
-        fun should_reduce_second_half/2
-    ].
-
-
-btree_open_test_() ->
-    {ok, Fd} = couch_file:open(?tempfile(), [create, overwrite]),
-    {ok, Btree} = couch_btree:open(nil, Fd, [{compression, none}]),
-    {
-        "Ensure that created btree is really a btree record",
-        ?_assert(is_record(Btree, btree))
-    }.
-
-sorted_kvs_test_() ->
-    Funs = kvs_test_funs(),
-    Sorted = [{Seq, random:uniform()} || Seq <- lists:seq(1, ?ROWS)],
-    {
-        "BTree with sorted keys",
-        {
-            foreachx,
-            fun setup_kvs/1, fun teardown/2,
-            [{Sorted, Fun} || Fun <- Funs]
-        }
-    }.
-
-rsorted_kvs_test_() ->
-    Sorted = [{Seq, random:uniform()} || Seq <- lists:seq(1, ?ROWS)],
-    Funs = kvs_test_funs(),
-    Reversed = Sorted,
-    {
-        "BTree with backward sorted keys",
-        {
-            foreachx,
-            fun setup_kvs/1, fun teardown/2,
-            [{Reversed, Fun} || Fun <- Funs]
-        }
-    }.
-
-shuffled_kvs_test_() ->
-    Funs = kvs_test_funs(),
-    Sorted = [{Seq, random:uniform()} || Seq <- lists:seq(1, ?ROWS)],
-    Shuffled = shuffle(Sorted),
-    {
-        "BTree with shuffled keys",
-        {
-            foreachx,
-            fun setup_kvs/1, fun teardown/2,
-            [{Shuffled, Fun} || Fun <- Funs]
-        }
-    }.
-
-reductions_test_() ->
-    {
-        "BTree reductions",
-        [
-            {
-                "Common tests",
-                {
-                    foreach,
-                    fun setup_red/0, fun teardown/1,
-                    [
-                        fun should_reduce_without_specified_direction/1,
-                        fun should_reduce_forward/1,
-                        fun should_reduce_backward/1
-                    ]
-                }
-            },
-            {
-                "Range requests",
-                [
-                    {
-                        "Forward direction",
-                        {
-                            foreachx,
-                            fun setup_red/1, fun teardown/2,
-                            [{fwd, F} || F <- red_test_funs()]
-                        }
-                    },
-                    {
-                        "Backward direction",
-                        {
-                            foreachx,
-                            fun setup_red/1, fun teardown/2,
-                            [{rev, F} || F <- red_test_funs()]
-                        }
-                    }
-                ]
-            }
-        ]
-    }.
-
-
-should_set_fd_correctly(_, {Fd, Btree}) ->
-    ?_assertMatch(Fd, Btree#btree.fd).
-
-should_set_root_correctly(_, {_, Btree}) ->
-    ?_assertMatch(nil, Btree#btree.root).
-
-should_create_zero_sized_btree(_, {_, Btree}) ->
-    ?_assertMatch(0, couch_btree:size(Btree)).
-
-should_set_reduce_option(_, {_, Btree}) ->
-    ReduceFun = fun reduce_fun/2,
-    Btree1 = couch_btree:set_options(Btree, [{reduce, ReduceFun}]),
-    ?_assertMatch(ReduceFun, Btree1#btree.reduce).
-
-should_fold_over_empty_btree(_, {_, Btree}) ->
-    {ok, _, EmptyRes} = couch_btree:foldl(Btree, fun(_, X) -> {ok, X+1} end, 0),
-    ?_assertEqual(EmptyRes, 0).
-
-should_add_all_keys(KeyValues, {Fd, Btree}) ->
-    {ok, Btree1} = couch_btree:add_remove(Btree, KeyValues, []),
-    [
-        should_return_complete_btree_on_adding_all_keys(KeyValues, Btree1),
-        should_have_non_zero_size(Btree1),
-        should_have_lesser_size_than_file(Fd, Btree1),
-        should_keep_root_pointer_to_kp_node(Fd, Btree1),
-        should_remove_all_keys(KeyValues, Btree1)
-    ].
-
-should_return_complete_btree_on_adding_all_keys(KeyValues, Btree) ->
-    ?_assert(test_btree(Btree, KeyValues)).
-
-should_have_non_zero_size(Btree) ->
-    ?_assert(couch_btree:size(Btree) > 0).
-
-should_have_lesser_size_than_file(Fd, Btree) ->
-    ?_assert((couch_btree:size(Btree) =< couch_file:bytes(Fd))).
-
-should_keep_root_pointer_to_kp_node(Fd, Btree) ->
-    ?_assertMatch({ok, {kp_node, _}},
-                  couch_file:pread_term(Fd, element(1, Btree#btree.root))).
-
-should_remove_all_keys(KeyValues, Btree) ->
-    Keys = keys(KeyValues),
-    {ok, Btree1} = couch_btree:add_remove(Btree, [], Keys),
-    {
-        "Should remove all the keys",
-        [
-            should_produce_valid_btree(Btree1, []),
-            should_be_empty(Btree1)
-        ]
-    }.
-
-should_continuously_add_new_kv(KeyValues, {_, Btree}) ->
-    {Btree1, _} = lists:foldl(
-        fun(KV, {BtAcc, PrevSize}) ->
-            {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [KV], []),
-            ?assert(couch_btree:size(BtAcc2) > PrevSize),
-            {BtAcc2, couch_btree:size(BtAcc2)}
-        end, {Btree, couch_btree:size(Btree)}, KeyValues),
-    {
-        "Should continuously add key-values to btree",
-        [
-            should_produce_valid_btree(Btree1, KeyValues),
-            should_not_be_empty(Btree1)
-        ]
-    }.
-
-should_continuously_remove_keys(KeyValues, {_, Btree}) ->
-    {ok, Btree1} = couch_btree:add_remove(Btree, KeyValues, []),
-    {Btree2, _} = lists:foldl(
-        fun({K, _}, {BtAcc, PrevSize}) ->
-            {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [], [K]),
-            ?assert(couch_btree:size(BtAcc2) < PrevSize),
-            {BtAcc2, couch_btree:size(BtAcc2)}
-        end, {Btree1, couch_btree:size(Btree1)}, KeyValues),
-    {
-        "Should continuously remove keys from btree",
-        [
-            should_produce_valid_btree(Btree2, []),
-            should_be_empty(Btree2)
-        ]
-    }.
-
-should_insert_keys_in_reversed_order(KeyValues, {_, Btree}) ->
-    KeyValuesRev = lists:reverse(KeyValues),
-    {Btree1, _} = lists:foldl(
-        fun(KV, {BtAcc, PrevSize}) ->
-            {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [KV], []),
-            ?assert(couch_btree:size(BtAcc2) > PrevSize),
-            {BtAcc2, couch_btree:size(BtAcc2)}
-        end, {Btree, couch_btree:size(Btree)}, KeyValuesRev),
-    should_produce_valid_btree(Btree1, KeyValues).
-
-should_add_every_odd_key_remove_every_even(KeyValues, {_, Btree}) ->
-    {ok, Btree1} = couch_btree:add_remove(Btree, KeyValues, []),
-    {_, Rem2Keys0, Rem2Keys1} = lists:foldl(fun(X, {Count, Left, Right}) ->
-        case Count rem 2 == 0 of
-            true -> {Count + 1, [X | Left], Right};
-            false -> {Count + 1, Left, [X | Right]}
-        end
-                                            end, {0, [], []}, KeyValues),
-    ?_assert(test_add_remove(Btree1, Rem2Keys0, Rem2Keys1)).
-
-should_add_every_even_key_remove_every_old(KeyValues, {_, Btree}) ->
-    {ok, Btree1} = couch_btree:add_remove(Btree, KeyValues, []),
-    {_, Rem2Keys0, Rem2Keys1} = lists:foldl(fun(X, {Count, Left, Right}) ->
-        case Count rem 2 == 0 of
-            true -> {Count + 1, [X | Left], Right};
-            false -> {Count + 1, Left, [X | Right]}
-        end
-                                            end, {0, [], []}, KeyValues),
-    ?_assert(test_add_remove(Btree1, Rem2Keys1, Rem2Keys0)).
-
-
-should_reduce_without_specified_direction({_, Btree}) ->
-    ?_assertMatch(
-        {ok, [{{"odd", _}, ?ROWS div 2}, {{"even", _}, ?ROWS div 2}]},
-        fold_reduce(Btree, [])).
-
-should_reduce_forward({_, Btree}) ->
-    ?_assertMatch(
-        {ok, [{{"odd", _}, ?ROWS div 2}, {{"even", _}, ?ROWS div 2}]},
-        fold_reduce(Btree, [{dir, fwd}])).
-
-should_reduce_backward({_, Btree}) ->
-    ?_assertMatch(
-        {ok, [{{"even", _}, ?ROWS div 2}, {{"odd", _}, ?ROWS div 2}]},
-        fold_reduce(Btree, [{dir, rev}])).
-
-should_reduce_whole_range(fwd, {_, Btree}) ->
-    {SK, EK} = {{"even", 0}, {"odd", ?ROWS - 1}},
-    [
-        {
-            "include endkey",
-            ?_assertMatch(
-                {ok, [{{"odd", 1}, ?ROWS div 2},
-                      {{"even", 2}, ?ROWS div 2}]},
-                fold_reduce(Btree, [{dir, fwd},
-                                    {start_key, SK},
-                                    {end_key, EK}]))
-        },
-        {
-            "exclude endkey",
-            ?_assertMatch(
-                {ok, [{{"odd", 1}, (?ROWS div 2) - 1},
-                      {{"even", 2}, ?ROWS div 2}]},
-                fold_reduce(Btree, [{dir, fwd},
-                                    {start_key, SK},
-                                    {end_key_gt, EK}]))
-        }
-    ];
-should_reduce_whole_range(rev, {_, Btree}) ->
-    {SK, EK} = {{"odd", ?ROWS - 1}, {"even", 2}},
-    [
-        {
-            "include endkey",
-            ?_assertMatch(
-                {ok, [{{"even", ?ROWS}, ?ROWS div 2},
-                      {{"odd", ?ROWS - 1}, ?ROWS div 2}]},
-                fold_reduce(Btree, [{dir, rev},
-                                    {start_key, SK},
-                                    {end_key, EK}]))
-        },
-        {
-            "exclude endkey",
-            ?_assertMatch(
-                {ok, [{{"even", ?ROWS}, (?ROWS div 2) - 1},
-                      {{"odd", ?ROWS - 1}, ?ROWS div 2}]},
-                fold_reduce(Btree, [{dir, rev},
-                                    {start_key, SK},
-                                    {end_key_gt, EK}]))
-        }
-    ].
-
-should_reduce_first_half(fwd, {_, Btree}) ->
-    {SK, EK} = {{"even", 0}, {"odd", (?ROWS div 2) - 1}},
-    [
-        {
-            "include endkey",
-            ?_assertMatch(
-                {ok, [{{"odd", 1}, ?ROWS div 4},
-                      {{"even", 2}, ?ROWS div 2}]},
-                fold_reduce(Btree, [{dir, fwd},
-                                    {start_key, SK}, {end_key, EK}]))
-        },
-        {
-            "exclude endkey",
-            ?_assertMatch(
-                {ok, [{{"odd", 1}, (?ROWS div 4) - 1},
-                      {{"even", 2}, ?ROWS div 2}]},
-                fold_reduce(Btree, [{dir, fwd},
-                                    {start_key, SK},
-                                    {end_key_gt, EK}]))
-        }
-    ];
-should_reduce_first_half(rev, {_, Btree}) ->
-    {SK, EK} = {{"odd", ?ROWS - 1}, {"even", ?ROWS div 2}},
-    [
-        {
-            "include endkey",
-            ?_assertMatch(
-                {ok, [{{"even", ?ROWS}, (?ROWS div 4) + 1},
-                      {{"odd", ?ROWS - 1}, ?ROWS div 2}]},
-                fold_reduce(Btree, [{dir, rev},
-                                    {start_key, SK},
-                                    {end_key, EK}]))
-        },
-        {
-            "exclude endkey",
-            ?_assertMatch(
-                {ok, [{{"even", ?ROWS}, ?ROWS div 4},
-                      {{"odd", ?ROWS - 1}, ?ROWS div 2}]},
-                fold_reduce(Btree, [{dir, rev},
-                                    {start_key, SK},
-                                    {end_key_gt, EK}]))
-        }
-    ].
-
-should_reduce_second_half(fwd, {_, Btree}) ->
-    {SK, EK} = {{"even", ?ROWS div 2}, {"odd", ?ROWS - 1}},
-    [
-        {
-            "include endkey",
-            ?_assertMatch(
-                {ok, [{{"odd", 1}, ?ROWS div 2},
-                      {{"even", ?ROWS div 2}, (?ROWS div 4) + 1}]},
-                fold_reduce(Btree, [{dir, fwd},
-                                    {start_key, SK},
-                                    {end_key, EK}]))
-        },
-        {
-            "exclude endkey",
-            ?_assertMatch(
-                {ok, [{{"odd", 1}, (?ROWS div 2) - 1},
-                      {{"even", ?ROWS div 2}, (?ROWS div 4) + 1}]},
-                fold_reduce(Btree, [{dir, fwd},
-                                    {start_key, SK},
-                                    {end_key_gt, EK}]))
-        }
-    ];
-should_reduce_second_half(rev, {_, Btree}) ->
-    {SK, EK} = {{"odd", (?ROWS div 2) + 1}, {"even", 2}},
-    [
-        {
-            "include endkey",
-            ?_assertMatch(
-                {ok, [{{"even", ?ROWS}, ?ROWS div 2},
-                      {{"odd", (?ROWS div 2) + 1}, (?ROWS div 4) + 1}]},
-                fold_reduce(Btree, [{dir, rev},
-                                    {start_key, SK},
-                                    {end_key, EK}]))
-        },
-        {
-            "exclude endkey",
-            ?_assertMatch(
-                {ok, [{{"even", ?ROWS}, (?ROWS div 2) - 1},
-                      {{"odd", (?ROWS div 2) + 1}, (?ROWS div 4) + 1}]},
-                fold_reduce(Btree, [{dir, rev},
-                                    {start_key, SK},
-                                    {end_key_gt, EK}]))
-        }
-    ].
-
-should_produce_valid_btree(Btree, KeyValues) ->
-    ?_assert(test_btree(Btree, KeyValues)).
-
-should_be_empty(Btree) ->
-    ?_assertEqual(couch_btree:size(Btree), 0).
-
-should_not_be_empty(Btree) ->
-    ?_assert(couch_btree:size(Btree) > 0).
-
-fold_reduce(Btree, Opts) ->
-    GroupFun = fun({K1, _}, {K2, _}) ->
-        K1 == K2
-    end,
-    FoldFun = fun(GroupedKey, Unreduced, Acc) ->
-        {ok, [{GroupedKey, couch_btree:final_reduce(Btree, Unreduced)} | Acc]}
-    end,
-    couch_btree:fold_reduce(Btree, FoldFun, [],
-                            [{key_group_fun, GroupFun}] ++ Opts).
-
-
-keys(KVs) ->
-    [K || {K, _} <- KVs].
-
-reduce_fun(reduce, KVs) ->
-    length(KVs);
-reduce_fun(rereduce, Reds) ->
-    lists:sum(Reds).
-
-
-shuffle(List) ->
-    randomize(round(math:log(length(List)) + 0.5), List).
-
-randomize(1, List) ->
-    randomize(List);
-randomize(T, List) ->
-    lists:foldl(
-        fun(_E, Acc) ->
-            randomize(Acc)
-        end, randomize(List), lists:seq(1, (T - 1))).
-
-randomize(List) ->
-    D = lists:map(fun(A) -> {random:uniform(), A} end, List),
-    {_, D1} = lists:unzip(lists:keysort(1, D)),
-    D1.
-
-test_btree(Btree, KeyValues) ->
-    ok = test_key_access(Btree, KeyValues),
-    ok = test_lookup_access(Btree, KeyValues),
-    ok = test_final_reductions(Btree, KeyValues),
-    ok = test_traversal_callbacks(Btree, KeyValues),
-    true.
-
-test_add_remove(Btree, OutKeyValues, RemainingKeyValues) ->
-    Btree2 = lists:foldl(
-        fun({K, _}, BtAcc) ->
-            {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [], [K]),
-            BtAcc2
-        end, Btree, OutKeyValues),
-    true = test_btree(Btree2, RemainingKeyValues),
-
-    Btree3 = lists:foldl(
-        fun(KV, BtAcc) ->
-            {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [KV], []),
-            BtAcc2
-        end, Btree2, OutKeyValues),
-    true = test_btree(Btree3, OutKeyValues ++ RemainingKeyValues).
-
-test_key_access(Btree, List) ->
-    FoldFun = fun(Element, {[HAcc|TAcc], Count}) ->
-        case Element == HAcc of
-            true -> {ok, {TAcc, Count + 1}};
-            _ -> {ok, {TAcc, Count + 1}}
-        end
-    end,
-    Length = length(List),
-    Sorted = lists:sort(List),
-    {ok, _, {[], Length}} = couch_btree:foldl(Btree, FoldFun, {Sorted, 0}),
-    {ok, _, {[], Length}} = couch_btree:fold(Btree, FoldFun,
-                                             {Sorted, 0}, [{dir, rev}]),
-    ok.
-
-test_lookup_access(Btree, KeyValues) ->
-    FoldFun = fun({Key, Value}, {Key, Value}) -> {stop, true} end,
-    lists:foreach(
-        fun({Key, Value}) ->
-            [{ok, {Key, Value}}] = couch_btree:lookup(Btree, [Key]),
-            {ok, _, true} = couch_btree:foldl(Btree, FoldFun,
-                                              {Key, Value}, [{start_key, Key}])
-        end, KeyValues).
-
-test_final_reductions(Btree, KeyValues) ->
-    KVLen = length(KeyValues),
-    FoldLFun = fun(_X, LeadingReds, Acc) ->
-        CountToStart = KVLen div 3 + Acc,
-        CountToStart = couch_btree:final_reduce(Btree, LeadingReds),
-        {ok, Acc + 1}
-    end,
-    FoldRFun = fun(_X, LeadingReds, Acc) ->
-        CountToEnd = KVLen - KVLen div 3 + Acc,
-        CountToEnd = couch_btree:final_reduce(Btree, LeadingReds),
-        {ok, Acc + 1}
-    end,
-    {LStartKey, _} = case KVLen of
-        0 -> {nil, nil};
-        _ -> lists:nth(KVLen div 3 + 1, lists:sort(KeyValues))
-    end,
-    {RStartKey, _} = case KVLen of
-        0 -> {nil, nil};
-        _ -> lists:nth(KVLen div 3, lists:sort(KeyValues))
-    end,
-    {ok, _, FoldLRed} = couch_btree:foldl(Btree, FoldLFun, 0,
-                                          [{start_key, LStartKey}]),
-    {ok, _, FoldRRed} = couch_btree:fold(Btree, FoldRFun, 0,
-                                         [{dir, rev}, {start_key, RStartKey}]),
-    KVLen = FoldLRed + FoldRRed,
-    ok.
-
-test_traversal_callbacks(Btree, _KeyValues) ->
-    FoldFun = fun
-        (visit, _GroupedKey, _Unreduced, Acc) ->
-            {ok, Acc andalso false};
-        (traverse, _LK, _Red, Acc) ->
-            {skip, Acc andalso true}
-    end,
-    % With 250 items the root is a kp. Always skipping should reduce to true.
-    {ok, _, true} = couch_btree:fold(Btree, FoldFun, true, [{dir, fwd}]),
-    ok.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_changes_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_changes_tests.erl b/test/couchdb/couch_changes_tests.erl
deleted file mode 100644
index a129ba2..0000000
--- a/test/couchdb/couch_changes_tests.erl
+++ /dev/null
@@ -1,612 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_changes_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(ADMIN_USER, {user_ctx, #user_ctx{roles = [<<"_admin">>]}}).
--define(TIMEOUT, 3000).
--define(TEST_TIMEOUT, 10000).
-
--record(row, {
-    id,
-    seq,
-    deleted = false
-}).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-setup() ->
-    DbName = ?tempdb(),
-    {ok, Db} = create_db(DbName),
-    Revs = [R || {ok, R} <- [
-        save_doc(Db, {[{<<"_id">>, <<"doc1">>}]}),
-        save_doc(Db, {[{<<"_id">>, <<"doc2">>}]}),
-        save_doc(Db, {[{<<"_id">>, <<"doc3">>}]}),
-        save_doc(Db, {[{<<"_id">>, <<"doc4">>}]}),
-        save_doc(Db, {[{<<"_id">>, <<"doc5">>}]})
-    ]],
-    Rev = lists:nth(3, Revs),
-    {ok, Rev1} = save_doc(Db, {[{<<"_id">>, <<"doc3">>}, {<<"_rev">>, Rev}]}),
-    Revs1 = Revs ++ [Rev1],
-    Revs2 = Revs1 ++ [R || {ok, R} <- [
-        save_doc(Db, {[{<<"_id">>, <<"doc6">>}]}),
-        save_doc(Db, {[{<<"_id">>, <<"_design/foo">>}]}),
-        save_doc(Db, {[{<<"_id">>, <<"doc7">>}]}),
-        save_doc(Db, {[{<<"_id">>, <<"doc8">>}]})
-    ]],
-    {DbName, list_to_tuple(Revs2)}.
-
-teardown({DbName, _}) ->
-    delete_db(DbName),
-    ok.
-
-
-changes_test_() ->
-    {
-        "Changes feeed",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            [
-                filter_by_doc_id(),
-                filter_by_design(),
-                continuous_feed(),
-                filter_by_custom_function()
-            ]
-        }
-    }.
-
-filter_by_doc_id() ->
-    {
-        "Filter _doc_id",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [
-                fun should_filter_by_specific_doc_ids/1,
-                fun should_filter_by_specific_doc_ids_descending/1,
-                fun should_filter_by_specific_doc_ids_with_since/1,
-                fun should_filter_by_specific_doc_ids_no_result/1,
-                fun should_handle_deleted_docs/1
-            ]
-        }
-    }.
-
-filter_by_design() ->
-    {
-        "Filter _design",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [
-                fun should_emit_only_design_documents/1
-            ]
-        }
-    }.
-
-filter_by_custom_function() ->
-    {
-        "Filter function",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [
-                fun should_receive_heartbeats/1
-            ]
-        }
-    }.
-
-continuous_feed() ->
-    {
-        "Continuous Feed",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [
-                fun should_filter_continuous_feed_by_specific_doc_ids/1
-            ]
-        }
-    }.
-
-
-should_filter_by_specific_doc_ids({DbName, _}) ->
-    ?_test(
-        begin
-            ChangesArgs = #changes_args{
-                filter = "_doc_ids"
-            },
-            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
-            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
-            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
-
-            {Rows, LastSeq} = wait_finished(Consumer),
-            {ok, Db} = couch_db:open_int(DbName, []),
-            UpSeq = couch_db:get_update_seq(Db),
-            couch_db:close(Db),
-            stop_consumer(Consumer),
-
-            ?assertEqual(2, length(Rows)),
-            [#row{seq = Seq1, id = Id1}, #row{seq = Seq2, id = Id2}] = Rows,
-            ?assertEqual(<<"doc4">>, Id1),
-            ?assertEqual(4, Seq1),
-            ?assertEqual(<<"doc3">>, Id2),
-            ?assertEqual(6, Seq2),
-            ?assertEqual(UpSeq, LastSeq)
-        end).
-
-should_filter_by_specific_doc_ids_descending({DbName, _}) ->
-    ?_test(
-        begin
-            ChangesArgs = #changes_args{
-                filter = "_doc_ids",
-                dir = rev
-            },
-            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
-            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
-            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
-
-            {Rows, LastSeq} = wait_finished(Consumer),
-            {ok, Db} = couch_db:open_int(DbName, []),
-            couch_db:close(Db),
-            stop_consumer(Consumer),
-
-            ?assertEqual(2, length(Rows)),
-            [#row{seq = Seq1, id = Id1}, #row{seq = Seq2, id = Id2}] = Rows,
-            ?assertEqual(<<"doc3">>, Id1),
-            ?assertEqual(6, Seq1),
-            ?assertEqual(<<"doc4">>, Id2),
-            ?assertEqual(4, Seq2),
-            ?assertEqual(4, LastSeq)
-        end).
-
-should_filter_by_specific_doc_ids_with_since({DbName, _}) ->
-    ?_test(
-        begin
-            ChangesArgs = #changes_args{
-                filter = "_doc_ids",
-                since = 5
-            },
-            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
-            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
-            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
-
-            {Rows, LastSeq} = wait_finished(Consumer),
-            {ok, Db} = couch_db:open_int(DbName, []),
-            UpSeq = couch_db:get_update_seq(Db),
-            couch_db:close(Db),
-            stop_consumer(Consumer),
-
-            ?assertEqual(1, length(Rows)),
-            [#row{seq = Seq1, id = Id1}] = Rows,
-            ?assertEqual(<<"doc3">>, Id1),
-            ?assertEqual(6, Seq1),
-            ?assertEqual(UpSeq, LastSeq)
-        end).
-
-should_filter_by_specific_doc_ids_no_result({DbName, _}) ->
-    ?_test(
-        begin
-            ChangesArgs = #changes_args{
-                filter = "_doc_ids",
-                since = 6
-            },
-            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
-            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
-            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
-
-            {Rows, LastSeq} = wait_finished(Consumer),
-            {ok, Db} = couch_db:open_int(DbName, []),
-            UpSeq = couch_db:get_update_seq(Db),
-            couch_db:close(Db),
-            stop_consumer(Consumer),
-
-            ?assertEqual(0, length(Rows)),
-            ?assertEqual(UpSeq, LastSeq)
-        end).
-
-should_handle_deleted_docs({DbName, Revs}) ->
-    ?_test(
-        begin
-            Rev3_2 = element(6, Revs),
-            {ok, Db} = couch_db:open_int(DbName, []),
-            {ok, _} = save_doc(
-                Db,
-                {[{<<"_id">>, <<"doc3">>},
-                  {<<"_deleted">>, true},
-                  {<<"_rev">>, Rev3_2}]}),
-
-            ChangesArgs = #changes_args{
-                filter = "_doc_ids",
-                since = 9
-            },
-            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
-            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
-            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
-
-            {Rows, LastSeq} = wait_finished(Consumer),
-            couch_db:close(Db),
-            stop_consumer(Consumer),
-
-            ?assertEqual(1, length(Rows)),
-            ?assertMatch(
-                [#row{seq = LastSeq, id = <<"doc3">>, deleted = true}],
-                Rows
-            ),
-            ?assertEqual(11, LastSeq)
-        end).
-
-should_filter_continuous_feed_by_specific_doc_ids({DbName, Revs}) ->
-    ?_test(
-        begin
-            {ok, Db} = couch_db:open_int(DbName, []),
-            ChangesArgs = #changes_args{
-                filter = "_doc_ids",
-                feed = "continuous"
-            },
-            DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>],
-            Req = {json_req, {[{<<"doc_ids">>, DocIds}]}},
-            Consumer = spawn_consumer(DbName, ChangesArgs, Req),
-            pause(Consumer),
-
-            Rows = get_rows(Consumer),
-            ?assertEqual(2, length(Rows)),
-            [#row{seq = Seq1, id = Id1}, #row{seq = Seq2, id = Id2}] = Rows,
-            ?assertEqual(<<"doc4">>, Id1),
-            ?assertEqual(4, Seq1),
-            ?assertEqual(<<"doc3">>, Id2),
-            ?assertEqual(6, Seq2),
-
-            clear_rows(Consumer),
-            {ok, _Rev9} = save_doc(Db, {[{<<"_id">>, <<"doc9">>}]}),
-            {ok, _Rev10} = save_doc(Db, {[{<<"_id">>, <<"doc10">>}]}),
-            unpause(Consumer),
-            pause(Consumer),
-            ?assertEqual([], get_rows(Consumer)),
-
-            Rev4 = element(4, Revs),
-            Rev3_2 = element(6, Revs),
-            {ok, Rev4_2} = save_doc(Db, {[{<<"_id">>, <<"doc4">>},
-                                          {<<"_rev">>, Rev4}]}),
-            {ok, _} = save_doc(Db, {[{<<"_id">>, <<"doc11">>}]}),
-            {ok, _} = save_doc(Db, {[{<<"_id">>, <<"doc4">>},
-                                     {<<"_rev">>, Rev4_2}]}),
-            {ok, _} = save_doc(Db, {[{<<"_id">>, <<"doc12">>}]}),
-            {ok, Rev3_3} = save_doc(Db, {[{<<"_id">>, <<"doc3">>},
-                                          {<<"_rev">>, Rev3_2}]}),
-            unpause(Consumer),
-            pause(Consumer),
-
-            NewRows = get_rows(Consumer),
-            ?assertEqual(2, length(NewRows)),
-            [Row14, Row16] = NewRows,
-            ?assertEqual(<<"doc4">>, Row14#row.id),
-            ?assertEqual(15, Row14#row.seq),
-            ?assertEqual(<<"doc3">>, Row16#row.id),
-            ?assertEqual(17, Row16#row.seq),
-
-            clear_rows(Consumer),
-            {ok, _Rev3_4} = save_doc(Db, {[{<<"_id">>, <<"doc3">>},
-                                           {<<"_rev">>, Rev3_3}]}),
-            unpause(Consumer),
-            pause(Consumer),
-
-            FinalRows = get_rows(Consumer),
-
-            unpause(Consumer),
-            stop_consumer(Consumer),
-
-            ?assertMatch([#row{seq = 18, id = <<"doc3">>}], FinalRows)
-        end).
-
-should_emit_only_design_documents({DbName, Revs}) ->
-    ?_test(
-        begin
-            ChangesArgs = #changes_args{
-                filter = "_design"
-            },
-            Consumer = spawn_consumer(DbName, ChangesArgs, {json_req, null}),
-
-            {Rows, LastSeq} = wait_finished(Consumer),
-            {ok, Db} = couch_db:open_int(DbName, []),
-            UpSeq = couch_db:get_update_seq(Db),
-            couch_db:close(Db),
-
-            ?assertEqual(1, length(Rows)),
-            ?assertEqual(UpSeq, LastSeq),
-            ?assertEqual([#row{seq = 8, id = <<"_design/foo">>}], Rows),
-
-            stop_consumer(Consumer),
-
-            {ok, Db2} = couch_db:open_int(DbName, [?ADMIN_USER]),
-            {ok, _} = save_doc(Db2, {[{<<"_id">>, <<"_design/foo">>},
-                                      {<<"_rev">>, element(8, Revs)},
-                                      {<<"_deleted">>, true}]}),
-
-            Consumer2 = spawn_consumer(DbName, ChangesArgs, {json_req, null}),
-
-            {Rows2, LastSeq2} = wait_finished(Consumer2),
-            UpSeq2 = UpSeq + 1,
-            couch_db:close(Db2),
-
-            ?assertEqual(1, length(Rows2)),
-            ?assertEqual(UpSeq2, LastSeq2),
-            ?assertEqual([#row{seq = 11,
-                               id = <<"_design/foo">>,
-                               deleted = true}],
-                          Rows2)
-        end).
-
-should_receive_heartbeats(_) ->
-    {timeout, ?TEST_TIMEOUT div 1000,
-     ?_test(
-         begin
-             DbName = ?tempdb(),
-             Timeout = 100,
-             {ok, Db} = create_db(DbName),
-
-             {ok, _} = save_doc(Db, {[
-                 {<<"_id">>, <<"_design/filtered">>},
-                 {<<"language">>, <<"javascript">>},
-                     {<<"filters">>, {[
-                         {<<"foo">>, <<"function(doc) {
-                             return ['doc10', 'doc11', 'doc12'].indexOf(doc._id) != -1;}">>
-                     }]}}
-             ]}),
-
-             ChangesArgs = #changes_args{
-                 filter = "filtered/foo",
-                 feed = "continuous",
-                 timeout = 10000,
-                 heartbeat = 1000
-             },
-             Consumer = spawn_consumer(DbName, ChangesArgs, {json_req, null}),
-
-             {ok, _Rev1} = save_doc(Db, {[{<<"_id">>, <<"doc1">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev2} = save_doc(Db, {[{<<"_id">>, <<"doc2">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev3} = save_doc(Db, {[{<<"_id">>, <<"doc3">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev4} = save_doc(Db, {[{<<"_id">>, <<"doc4">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev5} = save_doc(Db, {[{<<"_id">>, <<"doc5">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev6} = save_doc(Db, {[{<<"_id">>, <<"doc6">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev7} = save_doc(Db, {[{<<"_id">>, <<"doc7">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev8} = save_doc(Db, {[{<<"_id">>, <<"doc8">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev9} = save_doc(Db, {[{<<"_id">>, <<"doc9">>}]}),
-
-             Heartbeats = get_heartbeats(Consumer),
-             ?assert(Heartbeats > 0),
-
-             {ok, _Rev10} = save_doc(Db, {[{<<"_id">>, <<"doc10">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev11} = save_doc(Db, {[{<<"_id">>, <<"doc11">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev12} = save_doc(Db, {[{<<"_id">>, <<"doc12">>}]}),
-
-             Heartbeats2 = get_heartbeats(Consumer),
-             ?assert(Heartbeats2 > Heartbeats),
-
-             Rows = get_rows(Consumer),
-             ?assertEqual(3, length(Rows)),
-
-             {ok, _Rev13} = save_doc(Db, {[{<<"_id">>, <<"doc13">>}]}),
-             timer:sleep(Timeout),
-             {ok, _Rev14} = save_doc(Db, {[{<<"_id">>, <<"doc14">>}]}),
-             timer:sleep(Timeout),
-
-             Heartbeats3 = get_heartbeats(Consumer),
-             ?assert(Heartbeats3 > Heartbeats2)
-        end)}.
-
-
-save_doc(Db, Json) ->
-    Doc = couch_doc:from_json_obj(Json),
-    {ok, Rev} = couch_db:update_doc(Db, Doc, []),
-    {ok, couch_doc:rev_to_str(Rev)}.
-
-get_rows(Consumer) ->
-    Ref = make_ref(),
-    Consumer ! {get_rows, Ref},
-    Resp = receive
-        {rows, Ref, Rows} ->
-            Rows
-    after ?TIMEOUT ->
-        timeout
-    end,
-    ?assertNotEqual(timeout, Resp),
-    Resp.
-
-get_heartbeats(Consumer) ->
-    Ref = make_ref(),
-    Consumer ! {get_heartbeats, Ref},
-    Resp = receive
-        {hearthbeats, Ref, HeartBeats} ->
-            HeartBeats
-    after ?TIMEOUT ->
-        timeout
-    end,
-    ?assertNotEqual(timeout, Resp),
-    Resp.
-
-clear_rows(Consumer) ->
-    Ref = make_ref(),
-    Consumer ! {reset, Ref},
-    Resp = receive
-        {ok, Ref} ->
-            ok
-    after ?TIMEOUT ->
-        timeout
-    end,
-    ?assertNotEqual(timeout, Resp),
-    Resp.
-
-stop_consumer(Consumer) ->
-    Ref = make_ref(),
-    Consumer ! {stop, Ref},
-    Resp = receive
-        {ok, Ref} ->
-            ok
-    after ?TIMEOUT ->
-        timeout
-    end,
-    ?assertNotEqual(timeout, Resp),
-    Resp.
-
-pause(Consumer) ->
-    Ref = make_ref(),
-    Consumer ! {pause, Ref},
-    Resp = receive
-        {paused, Ref} ->
-            ok
-    after ?TIMEOUT ->
-        timeout
-    end,
-    ?assertNotEqual(timeout, Resp),
-    Resp.
-
-unpause(Consumer) ->
-    Ref = make_ref(),
-    Consumer ! {continue, Ref},
-    Resp = receive
-        {ok, Ref} ->
-            ok
-    after ?TIMEOUT ->
-       timeout
-    end,
-    ?assertNotEqual(timeout, Resp),
-    Resp.
-
-wait_finished(_Consumer) ->
-    Resp = receive
-        {consumer_finished, Rows, LastSeq} ->
-            {Rows, LastSeq}
-    after ?TIMEOUT ->
-        timeout
-    end,
-    ?assertNotEqual(timeout, Resp),
-    Resp.
-
-spawn_consumer(DbName, ChangesArgs0, Req) ->
-    Parent = self(),
-    spawn(fun() ->
-        put(heartbeat_count, 0),
-        Callback = fun
-            ({change, {Change}, _}, _, Acc) ->
-                Id = couch_util:get_value(<<"id">>, Change),
-                Seq = couch_util:get_value(<<"seq">>, Change),
-                Del = couch_util:get_value(<<"deleted">>, Change, false),
-                [#row{id = Id, seq = Seq, deleted = Del} | Acc];
-            ({stop, LastSeq}, _, Acc) ->
-                Parent ! {consumer_finished, lists:reverse(Acc), LastSeq},
-                stop_loop(Parent, Acc);
-            (timeout, _, Acc) ->
-                put(heartbeat_count, get(heartbeat_count) + 1),
-                maybe_pause(Parent, Acc);
-            (_, _, Acc) ->
-                maybe_pause(Parent, Acc)
-        end,
-        {ok, Db} = couch_db:open_int(DbName, []),
-        ChangesArgs = case (ChangesArgs0#changes_args.timeout =:= undefined)
-            andalso (ChangesArgs0#changes_args.heartbeat =:= undefined) of
-            true ->
-                ChangesArgs0#changes_args{timeout = 10, heartbeat = 10};
-            false ->
-                ChangesArgs0
-        end,
-        FeedFun = couch_changes:handle_changes(ChangesArgs, Req, Db),
-        try
-            FeedFun({Callback, []})
-        catch throw:{stop, _} ->
-            ok
-        end,
-        catch couch_db:close(Db)
-    end).
-
-maybe_pause(Parent, Acc) ->
-    receive
-        {get_rows, Ref} ->
-            Parent ! {rows, Ref, lists:reverse(Acc)},
-            maybe_pause(Parent, Acc);
-        {get_heartbeats, Ref} ->
-            Parent ! {hearthbeats, Ref, get(heartbeat_count)},
-            maybe_pause(Parent, Acc);
-        {reset, Ref} ->
-            Parent ! {ok, Ref},
-            maybe_pause(Parent, []);
-        {pause, Ref} ->
-            Parent ! {paused, Ref},
-            pause_loop(Parent, Acc);
-        {stop, Ref} ->
-            Parent ! {ok, Ref},
-            throw({stop, Acc});
-        V ->
-            erlang:error({assertion_failed,
-                      [{module, ?MODULE},
-                       {line, ?LINE},
-                       {value, V},
-                       {reason, "Received unexpected message"}]})
-    after 0 ->
-        Acc
-    end.
-
-pause_loop(Parent, Acc) ->
-    receive
-        {stop, Ref} ->
-            Parent ! {ok, Ref},
-            throw({stop, Acc});
-        {reset, Ref} ->
-            Parent ! {ok, Ref},
-            pause_loop(Parent, []);
-        {continue, Ref} ->
-            Parent ! {ok, Ref},
-            Acc;
-        {get_rows, Ref} ->
-            Parent ! {rows, Ref, lists:reverse(Acc)},
-            pause_loop(Parent, Acc)
-    end.
-
-stop_loop(Parent, Acc) ->
-    receive
-        {get_rows, Ref} ->
-            Parent ! {rows, Ref, lists:reverse(Acc)},
-            stop_loop(Parent, Acc);
-        {stop, Ref} ->
-            Parent ! {ok, Ref},
-            Acc
-    end.
-
-create_db(DbName) ->
-    couch_db:create(DbName, [?ADMIN_USER, overwrite]).
-
-delete_db(DbName) ->
-    ok = couch_server:delete(DbName, [?ADMIN_USER]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_db_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_db_tests.erl b/test/couchdb/couch_db_tests.erl
deleted file mode 100644
index 3089714..0000000
--- a/test/couchdb/couch_db_tests.erl
+++ /dev/null
@@ -1,114 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_db_tests).
-
--include("couch_eunit.hrl").
-
--define(TIMEOUT, 120).
-
-
-setup() ->
-    {ok, _} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    couch_config:set("log", "include_sasl", "false", false),
-    ok.
-
-teardown(_) ->
-    couch_server_sup:stop().
-
-
-create_delete_db_test_()->
-    {
-        "Database create/delete tests",
-        {
-            setup,
-            fun setup/0, fun teardown/1,
-            fun(_) ->
-                [should_create_db(),
-                 should_delete_db(),
-                 should_create_multiple_dbs(),
-                 should_delete_multiple_dbs(),
-                 should_create_delete_database_continuously()]
-            end
-        }
-    }.
-
-
-should_create_db() ->
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, []),
-    ok = couch_db:close(Db),
-    {ok, AllDbs} = couch_server:all_databases(),
-    ?_assert(lists:member(DbName, AllDbs)).
-
-should_delete_db() ->
-    DbName = ?tempdb(),
-    couch_db:create(DbName, []),
-    couch_server:delete(DbName, []),
-    {ok, AllDbs} = couch_server:all_databases(),
-    ?_assertNot(lists:member(DbName, AllDbs)).
-
-should_create_multiple_dbs() ->
-    gen_server:call(couch_server, {set_max_dbs_open, 3}),
-
-    DbNames = [?tempdb() || _ <- lists:seq(1, 6)],
-    lists:foreach(fun(DbName) ->
-        {ok, Db} = couch_db:create(DbName, []),
-        ok = couch_db:close(Db)
-    end, DbNames),
-
-    {ok, AllDbs} = couch_server:all_databases(),
-    NumCreated = lists:foldl(fun(DbName, Acc) ->
-        ?assert(lists:member(DbName, AllDbs)),
-        Acc+1
-    end, 0, DbNames),
-
-    ?_assertEqual(NumCreated, 6).
-
-should_delete_multiple_dbs() ->
-    DbNames = [?tempdb() || _ <- lists:seq(1, 6)],
-    lists:foreach(fun(DbName) ->
-        {ok, Db} = couch_db:create(DbName, []),
-        ok = couch_db:close(Db)
-    end, DbNames),
-
-    lists:foreach(fun(DbName) ->
-        ok = couch_server:delete(DbName, [])
-    end, DbNames),
-
-    {ok, AllDbs} = couch_server:all_databases(),
-    NumDeleted = lists:foldl(fun(DbName, Acc) ->
-        ?assertNot(lists:member(DbName, AllDbs)),
-        Acc + 1
-    end, 0, DbNames),
-
-    ?_assertEqual(NumDeleted, 6).
-
-should_create_delete_database_continuously() ->
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, []),
-    couch_db:close(Db),
-    [{timeout, ?TIMEOUT, {integer_to_list(N) ++ " times",
-                           ?_assert(loop(DbName, N))}}
-     || N <- [10, 100, 1000]].
-
-loop(_, 0) ->
-    true;
-loop(DbName, N) ->
-    ok = cycle(DbName),
-    loop(DbName, N - 1).
-
-cycle(DbName) ->
-    ok = couch_server:delete(DbName, []),
-    {ok, Db} = couch_db:create(DbName, []),
-    couch_db:close(Db),
-    ok.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_doc_json_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_doc_json_tests.erl b/test/couchdb/couch_doc_json_tests.erl
deleted file mode 100644
index 1592b6b..0000000
--- a/test/couchdb/couch_doc_json_tests.erl
+++ /dev/null
@@ -1,391 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_doc_json_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
-
-setup() ->
-    couch_config:start_link(?CONFIG_CHAIN),
-    couch_config:set("attachments", "compression_level", "0", false),
-    ok.
-
-teardown(_) ->
-    couch_config:stop().
-
-
-json_doc_test_() ->
-    {
-        setup,
-        fun setup/0, fun teardown/1,
-        [
-            {
-                "Document from JSON",
-                [
-                    from_json_success_cases(),
-                    from_json_error_cases()
-                ]
-            },
-            {
-                "Document to JSON",
-                [
-                    to_json_success_cases()
-                ]
-            }
-        ]
-    }.
-
-from_json_success_cases() ->
-    Cases = [
-        {
-            {[]},
-            #doc{},
-            "Return an empty document for an empty JSON object."
-        },
-        {
-            {[{<<"_id">>, <<"zing!">>}]},
-            #doc{id = <<"zing!">>},
-            "Parses document ids."
-        },
-        {
-            {[{<<"_id">>, <<"_design/foo">>}]},
-            #doc{id = <<"_design/foo">>},
-            "_design/document ids."
-        },
-        {
-            {[{<<"_id">>, <<"_local/bam">>}]},
-            #doc{id = <<"_local/bam">>},
-            "_local/document ids."
-        },
-        {
-            {[{<<"_rev">>, <<"4-230234">>}]},
-            #doc{revs = {4, [<<"230234">>]}},
-            "_rev stored in revs."
-        },
-        {
-            {[{<<"soap">>, 35}]},
-            #doc{body = {[{<<"soap">>, 35}]}},
-            "Non underscore prefixed fields stored in body."
-        },
-        {
-            {[{<<"_attachments">>, {[
-                {<<"my_attachment.fu">>, {[
-                    {<<"stub">>, true},
-                    {<<"content_type">>, <<"application/awesome">>},
-                    {<<"length">>, 45}
-                ]}},
-                {<<"noahs_private_key.gpg">>, {[
-                    {<<"data">>, <<"SSBoYXZlIGEgcGV0IGZpc2gh">>},
-                    {<<"content_type">>, <<"application/pgp-signature">>}
-                ]}}
-            ]}}]},
-            #doc{atts = [
-                #att{
-                    name = <<"my_attachment.fu">>,
-                    data = stub,
-                    type = <<"application/awesome">>,
-                    att_len = 45,
-                    disk_len = 45,
-                    revpos = nil
-                },
-                #att{
-                    name = <<"noahs_private_key.gpg">>,
-                    data = <<"I have a pet fish!">>,
-                    type = <<"application/pgp-signature">>,
-                    att_len = 18,
-                    disk_len = 18,
-                    revpos = 0
-                }
-            ]},
-            "Attachments are parsed correctly."
-        },
-        {
-            {[{<<"_deleted">>, true}]},
-            #doc{deleted = true},
-            "_deleted controls the deleted field."
-        },
-        {
-            {[{<<"_deleted">>, false}]},
-            #doc{},
-            "{\"_deleted\": false} is ok."
-        },
-        {
-            {[
-                 {<<"_revisions">>,
-                  {[{<<"start">>, 4},
-                    {<<"ids">>, [<<"foo1">>, <<"phi3">>, <<"omega">>]}]}},
-                 {<<"_rev">>, <<"6-something">>}
-             ]},
-            #doc{revs = {4, [<<"foo1">>, <<"phi3">>, <<"omega">>]}},
-            "_revisions attribute are preferred to _rev."
-        },
-        {
-            {[{<<"_revs_info">>, dropping}]},
-            #doc{},
-            "Drops _revs_info."
-        },
-        {
-            {[{<<"_local_seq">>, dropping}]},
-            #doc{},
-            "Drops _local_seq."
-        },
-        {
-            {[{<<"_conflicts">>, dropping}]},
-            #doc{},
-            "Drops _conflicts."
-        },
-        {
-            {[{<<"_deleted_conflicts">>, dropping}]},
-            #doc{},
-            "Drops _deleted_conflicts."
-        }
-    ],
-    lists:map(
-        fun({EJson, Expect, Msg}) ->
-            {Msg, ?_assertMatch(Expect, couch_doc:from_json_obj(EJson))}
-        end,
-        Cases).
-
-from_json_error_cases() ->
-    Cases = [
-        {
-            [],
-            {bad_request, "Document must be a JSON object"},
-            "arrays are invalid"
-        },
-        {
-            4,
-            {bad_request, "Document must be a JSON object"},
-            "integers are invalid"
-        },
-        {
-            true,
-            {bad_request, "Document must be a JSON object"},
-            "literals are invalid"
-        },
-        {
-            {[{<<"_id">>, {[{<<"foo">>, 5}]}}]},
-            {bad_request, <<"Document id must be a string">>},
-            "Document id must be a string."
-        },
-        {
-            {[{<<"_id">>, <<"_random">>}]},
-            {bad_request,
-             <<"Only reserved document ids may start with underscore.">>},
-            "Disallow arbitrary underscore prefixed docids."
-        },
-        {
-            {[{<<"_rev">>, 5}]},
-            {bad_request, <<"Invalid rev format">>},
-            "_rev must be a string"
-        },
-        {
-            {[{<<"_rev">>, "foobar"}]},
-            {bad_request, <<"Invalid rev format">>},
-            "_rev must be %d-%s"
-        },
-        {
-            {[{<<"_rev">>, "foo-bar"}]},
-            "Error if _rev's integer expection is broken."
-        },
-        {
-            {[{<<"_revisions">>, {[{<<"start">>, true}]}}]},
-            {doc_validation, "_revisions.start isn't an integer."},
-            "_revisions.start must be an integer."
-        },
-        {
-            {[{<<"_revisions">>, {[{<<"start">>, 0}, {<<"ids">>, 5}]}}]},
-            {doc_validation, "_revisions.ids isn't a array."},
-            "_revions.ids must be a list."
-        },
-        {
-            {[{<<"_revisions">>, {[{<<"start">>, 0}, {<<"ids">>, [5]}]}}]},
-            {doc_validation, "RevId isn't a string"},
-            "Revision ids must be strings."
-        },
-        {
-            {[{<<"_something">>, 5}]},
-            {doc_validation, <<"Bad special document member: _something">>},
-            "Underscore prefix fields are reserved."
-        }
-    ],
-
-    lists:map(fun
-        ({EJson, Expect, Msg}) ->
-            Error = (catch couch_doc:from_json_obj(EJson)),
-            {Msg, ?_assertMatch(Expect, Error)};
-        ({EJson, Msg}) ->
-            try
-                couch_doc:from_json_obj(EJson),
-                {"Conversion failed to raise an exception", ?_assert(false)}
-            catch
-                _:_ -> {Msg, ?_assert(true)}
-            end
-    end, Cases).
-
-to_json_success_cases() ->
-    Cases = [
-        {
-            #doc{},
-            {[{<<"_id">>, <<"">>}]},
-            "Empty docs are {\"_id\": \"\"}"
-        },
-        {
-            #doc{id = <<"foo">>},
-            {[{<<"_id">>, <<"foo">>}]},
-            "_id is added."
-        },
-        {
-            #doc{revs = {5, ["foo"]}},
-            {[{<<"_id">>, <<>>}, {<<"_rev">>, <<"5-foo">>}]},
-            "_rev is added."
-        },
-        {
-            [revs],
-            #doc{revs = {5, [<<"first">>, <<"second">>]}},
-            {[
-                 {<<"_id">>, <<>>},
-                 {<<"_rev">>, <<"5-first">>},
-                 {<<"_revisions">>, {[
-                     {<<"start">>, 5},
-                     {<<"ids">>, [<<"first">>, <<"second">>]}
-                 ]}}
-             ]},
-            "_revisions include with revs option"
-        },
-        {
-            #doc{body = {[{<<"foo">>, <<"bar">>}]}},
-            {[{<<"_id">>, <<>>}, {<<"foo">>, <<"bar">>}]},
-            "Arbitrary fields are added."
-        },
-        {
-            #doc{deleted = true, body = {[{<<"foo">>, <<"bar">>}]}},
-            {[{<<"_id">>, <<>>}, {<<"foo">>, <<"bar">>}, {<<"_deleted">>, true}]},
-            "Deleted docs no longer drop body members."
-        },
-        {
-            #doc{meta = [
-                {revs_info, 4, [{<<"fin">>, deleted}, {<<"zim">>, missing}]}
-            ]},
-            {[
-                 {<<"_id">>, <<>>},
-                 {<<"_revs_info">>, [
-                     {[{<<"rev">>, <<"4-fin">>}, {<<"status">>, <<"deleted">>}]},
-                     {[{<<"rev">>, <<"3-zim">>}, {<<"status">>, <<"missing">>}]}
-                 ]}
-             ]},
-            "_revs_info field is added correctly."
-        },
-        {
-            #doc{meta = [{local_seq, 5}]},
-            {[{<<"_id">>, <<>>}, {<<"_local_seq">>, 5}]},
-            "_local_seq is added as an integer."
-        },
-        {
-            #doc{meta = [{conflicts, [{3, <<"yep">>}, {1, <<"snow">>}]}]},
-            {[
-                {<<"_id">>, <<>>},
-                {<<"_conflicts">>, [<<"3-yep">>, <<"1-snow">>]}
-            ]},
-            "_conflicts is added as an array of strings."
-        },
-        {
-            #doc{meta = [{deleted_conflicts, [{10923, <<"big_cowboy_hat">>}]}]},
-            {[
-                 {<<"_id">>, <<>>},
-                 {<<"_deleted_conflicts">>, [<<"10923-big_cowboy_hat">>]}
-             ]},
-            "_deleted_conflicsts is added as an array of strings."
-        },
-        {
-            #doc{atts = [
-                #att{
-                    name = <<"big.xml">>,
-                    type = <<"xml/sucks">>,
-                    data = fun() -> ok end,
-                    revpos = 1,
-                    att_len = 400,
-                    disk_len = 400
-                },
-                #att{
-                    name = <<"fast.json">>,
-                    type = <<"json/ftw">>,
-                    data = <<"{\"so\": \"there!\"}">>,
-                    revpos = 1,
-                    att_len = 16,
-                    disk_len = 16
-                }
-            ]},
-            {[
-                 {<<"_id">>, <<>>},
-                 {<<"_attachments">>, {[
-                       {<<"big.xml">>, {[
-                           {<<"content_type">>, <<"xml/sucks">>},
-                           {<<"revpos">>, 1},
-                           {<<"length">>, 400},
-                           {<<"stub">>, true}
-                       ]}},
-                       {<<"fast.json">>, {[
-                           {<<"content_type">>, <<"json/ftw">>},
-                           {<<"revpos">>, 1},
-                           {<<"length">>, 16},
-                           {<<"stub">>, true}
-                       ]}}
-                ]}}
-            ]},
-            "Attachments attached as stubs only include a length."
-        },
-        {
-            [attachments],
-            #doc{atts = [
-                #att{
-                    name = <<"stuff.txt">>,
-                    type = <<"text/plain">>,
-                    data = fun() -> <<"diet pepsi">> end,
-                    revpos = 1,
-                    att_len = 10,
-                    disk_len = 10
-                },
-                #att{
-                    name = <<"food.now">>,
-                    type = <<"application/food">>,
-                    revpos = 1,
-                    data = <<"sammich">>
-                }
-            ]},
-            {[
-                {<<"_id">>, <<>>},
-                {<<"_attachments">>, {[
-                   {<<"stuff.txt">>, {[
-                       {<<"content_type">>, <<"text/plain">>},
-                       {<<"revpos">>, 1},
-                       {<<"data">>, <<"ZGlldCBwZXBzaQ==">>}
-                   ]}},
-                   {<<"food.now">>, {[
-                       {<<"content_type">>, <<"application/food">>},
-                       {<<"revpos">>, 1},
-                       {<<"data">>, <<"c2FtbWljaA==">>}
-                   ]}}
-                ]}}
-            ]},
-            "Attachments included inline with attachments option."
-        }
-    ],
-
-    lists:map(fun
-        ({Doc, EJson, Msg}) ->
-            {Msg, ?_assertMatch(EJson, couch_doc:to_json_obj(Doc, []))};
-        ({Options, Doc, EJson, Msg}) ->
-            {Msg, ?_assertMatch(EJson, couch_doc:to_json_obj(Doc, Options))}
-    end, Cases).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_file_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_file_tests.erl b/test/couchdb/couch_file_tests.erl
deleted file mode 100644
index ad13383..0000000
--- a/test/couchdb/couch_file_tests.erl
+++ /dev/null
@@ -1,265 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_file_tests).
-
--include("couch_eunit.hrl").
-
--define(BLOCK_SIZE, 4096).
--define(setup(F), {setup, fun setup/0, fun teardown/1, F}).
--define(foreach(Fs), {foreach, fun setup/0, fun teardown/1, Fs}).
-
-
-setup() ->
-    {ok, Fd} = couch_file:open(?tempfile(), [create, overwrite]),
-    Fd.
-
-teardown(Fd) ->
-    ok = couch_file:close(Fd).
-
-
-open_close_test_() ->
-    {
-        "Test for proper file open and close",
-        [
-            should_return_enoent_if_missed(),
-            should_ignore_invalid_flags_with_open(),
-            ?setup(fun should_return_pid_on_file_open/1),
-            should_close_file_properly(),
-            ?setup(fun should_create_empty_new_files/1)
-        ]
-    }.
-
-should_return_enoent_if_missed() ->
-    ?_assertEqual({error, enoent}, couch_file:open("not a real file")).
-
-should_ignore_invalid_flags_with_open() ->
-    ?_assertMatch({ok, _},
-                  couch_file:open(?tempfile(), [create, invalid_option])).
-
-should_return_pid_on_file_open(Fd) ->
-    ?_assert(is_pid(Fd)).
-
-should_close_file_properly() ->
-    {ok, Fd} = couch_file:open(?tempfile(), [create, overwrite]),
-    ok = couch_file:close(Fd),
-    ?_assert(true).
-
-should_create_empty_new_files(Fd) ->
-    ?_assertMatch({ok, 0}, couch_file:bytes(Fd)).
-
-
-read_write_test_() ->
-    {
-        "Common file read/write tests",
-        ?foreach([
-            fun should_increase_file_size_on_write/1,
-            fun should_return_current_file_size_on_write/1,
-            fun should_write_and_read_term/1,
-            fun should_write_and_read_binary/1,
-            fun should_write_and_read_large_binary/1,
-            fun should_return_term_as_binary_for_reading_binary/1,
-            fun should_read_term_written_as_binary/1,
-            fun should_read_iolist/1,
-            fun should_fsync/1,
-            fun should_not_read_beyond_eof/1,
-            fun should_truncate/1
-        ])
-    }.
-
-
-should_increase_file_size_on_write(Fd) ->
-    {ok, 0, _} = couch_file:append_term(Fd, foo),
-    {ok, Size} = couch_file:bytes(Fd),
-    ?_assert(Size > 0).
-
-should_return_current_file_size_on_write(Fd) ->
-    {ok, 0, _} = couch_file:append_term(Fd, foo),
-    {ok, Size} = couch_file:bytes(Fd),
-    ?_assertMatch({ok, Size, _}, couch_file:append_term(Fd, bar)).
-
-should_write_and_read_term(Fd) ->
-    {ok, Pos, _} = couch_file:append_term(Fd, foo),
-    ?_assertMatch({ok, foo}, couch_file:pread_term(Fd, Pos)).
-
-should_write_and_read_binary(Fd) ->
-    {ok, Pos, _} = couch_file:append_binary(Fd, <<"fancy!">>),
-    ?_assertMatch({ok, <<"fancy!">>}, couch_file:pread_binary(Fd, Pos)).
-
-should_return_term_as_binary_for_reading_binary(Fd) ->
-    {ok, Pos, _} = couch_file:append_term(Fd, foo),
-    Foo = couch_compress:compress(foo, snappy),
-    ?_assertMatch({ok, Foo}, couch_file:pread_binary(Fd, Pos)).
-
-should_read_term_written_as_binary(Fd) ->
-    {ok, Pos, _} = couch_file:append_binary(Fd, <<131,100,0,3,102,111,111>>),
-    ?_assertMatch({ok, foo}, couch_file:pread_term(Fd, Pos)).
-
-should_write_and_read_large_binary(Fd) ->
-    BigBin = list_to_binary(lists:duplicate(100000, 0)),
-    {ok, Pos, _} = couch_file:append_binary(Fd, BigBin),
-    ?_assertMatch({ok, BigBin}, couch_file:pread_binary(Fd, Pos)).
-
-should_read_iolist(Fd) ->
-    %% append_binary == append_iolist?
-    %% Possible bug in pread_iolist or iolist() -> append_binary
-    {ok, Pos, _} = couch_file:append_binary(Fd, ["foo", $m, <<"bam">>]),
-    {ok, IoList} = couch_file:pread_iolist(Fd, Pos),
-    ?_assertMatch(<<"foombam">>, iolist_to_binary(IoList)).
-
-should_fsync(Fd) ->
-    {"How does on test fsync?", ?_assertMatch(ok, couch_file:sync(Fd))}.
-
-should_not_read_beyond_eof(_) ->
-    {"No idea how to test reading beyond EOF", ?_assert(true)}.
-
-should_truncate(Fd) ->
-    {ok, 0, _} = couch_file:append_term(Fd, foo),
-    {ok, Size} = couch_file:bytes(Fd),
-    BigBin = list_to_binary(lists:duplicate(100000, 0)),
-    {ok, _, _} = couch_file:append_binary(Fd, BigBin),
-    ok = couch_file:truncate(Fd, Size),
-    ?_assertMatch({ok, foo}, couch_file:pread_term(Fd, 0)).
-
-
-header_test_() ->
-    {
-        "File header read/write tests",
-        [
-            ?foreach([
-                fun should_write_and_read_atom_header/1,
-                fun should_write_and_read_tuple_header/1,
-                fun should_write_and_read_second_header/1,
-                fun should_truncate_second_header/1,
-                fun should_produce_same_file_size_on_rewrite/1,
-                fun should_save_headers_larger_than_block_size/1
-            ]),
-            should_recover_header_marker_corruption(),
-            should_recover_header_size_corruption(),
-            should_recover_header_md5sig_corruption(),
-            should_recover_header_data_corruption()
-        ]
-    }.
-
-
-should_write_and_read_atom_header(Fd) ->
-    ok = couch_file:write_header(Fd, hello),
-    ?_assertMatch({ok, hello}, couch_file:read_header(Fd)).
-
-should_write_and_read_tuple_header(Fd) ->
-    ok = couch_file:write_header(Fd, {<<"some_data">>, 32}),
-    ?_assertMatch({ok, {<<"some_data">>, 32}}, couch_file:read_header(Fd)).
-
-should_write_and_read_second_header(Fd) ->
-    ok = couch_file:write_header(Fd, {<<"some_data">>, 32}),
-    ok = couch_file:write_header(Fd, [foo, <<"more">>]),
-    ?_assertMatch({ok, [foo, <<"more">>]}, couch_file:read_header(Fd)).
-
-should_truncate_second_header(Fd) ->
-    ok = couch_file:write_header(Fd, {<<"some_data">>, 32}),
-    {ok, Size} = couch_file:bytes(Fd),
-    ok = couch_file:write_header(Fd, [foo, <<"more">>]),
-    ok = couch_file:truncate(Fd, Size),
-    ?_assertMatch({ok, {<<"some_data">>, 32}}, couch_file:read_header(Fd)).
-
-should_produce_same_file_size_on_rewrite(Fd) ->
-    ok = couch_file:write_header(Fd, {<<"some_data">>, 32}),
-    {ok, Size1} = couch_file:bytes(Fd),
-    ok = couch_file:write_header(Fd, [foo, <<"more">>]),
-    {ok, Size2} = couch_file:bytes(Fd),
-    ok = couch_file:truncate(Fd, Size1),
-    ok = couch_file:write_header(Fd, [foo, <<"more">>]),
-    ?_assertMatch({ok, Size2}, couch_file:bytes(Fd)).
-
-should_save_headers_larger_than_block_size(Fd) ->
-    Header = erlang:make_tuple(5000, <<"CouchDB">>),
-    couch_file:write_header(Fd, Header),
-    {"COUCHDB-1319", ?_assertMatch({ok, Header}, couch_file:read_header(Fd))}.
-
-
-should_recover_header_marker_corruption() ->
-    ?_assertMatch(
-        ok,
-        check_header_recovery(
-            fun(CouchFd, RawFd, Expect, HeaderPos) ->
-                ?assertNotMatch(Expect, couch_file:read_header(CouchFd)),
-                file:pwrite(RawFd, HeaderPos, <<0>>),
-                ?assertMatch(Expect, couch_file:read_header(CouchFd))
-            end)
-    ).
-
-should_recover_header_size_corruption() ->
-    ?_assertMatch(
-        ok,
-        check_header_recovery(
-            fun(CouchFd, RawFd, Expect, HeaderPos) ->
-                ?assertNotMatch(Expect, couch_file:read_header(CouchFd)),
-                % +1 for 0x1 byte marker
-                file:pwrite(RawFd, HeaderPos + 1, <<10/integer>>),
-                ?assertMatch(Expect, couch_file:read_header(CouchFd))
-            end)
-    ).
-
-should_recover_header_md5sig_corruption() ->
-    ?_assertMatch(
-        ok,
-        check_header_recovery(
-            fun(CouchFd, RawFd, Expect, HeaderPos) ->
-                ?assertNotMatch(Expect, couch_file:read_header(CouchFd)),
-                % +5 = +1 for 0x1 byte and +4 for term size.
-                file:pwrite(RawFd, HeaderPos + 5, <<"F01034F88D320B22">>),
-                ?assertMatch(Expect, couch_file:read_header(CouchFd))
-            end)
-    ).
-
-should_recover_header_data_corruption() ->
-    ?_assertMatch(
-        ok,
-        check_header_recovery(
-            fun(CouchFd, RawFd, Expect, HeaderPos) ->
-                ?assertNotMatch(Expect, couch_file:read_header(CouchFd)),
-                % +21 = +1 for 0x1 byte, +4 for term size and +16 for MD5 sig
-                file:pwrite(RawFd, HeaderPos + 21, <<"some data goes here!">>),
-                ?assertMatch(Expect, couch_file:read_header(CouchFd))
-            end)
-    ).
-
-
-check_header_recovery(CheckFun) ->
-    Path = ?tempfile(),
-    {ok, Fd} = couch_file:open(Path, [create, overwrite]),
-    {ok, RawFd} = file:open(Path, [read, write, raw, binary]),
-
-    {ok, _} = write_random_data(Fd),
-    ExpectHeader = {some_atom, <<"a binary">>, 756},
-    ok = couch_file:write_header(Fd, ExpectHeader),
-
-    {ok, HeaderPos} = write_random_data(Fd),
-    ok = couch_file:write_header(Fd, {2342, <<"corruption! greed!">>}),
-
-    CheckFun(Fd, RawFd, {ok, ExpectHeader}, HeaderPos),
-
-    ok = file:close(RawFd),
-    ok = couch_file:close(Fd),
-    ok.
-
-write_random_data(Fd) ->
-    write_random_data(Fd, 100 + random:uniform(1000)).
-
-write_random_data(Fd, 0) ->
-    {ok, Bytes} = couch_file:bytes(Fd),
-    {ok, (1 + Bytes div ?BLOCK_SIZE) * ?BLOCK_SIZE};
-write_random_data(Fd, N) ->
-    Choices = [foo, bar, <<"bizzingle">>, "bank", ["rough", stuff]],
-    Term = lists:nth(random:uniform(4) + 1, Choices),
-    {ok, _, _} = couch_file:append_term(Fd, Term),
-    write_random_data(Fd, N - 1).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_key_tree_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_key_tree_tests.erl b/test/couchdb/couch_key_tree_tests.erl
deleted file mode 100644
index 753ecc4..0000000
--- a/test/couchdb/couch_key_tree_tests.erl
+++ /dev/null
@@ -1,380 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_key_tree_tests).
-
--include("couch_eunit.hrl").
-
--define(DEPTH, 10).
-
-
-key_tree_merge_test_()->
-    {
-        "Key tree merge",
-        [
-            should_merge_with_empty_tree(),
-            should_merge_reflexive(),
-            should_merge_prefix_of_a_tree_with_tree(),
-            should_produce_conflict_on_merge_with_unrelated_branch(),
-            should_merge_reflexive_for_child_nodes(),
-            should_merge_tree_to_itself(),
-            should_merge_tree_of_odd_length(),
-            should_merge_tree_with_stem(),
-            should_merge_with_stem_at_deeper_level(),
-            should_merge_with_stem_at_deeper_level_with_deeper_paths(),
-            should_merge_single_tree_with_deeper_stem(),
-            should_merge_tree_with_large_stem(),
-            should_merge_stems(),
-            should_create_conflicts_on_merge(),
-            should_create_no_conflicts_on_merge(),
-            should_ignore_conflicting_branch()
-        ]
-    }.
-
-key_tree_missing_leaves_test_()->
-    {
-        "Missing tree leaves",
-        [
-            should_not_find_missing_leaves(),
-            should_find_missing_leaves()
-        ]
-    }.
-
-key_tree_remove_leaves_test_()->
-    {
-        "Remove tree leaves",
-        [
-            should_have_no_effect_on_removing_no_leaves(),
-            should_have_no_effect_on_removing_non_existant_branch(),
-            should_remove_leaf(),
-            should_produce_empty_tree_on_removing_all_leaves(),
-            should_have_no_effect_on_removing_non_existant_node(),
-            should_produce_empty_tree_on_removing_last_leaf()
-        ]
-    }.
-
-key_tree_get_leaves_test_()->
-    {
-        "Leaves retrieving",
-        [
-            should_extract_subtree(),
-            should_extract_subsubtree(),
-            should_gather_non_existant_leaf(),
-            should_gather_leaf(),
-            shoul_gather_multiple_leaves(),
-            should_retrieve_full_key_path(),
-            should_retrieve_full_key_path_for_node(),
-            should_retrieve_leaves_with_parent_node(),
-            should_retrieve_all_leaves()
-        ]
-    }.
-
-key_tree_leaf_counting_test_()->
-    {
-        "Leaf counting",
-        [
-            should_have_no_leaves_for_empty_tree(),
-            should_have_single_leaf_for_tree_with_single_node(),
-            should_have_two_leaves_for_tree_with_chindler_siblings(),
-            should_not_affect_on_leaf_counting_for_stemmed_tree()
-        ]
-    }.
-
-key_tree_stemming_test_()->
-    {
-        "Stemming",
-        [
-            should_have_no_effect_for_stemming_more_levels_than_exists(),
-            should_return_one_deepest_node(),
-            should_return_two_deepest_nodes()
-        ]
-    }.
-
-
-should_merge_with_empty_tree()->
-    One = {1, {"1","foo",[]}},
-    ?_assertEqual({[One], no_conflicts},
-                  couch_key_tree:merge([], One, ?DEPTH)).
-
-should_merge_reflexive()->
-    One = {1, {"1","foo",[]}},
-    ?_assertEqual({[One], no_conflicts},
-                  couch_key_tree:merge([One], One, ?DEPTH)).
-
-should_merge_prefix_of_a_tree_with_tree()->
-    One = {1, {"1","foo",[]}},
-    TwoSibs = [{1, {"1","foo",[]}},
-               {1, {"2","foo",[]}}],
-    ?_assertEqual({TwoSibs, no_conflicts},
-                  couch_key_tree:merge(TwoSibs, One, ?DEPTH)).
-
-should_produce_conflict_on_merge_with_unrelated_branch()->
-    TwoSibs = [{1, {"1","foo",[]}},
-               {1, {"2","foo",[]}}],
-    Three = {1, {"3","foo",[]}},
-    ThreeSibs = [{1, {"1","foo",[]}},
-                 {1, {"2","foo",[]}},
-                 {1, {"3","foo",[]}}],
-    ?_assertEqual({ThreeSibs, conflicts},
-                  couch_key_tree:merge(TwoSibs, Three, ?DEPTH)).
-
-should_merge_reflexive_for_child_nodes()->
-    TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}},
-    ?_assertEqual({[TwoChild], no_conflicts},
-                  couch_key_tree:merge([TwoChild], TwoChild, ?DEPTH)).
-
-should_merge_tree_to_itself()->
-    TwoChildSibs = {1, {"1","foo", [{"1a", "bar", []},
-                                    {"1b", "bar", []}]}},
-    ?_assertEqual({[TwoChildSibs], no_conflicts},
-                  couch_key_tree:merge([TwoChildSibs], TwoChildSibs, ?DEPTH)).
-
-should_merge_tree_of_odd_length()->
-    TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}},
-    TwoChildSibs = {1, {"1","foo", [{"1a", "bar", []},
-                                    {"1b", "bar", []}]}},
-    TwoChildPlusSibs = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]},
-                                        {"1b", "bar", []}]}},
-
-    ?_assertEqual({[TwoChildPlusSibs], no_conflicts},
-                  couch_key_tree:merge([TwoChild], TwoChildSibs, ?DEPTH)).
-
-should_merge_tree_with_stem()->
-    Stemmed = {2, {"1a", "bar", []}},
-    TwoChildSibs = {1, {"1","foo", [{"1a", "bar", []},
-                                    {"1b", "bar", []}]}},
-
-    ?_assertEqual({[TwoChildSibs], no_conflicts},
-                  couch_key_tree:merge([TwoChildSibs], Stemmed, ?DEPTH)).
-
-should_merge_with_stem_at_deeper_level()->
-    Stemmed = {3, {"1bb", "boo", []}},
-    TwoChildSibs = {1, {"1","foo", [{"1a", "bar", []},
-                                    {"1b", "bar", [{"1bb", "boo", []}]}]}},
-    ?_assertEqual({[TwoChildSibs], no_conflicts},
-                  couch_key_tree:merge([TwoChildSibs], Stemmed, ?DEPTH)).
-
-should_merge_with_stem_at_deeper_level_with_deeper_paths()->
-    Stemmed = {3, {"1bb", "boo", []}},
-    StemmedTwoChildSibs = [{2,{"1a", "bar", []}},
-                           {2,{"1b", "bar", [{"1bb", "boo", []}]}}],
-    ?_assertEqual({StemmedTwoChildSibs, no_conflicts},
-                  couch_key_tree:merge(StemmedTwoChildSibs, Stemmed, ?DEPTH)).
-
-should_merge_single_tree_with_deeper_stem()->
-    Stemmed = {3, {"1aa", "bar", []}},
-    TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}},
-    ?_assertEqual({[TwoChild], no_conflicts},
-                  couch_key_tree:merge([TwoChild], Stemmed, ?DEPTH)).
-
-should_merge_tree_with_large_stem()->
-    Stemmed = {2, {"1a", "bar", [{"1aa", "bar", []}]}},
-    TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}},
-    ?_assertEqual({[TwoChild], no_conflicts},
-                  couch_key_tree:merge([TwoChild], Stemmed, ?DEPTH)).
-
-should_merge_stems()->
-    StemmedA = {2, {"1a", "bar", [{"1aa", "bar", []}]}},
-    StemmedB = {3, {"1aa", "bar", []}},
-    ?_assertEqual({[StemmedA], no_conflicts},
-                  couch_key_tree:merge([StemmedA], StemmedB, ?DEPTH)).
-
-should_create_conflicts_on_merge()->
-    OneChild = {1, {"1","foo",[{"1a", "bar", []}]}},
-    Stemmed = {3, {"1aa", "bar", []}},
-    ?_assertEqual({[OneChild, Stemmed], conflicts},
-                  couch_key_tree:merge([OneChild], Stemmed, ?DEPTH)).
-
-should_create_no_conflicts_on_merge()->
-    OneChild = {1, {"1","foo",[{"1a", "bar", []}]}},
-    Stemmed = {3, {"1aa", "bar", []}},
-    TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}},
-    ?_assertEqual({[TwoChild], no_conflicts},
-                  couch_key_tree:merge([OneChild, Stemmed], TwoChild, ?DEPTH)).
-
-should_ignore_conflicting_branch()->
-    %% this test is based on couch-902-test-case2.py
-    %% foo has conflicts from replication at depth two
-    %% foo3 is the current value
-    Foo = {1, {"foo",
-               "val1",
-               [{"foo2","val2",[]},
-                {"foo3", "val3", []}
-               ]}},
-    %% foo now has an attachment added, which leads to foo4 and val4
-    %% off foo3
-    Bar = {1, {"foo",
-               [],
-               [{"foo3",
-                 [],
-                 [{"foo4","val4",[]}
-                  ]}]}},
-    %% this is what the merge returns
-    %% note that it ignore the conflicting branch as there's no match
-    FooBar = {1, {"foo",
-               "val1",
-               [{"foo2","val2",[]},
-                {"foo3", "val3", [{"foo4","val4",[]}]}
-               ]}},
-    {
-        "COUCHDB-902",
-        ?_assertEqual({[FooBar], no_conflicts},
-                      couch_key_tree:merge([Foo], Bar, ?DEPTH))
-    }.
-
-should_not_find_missing_leaves()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual([],
-                  couch_key_tree:find_missing(TwoChildSibs,
-                                              [{0,"1"}, {1,"1a"}])).
-
-should_find_missing_leaves()->
-    Stemmed1 = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
-    Stemmed2 = [{2, {"1aa", "bar", []}}],
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    [
-        ?_assertEqual(
-            [{0, "10"}, {100, "x"}],
-            couch_key_tree:find_missing(
-                TwoChildSibs,
-                [{0,"1"}, {0, "10"}, {1,"1a"}, {100, "x"}])),
-        ?_assertEqual(
-            [{0, "1"}, {100, "x"}],
-            couch_key_tree:find_missing(
-                Stemmed1,
-                [{0,"1"}, {1,"1a"}, {100, "x"}])),
-        ?_assertEqual(
-            [{0, "1"}, {1,"1a"}, {100, "x"}],
-            couch_key_tree:find_missing(
-                Stemmed2,
-                [{0,"1"}, {1,"1a"}, {100, "x"}]))
-    ].
-
-should_have_no_effect_on_removing_no_leaves()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({TwoChildSibs, []},
-                  couch_key_tree:remove_leafs(TwoChildSibs,
-                                              [])).
-
-should_have_no_effect_on_removing_non_existant_branch()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({TwoChildSibs, []},
-                  couch_key_tree:remove_leafs(TwoChildSibs,
-                                              [{0, "1"}])).
-
-should_remove_leaf()->
-    OneChild = [{0, {"1","foo",[{"1a", "bar", []}]}}],
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({OneChild, [{1, "1b"}]},
-                  couch_key_tree:remove_leafs(TwoChildSibs,
-                                              [{1, "1b"}])).
-
-should_produce_empty_tree_on_removing_all_leaves()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({[], [{1, "1b"}, {1, "1a"}]},
-                  couch_key_tree:remove_leafs(TwoChildSibs,
-                                              [{1, "1b"}, {1, "1a"}])).
-
-should_have_no_effect_on_removing_non_existant_node()->
-    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
-    ?_assertEqual({Stemmed, []},
-                  couch_key_tree:remove_leafs(Stemmed,
-                                              [{1, "1a"}])).
-
-should_produce_empty_tree_on_removing_last_leaf()->
-    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
-    ?_assertEqual({[], [{2, "1aa"}]},
-                  couch_key_tree:remove_leafs(Stemmed,
-                                              [{2, "1aa"}])).
-
-should_extract_subtree()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({[{"foo", {0, ["1"]}}],[]},
-                  couch_key_tree:get(TwoChildSibs, [{0, "1"}])).
-
-should_extract_subsubtree()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({[{"bar", {1, ["1a", "1"]}}],[]},
-                  couch_key_tree:get(TwoChildSibs, [{1, "1a"}])).
-
-should_gather_non_existant_leaf()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({[],[{0, "x"}]},
-                  couch_key_tree:get_key_leafs(TwoChildSibs, [{0, "x"}])).
-
-should_gather_leaf()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({[{"bar", {1, ["1a","1"]}}],[]},
-                  couch_key_tree:get_key_leafs(TwoChildSibs, [{1, "1a"}])).
-
-shoul_gather_multiple_leaves()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({[{"bar", {1, ["1a","1"]}},{"bar",{1, ["1b","1"]}}],[]},
-                  couch_key_tree:get_key_leafs(TwoChildSibs, [{0, "1"}])).
-
-should_retrieve_full_key_path()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({[{0,[{"1", "foo"}]}],[]},
-                  couch_key_tree:get_full_key_paths(TwoChildSibs, [{0, "1"}])).
-
-should_retrieve_full_key_path_for_node()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual({[{1,[{"1a", "bar"},{"1", "foo"}]}],[]},
-                  couch_key_tree:get_full_key_paths(TwoChildSibs, [{1, "1a"}])).
-
-should_retrieve_leaves_with_parent_node()->
-    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    [
-        ?_assertEqual([{2, [{"1aa", "bar"},{"1a", "bar"}]}],
-                      couch_key_tree:get_all_leafs_full(Stemmed)),
-        ?_assertEqual([{1, [{"1a", "bar"},{"1", "foo"}]},
-                       {1, [{"1b", "bar"},{"1", "foo"}]}],
-                      couch_key_tree:get_all_leafs_full(TwoChildSibs))
-    ].
-
-should_retrieve_all_leaves()->
-    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    [
-        ?_assertEqual([{"bar", {2, ["1aa","1a"]}}],
-                      couch_key_tree:get_all_leafs(Stemmed)),
-        ?_assertEqual([{"bar", {1, ["1a", "1"]}}, {"bar", {1, ["1b","1"]}}],
-                      couch_key_tree:get_all_leafs(TwoChildSibs))
-    ].
-
-should_have_no_leaves_for_empty_tree()->
-    ?_assertEqual(0, couch_key_tree:count_leafs([])).
-
-should_have_single_leaf_for_tree_with_single_node()->
-    ?_assertEqual(1, couch_key_tree:count_leafs([{0, {"1","foo",[]}}])).
-
-should_have_two_leaves_for_tree_with_chindler_siblings()->
-    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
-    ?_assertEqual(2, couch_key_tree:count_leafs(TwoChildSibs)).
-
-should_not_affect_on_leaf_counting_for_stemmed_tree()->
-    ?_assertEqual(1, couch_key_tree:count_leafs([{2, {"1bb", "boo", []}}])).
-
-should_have_no_effect_for_stemming_more_levels_than_exists()->
-    TwoChild = [{0, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}],
-    ?_assertEqual(TwoChild, couch_key_tree:stem(TwoChild, 3)).
-
-should_return_one_deepest_node()->
-    TwoChild = [{0, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}],
-    Stemmed = [{2, {"1aa", "bar", []}}],
-    ?_assertEqual(Stemmed, couch_key_tree:stem(TwoChild, 1)).
-
-should_return_two_deepest_nodes()->
-    TwoChild = [{0, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}],
-    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
-    ?_assertEqual(Stemmed, couch_key_tree:stem(TwoChild, 2)).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_passwords_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_passwords_tests.erl b/test/couchdb/couch_passwords_tests.erl
deleted file mode 100644
index 116265c..0000000
--- a/test/couchdb/couch_passwords_tests.erl
+++ /dev/null
@@ -1,54 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_passwords_tests).
-
--include("couch_eunit.hrl").
-
-
-pbkdf2_test_()->
-    {"PBKDF2",
-     [
-         {"Iterations: 1, length: 20",
-          ?_assertEqual(
-              {ok, <<"0c60c80f961f0e71f3a9b524af6012062fe037a6">>},
-              couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 1, 20))},
-
-         {"Iterations: 2, length: 20",
-          ?_assertEqual(
-              {ok, <<"ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957">>},
-              couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 2, 20))},
-
-         {"Iterations: 4096, length: 20",
-          ?_assertEqual(
-              {ok, <<"4b007901b765489abead49d926f721d065a429c1">>},
-              couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 4096, 20))},
-
-         {"Iterations: 4096, length: 25",
-          ?_assertEqual(
-              {ok, <<"3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038">>},
-              couch_passwords:pbkdf2(<<"passwordPASSWORDpassword">>,
-                                     <<"saltSALTsaltSALTsaltSALTsaltSALTsalt">>,
-                                     4096, 25))},
-         {"Null byte",
-          ?_assertEqual(
-              {ok, <<"56fa6aa75548099dcc37d7f03425e0c3">>},
-              couch_passwords:pbkdf2(<<"pass\0word">>,
-                                     <<"sa\0lt">>,
-                                     4096, 16))},
-
-         {timeout, 180,  %% this may runs too long on slow hosts
-          {"Iterations: 16777216 - this may take some time",
-           ?_assertEqual(
-               {ok, <<"eefe3d61cd4da4e4e9945b3d6ba2158c2634e984">>},
-               couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 16777216, 20)
-           )}}]}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_ref_counter_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_ref_counter_tests.erl b/test/couchdb/couch_ref_counter_tests.erl
deleted file mode 100644
index b7e97b4..0000000
--- a/test/couchdb/couch_ref_counter_tests.erl
+++ /dev/null
@@ -1,107 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_ref_counter_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(TIMEOUT, 1000).
-
-
-setup() ->
-    {ok, RefCtr} = couch_ref_counter:start([]),
-    ChildPid = spawn(fun() -> loop() end),
-    {RefCtr, ChildPid}.
-
-teardown({_, ChildPid}) ->
-    erlang:monitor(process, ChildPid),
-    ChildPid ! close,
-    wait().
-
-
-couch_ref_counter_test_() ->
-    {
-        "CouchDB reference counter tests",
-        {
-            foreach,
-            fun setup/0, fun teardown/1,
-            [
-                fun should_initialize_with_calling_process_as_referrer/1,
-                fun should_ignore_unknown_pid/1,
-                fun should_increment_counter_on_pid_add/1,
-                fun should_not_increase_counter_on_readding_same_pid/1,
-                fun should_drop_ref_for_double_added_pid/1,
-                fun should_decrement_counter_on_pid_drop/1,
-                fun should_add_after_drop/1,
-                fun should_decrement_counter_on_process_exit/1
-
-            ]
-        }
-    }.
-
-
-should_initialize_with_calling_process_as_referrer({RefCtr, _}) ->
-    ?_assertEqual(1, couch_ref_counter:count(RefCtr)).
-
-should_ignore_unknown_pid({RefCtr, ChildPid}) ->
-    ?_assertEqual(ok, couch_ref_counter:drop(RefCtr, ChildPid)).
-
-should_increment_counter_on_pid_add({RefCtr, ChildPid}) ->
-    couch_ref_counter:add(RefCtr, ChildPid),
-    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
-
-should_not_increase_counter_on_readding_same_pid({RefCtr, ChildPid}) ->
-    couch_ref_counter:add(RefCtr, ChildPid),
-    couch_ref_counter:add(RefCtr, ChildPid),
-    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
-
-should_drop_ref_for_double_added_pid({RefCtr, ChildPid}) ->
-    couch_ref_counter:add(RefCtr, ChildPid),
-    couch_ref_counter:add(RefCtr, ChildPid),
-    couch_ref_counter:drop(RefCtr, ChildPid),
-    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
-
-should_decrement_counter_on_pid_drop({RefCtr, ChildPid}) ->
-    couch_ref_counter:add(RefCtr, ChildPid),
-    couch_ref_counter:drop(RefCtr, ChildPid),
-    ?_assertEqual(1, couch_ref_counter:count(RefCtr)).
-
-should_add_after_drop({RefCtr, ChildPid}) ->
-    couch_ref_counter:add(RefCtr, ChildPid),
-    couch_ref_counter:drop(RefCtr, ChildPid),
-    couch_ref_counter:add(RefCtr, ChildPid),
-    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
-
-should_decrement_counter_on_process_exit({RefCtr, ChildPid}) ->
-    ?_assertEqual(1,
-        begin
-            couch_ref_counter:add(RefCtr, ChildPid),
-            erlang:monitor(process, ChildPid),
-            ChildPid ! close,
-            wait(),
-            couch_ref_counter:count(RefCtr)
-        end).
-
-
-loop() ->
-    receive
-        close -> ok
-    end.
-
-wait() ->
-    receive
-        {'DOWN', _, _, _, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw(timeout_error)
-    end.


[05/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 130-attachments-md5.t etap test suite to eunit

Add random document id generator macros.
Have to use handmade http client instead of ibrowse since it makes too
complicated sending chunked requests.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/5812bf87
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/5812bf87
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/5812bf87

Branch: refs/heads/1963-eunit-bigcouch
Commit: 5812bf8745724354bc53da148c1d619d2400645d
Parents: 069dfb7
Author: Alexander Shorin <kx...@apache.org>
Authored: Wed May 28 05:32:11 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_attachments_tests.erl | 264 ++++++++++++++++++++++++
 1 file changed, 264 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/5812bf87/test/couchdb/couchdb_attachments_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_attachments_tests.erl b/test/couchdb/couchdb_attachments_tests.erl
new file mode 100644
index 0000000..aef1873
--- /dev/null
+++ b/test/couchdb/couchdb_attachments_tests.erl
@@ -0,0 +1,264 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_attachments_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(TIMEOUT, 1000).
+-define(TIMEWAIT, 100).
+-define(i2l(I), integer_to_list(I)).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, []),
+    ok = couch_db:close(Db),
+    Addr = couch_config:get("httpd", "bind_address", any),
+    Port = mochiweb_socket_server:get(couch_httpd, port),
+    Host = Addr ++ ":" ++ ?i2l(Port),
+    {Host, ?b2l(DbName)}.
+
+teardown({_, DbName}) ->
+    ok = couch_server:delete(?l2b(DbName), []),
+    ok.
+
+
+attachments_test_() ->
+    {
+        "Attachments tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [
+                attachments_md5_tests()
+            ]
+        }
+    }.
+
+attachments_md5_tests() ->
+    {
+        "Attachments MD5 tests",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_upload_attachment_without_md5/1,
+                fun should_upload_attachment_by_chunks_without_md5/1,
+                fun should_upload_attachment_with_valid_md5_header/1,
+                fun should_upload_attachment_by_chunks_with_valid_md5_header/1,
+                fun should_upload_attachment_by_chunks_with_valid_md5_trailer/1,
+                fun should_reject_attachment_with_invalid_md5/1,
+                fun should_reject_chunked_attachment_with_invalid_md5/1,
+                fun should_reject_chunked_attachment_with_invalid_md5_trailer/1
+            ]
+        }
+    }.
+
+
+should_upload_attachment_without_md5({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        Body = "We all live in a yellow submarine!",
+        Headers = [
+            {"Content-Length", "34"},
+            {"Content-Type", "text/plain"},
+            {"Host", Host}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(201, Code),
+        ?assertEqual(true, get_json(Json, [<<"ok">>]))
+    end).
+
+should_upload_attachment_by_chunks_without_md5({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        AttData = <<"We all live in a yellow submarine!">>,
+        <<Part1:21/binary, Part2:13/binary>> = AttData,
+        Body = chunked_body([Part1, Part2]),
+        Headers = [
+            {"Content-Type", "text/plain"},
+            {"Transfer-Encoding", "chunked"},
+            {"Host", Host}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(201, Code),
+        ?assertEqual(true, get_json(Json, [<<"ok">>]))
+    end).
+
+should_upload_attachment_with_valid_md5_header({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        Body = "We all live in a yellow submarine!",
+        Headers = [
+            {"Content-Length", "34"},
+            {"Content-Type", "text/plain"},
+            {"Content-MD5", ?b2l(base64:encode(couch_util:md5(Body)))},
+            {"Host", Host}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(201, Code),
+        ?assertEqual(true, get_json(Json, [<<"ok">>]))
+    end).
+
+should_upload_attachment_by_chunks_with_valid_md5_header({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        AttData = <<"We all live in a yellow submarine!">>,
+        <<Part1:21/binary, Part2:13/binary>> = AttData,
+        Body = chunked_body([Part1, Part2]),
+        Headers = [
+            {"Content-Type", "text/plain"},
+            {"Content-MD5", ?b2l(base64:encode(couch_util:md5(AttData)))},
+            {"Host", Host},
+            {"Transfer-Encoding", "chunked"}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(201, Code),
+        ?assertEqual(true, get_json(Json, [<<"ok">>]))
+    end).
+
+should_upload_attachment_by_chunks_with_valid_md5_trailer({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        AttData = <<"We all live in a yellow submarine!">>,
+        <<Part1:21/binary, Part2:13/binary>> = AttData,
+        Body = [chunked_body([Part1, Part2]),
+                "Content-MD5: ", base64:encode(couch_util:md5(AttData)),
+                "\r\n"],
+        Headers = [
+            {"Content-Type", "text/plain"},
+            {"Host", Host},
+            {"Trailer", "Content-MD5"},
+            {"Transfer-Encoding", "chunked"}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(201, Code),
+        ?assertEqual(true, get_json(Json, [<<"ok">>]))
+    end).
+
+should_reject_attachment_with_invalid_md5({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        Body = "We all live in a yellow submarine!",
+        Headers = [
+            {"Content-Length", "34"},
+            {"Content-Type", "text/plain"},
+            {"Content-MD5", ?b2l(base64:encode(<<"foobar!">>))},
+            {"Host", Host}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(400, Code),
+        ?assertEqual(<<"content_md5_mismatch">>,
+                     get_json(Json, [<<"error">>]))
+    end).
+
+
+should_reject_chunked_attachment_with_invalid_md5({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        AttData = <<"We all live in a yellow submarine!">>,
+        <<Part1:21/binary, Part2:13/binary>> = AttData,
+        Body = chunked_body([Part1, Part2]),
+        Headers = [
+            {"Content-Type", "text/plain"},
+            {"Content-MD5", ?b2l(base64:encode(<<"foobar!">>))},
+            {"Host", Host},
+            {"Transfer-Encoding", "chunked"}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(400, Code),
+        ?assertEqual(<<"content_md5_mismatch">>,
+                     get_json(Json, [<<"error">>]))
+    end).
+
+should_reject_chunked_attachment_with_invalid_md5_trailer({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        AttData = <<"We all live in a yellow submarine!">>,
+        <<Part1:21/binary, Part2:13/binary>> = AttData,
+        Body = [chunked_body([Part1, Part2]),
+                "Content-MD5: ", base64:encode(<<"foobar!">>),
+                "\r\n"],
+        Headers = [
+            {"Content-Type", "text/plain"},
+            {"Host", Host},
+            {"Trailer", "Content-MD5"},
+            {"Transfer-Encoding", "chunked"}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(400, Code),
+        ?assertEqual(<<"content_md5_mismatch">>,
+                     get_json(Json, [<<"error">>]))
+    end).
+
+
+get_json(Json, Path) ->
+    couch_util:get_nested_json_value(Json, Path).
+
+to_hex(Val) ->
+    to_hex(Val, []).
+
+to_hex(0, Acc) ->
+    Acc;
+to_hex(Val, Acc) ->
+    to_hex(Val div 16, [hex_char(Val rem 16) | Acc]).
+
+hex_char(V) when V < 10 -> $0 + V;
+hex_char(V) -> $A + V - 10.
+
+chunked_body(Chunks) ->
+    chunked_body(Chunks, []).
+
+chunked_body([], Acc) ->
+    iolist_to_binary(lists:reverse(Acc, "0\r\n"));
+chunked_body([Chunk | Rest], Acc) ->
+    Size = to_hex(size(Chunk)),
+    chunked_body(Rest, ["\r\n", Chunk, "\r\n", Size | Acc]).
+
+get_socket() ->
+    Options = [binary, {packet, 0}, {active, false}],
+    Addr = couch_config:get("httpd", "bind_address", any),
+    Port = mochiweb_socket_server:get(couch_httpd, port),
+    {ok, Sock} = gen_tcp:connect(Addr, Port, Options),
+    Sock.
+
+request(Method, Url, Headers, Body) ->
+    RequestHead = [Method, " ", Url, " HTTP/1.1"],
+    RequestHeaders = [[string:join([Key, Value], ": "), "\r\n"]
+                      || {Key, Value} <- Headers],
+    Request = [RequestHead, "\r\n", RequestHeaders, "\r\n", Body, "\r\n"],
+    Sock = get_socket(),
+    gen_tcp:send(Sock, list_to_binary(lists:flatten(Request))),
+    timer:sleep(?TIMEWAIT),  % must wait to receive complete response
+    {ok, R} = gen_tcp:recv(Sock, 0),
+    gen_tcp:close(Sock),
+    [Header, Body1] = re:split(R, "\r\n\r\n", [{return, binary}]),
+    {ok, {http_response, _, Code, _}, _} =
+        erlang:decode_packet(http, Header, []),
+    Json = ejson:decode(Body1),
+    {ok, Code, Json}.


[12/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 190-json-stream-parse.t etap test suite to eunit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/97aa93ad
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/97aa93ad
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/97aa93ad

Branch: refs/heads/1963-eunit-bigcouch
Commit: 97aa93adbfcbad0ca8b68c1db762b80cec14bd4a
Parents: f8dd4b2
Author: Alexander Shorin <kx...@apache.org>
Authored: Wed Jun 4 13:22:13 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/json_stream_parse_tests.erl | 151 ++++++++++++++++++++++++++
 1 file changed, 151 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/97aa93ad/test/couchdb/json_stream_parse_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/json_stream_parse_tests.erl b/test/couchdb/json_stream_parse_tests.erl
new file mode 100644
index 0000000..92303b6
--- /dev/null
+++ b/test/couchdb/json_stream_parse_tests.erl
@@ -0,0 +1,151 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(json_stream_parse_tests).
+
+-include("couch_eunit.hrl").
+
+-define(CASES,
+    [
+        {1, "1", "integer numeric literial"},
+        {3.1416, "3.14160", "float numeric literal"},  % text representation may truncate, trail zeroes
+        {-1, "-1", "negative integer numeric literal"},
+        {-3.1416, "-3.14160", "negative float numeric literal"},
+        {12.0e10, "1.20000e+11", "float literal in scientific notation"},
+        {1.234E+10, "1.23400e+10", "another float literal in scientific notation"},
+        {-1.234E-10, "-1.23400e-10", "negative float literal in scientific notation"},
+        {10.0, "1.0e+01", "yet another float literal in scientific notation"},
+        {123.456, "1.23456E+2", "yet another float literal in scientific notation"},
+        {10.0, "1e1", "yet another float literal in scientific notation"},
+        {<<"foo">>, "\"foo\"", "string literal"},
+        {<<"foo", 5, "bar">>, "\"foo\\u0005bar\"", "string literal with \\u0005"},
+        {<<"">>, "\"\"", "empty string literal"},
+        {<<"\n\n\n">>, "\"\\n\\n\\n\"", "only new lines literal"},
+        {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\"",
+            "only white spaces string literal"},
+        {null, "null", "null literal"},
+        {true, "true", "true literal"},
+        {false, "false", "false literal"},
+        {<<"null">>, "\"null\"", "null string literal"},
+        {<<"true">>, "\"true\"", "true string literal"},
+        {<<"false">>, "\"false\"", "false string literal"},
+        {{[]}, "{}", "empty object literal"},
+        {{[{<<"foo">>, <<"bar">>}]}, "{\"foo\":\"bar\"}",
+            "simple object literal"},
+        {{[{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]},
+            "{\"foo\":\"bar\",\"baz\":123}", "another simple object literal"},
+        {[], "[]", "empty array literal"},
+        {[[]], "[[]]", "empty array literal inside a single element array literal"},
+        {[1, <<"foo">>], "[1,\"foo\"]", "simple non-empty array literal"},
+        {[1199344435545.0, 1], "[1199344435545.0,1]",
+             "another simple non-empty array literal"},
+        {[false, true, 321, null], "[false, true, 321, null]", "array of literals"},
+        {{[{<<"foo">>, [123]}]}, "{\"foo\":[123]}",
+             "object literal with an array valued property"},
+        {{[{<<"foo">>, {[{<<"bar">>, true}]}}]},
+            "{\"foo\":{\"bar\":true}}", "nested object literal"},
+        {{[{<<"foo">>, []}, {<<"bar">>, {[{<<"baz">>, true}]}},
+                {<<"alice">>, <<"bob">>}]},
+            "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}",
+            "complex object literal"},
+        {[-123, <<"foo">>, {[{<<"bar">>, []}]}, null],
+            "[-123,\"foo\",{\"bar\":[]},null]",
+            "complex array literal"}
+    ]
+).
+
+
+raw_json_input_test_() ->
+    Tests = lists:map(
+        fun({EJson, JsonString, Desc}) ->
+            {Desc,
+             ?_assert(equiv(EJson, json_stream_parse:to_ejson(JsonString)))}
+        end, ?CASES),
+    {"Tests with raw JSON string as the input", Tests}.
+
+one_byte_data_fun_test_() ->
+    Tests = lists:map(
+        fun({EJson, JsonString, Desc}) ->
+            DataFun = fun() -> single_byte_data_fun(JsonString) end,
+            {Desc,
+             ?_assert(equiv(EJson, json_stream_parse:to_ejson(DataFun)))}
+        end, ?CASES),
+    {"Tests with a 1 byte output data function as the input", Tests}.
+
+test_multiple_bytes_data_fun_test_() ->
+    Tests = lists:map(
+        fun({EJson, JsonString, Desc}) ->
+            DataFun = fun() -> multiple_bytes_data_fun(JsonString) end,
+            {Desc,
+             ?_assert(equiv(EJson, json_stream_parse:to_ejson(DataFun)))}
+        end, ?CASES),
+    {"Tests with a multiple bytes output data function as the input", Tests}.
+
+
+%% Test for equivalence of Erlang terms.
+%% Due to arbitrary order of construction, equivalent objects might
+%% compare unequal as erlang terms, so we need to carefully recurse
+%% through aggregates (tuples and objects).
+equiv({Props1}, {Props2}) ->
+    equiv_object(Props1, Props2);
+equiv(L1, L2) when is_list(L1), is_list(L2) ->
+    equiv_list(L1, L2);
+equiv(N1, N2) when is_number(N1), is_number(N2) ->
+    N1 == N2;
+equiv(B1, B2) when is_binary(B1), is_binary(B2) ->
+    B1 == B2;
+equiv(true, true) ->
+    true;
+equiv(false, false) ->
+    true;
+equiv(null, null) ->
+    true.
+
+%% Object representation and traversal order is unknown.
+%% Use the sledgehammer and sort property lists.
+equiv_object(Props1, Props2) ->
+    L1 = lists:keysort(1, Props1),
+    L2 = lists:keysort(1, Props2),
+    Pairs = lists:zip(L1, L2),
+    true = lists:all(
+        fun({{K1, V1}, {K2, V2}}) ->
+            equiv(K1, K2) andalso equiv(V1, V2)
+        end,
+        Pairs).
+
+%% Recursively compare tuple elements for equivalence.
+equiv_list([], []) ->
+    true;
+equiv_list([V1 | L1], [V2 | L2]) ->
+    equiv(V1, V2) andalso equiv_list(L1, L2).
+
+single_byte_data_fun([]) ->
+    done;
+single_byte_data_fun([H | T]) ->
+    {<<H>>, fun() -> single_byte_data_fun(T) end}.
+
+multiple_bytes_data_fun([]) ->
+    done;
+multiple_bytes_data_fun(L) ->
+    N = crypto:rand_uniform(0, 7),
+    {Part, Rest} = split(L, N),
+    {list_to_binary(Part), fun() -> multiple_bytes_data_fun(Rest) end}.
+
+split(L, N) when length(L) =< N ->
+    {L, []};
+split(L, N) ->
+    take(N, L, []).
+
+take(0, L, Acc) ->
+    {lists:reverse(Acc), L};
+take(N, [H|L], Acc) ->
+    take(N - 1, L, [H | Acc]).


[19/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 230-pbkfd2.t etap test suite to eunit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/3d300155
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/3d300155
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/3d300155

Branch: refs/heads/1963-eunit-bigcouch
Commit: 3d3001558a330eeccdfb81f593319280ae9a433e
Parents: a701f76
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Jun 10 00:19:52 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:40 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couch_passwords_tests.erl | 54 +++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/3d300155/test/couchdb/couch_passwords_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_passwords_tests.erl b/test/couchdb/couch_passwords_tests.erl
new file mode 100644
index 0000000..116265c
--- /dev/null
+++ b/test/couchdb/couch_passwords_tests.erl
@@ -0,0 +1,54 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_passwords_tests).
+
+-include("couch_eunit.hrl").
+
+
+pbkdf2_test_()->
+    {"PBKDF2",
+     [
+         {"Iterations: 1, length: 20",
+          ?_assertEqual(
+              {ok, <<"0c60c80f961f0e71f3a9b524af6012062fe037a6">>},
+              couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 1, 20))},
+
+         {"Iterations: 2, length: 20",
+          ?_assertEqual(
+              {ok, <<"ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957">>},
+              couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 2, 20))},
+
+         {"Iterations: 4096, length: 20",
+          ?_assertEqual(
+              {ok, <<"4b007901b765489abead49d926f721d065a429c1">>},
+              couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 4096, 20))},
+
+         {"Iterations: 4096, length: 25",
+          ?_assertEqual(
+              {ok, <<"3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038">>},
+              couch_passwords:pbkdf2(<<"passwordPASSWORDpassword">>,
+                                     <<"saltSALTsaltSALTsaltSALTsaltSALTsalt">>,
+                                     4096, 25))},
+         {"Null byte",
+          ?_assertEqual(
+              {ok, <<"56fa6aa75548099dcc37d7f03425e0c3">>},
+              couch_passwords:pbkdf2(<<"pass\0word">>,
+                                     <<"sa\0lt">>,
+                                     4096, 16))},
+
+         {timeout, 180,  %% this may runs too long on slow hosts
+          {"Iterations: 16777216 - this may take some time",
+           ?_assertEqual(
+               {ok, <<"eefe3d61cd4da4e4e9945b3d6ba2158c2634e984">>},
+               couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 16777216, 20)
+           )}}]}.


[04/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 140-attachments-comp.t etap test suite to eunit

- Merge into couchdb_attachments_tests suite;
- Add PUT requests to test_request util;
- Remove dependency from files outside fixtures directory;
- Group test cases to reduce amount of duplicate code;
- Fix hidden issue with gzip encoding: for encoding_length stub info
  check using zlib:gzip on 2KiB+ files leads to mismatch by 2-4 bytes
  and this difference grows with file size. Using gzip fun code from
  couch_stream solves the issue.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/456e0b92
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/456e0b92
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/456e0b92

Branch: refs/heads/1963-eunit-bigcouch
Commit: 456e0b929b283f19a7839b053311767a89bbb198
Parents: 5812bf8
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Jun 3 01:51:46 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_attachments_tests.erl | 380 +++++++++++++++++++++++-
 test/couchdb/fixtures/logo.png             | Bin 0 -> 3010 bytes
 test/couchdb/test_request.erl              |   9 +-
 3 files changed, 385 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/456e0b92/test/couchdb/couchdb_attachments_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_attachments_tests.erl b/test/couchdb/couchdb_attachments_tests.erl
index aef1873..cf59785 100644
--- a/test/couchdb/couchdb_attachments_tests.erl
+++ b/test/couchdb/couchdb_attachments_tests.erl
@@ -15,13 +15,23 @@
 -include("couch_eunit.hrl").
 -include_lib("couchdb/couch_db.hrl").
 
+-define(COMPRESSION_LEVEL, 8).
+-define(ATT_BIN_NAME, <<"logo.png">>).
+-define(ATT_TXT_NAME, <<"file.erl">>).
+-define(FIXTURE_PNG, filename:join([?FIXTURESDIR, "logo.png"])).
+-define(FIXTURE_TXT, ?FILE).
 -define(TIMEOUT, 1000).
+-define(TIMEOUT_EUNIT, 10).
 -define(TIMEWAIT, 100).
 -define(i2l(I), integer_to_list(I)).
 
 
 start() ->
     {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    % ensure in default compression settings for attachments_compression_tests
+    couch_config:set("attachments", "compression_level",
+                     ?i2l(?COMPRESSION_LEVEL), false),
+    couch_config:set("attachments", "compressible_types", "text/*", false),
     Pid.
 
 stop(Pid) ->
@@ -43,7 +53,35 @@ setup() ->
     Host = Addr ++ ":" ++ ?i2l(Port),
     {Host, ?b2l(DbName)}.
 
+setup({binary, standalone}) ->
+    {Host, DbName} = setup(),
+        setup_att(fun create_standalone_png_att/2, Host, DbName, ?FIXTURE_PNG);
+setup({text, standalone}) ->
+    {Host, DbName} = setup(),
+    setup_att(fun create_standalone_text_att/2, Host, DbName, ?FIXTURE_TXT);
+setup({binary, inline}) ->
+    {Host, DbName} = setup(),
+    setup_att(fun create_inline_png_att/2, Host, DbName, ?FIXTURE_PNG);
+setup({text, inline}) ->
+    {Host, DbName} = setup(),
+    setup_att(fun create_inline_text_att/2, Host, DbName, ?FIXTURE_TXT);
+setup(compressed) ->
+    {Host, DbName} = setup(),
+    setup_att(fun create_already_compressed_att/2, Host, DbName, ?FIXTURE_TXT).
+setup_att(Fun, Host, DbName, File) ->
+    HttpHost = "http://" ++ Host,
+    AttUrl = Fun(HttpHost, DbName),
+    {ok, Data} = file:read_file(File),
+    DocUrl = string:join([HttpHost, DbName, "doc"], "/"),
+    Helpers = {DbName, DocUrl, AttUrl},
+    {Data, Helpers}.
+
+teardown(_, {_, {DbName, _, _}}) ->
+    teardown(DbName).
+
 teardown({_, DbName}) ->
+    teardown(DbName);
+teardown(DbName) ->
     ok = couch_server:delete(?l2b(DbName), []),
     ok.
 
@@ -55,7 +93,8 @@ attachments_test_() ->
             setup,
             fun start/0, fun stop/1,
             [
-                attachments_md5_tests()
+                attachments_md5_tests(),
+                attachments_compression_tests()
             ]
         }
     }.
@@ -79,6 +118,67 @@ attachments_md5_tests() ->
         }
     }.
 
+attachments_compression_tests() ->
+    Funs = [
+         fun should_get_att_without_accept_gzip_encoding/2,
+         fun should_get_att_with_accept_gzip_encoding/2,
+         fun should_get_att_with_accept_deflate_encoding/2,
+         fun should_return_406_response_on_unsupported_encoding/2,
+         fun should_get_doc_with_att_data/2,
+         fun should_get_doc_with_att_data_stub/2
+    ],
+    {
+        "Attachments compression tests",
+        [
+            {
+                "Created via Attachments API",
+                created_attachments_compression_tests(standalone, Funs)
+            },
+            {
+                "Created inline via Document API",
+                created_attachments_compression_tests(inline, Funs)
+            },
+            {
+                "Created already been compressed via Attachments API",
+                {
+                    foreachx,
+                    fun setup/1, fun teardown/2,
+                    [{compressed, Fun} || Fun <- Funs]
+                }
+            },
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_not_create_compressed_att_with_deflate_encoding/1,
+                    fun should_not_create_compressed_att_with_compress_encoding/1,
+                    fun should_create_compressible_att_with_ctype_params/1
+                ]
+            }
+        ]
+    }.
+
+created_attachments_compression_tests(Mod, Funs) ->
+    [
+        {
+            "Compressiable attachments",
+            {
+                foreachx,
+                fun setup/1, fun teardown/2,
+                [{{text, Mod}, Fun} || Fun <- Funs]
+            }
+        },
+        {
+            "Uncompressiable attachments",
+            {
+                foreachx,
+                fun setup/1, fun teardown/2,
+                [{{binary, Mod}, Fun} || Fun <- Funs]
+            }
+        }
+    ].
+
+
 
 should_upload_attachment_without_md5({Host, DbName}) ->
     ?_test(begin
@@ -212,10 +312,218 @@ should_reject_chunked_attachment_with_invalid_md5_trailer({Host, DbName}) ->
         ],
         {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
         ?assertEqual(400, Code),
-        ?assertEqual(<<"content_md5_mismatch">>,
-                     get_json(Json, [<<"error">>]))
+        ?assertEqual(<<"content_md5_mismatch">>, get_json(Json, [<<"error">>]))
+    end).
+
+should_get_att_without_accept_gzip_encoding(_, {Data, {_, _, AttUrl}}) ->
+    ?_test(begin
+        {ok, Code, Headers, Body} = test_request:get(AttUrl),
+        ?assertEqual(200, Code),
+        ?assertNot(lists:member({"Content-Encoding", "gzip"}, Headers)),
+        ?assertEqual(Data, iolist_to_binary(Body))
+    end).
+
+should_get_att_with_accept_gzip_encoding(compressed, {Data, {_, _, AttUrl}}) ->
+    ?_test(begin
+        {ok, Code, Headers, Body} = test_request:get(
+            AttUrl, [{"Accept-Encoding", "gzip"}]),
+        ?assertEqual(200, Code),
+        ?assert(lists:member({"Content-Encoding", "gzip"}, Headers)),
+        ?assertEqual(Data, zlib:gunzip(iolist_to_binary(Body)))
+    end);
+should_get_att_with_accept_gzip_encoding({text, _}, {Data, {_, _, AttUrl}}) ->
+    ?_test(begin
+        {ok, Code, Headers, Body} = test_request:get(
+            AttUrl, [{"Accept-Encoding", "gzip"}]),
+        ?assertEqual(200, Code),
+        ?assert(lists:member({"Content-Encoding", "gzip"}, Headers)),
+        ?assertEqual(Data, zlib:gunzip(iolist_to_binary(Body)))
+    end);
+should_get_att_with_accept_gzip_encoding({binary, _}, {Data, {_, _, AttUrl}}) ->
+    ?_test(begin
+        {ok, Code, Headers, Body} = test_request:get(
+            AttUrl, [{"Accept-Encoding", "gzip"}]),
+        ?assertEqual(200, Code),
+        ?assertEqual(undefined,
+                     couch_util:get_value("Content-Encoding", Headers)),
+        ?assertEqual(Data, iolist_to_binary(Body))
+    end).
+
+should_get_att_with_accept_deflate_encoding(_, {Data, {_, _, AttUrl}}) ->
+    ?_test(begin
+        {ok, Code, Headers, Body} = test_request:get(
+            AttUrl, [{"Accept-Encoding", "deflate"}]),
+        ?assertEqual(200, Code),
+        ?assertEqual(undefined,
+                     couch_util:get_value("Content-Encoding", Headers)),
+        ?assertEqual(Data, iolist_to_binary(Body))
+    end).
+
+should_return_406_response_on_unsupported_encoding(_, {_, {_, _, AttUrl}}) ->
+    ?_assertEqual(406,
+        begin
+            {ok, Code, _, _} = test_request:get(
+                AttUrl, [{"Accept-Encoding", "deflate, *;q=0"}]),
+            Code
+        end).
+
+should_get_doc_with_att_data(compressed, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?attachments=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        AttJson = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
+        AttData = couch_util:get_nested_json_value(
+            AttJson, [<<"data">>]),
+        ?assertEqual(
+            <<"text/plain">>,
+            couch_util:get_nested_json_value(AttJson,[<<"content_type">>])),
+        ?assertEqual(Data, base64:decode(AttData))
+    end);
+should_get_doc_with_att_data({text, _}, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?attachments=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        AttJson = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
+        AttData = couch_util:get_nested_json_value(
+            AttJson, [<<"data">>]),
+        ?assertEqual(
+            <<"text/plain">>,
+            couch_util:get_nested_json_value(AttJson,[<<"content_type">>])),
+        ?assertEqual(Data, base64:decode(AttData))
+    end);
+should_get_doc_with_att_data({binary, _}, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?attachments=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        AttJson = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_BIN_NAME]),
+        AttData = couch_util:get_nested_json_value(
+            AttJson, [<<"data">>]),
+        ?assertEqual(
+            <<"image/png">>,
+            couch_util:get_nested_json_value(AttJson,[<<"content_type">>])),
+        ?assertEqual(Data, base64:decode(AttData))
+    end).
+
+should_get_doc_with_att_data_stub(compressed, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?att_encoding_info=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        {AttJson} = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
+        ?assertEqual(<<"gzip">>,
+                     couch_util:get_value(<<"encoding">>, AttJson)),
+        AttLength = couch_util:get_value(<<"length">>, AttJson),
+        EncLength = couch_util:get_value(<<"encoded_length">>, AttJson),
+        ?assertEqual(AttLength, EncLength),
+        ?assertEqual(iolist_size(zlib:gzip(Data)), AttLength)
+    end);
+should_get_doc_with_att_data_stub({text, _}, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?att_encoding_info=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        {AttJson} = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
+        ?assertEqual(<<"gzip">>,
+                     couch_util:get_value(<<"encoding">>, AttJson)),
+        AttEncLength = iolist_size(gzip(Data)),
+        ?assertEqual(AttEncLength,
+                     couch_util:get_value(<<"encoded_length">>, AttJson)),
+        ?assertEqual(byte_size(Data),
+                     couch_util:get_value(<<"length">>, AttJson))
+    end);
+should_get_doc_with_att_data_stub({binary, _}, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?att_encoding_info=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        {AttJson} = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_BIN_NAME]),
+        ?assertEqual(undefined,
+                     couch_util:get_value(<<"encoding">>, AttJson)),
+        ?assertEqual(undefined,
+                     couch_util:get_value(<<"encoded_length">>, AttJson)),
+        ?assertEqual(byte_size(Data),
+                     couch_util:get_value(<<"length">>, AttJson))
     end).
 
+should_not_create_compressed_att_with_deflate_encoding({Host, DbName}) ->
+    ?_assertEqual(415,
+        begin
+            HttpHost = "http://" ++ Host,
+            AttUrl = string:join([HttpHost, DbName, ?docid(), "file.txt"], "/"),
+            {ok, Data} = file:read_file(?FIXTURE_TXT),
+            Body = zlib:compress(Data),
+            Headers = [
+                {"Content-Encoding", "deflate"},
+                {"Content-Type", "text/plain"}
+            ],
+            {ok, Code, _, _} = test_request:put(AttUrl, Headers, Body),
+            Code
+        end).
+
+should_not_create_compressed_att_with_compress_encoding({Host, DbName}) ->
+    % Note: As of OTP R13B04, it seems there's no LZW compression
+    % (i.e. UNIX compress utility implementation) lib in OTP.
+    % However there's a simple working Erlang implementation at:
+    % http://scienceblogs.com/goodmath/2008/01/simple_lempelziv_compression_i.php
+    ?_assertEqual(415,
+        begin
+            HttpHost = "http://" ++ Host,
+            AttUrl = string:join([HttpHost, DbName, ?docid(), "file.txt"], "/"),
+            {ok, Data} = file:read_file(?FIXTURE_TXT),
+            Headers = [
+                {"Content-Encoding", "compress"},
+                {"Content-Type", "text/plain"}
+            ],
+            {ok, Code, _, _} = test_request:put(AttUrl, Headers, Data),
+            Code
+        end).
+
+should_create_compressible_att_with_ctype_params({Host, DbName}) ->
+    {timeout, ?TIMEOUT_EUNIT, ?_test(begin
+        HttpHost = "http://" ++ Host,
+        DocUrl = string:join([HttpHost, DbName, ?docid()], "/"),
+        AttUrl = string:join([DocUrl, ?b2l(?ATT_TXT_NAME)], "/"),
+        {ok, Data} = file:read_file(?FIXTURE_TXT),
+        Headers = [{"Content-Type", "text/plain; charset=UTF-8"}],
+        {ok, Code0, _, _} = test_request:put(AttUrl, Headers, Data),
+        ?assertEqual(201, Code0),
+
+        {ok, Code1, _, Body} = test_request:get(
+            DocUrl ++ "?att_encoding_info=true"),
+        ?assertEqual(200, Code1),
+        Json = ejson:decode(Body),
+        {AttJson} = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
+        ?assertEqual(<<"gzip">>,
+                     couch_util:get_value(<<"encoding">>, AttJson)),
+        AttEncLength = iolist_size(gzip(Data)),
+        ?assertEqual(AttEncLength,
+                     couch_util:get_value(<<"encoded_length">>, AttJson)),
+        ?assertEqual(byte_size(Data),
+                     couch_util:get_value(<<"length">>, AttJson))
+    end)}.
+
 
 get_json(Json, Path) ->
     couch_util:get_nested_json_value(Json, Path).
@@ -262,3 +570,69 @@ request(Method, Url, Headers, Body) ->
         erlang:decode_packet(http, Header, []),
     Json = ejson:decode(Body1),
     {ok, Code, Json}.
+
+create_standalone_text_att(Host, DbName) ->
+    {ok, Data} = file:read_file(?FIXTURE_TXT),
+    Url = string:join([Host, DbName, "doc", ?b2l(?ATT_TXT_NAME)], "/"),
+    {ok, Code, _Headers, _Body} = test_request:put(
+        Url, [{"Content-Type", "text/plain"}], Data),
+    ?assertEqual(201, Code),
+    Url.
+
+create_standalone_png_att(Host, DbName) ->
+    {ok, Data} = file:read_file(?FIXTURE_PNG),
+    Url = string:join([Host, DbName, "doc", ?b2l(?ATT_BIN_NAME)], "/"),
+    {ok, Code, _Headers, _Body} = test_request:put(
+        Url, [{"Content-Type", "image/png"}], Data),
+    ?assertEqual(201, Code),
+    Url.
+
+create_inline_text_att(Host, DbName) ->
+    {ok, Data} = file:read_file(?FIXTURE_TXT),
+    Url = string:join([Host, DbName, "doc"], "/"),
+    Doc = {[
+        {<<"_attachments">>, {[
+            {?ATT_TXT_NAME, {[
+                {<<"content_type">>, <<"text/plain">>},
+                {<<"data">>, base64:encode(Data)}
+            ]}
+        }]}}
+    ]},
+    {ok, Code, _Headers, _Body} = test_request:put(
+        Url, [{"Content-Type", "application/json"}], ejson:encode(Doc)),
+    ?assertEqual(201, Code),
+    string:join([Url, ?b2l(?ATT_TXT_NAME)], "/").
+
+create_inline_png_att(Host, DbName) ->
+    {ok, Data} = file:read_file(?FIXTURE_PNG),
+    Url = string:join([Host, DbName, "doc"], "/"),
+    Doc = {[
+        {<<"_attachments">>, {[
+            {?ATT_BIN_NAME, {[
+                {<<"content_type">>, <<"image/png">>},
+                {<<"data">>, base64:encode(Data)}
+            ]}
+        }]}}
+    ]},
+    {ok, Code, _Headers, _Body} = test_request:put(
+        Url, [{"Content-Type", "application/json"}], ejson:encode(Doc)),
+    ?assertEqual(201, Code),
+    string:join([Url, ?b2l(?ATT_BIN_NAME)], "/").
+
+create_already_compressed_att(Host, DbName) ->
+    {ok, Data} = file:read_file(?FIXTURE_TXT),
+    Url = string:join([Host, DbName, "doc", ?b2l(?ATT_TXT_NAME)], "/"),
+    {ok, Code, _Headers, _Body} = test_request:put(
+        Url, [{"Content-Type", "text/plain"}, {"Content-Encoding", "gzip"}],
+        zlib:gzip(Data)),
+    ?assertEqual(201, Code),
+    Url.
+
+gzip(Data) ->
+    Z = zlib:open(),
+    ok = zlib:deflateInit(Z, ?COMPRESSION_LEVEL, deflated, 16 + 15, 8, default),
+    zlib:deflate(Z, Data),
+    Last = zlib:deflate(Z, [], finish),
+    ok = zlib:deflateEnd(Z),
+    ok = zlib:close(Z),
+    Last.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/456e0b92/test/couchdb/fixtures/logo.png
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/logo.png b/test/couchdb/fixtures/logo.png
new file mode 100644
index 0000000..d21ac02
Binary files /dev/null and b/test/couchdb/fixtures/logo.png differ

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/456e0b92/test/couchdb/test_request.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/test_request.erl b/test/couchdb/test_request.erl
index 7abb92f..d6725ea 100644
--- a/test/couchdb/test_request.erl
+++ b/test/couchdb/test_request.erl
@@ -12,7 +12,7 @@
 
 -module(test_request).
 
--export([get/1, get/2]).
+-export([get/1, get/2, put/2, put/3]).
 -export([request/3, request/4]).
 
 get(Url) ->
@@ -22,6 +22,13 @@ get(Url, Headers) ->
     request(get, Url, Headers).
 
 
+put(Url, Body) ->
+    request(put, Url, [], Body).
+
+put(Url, Headers, Body) ->
+    request(put, Url, Headers, Body).
+
+
 request(Method, Url, Headers) ->
     request(Method, Url, Headers, []).
 


[22/42] Move files out of test/couchdb into top level test/ folder

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_views_tests.erl b/test/couchdb_views_tests.erl
new file mode 100644
index 0000000..6d81f32
--- /dev/null
+++ b/test/couchdb_views_tests.erl
@@ -0,0 +1,669 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_views_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+-include_lib("couch_mrview/include/couch_mrview.hrl").
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(DELAY, 100).
+-define(TIMEOUT, 1000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    ok = couch_db:close(Db),
+    FooRev = create_design_doc(DbName, <<"_design/foo">>, <<"bar">>),
+    query_view(DbName, "foo", "bar"),
+    BooRev = create_design_doc(DbName, <<"_design/boo">>, <<"baz">>),
+    query_view(DbName, "boo", "baz"),
+    {DbName, {FooRev, BooRev}}.
+
+setup_with_docs() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    ok = couch_db:close(Db),
+    create_docs(DbName),
+    create_design_doc(DbName, <<"_design/foo">>, <<"bar">>),
+    DbName.
+
+teardown({DbName, _}) ->
+    teardown(DbName);
+teardown(DbName) when is_binary(DbName) ->
+    couch_server:delete(DbName, [?ADMIN_USER]),
+    ok.
+
+
+view_indexes_cleanup_test_() ->
+    {
+        "View indexes cleanup",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_have_two_indexes_alive_before_deletion/1,
+                    fun should_cleanup_index_file_after_ddoc_deletion/1,
+                    fun should_cleanup_all_index_files/1
+                ]
+            }
+        }
+    }.
+
+view_group_db_leaks_test_() ->
+    {
+        "View group db leaks",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup_with_docs/0, fun teardown/1,
+                [
+                    fun couchdb_1138/1,
+                    fun couchdb_1309/1
+                ]
+            }
+        }
+    }.
+
+view_group_shutdown_test_() ->
+    {
+        "View group shutdown",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [couchdb_1283()]
+        }
+    }.
+
+
+should_not_remember_docs_in_index_after_backup_restore_test() ->
+    %% COUCHDB-640
+    start(),
+    DbName = setup_with_docs(),
+
+    ok = backup_db_file(DbName),
+    create_doc(DbName, "doc666"),
+
+    Rows0 = query_view(DbName, "foo", "bar"),
+    ?assert(has_doc("doc1", Rows0)),
+    ?assert(has_doc("doc2", Rows0)),
+    ?assert(has_doc("doc3", Rows0)),
+    ?assert(has_doc("doc666", Rows0)),
+
+    restore_backup_db_file(DbName),
+
+    Rows1 = query_view(DbName, "foo", "bar"),
+    ?assert(has_doc("doc1", Rows1)),
+    ?assert(has_doc("doc2", Rows1)),
+    ?assert(has_doc("doc3", Rows1)),
+    ?assertNot(has_doc("doc666", Rows1)),
+
+    teardown(DbName),
+    stop(whereis(couch_server_sup)).
+
+
+should_upgrade_legacy_view_files_test() ->
+    start(),
+
+    ok = couch_config:set("query_server_config", "commit_freq", "0", false),
+
+    DbName = <<"test">>,
+    DbFileName = "test.couch",
+    DbFilePath = filename:join([?FIXTURESDIR, DbFileName]),
+    OldViewName = "3b835456c235b1827e012e25666152f3.view",
+    FixtureViewFilePath = filename:join([?FIXTURESDIR, OldViewName]),
+    NewViewName = "a1c5929f912aca32f13446122cc6ce50.view",
+
+    DbDir = couch_config:get("couchdb", "database_dir"),
+    ViewDir = couch_config:get("couchdb", "view_index_dir"),
+    OldViewFilePath = filename:join([ViewDir, ".test_design", OldViewName]),
+    NewViewFilePath = filename:join([ViewDir, ".test_design", "mrview",
+                                     NewViewName]),
+
+    % cleanup
+    Files = [
+        filename:join([DbDir, DbFileName]),
+        OldViewFilePath,
+        NewViewFilePath
+    ],
+    lists:foreach(fun(File) -> file:delete(File) end, Files),
+
+    % copy old db file into db dir
+    {ok, _} = file:copy(DbFilePath, filename:join([DbDir, DbFileName])),
+
+    % copy old view file into view dir
+    ok = filelib:ensure_dir(filename:join([ViewDir, ".test_design"])),
+    {ok, _} = file:copy(FixtureViewFilePath, OldViewFilePath),
+
+    % ensure old header
+    OldHeader = read_header(OldViewFilePath),
+    ?assertMatch(#index_header{}, OldHeader),
+
+    % query view for expected results
+    Rows0 = query_view(DbName, "test", "test"),
+    ?assertEqual(2, length(Rows0)),
+
+    % ensure old file gone
+    ?assertNot(filelib:is_regular(OldViewFilePath)),
+
+    % add doc to trigger update
+    DocUrl = db_url(DbName) ++ "/boo",
+    {ok, _, _, _} = test_request:put(
+        DocUrl, [{"Content-Type", "application/json"}], <<"{\"a\":3}">>),
+
+    % query view for expected results
+    Rows1 = query_view(DbName, "test", "test"),
+    ?assertEqual(3, length(Rows1)),
+
+    % ensure new header
+    timer:sleep(2000),  % have to wait for awhile to upgrade the index
+    NewHeader = read_header(NewViewFilePath),
+    ?assertMatch(#mrheader{}, NewHeader),
+
+    teardown(DbName),
+    stop(whereis(couch_server_sup)).
+
+
+should_have_two_indexes_alive_before_deletion({DbName, _}) ->
+    view_cleanup(DbName),
+    ?_assertEqual(2, count_index_files(DbName)).
+
+should_cleanup_index_file_after_ddoc_deletion({DbName, {FooRev, _}}) ->
+    delete_design_doc(DbName, <<"_design/foo">>, FooRev),
+    view_cleanup(DbName),
+    ?_assertEqual(1, count_index_files(DbName)).
+
+should_cleanup_all_index_files({DbName, {FooRev, BooRev}})->
+    delete_design_doc(DbName, <<"_design/foo">>, FooRev),
+    delete_design_doc(DbName, <<"_design/boo">>, BooRev),
+    view_cleanup(DbName),
+    ?_assertEqual(0, count_index_files(DbName)).
+
+couchdb_1138(DbName) ->
+    ?_test(begin
+        {ok, IndexerPid} = couch_index_server:get_index(
+            couch_mrview_index, DbName, <<"_design/foo">>),
+        ?assert(is_pid(IndexerPid)),
+        ?assert(is_process_alive(IndexerPid)),
+        ?assertEqual(2, count_db_refs(DbName)),
+
+        Rows0 = query_view(DbName, "foo", "bar"),
+        ?assertEqual(3, length(Rows0)),
+        ?assertEqual(2, count_db_refs(DbName)),
+        ?assert(is_process_alive(IndexerPid)),
+
+        create_doc(DbName, "doc1000"),
+        Rows1 = query_view(DbName, "foo", "bar"),
+        ?assertEqual(4, length(Rows1)),
+        ?assertEqual(2, count_db_refs(DbName)),
+        ?assert(is_process_alive(IndexerPid)),
+
+        Ref1 = get_db_ref_counter(DbName),
+        compact_db(DbName),
+        Ref2 = get_db_ref_counter(DbName),
+        ?assertEqual(2, couch_ref_counter:count(Ref2)),
+        ?assertNotEqual(Ref2, Ref1),
+        ?assertNot(is_process_alive(Ref1)),
+        ?assert(is_process_alive(IndexerPid)),
+
+        compact_view_group(DbName, "foo"),
+        ?assertEqual(2, count_db_refs(DbName)),
+        Ref3 = get_db_ref_counter(DbName),
+        ?assertEqual(Ref3, Ref2),
+        ?assert(is_process_alive(IndexerPid)),
+
+        create_doc(DbName, "doc1001"),
+        Rows2 = query_view(DbName, "foo", "bar"),
+        ?assertEqual(5, length(Rows2)),
+        ?assertEqual(2, count_db_refs(DbName)),
+        ?assert(is_process_alive(IndexerPid))
+    end).
+
+couchdb_1309(DbName) ->
+    ?_test(begin
+        {ok, IndexerPid} = couch_index_server:get_index(
+            couch_mrview_index, DbName, <<"_design/foo">>),
+        ?assert(is_pid(IndexerPid)),
+        ?assert(is_process_alive(IndexerPid)),
+        ?assertEqual(2, count_db_refs(DbName)),
+
+        create_doc(DbName, "doc1001"),
+        Rows0 = query_view(DbName, "foo", "bar"),
+        check_rows_value(Rows0, null),
+        ?assertEqual(4, length(Rows0)),
+        ?assertEqual(2, count_db_refs(DbName)),
+        ?assert(is_process_alive(IndexerPid)),
+
+        update_design_doc(DbName,  <<"_design/foo">>, <<"bar">>),
+        {ok, NewIndexerPid} = couch_index_server:get_index(
+            couch_mrview_index, DbName, <<"_design/foo">>),
+        ?assert(is_pid(NewIndexerPid)),
+        ?assert(is_process_alive(NewIndexerPid)),
+        ?assertNotEqual(IndexerPid, NewIndexerPid),
+        ?assertEqual(2, count_db_refs(DbName)),
+
+        Rows1 = query_view(DbName, "foo", "bar", ok),
+        ?assertEqual(0, length(Rows1)),
+        Rows2 = query_view(DbName, "foo", "bar"),
+        check_rows_value(Rows2, 1),
+        ?assertEqual(4, length(Rows2)),
+
+        MonRef0 = erlang:monitor(process, IndexerPid),
+        receive
+            {'DOWN', MonRef0, _, _, _} ->
+                ok
+        after ?TIMEOUT ->
+            erlang:error(
+                {assertion_failed,
+                 [{module, ?MODULE}, {line, ?LINE},
+                  {reason, "old view group is not dead after ddoc update"}]})
+        end,
+
+        MonRef1 = erlang:monitor(process, NewIndexerPid),
+        ok = couch_server:delete(DbName, [?ADMIN_USER]),
+        receive
+            {'DOWN', MonRef1, _, _, _} ->
+                ok
+        after ?TIMEOUT ->
+            erlang:error(
+                {assertion_failed,
+                 [{module, ?MODULE}, {line, ?LINE},
+                  {reason, "new view group did not die after DB deletion"}]})
+        end
+    end).
+
+couchdb_1283() ->
+    ?_test(begin
+        ok = couch_config:set("couchdb", "max_dbs_open", "3", false),
+        ok = couch_config:set("couchdb", "delayed_commits", "false", false),
+
+        {ok, MDb1} = couch_db:create(?tempdb(), [?ADMIN_USER]),
+        DDoc = couch_doc:from_json_obj({[
+            {<<"_id">>, <<"_design/foo">>},
+            {<<"language">>, <<"javascript">>},
+            {<<"views">>, {[
+                {<<"foo">>, {[
+                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+                ]}},
+                {<<"foo2">>, {[
+                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+                ]}},
+                {<<"foo3">>, {[
+                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+                ]}},
+                {<<"foo4">>, {[
+                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+                ]}},
+                {<<"foo5">>, {[
+                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+                ]}}
+            ]}}
+        ]}),
+        {ok, _} = couch_db:update_doc(MDb1, DDoc, []),
+        ok = populate_db(MDb1, 100, 100),
+        query_view(MDb1#db.name, "foo", "foo"),
+        ok = couch_db:close(MDb1),
+
+        {ok, Db1} = couch_db:create(?tempdb(), [?ADMIN_USER]),
+        ok = couch_db:close(Db1),
+        {ok, Db2} = couch_db:create(?tempdb(), [?ADMIN_USER]),
+        ok = couch_db:close(Db2),
+        {ok, Db3} = couch_db:create(?tempdb(), [?ADMIN_USER]),
+        ok = couch_db:close(Db3),
+
+        Writer1 = spawn_writer(Db1#db.name),
+        Writer2 = spawn_writer(Db2#db.name),
+
+        ?assert(is_process_alive(Writer1)),
+        ?assert(is_process_alive(Writer2)),
+
+        ?assertEqual(ok, get_writer_status(Writer1)),
+        ?assertEqual(ok, get_writer_status(Writer2)),
+
+        {ok, MonRef} = couch_mrview:compact(MDb1#db.name, <<"_design/foo">>,
+                                            [monitor]),
+
+        Writer3 = spawn_writer(Db3#db.name),
+        ?assert(is_process_alive(Writer3)),
+        ?assertEqual({error, all_dbs_active}, get_writer_status(Writer3)),
+
+        ?assert(is_process_alive(Writer1)),
+        ?assert(is_process_alive(Writer2)),
+        ?assert(is_process_alive(Writer3)),
+
+        receive
+            {'DOWN', MonRef, process, _, Reason} ->
+                ?assertEqual(normal, Reason)
+        after ?TIMEOUT ->
+            erlang:error(
+                {assertion_failed,
+                 [{module, ?MODULE}, {line, ?LINE},
+                  {reason, "Failure compacting view group"}]})
+        end,
+
+        ?assertEqual(ok, writer_try_again(Writer3)),
+        ?assertEqual(ok, get_writer_status(Writer3)),
+
+        ?assert(is_process_alive(Writer1)),
+        ?assert(is_process_alive(Writer2)),
+        ?assert(is_process_alive(Writer3)),
+
+        ?assertEqual(ok, stop_writer(Writer1)),
+        ?assertEqual(ok, stop_writer(Writer2)),
+        ?assertEqual(ok, stop_writer(Writer3))
+    end).
+
+create_doc(DbName, DocId) when is_list(DocId) ->
+    create_doc(DbName, ?l2b(DocId));
+create_doc(DbName, DocId) when is_binary(DocId) ->
+    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
+    Doc666 = couch_doc:from_json_obj({[
+        {<<"_id">>, DocId},
+        {<<"value">>, 999}
+    ]}),
+    {ok, _} = couch_db:update_docs(Db, [Doc666]),
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db).
+
+create_docs(DbName) ->
+    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
+    Doc1 = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"doc1">>},
+        {<<"value">>, 1}
+
+    ]}),
+    Doc2 = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"doc2">>},
+        {<<"value">>, 2}
+
+    ]}),
+    Doc3 = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"doc3">>},
+        {<<"value">>, 3}
+
+    ]}),
+    {ok, _} = couch_db:update_docs(Db, [Doc1, Doc2, Doc3]),
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db).
+
+populate_db(Db, BatchSize, N) when N > 0 ->
+    Docs = lists:map(
+        fun(_) ->
+            couch_doc:from_json_obj({[
+                {<<"_id">>, couch_uuids:new()},
+                {<<"value">>, base64:encode(crypto:rand_bytes(1000))}
+            ]})
+        end,
+        lists:seq(1, BatchSize)),
+    {ok, _} = couch_db:update_docs(Db, Docs, []),
+    populate_db(Db, BatchSize, N - length(Docs));
+populate_db(_Db, _, _) ->
+    ok.
+
+create_design_doc(DbName, DDName, ViewName) ->
+    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, DDName},
+        {<<"language">>, <<"javascript">>},
+        {<<"views">>, {[
+            {ViewName, {[
+                {<<"map">>, <<"function(doc) { emit(doc.value, null); }">>}
+            ]}}
+        ]}}
+    ]}),
+    {ok, Rev} = couch_db:update_doc(Db, DDoc, []),
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db),
+    Rev.
+
+update_design_doc(DbName, DDName, ViewName) ->
+    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
+    {ok, Doc} = couch_db:open_doc(Db, DDName, [?ADMIN_USER]),
+    {Props} = couch_doc:to_json_obj(Doc, []),
+    Rev = couch_util:get_value(<<"_rev">>, Props),
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, DDName},
+        {<<"_rev">>, Rev},
+        {<<"language">>, <<"javascript">>},
+        {<<"views">>, {[
+            {ViewName, {[
+                {<<"map">>, <<"function(doc) { emit(doc.value, 1); }">>}
+            ]}}
+        ]}}
+    ]}),
+    {ok, NewRev} = couch_db:update_doc(Db, DDoc, [?ADMIN_USER]),
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db),
+    NewRev.
+
+delete_design_doc(DbName, DDName, Rev) ->
+    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, DDName},
+        {<<"_rev">>, couch_doc:rev_to_str(Rev)},
+        {<<"_deleted">>, true}
+    ]}),
+    {ok, _} = couch_db:update_doc(Db, DDoc, [Rev]),
+    couch_db:close(Db).
+
+db_url(DbName) ->
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    "http://" ++ Addr ++ ":" ++ Port ++ "/" ++ ?b2l(DbName).
+
+query_view(DbName, DDoc, View) ->
+    query_view(DbName, DDoc, View, false).
+
+query_view(DbName, DDoc, View, Stale) ->
+    {ok, Code, _Headers, Body} = test_request:get(
+        db_url(DbName) ++ "/_design/" ++ DDoc ++ "/_view/" ++ View
+        ++ case Stale of
+               false -> [];
+               _ -> "?stale=" ++ atom_to_list(Stale)
+           end),
+    ?assertEqual(200, Code),
+    {Props} = ejson:decode(Body),
+    couch_util:get_value(<<"rows">>, Props, []).
+
+check_rows_value(Rows, Value) ->
+    lists:foreach(
+        fun({Row}) ->
+            ?assertEqual(Value, couch_util:get_value(<<"value">>, Row))
+        end, Rows).
+
+view_cleanup(DbName) ->
+    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
+    couch_mrview:cleanup(Db),
+    couch_db:close(Db).
+
+get_db_ref_counter(DbName) ->
+    {ok, #db{fd_ref_counter = Ref} = Db} = couch_db:open_int(DbName, []),
+    ok = couch_db:close(Db),
+    Ref.
+
+count_db_refs(DbName) ->
+    Ref = get_db_ref_counter(DbName),
+    % have to sleep a bit to let couchdb cleanup all refs and leave only
+    % active ones. otherwise the related tests will randomly fail due to
+    % count number mismatch
+    timer:sleep(200),
+    couch_ref_counter:count(Ref).
+
+count_index_files(DbName) ->
+    % call server to fetch the index files
+    RootDir = couch_config:get("couchdb", "view_index_dir"),
+    length(filelib:wildcard(RootDir ++ "/." ++
+        binary_to_list(DbName) ++ "_design"++"/mrview/*")).
+
+has_doc(DocId1, Rows) ->
+    DocId = iolist_to_binary(DocId1),
+    lists:any(fun({R}) -> lists:member({<<"id">>, DocId}, R) end, Rows).
+
+backup_db_file(DbName) ->
+    DbDir = couch_config:get("couchdb", "database_dir"),
+    DbFile = filename:join([DbDir, ?b2l(DbName) ++ ".couch"]),
+    {ok, _} = file:copy(DbFile, DbFile ++ ".backup"),
+    ok.
+
+restore_backup_db_file(DbName) ->
+    DbDir = couch_config:get("couchdb", "database_dir"),
+    stop(whereis(couch_server_sup)),
+    DbFile = filename:join([DbDir, ?b2l(DbName) ++ ".couch"]),
+    ok = file:delete(DbFile),
+    ok = file:rename(DbFile ++ ".backup", DbFile),
+    start(),
+    ok.
+
+compact_db(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, _} = couch_db:start_compact(Db),
+    ok = couch_db:close(Db),
+    wait_db_compact_done(DbName, 10).
+
+wait_db_compact_done(_DbName, 0) ->
+    erlang:error({assertion_failed,
+                  [{module, ?MODULE},
+                   {line, ?LINE},
+                   {reason, "DB compaction failed to finish"}]});
+wait_db_compact_done(DbName, N) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    ok = couch_db:close(Db),
+    case is_pid(Db#db.compactor_pid) of
+    false ->
+        ok;
+    true ->
+        ok = timer:sleep(?DELAY),
+        wait_db_compact_done(DbName, N - 1)
+    end.
+
+compact_view_group(DbName, DDocId) when is_list(DDocId) ->
+    compact_view_group(DbName, ?l2b("_design/" ++ DDocId));
+compact_view_group(DbName, DDocId) when is_binary(DDocId) ->
+    ok = couch_mrview:compact(DbName, DDocId),
+    wait_view_compact_done(DbName, DDocId, 10).
+
+wait_view_compact_done(_DbName, _DDocId, 0) ->
+    erlang:error({assertion_failed,
+                  [{module, ?MODULE},
+                   {line, ?LINE},
+                   {reason, "DB compaction failed to finish"}]});
+wait_view_compact_done(DbName, DDocId, N) ->
+    {ok, Code, _Headers, Body} = test_request:get(
+        db_url(DbName) ++ "/" ++ ?b2l(DDocId) ++ "/_info"),
+    ?assertEqual(200, Code),
+    {Info} = ejson:decode(Body),
+    {IndexInfo} = couch_util:get_value(<<"view_index">>, Info),
+    CompactRunning = couch_util:get_value(<<"compact_running">>, IndexInfo),
+    case CompactRunning of
+        false ->
+            ok;
+        true ->
+            ok = timer:sleep(?DELAY),
+            wait_view_compact_done(DbName, DDocId, N - 1)
+    end.
+
+spawn_writer(DbName) ->
+    Parent = self(),
+    spawn(fun() ->
+        process_flag(priority, high),
+        writer_loop(DbName, Parent)
+    end).
+
+get_writer_status(Writer) ->
+    Ref = make_ref(),
+    Writer ! {get_status, Ref},
+    receive
+        {db_open, Ref} ->
+            ok;
+        {db_open_error, Error, Ref} ->
+            Error
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+writer_try_again(Writer) ->
+    Ref = make_ref(),
+    Writer ! {try_again, Ref},
+    receive
+        {ok, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+stop_writer(Writer) ->
+    Ref = make_ref(),
+    Writer ! {stop, Ref},
+    receive
+        {ok, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        erlang:error({assertion_failed,
+                      [{module, ?MODULE},
+                       {line, ?LINE},
+                       {reason, "Timeout on stopping process"}]})
+    end.
+
+writer_loop(DbName, Parent) ->
+    case couch_db:open_int(DbName, []) of
+        {ok, Db} ->
+            writer_loop_1(Db, Parent);
+        Error ->
+            writer_loop_2(DbName, Parent, Error)
+    end.
+
+writer_loop_1(Db, Parent) ->
+    receive
+        {get_status, Ref} ->
+            Parent ! {db_open, Ref},
+            writer_loop_1(Db, Parent);
+        {stop, Ref} ->
+            ok = couch_db:close(Db),
+            Parent ! {ok, Ref}
+    end.
+
+writer_loop_2(DbName, Parent, Error) ->
+    receive
+        {get_status, Ref} ->
+            Parent ! {db_open_error, Error, Ref},
+            writer_loop_2(DbName, Parent, Error);
+        {try_again, Ref} ->
+            Parent ! {ok, Ref},
+            writer_loop(DbName, Parent)
+    end.
+
+read_header(File) ->
+    {ok, Fd} = couch_file:open(File),
+    {ok, {_Sig, Header}} = couch_file:read_header(Fd),
+    couch_file:close(Fd),
+    Header.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/3b835456c235b1827e012e25666152f3.view
----------------------------------------------------------------------
diff --git a/test/fixtures/3b835456c235b1827e012e25666152f3.view b/test/fixtures/3b835456c235b1827e012e25666152f3.view
new file mode 100644
index 0000000..9c67648
Binary files /dev/null and b/test/fixtures/3b835456c235b1827e012e25666152f3.view differ

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/couch_stats_aggregates.cfg
----------------------------------------------------------------------
diff --git a/test/fixtures/couch_stats_aggregates.cfg b/test/fixtures/couch_stats_aggregates.cfg
new file mode 100644
index 0000000..30e475d
--- /dev/null
+++ b/test/fixtures/couch_stats_aggregates.cfg
@@ -0,0 +1,19 @@
+% Licensed to the Apache Software Foundation (ASF) under one
+% or more contributor license agreements.  See the NOTICE file
+% distributed with this work for additional information
+% regarding copyright ownership.  The ASF licenses this file
+% to you under the Apache License, Version 2.0 (the
+% "License"); you may not use this file except in compliance
+% with the License.  You may obtain a copy of the License at
+% 
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing,
+% software distributed under the License is distributed on an
+% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+% KIND, either express or implied.  See the License for the
+% specific language governing permissions and limitations
+% under the License.
+
+{testing, stuff, "yay description"}.
+{number, '11', "randomosity"}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/couch_stats_aggregates.ini
----------------------------------------------------------------------
diff --git a/test/fixtures/couch_stats_aggregates.ini b/test/fixtures/couch_stats_aggregates.ini
new file mode 100644
index 0000000..cc5cd21
--- /dev/null
+++ b/test/fixtures/couch_stats_aggregates.ini
@@ -0,0 +1,20 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements.  See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership.  The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License.  You may obtain a copy of the License at
+; 
+;   http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied.  See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+[stats]
+rate = 10000000 ; We call collect_sample in testing
+samples = [0, 1]

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/logo.png
----------------------------------------------------------------------
diff --git a/test/fixtures/logo.png b/test/fixtures/logo.png
new file mode 100644
index 0000000..d21ac02
Binary files /dev/null and b/test/fixtures/logo.png differ

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/os_daemon_bad_perm.sh
----------------------------------------------------------------------
diff --git a/test/fixtures/os_daemon_bad_perm.sh b/test/fixtures/os_daemon_bad_perm.sh
new file mode 100644
index 0000000..345c8b4
--- /dev/null
+++ b/test/fixtures/os_daemon_bad_perm.sh
@@ -0,0 +1,17 @@
+#!/bin/sh -e
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+# 
+# Please do not make this file executable as that's the error being tested.
+
+sleep 5

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/os_daemon_can_reboot.sh
----------------------------------------------------------------------
diff --git a/test/fixtures/os_daemon_can_reboot.sh b/test/fixtures/os_daemon_can_reboot.sh
new file mode 100755
index 0000000..5bc10e8
--- /dev/null
+++ b/test/fixtures/os_daemon_can_reboot.sh
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+sleep 2

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/os_daemon_configer.escript
----------------------------------------------------------------------
diff --git a/test/fixtures/os_daemon_configer.escript b/test/fixtures/os_daemon_configer.escript
new file mode 100755
index 0000000..d437423
--- /dev/null
+++ b/test/fixtures/os_daemon_configer.escript
@@ -0,0 +1,101 @@
+#! /usr/bin/env escript
+
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-include("../couch_eunit.hrl").
+
+
+read() ->
+    case io:get_line('') of
+        eof ->
+            stop;
+        Data ->
+            ejson:decode(Data)
+    end.
+
+write(Mesg) ->
+    Data = iolist_to_binary(ejson:encode(Mesg)),
+    io:format(binary_to_list(Data) ++ "\n", []).
+
+get_cfg(Section) ->
+    write([<<"get">>, Section]),
+    read().
+
+get_cfg(Section, Name) ->
+    write([<<"get">>, Section, Name]),
+    read().
+
+log(Mesg) ->
+    write([<<"log">>, Mesg]).
+
+log(Mesg, Level) ->
+    write([<<"log">>, Mesg, {[{<<"level">>, Level}]}]).
+
+test_get_cfg1() ->
+    Path = list_to_binary(?FILE),
+    FileName = list_to_binary(filename:basename(?FILE)),
+    {[{FileName, Path}]} = get_cfg(<<"os_daemons">>).
+
+test_get_cfg2() ->
+    Path = list_to_binary(?FILE),
+    FileName = list_to_binary(filename:basename(?FILE)),
+    Path = get_cfg(<<"os_daemons">>, FileName),
+    <<"sequential">> = get_cfg(<<"uuids">>, <<"algorithm">>).
+
+
+test_get_unknown_cfg() ->
+    {[]} = get_cfg(<<"aal;3p4">>),
+    null = get_cfg(<<"aal;3p4">>, <<"313234kjhsdfl">>).
+
+test_log() ->
+    log(<<"foobar!">>),
+    log(<<"some stuff!">>, <<"debug">>),
+    log(2),
+    log(true),
+    write([<<"log">>, <<"stuff">>, 2]),
+    write([<<"log">>, 3, null]),
+    write([<<"log">>, [1, 2], {[{<<"level">>, <<"debug">>}]}]),
+    write([<<"log">>, <<"true">>, {[]}]).
+
+do_tests() ->
+    test_get_cfg1(),
+    test_get_cfg2(),
+    test_get_unknown_cfg(),
+    test_log(),
+    loop(io:read("")).
+
+loop({ok, _}) ->
+    loop(io:read(""));
+loop(eof) ->
+    init:stop();
+loop({error, _Reason}) ->
+    init:stop().
+
+main([]) ->
+    init_code_path(),
+    couch_config:start_link(?CONFIG_CHAIN),
+    couch_drv:start_link(),
+    do_tests().
+
+init_code_path() ->
+    Paths = [
+        "couchdb",
+        "ejson",
+        "erlang-oauth",
+        "ibrowse",
+        "mochiweb",
+        "snappy"
+    ],
+    lists:foreach(fun(Name) ->
+        code:add_patha(filename:join([?BUILDDIR, "src", Name]))
+    end, Paths).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/os_daemon_die_on_boot.sh
----------------------------------------------------------------------
diff --git a/test/fixtures/os_daemon_die_on_boot.sh b/test/fixtures/os_daemon_die_on_boot.sh
new file mode 100755
index 0000000..256ee79
--- /dev/null
+++ b/test/fixtures/os_daemon_die_on_boot.sh
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+exit 1

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/os_daemon_die_quickly.sh
----------------------------------------------------------------------
diff --git a/test/fixtures/os_daemon_die_quickly.sh b/test/fixtures/os_daemon_die_quickly.sh
new file mode 100755
index 0000000..f5a1368
--- /dev/null
+++ b/test/fixtures/os_daemon_die_quickly.sh
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+sleep 1

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/os_daemon_looper.escript
----------------------------------------------------------------------
diff --git a/test/fixtures/os_daemon_looper.escript b/test/fixtures/os_daemon_looper.escript
new file mode 100755
index 0000000..73974e9
--- /dev/null
+++ b/test/fixtures/os_daemon_looper.escript
@@ -0,0 +1,26 @@
+#! /usr/bin/env escript
+
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+loop() ->
+    loop(io:read("")).
+
+loop({ok, _}) ->
+    loop(io:read(""));
+loop(eof) ->
+    stop;
+loop({error, Reason}) ->
+    throw({error, Reason}).
+
+main([]) ->
+    loop().

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/fixtures/test.couch
----------------------------------------------------------------------
diff --git a/test/fixtures/test.couch b/test/fixtures/test.couch
new file mode 100644
index 0000000..32c79af
Binary files /dev/null and b/test/fixtures/test.couch differ

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/json_stream_parse_tests.erl
----------------------------------------------------------------------
diff --git a/test/json_stream_parse_tests.erl b/test/json_stream_parse_tests.erl
new file mode 100644
index 0000000..92303b6
--- /dev/null
+++ b/test/json_stream_parse_tests.erl
@@ -0,0 +1,151 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(json_stream_parse_tests).
+
+-include("couch_eunit.hrl").
+
+-define(CASES,
+    [
+        {1, "1", "integer numeric literial"},
+        {3.1416, "3.14160", "float numeric literal"},  % text representation may truncate, trail zeroes
+        {-1, "-1", "negative integer numeric literal"},
+        {-3.1416, "-3.14160", "negative float numeric literal"},
+        {12.0e10, "1.20000e+11", "float literal in scientific notation"},
+        {1.234E+10, "1.23400e+10", "another float literal in scientific notation"},
+        {-1.234E-10, "-1.23400e-10", "negative float literal in scientific notation"},
+        {10.0, "1.0e+01", "yet another float literal in scientific notation"},
+        {123.456, "1.23456E+2", "yet another float literal in scientific notation"},
+        {10.0, "1e1", "yet another float literal in scientific notation"},
+        {<<"foo">>, "\"foo\"", "string literal"},
+        {<<"foo", 5, "bar">>, "\"foo\\u0005bar\"", "string literal with \\u0005"},
+        {<<"">>, "\"\"", "empty string literal"},
+        {<<"\n\n\n">>, "\"\\n\\n\\n\"", "only new lines literal"},
+        {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\"",
+            "only white spaces string literal"},
+        {null, "null", "null literal"},
+        {true, "true", "true literal"},
+        {false, "false", "false literal"},
+        {<<"null">>, "\"null\"", "null string literal"},
+        {<<"true">>, "\"true\"", "true string literal"},
+        {<<"false">>, "\"false\"", "false string literal"},
+        {{[]}, "{}", "empty object literal"},
+        {{[{<<"foo">>, <<"bar">>}]}, "{\"foo\":\"bar\"}",
+            "simple object literal"},
+        {{[{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]},
+            "{\"foo\":\"bar\",\"baz\":123}", "another simple object literal"},
+        {[], "[]", "empty array literal"},
+        {[[]], "[[]]", "empty array literal inside a single element array literal"},
+        {[1, <<"foo">>], "[1,\"foo\"]", "simple non-empty array literal"},
+        {[1199344435545.0, 1], "[1199344435545.0,1]",
+             "another simple non-empty array literal"},
+        {[false, true, 321, null], "[false, true, 321, null]", "array of literals"},
+        {{[{<<"foo">>, [123]}]}, "{\"foo\":[123]}",
+             "object literal with an array valued property"},
+        {{[{<<"foo">>, {[{<<"bar">>, true}]}}]},
+            "{\"foo\":{\"bar\":true}}", "nested object literal"},
+        {{[{<<"foo">>, []}, {<<"bar">>, {[{<<"baz">>, true}]}},
+                {<<"alice">>, <<"bob">>}]},
+            "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}",
+            "complex object literal"},
+        {[-123, <<"foo">>, {[{<<"bar">>, []}]}, null],
+            "[-123,\"foo\",{\"bar\":[]},null]",
+            "complex array literal"}
+    ]
+).
+
+
+raw_json_input_test_() ->
+    Tests = lists:map(
+        fun({EJson, JsonString, Desc}) ->
+            {Desc,
+             ?_assert(equiv(EJson, json_stream_parse:to_ejson(JsonString)))}
+        end, ?CASES),
+    {"Tests with raw JSON string as the input", Tests}.
+
+one_byte_data_fun_test_() ->
+    Tests = lists:map(
+        fun({EJson, JsonString, Desc}) ->
+            DataFun = fun() -> single_byte_data_fun(JsonString) end,
+            {Desc,
+             ?_assert(equiv(EJson, json_stream_parse:to_ejson(DataFun)))}
+        end, ?CASES),
+    {"Tests with a 1 byte output data function as the input", Tests}.
+
+test_multiple_bytes_data_fun_test_() ->
+    Tests = lists:map(
+        fun({EJson, JsonString, Desc}) ->
+            DataFun = fun() -> multiple_bytes_data_fun(JsonString) end,
+            {Desc,
+             ?_assert(equiv(EJson, json_stream_parse:to_ejson(DataFun)))}
+        end, ?CASES),
+    {"Tests with a multiple bytes output data function as the input", Tests}.
+
+
+%% Test for equivalence of Erlang terms.
+%% Due to arbitrary order of construction, equivalent objects might
+%% compare unequal as erlang terms, so we need to carefully recurse
+%% through aggregates (tuples and objects).
+equiv({Props1}, {Props2}) ->
+    equiv_object(Props1, Props2);
+equiv(L1, L2) when is_list(L1), is_list(L2) ->
+    equiv_list(L1, L2);
+equiv(N1, N2) when is_number(N1), is_number(N2) ->
+    N1 == N2;
+equiv(B1, B2) when is_binary(B1), is_binary(B2) ->
+    B1 == B2;
+equiv(true, true) ->
+    true;
+equiv(false, false) ->
+    true;
+equiv(null, null) ->
+    true.
+
+%% Object representation and traversal order is unknown.
+%% Use the sledgehammer and sort property lists.
+equiv_object(Props1, Props2) ->
+    L1 = lists:keysort(1, Props1),
+    L2 = lists:keysort(1, Props2),
+    Pairs = lists:zip(L1, L2),
+    true = lists:all(
+        fun({{K1, V1}, {K2, V2}}) ->
+            equiv(K1, K2) andalso equiv(V1, V2)
+        end,
+        Pairs).
+
+%% Recursively compare tuple elements for equivalence.
+equiv_list([], []) ->
+    true;
+equiv_list([V1 | L1], [V2 | L2]) ->
+    equiv(V1, V2) andalso equiv_list(L1, L2).
+
+single_byte_data_fun([]) ->
+    done;
+single_byte_data_fun([H | T]) ->
+    {<<H>>, fun() -> single_byte_data_fun(T) end}.
+
+multiple_bytes_data_fun([]) ->
+    done;
+multiple_bytes_data_fun(L) ->
+    N = crypto:rand_uniform(0, 7),
+    {Part, Rest} = split(L, N),
+    {list_to_binary(Part), fun() -> multiple_bytes_data_fun(Rest) end}.
+
+split(L, N) when length(L) =< N ->
+    {L, []};
+split(L, N) ->
+    take(N, L, []).
+
+take(0, L, Acc) ->
+    {lists:reverse(Acc), L};
+take(N, [H|L], Acc) ->
+    take(N - 1, L, [H | Acc]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/test_request.erl
----------------------------------------------------------------------
diff --git a/test/test_request.erl b/test/test_request.erl
new file mode 100644
index 0000000..68e4956
--- /dev/null
+++ b/test/test_request.erl
@@ -0,0 +1,75 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(test_request).
+
+-export([get/1, get/2, get/3]).
+-export([put/2, put/3]).
+-export([options/1, options/2, options/3]).
+-export([request/3, request/4]).
+
+get(Url) ->
+    request(get, Url, []).
+
+get(Url, Headers) ->
+    request(get, Url, Headers).
+get(Url, Headers, Opts) ->
+    request(get, Url, Headers, [], Opts).
+
+
+put(Url, Body) ->
+    request(put, Url, [], Body).
+
+put(Url, Headers, Body) ->
+    request(put, Url, Headers, Body).
+
+
+options(Url) ->
+    request(options, Url, []).
+
+options(Url, Headers) ->
+    request(options, Url, Headers).
+
+options(Url, Headers, Opts) ->
+    request(options, Url, Headers, [], Opts).
+
+
+request(Method, Url, Headers) ->
+    request(Method, Url, Headers, []).
+
+request(Method, Url, Headers, Body) ->
+    request(Method, Url, Headers, Body, [], 3).
+
+request(Method, Url, Headers, Body, Opts) ->
+    request(Method, Url, Headers, Body, Opts, 3).
+
+request(_Method, _Url, _Headers, _Body, _Opts, 0) ->
+    {error, request_failed};
+request(Method, Url, Headers, Body, Opts, N) ->
+    case code:is_loaded(ibrowse) of
+        false ->
+            {ok, _} = ibrowse:start();
+        _ ->
+            ok
+    end,
+    case ibrowse:send_req(Url, Headers, Method, Body, Opts) of
+        {ok, Code0, RespHeaders, RespBody0} ->
+            Code = list_to_integer(Code0),
+            RespBody = iolist_to_binary(RespBody0),
+            {ok, Code, RespHeaders, RespBody};
+        {error, {'EXIT', {normal, _}}} ->
+            % Connection closed right after a successful request that
+            % used the same connection.
+            request(Method, Url, Headers, Body, N - 1);
+        Error ->
+            Error
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/test_web.erl
----------------------------------------------------------------------
diff --git a/test/test_web.erl b/test/test_web.erl
new file mode 100644
index 0000000..1de2cd1
--- /dev/null
+++ b/test/test_web.erl
@@ -0,0 +1,112 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(test_web).
+-behaviour(gen_server).
+
+-include("couch_eunit.hrl").
+
+-export([start_link/0, stop/0, loop/1, get_port/0, set_assert/1, check_last/0]).
+-export([init/1, terminate/2, code_change/3]).
+-export([handle_call/3, handle_cast/2, handle_info/2]).
+
+-define(SERVER, test_web_server).
+-define(HANDLER, test_web_handler).
+-define(DELAY, 500).
+
+start_link() ->
+    gen_server:start({local, ?HANDLER}, ?MODULE, [], []),
+    mochiweb_http:start([
+        {name, ?SERVER},
+        {loop, {?MODULE, loop}},
+        {port, 0}
+    ]).
+
+loop(Req) ->
+    %?debugFmt("Handling request: ~p", [Req]),
+    case gen_server:call(?HANDLER, {check_request, Req}) of
+        {ok, RespInfo} ->
+            {ok, Req:respond(RespInfo)};
+        {raw, {Status, Headers, BodyChunks}} ->
+            Resp = Req:start_response({Status, Headers}),
+            lists:foreach(fun(C) -> Resp:send(C) end, BodyChunks),
+            erlang:put(mochiweb_request_force_close, true),
+            {ok, Resp};
+        {chunked, {Status, Headers, BodyChunks}} ->
+            Resp = Req:respond({Status, Headers, chunked}),
+            timer:sleep(?DELAY),
+            lists:foreach(fun(C) -> Resp:write_chunk(C) end, BodyChunks),
+            Resp:write_chunk([]),
+            {ok, Resp};
+        {error, Reason} ->
+            ?debugFmt("Error: ~p", [Reason]),
+            Body = lists:flatten(io_lib:format("Error: ~p", [Reason])),
+            {ok, Req:respond({200, [], Body})}
+    end.
+
+get_port() ->
+    mochiweb_socket_server:get(?SERVER, port).
+
+set_assert(Fun) ->
+    ?assertEqual(ok, gen_server:call(?HANDLER, {set_assert, Fun})).
+
+check_last() ->
+    gen_server:call(?HANDLER, last_status).
+
+init(_) ->
+    {ok, nil}.
+
+terminate(_Reason, _State) ->
+    ok.
+
+stop() ->
+    gen_server:cast(?SERVER, stop).
+
+
+handle_call({check_request, Req}, _From, State) when is_function(State, 1) ->
+    Resp2 = case (catch State(Req)) of
+        {ok, Resp} ->
+            {reply, {ok, Resp}, was_ok};
+        {raw, Resp} ->
+            {reply, {raw, Resp}, was_ok};
+        {chunked, Resp} ->
+            {reply, {chunked, Resp}, was_ok};
+        Error ->
+            {reply, {error, Error}, not_ok}
+    end,
+    Req:cleanup(),
+    Resp2;
+handle_call({check_request, _Req}, _From, _State) ->
+    {reply, {error, no_assert_function}, not_ok};
+handle_call(last_status, _From, State) when is_atom(State) ->
+    {reply, State, nil};
+handle_call(last_status, _From, State) ->
+    {reply, {error, not_checked}, State};
+handle_call({set_assert, Fun}, _From, nil) ->
+    {reply, ok, Fun};
+handle_call({set_assert, _}, _From, State) ->
+    {reply, {error, assert_function_set}, State};
+handle_call(Msg, _From, State) ->
+    {reply, {ignored, Msg}, State}.
+
+handle_cast(stop, State) ->
+    {stop, normal, State};
+handle_cast(Msg, State) ->
+    ?debugFmt("Ignoring cast message: ~p", [Msg]),
+    {noreply, State}.
+
+handle_info(Msg, State) ->
+    ?debugFmt("Ignoring info message: ~p", [Msg]),
+    {noreply, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.


[42/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Disable problematic tests


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/c3f38073
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/c3f38073
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/c3f38073

Branch: refs/heads/1963-eunit-bigcouch
Commit: c3f38073f3dc46aa48024cf8b633d98572c7f238
Parents: f49e1a6
Author: Russell Branca <ch...@apache.org>
Authored: Tue Aug 26 10:54:22 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 13:49:09 2014 -0700

----------------------------------------------------------------------
 test/couch_auth_cache_tests.erl         | 4 ++++
 test/couch_changes_tests.erl            | 4 ++++
 test/couch_doc_json_tests.erl           | 5 +++++
 test/couch_stats_tests.erl              | 4 ++++
 test/couch_uuids_tests.erl              | 4 ++++
 test/couchdb_attachments_tests.erl      | 4 ++++
 test/couchdb_compaction_daemon.erl      | 4 ++++
 test/couchdb_file_compression_tests.erl | 4 ++++
 test/couchdb_http_proxy_tests.erl       | 5 +++++
 test/couchdb_os_daemons_tests.erl       | 4 ++++
 test/couchdb_os_proc_pool.erl           | 4 ++++
 test/couchdb_vhosts_tests.erl           | 4 ++++
 test/couchdb_views_tests.erl            | 4 ++++
 13 files changed, 54 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couch_auth_cache_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_auth_cache_tests.erl b/test/couch_auth_cache_tests.erl
index afad552..d8841bd 100644
--- a/test/couch_auth_cache_tests.erl
+++ b/test/couch_auth_cache_tests.erl
@@ -19,6 +19,8 @@
 -define(TIMEOUT, 1000).
 
 
+-ifdef(run_broken_tests).
+
 setup() ->
     DbName = ?tempdb(),
     config:set("couch_httpd_auth", "authentication_db",
@@ -221,3 +223,5 @@ full_commit(DbName) ->
     {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
     {ok, _} = couch_db:ensure_full_commit(AuthDb),
     ok = couch_db:close(AuthDb).
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couch_changes_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_changes_tests.erl b/test/couch_changes_tests.erl
index 64754fa..7122035 100644
--- a/test/couch_changes_tests.erl
+++ b/test/couch_changes_tests.erl
@@ -25,6 +25,8 @@
 }).
 
 
+-ifdef(run_broken_tests).
+
 setup() ->
     DbName = ?tempdb(),
     {ok, Db} = create_db(DbName),
@@ -595,3 +597,5 @@ create_db(DbName) ->
 
 delete_db(DbName) ->
     ok = couch_server:delete(DbName, [?ADMIN_USER]).
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couch_doc_json_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_doc_json_tests.erl b/test/couch_doc_json_tests.erl
index 64dea96..20010ac 100644
--- a/test/couch_doc_json_tests.erl
+++ b/test/couch_doc_json_tests.erl
@@ -16,6 +16,8 @@
 -include_lib("couch/include/couch_db.hrl").
 
 
+-ifdef(run_broken_tests).
+
 setup() ->
     {ok, Pid} = test_util:start_config(?CONFIG_CHAIN),
     config:set("attachments", "compression_level", "0", false),
@@ -389,3 +391,6 @@ to_json_success_cases() ->
         ({Options, Doc, EJson, Msg}) ->
             {Msg, ?_assertMatch(EJson, couch_doc:to_json_obj(Doc, Options))}
     end, Cases).
+
+-endif.
+

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couch_stats_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_stats_tests.erl b/test/couch_stats_tests.erl
index d62afaf..fd4c8e1 100644
--- a/test/couch_stats_tests.erl
+++ b/test/couch_stats_tests.erl
@@ -23,6 +23,8 @@
 -define(TIMEWAIT, 500).
 
 
+-ifdef(run_broken_tests).
+
 setup_collector() ->
     couch_stats_collector:start(),
     ok.
@@ -410,3 +412,5 @@ make_agg(Desc, Sum, Mean, StdDev, Min, Max) ->
         {min, Min},
         {max, Max}
     ]}.
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couch_uuids_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_uuids_tests.erl b/test/couch_uuids_tests.erl
index 27c5cb8..6d91b4c 100644
--- a/test/couch_uuids_tests.erl
+++ b/test/couch_uuids_tests.erl
@@ -17,6 +17,8 @@
 -define(TIMEOUT_S, 20).
 
 
+-ifdef(run_broken_tests).
+
 setup() ->
     {ok, Pid} = config:start_link(?CONFIG_CHAIN),
     erlang:monitor(process, Pid),
@@ -159,3 +161,5 @@ test_same_suffix(N, Suffix) ->
         Suffix -> test_same_suffix(N - 1, Suffix);
         _ -> false
     end.
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couchdb_attachments_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_attachments_tests.erl b/test/couchdb_attachments_tests.erl
index b203a82..78f9f49 100644
--- a/test/couchdb_attachments_tests.erl
+++ b/test/couchdb_attachments_tests.erl
@@ -26,6 +26,8 @@
 -define(i2l(I), integer_to_list(I)).
 
 
+-ifdef(run_broken_tests).
+
 start() ->
     ok = test_util:start_couch(),
     % ensure in default compression settings for attachments_compression_tests
@@ -626,3 +628,5 @@ gzip(Data) ->
     ok = zlib:deflateEnd(Z),
     ok = zlib:close(Z),
     Last.
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couchdb_compaction_daemon.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_compaction_daemon.erl b/test/couchdb_compaction_daemon.erl
index 0d2195b..69b7f56 100644
--- a/test/couchdb_compaction_daemon.erl
+++ b/test/couchdb_compaction_daemon.erl
@@ -20,6 +20,8 @@
 -define(TIMEOUT_S, ?TIMEOUT div 1000).
 
 
+-ifdef(run_broken_tests).
+
 start() ->
     ok = test_util:start_couch(),
     config:set("compaction_daemon", "check_interval", "3", false),
@@ -218,3 +220,5 @@ wait_loop(DbName, Parent) ->
             ok = timer:sleep(?DELAY),
             wait_loop(DbName, Parent)
     end.
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couchdb_file_compression_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_file_compression_tests.erl b/test/couchdb_file_compression_tests.erl
index 8f590b9..d140474 100644
--- a/test/couchdb_file_compression_tests.erl
+++ b/test/couchdb_file_compression_tests.erl
@@ -20,6 +20,8 @@
 -define(TIMEOUT, 30000).
 
 
+-ifdef(run_broken_tests).
+
 setup() ->
     config:set("couchdb", "file_compression", "none", false),
     DbName = ?tempdb(),
@@ -222,3 +224,5 @@ view_disk_size(DbName) ->
     {ok, Info} = couch_mrview:get_info(Db, DDoc),
     ok = couch_db:close(Db),
     couch_util:get_value(disk_size, Info).
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couchdb_http_proxy_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_http_proxy_tests.erl b/test/couchdb_http_proxy_tests.erl
index cee6a02..28ac02a 100644
--- a/test/couchdb_http_proxy_tests.erl
+++ b/test/couchdb_http_proxy_tests.erl
@@ -27,6 +27,8 @@
 -define(TIMEOUT, 5000).
 
 
+-ifdef(run_broken_tests).
+
 start() ->
     % we have to write any config changes to temp ini file to not loose them
     % when supervisor will kill all children due to reaching restart threshold
@@ -456,3 +458,6 @@ recv_body(ReqId, Acc) ->
     after ?TIMEOUT ->
         throw({error, timeout})
     end.
+
+-endif.
+

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couchdb_os_daemons_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_os_daemons_tests.erl b/test/couchdb_os_daemons_tests.erl
index 3eeb7d7..6aea39c 100644
--- a/test/couchdb_os_daemons_tests.erl
+++ b/test/couchdb_os_daemons_tests.erl
@@ -36,6 +36,8 @@
 -define(TIMEOUT, 1000).
 
 
+-ifdef(run_broken_tests).
+
 setup(DName) ->
     {ok, CfgPid} = config:start_link(?CONFIG_CHAIN),
     {ok, OsDPid} = couch_os_daemons:start_link(),
@@ -226,3 +228,5 @@ check_dead(D, Name) ->
     ?assertEqual(halted, D#daemon.status),
     ?assertEqual(nil, D#daemon.errors),
     ?assertEqual(nil, D#daemon.buf).
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couchdb_os_proc_pool.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_os_proc_pool.erl b/test/couchdb_os_proc_pool.erl
index a8ab752..7f245e8 100644
--- a/test/couchdb_os_proc_pool.erl
+++ b/test/couchdb_os_proc_pool.erl
@@ -18,6 +18,8 @@
 -define(TIMEOUT, 3000).
 
 
+-ifdef(run_broken_tests).
+
 start() ->
     ok = test_util:start_couch(),
     config:set("query_server_config", "os_process_limit", "3", false),
@@ -167,3 +169,5 @@ loop(Parent, Ref, Proc) ->
             Parent ! {die, Ref},
             exit(some_error)
     end.
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couchdb_vhosts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_vhosts_tests.erl b/test/couchdb_vhosts_tests.erl
index 5eb266c..3325219 100644
--- a/test/couchdb_vhosts_tests.erl
+++ b/test/couchdb_vhosts_tests.erl
@@ -19,6 +19,8 @@
 -define(iofmt(S, A), lists:flatten(io_lib:format(S, A))).
 
 
+-ifdef(run_broken_tests).
+
 setup() ->
     DbName = ?tempdb(),
     {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
@@ -424,3 +426,5 @@ should_fail_oauth_with_wrong_credentials({Url, _}) ->
                               {reason, ?iofmt("Request failed: ~p", [Else])}]})
         end
     end).
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c3f38073/test/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_views_tests.erl b/test/couchdb_views_tests.erl
index 012d12e..fd6a3b9 100644
--- a/test/couchdb_views_tests.erl
+++ b/test/couchdb_views_tests.erl
@@ -20,6 +20,8 @@
 -define(TIMEOUT, 1000).
 
 
+-ifdef(run_broken_tests).
+
 setup() ->
     DbName = ?tempdb(),
     {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
@@ -652,3 +654,5 @@ read_header(File) ->
     {ok, {_Sig, Header}} = couch_file:read_header(Fd),
     couch_file:close(Fd),
     Header.
+
+-endif.


[24/42] Move files out of test/couchdb into top level test/ folder

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_views_tests.erl b/test/couchdb/couchdb_views_tests.erl
deleted file mode 100644
index 6d81f32..0000000
--- a/test/couchdb/couchdb_views_tests.erl
+++ /dev/null
@@ -1,669 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_views_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
--include_lib("couch_mrview/include/couch_mrview.hrl").
-
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
--define(DELAY, 100).
--define(TIMEOUT, 1000).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-setup() ->
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
-    ok = couch_db:close(Db),
-    FooRev = create_design_doc(DbName, <<"_design/foo">>, <<"bar">>),
-    query_view(DbName, "foo", "bar"),
-    BooRev = create_design_doc(DbName, <<"_design/boo">>, <<"baz">>),
-    query_view(DbName, "boo", "baz"),
-    {DbName, {FooRev, BooRev}}.
-
-setup_with_docs() ->
-    DbName = ?tempdb(),
-    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
-    ok = couch_db:close(Db),
-    create_docs(DbName),
-    create_design_doc(DbName, <<"_design/foo">>, <<"bar">>),
-    DbName.
-
-teardown({DbName, _}) ->
-    teardown(DbName);
-teardown(DbName) when is_binary(DbName) ->
-    couch_server:delete(DbName, [?ADMIN_USER]),
-    ok.
-
-
-view_indexes_cleanup_test_() ->
-    {
-        "View indexes cleanup",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            {
-                foreach,
-                fun setup/0, fun teardown/1,
-                [
-                    fun should_have_two_indexes_alive_before_deletion/1,
-                    fun should_cleanup_index_file_after_ddoc_deletion/1,
-                    fun should_cleanup_all_index_files/1
-                ]
-            }
-        }
-    }.
-
-view_group_db_leaks_test_() ->
-    {
-        "View group db leaks",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            {
-                foreach,
-                fun setup_with_docs/0, fun teardown/1,
-                [
-                    fun couchdb_1138/1,
-                    fun couchdb_1309/1
-                ]
-            }
-        }
-    }.
-
-view_group_shutdown_test_() ->
-    {
-        "View group shutdown",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            [couchdb_1283()]
-        }
-    }.
-
-
-should_not_remember_docs_in_index_after_backup_restore_test() ->
-    %% COUCHDB-640
-    start(),
-    DbName = setup_with_docs(),
-
-    ok = backup_db_file(DbName),
-    create_doc(DbName, "doc666"),
-
-    Rows0 = query_view(DbName, "foo", "bar"),
-    ?assert(has_doc("doc1", Rows0)),
-    ?assert(has_doc("doc2", Rows0)),
-    ?assert(has_doc("doc3", Rows0)),
-    ?assert(has_doc("doc666", Rows0)),
-
-    restore_backup_db_file(DbName),
-
-    Rows1 = query_view(DbName, "foo", "bar"),
-    ?assert(has_doc("doc1", Rows1)),
-    ?assert(has_doc("doc2", Rows1)),
-    ?assert(has_doc("doc3", Rows1)),
-    ?assertNot(has_doc("doc666", Rows1)),
-
-    teardown(DbName),
-    stop(whereis(couch_server_sup)).
-
-
-should_upgrade_legacy_view_files_test() ->
-    start(),
-
-    ok = couch_config:set("query_server_config", "commit_freq", "0", false),
-
-    DbName = <<"test">>,
-    DbFileName = "test.couch",
-    DbFilePath = filename:join([?FIXTURESDIR, DbFileName]),
-    OldViewName = "3b835456c235b1827e012e25666152f3.view",
-    FixtureViewFilePath = filename:join([?FIXTURESDIR, OldViewName]),
-    NewViewName = "a1c5929f912aca32f13446122cc6ce50.view",
-
-    DbDir = couch_config:get("couchdb", "database_dir"),
-    ViewDir = couch_config:get("couchdb", "view_index_dir"),
-    OldViewFilePath = filename:join([ViewDir, ".test_design", OldViewName]),
-    NewViewFilePath = filename:join([ViewDir, ".test_design", "mrview",
-                                     NewViewName]),
-
-    % cleanup
-    Files = [
-        filename:join([DbDir, DbFileName]),
-        OldViewFilePath,
-        NewViewFilePath
-    ],
-    lists:foreach(fun(File) -> file:delete(File) end, Files),
-
-    % copy old db file into db dir
-    {ok, _} = file:copy(DbFilePath, filename:join([DbDir, DbFileName])),
-
-    % copy old view file into view dir
-    ok = filelib:ensure_dir(filename:join([ViewDir, ".test_design"])),
-    {ok, _} = file:copy(FixtureViewFilePath, OldViewFilePath),
-
-    % ensure old header
-    OldHeader = read_header(OldViewFilePath),
-    ?assertMatch(#index_header{}, OldHeader),
-
-    % query view for expected results
-    Rows0 = query_view(DbName, "test", "test"),
-    ?assertEqual(2, length(Rows0)),
-
-    % ensure old file gone
-    ?assertNot(filelib:is_regular(OldViewFilePath)),
-
-    % add doc to trigger update
-    DocUrl = db_url(DbName) ++ "/boo",
-    {ok, _, _, _} = test_request:put(
-        DocUrl, [{"Content-Type", "application/json"}], <<"{\"a\":3}">>),
-
-    % query view for expected results
-    Rows1 = query_view(DbName, "test", "test"),
-    ?assertEqual(3, length(Rows1)),
-
-    % ensure new header
-    timer:sleep(2000),  % have to wait for awhile to upgrade the index
-    NewHeader = read_header(NewViewFilePath),
-    ?assertMatch(#mrheader{}, NewHeader),
-
-    teardown(DbName),
-    stop(whereis(couch_server_sup)).
-
-
-should_have_two_indexes_alive_before_deletion({DbName, _}) ->
-    view_cleanup(DbName),
-    ?_assertEqual(2, count_index_files(DbName)).
-
-should_cleanup_index_file_after_ddoc_deletion({DbName, {FooRev, _}}) ->
-    delete_design_doc(DbName, <<"_design/foo">>, FooRev),
-    view_cleanup(DbName),
-    ?_assertEqual(1, count_index_files(DbName)).
-
-should_cleanup_all_index_files({DbName, {FooRev, BooRev}})->
-    delete_design_doc(DbName, <<"_design/foo">>, FooRev),
-    delete_design_doc(DbName, <<"_design/boo">>, BooRev),
-    view_cleanup(DbName),
-    ?_assertEqual(0, count_index_files(DbName)).
-
-couchdb_1138(DbName) ->
-    ?_test(begin
-        {ok, IndexerPid} = couch_index_server:get_index(
-            couch_mrview_index, DbName, <<"_design/foo">>),
-        ?assert(is_pid(IndexerPid)),
-        ?assert(is_process_alive(IndexerPid)),
-        ?assertEqual(2, count_db_refs(DbName)),
-
-        Rows0 = query_view(DbName, "foo", "bar"),
-        ?assertEqual(3, length(Rows0)),
-        ?assertEqual(2, count_db_refs(DbName)),
-        ?assert(is_process_alive(IndexerPid)),
-
-        create_doc(DbName, "doc1000"),
-        Rows1 = query_view(DbName, "foo", "bar"),
-        ?assertEqual(4, length(Rows1)),
-        ?assertEqual(2, count_db_refs(DbName)),
-        ?assert(is_process_alive(IndexerPid)),
-
-        Ref1 = get_db_ref_counter(DbName),
-        compact_db(DbName),
-        Ref2 = get_db_ref_counter(DbName),
-        ?assertEqual(2, couch_ref_counter:count(Ref2)),
-        ?assertNotEqual(Ref2, Ref1),
-        ?assertNot(is_process_alive(Ref1)),
-        ?assert(is_process_alive(IndexerPid)),
-
-        compact_view_group(DbName, "foo"),
-        ?assertEqual(2, count_db_refs(DbName)),
-        Ref3 = get_db_ref_counter(DbName),
-        ?assertEqual(Ref3, Ref2),
-        ?assert(is_process_alive(IndexerPid)),
-
-        create_doc(DbName, "doc1001"),
-        Rows2 = query_view(DbName, "foo", "bar"),
-        ?assertEqual(5, length(Rows2)),
-        ?assertEqual(2, count_db_refs(DbName)),
-        ?assert(is_process_alive(IndexerPid))
-    end).
-
-couchdb_1309(DbName) ->
-    ?_test(begin
-        {ok, IndexerPid} = couch_index_server:get_index(
-            couch_mrview_index, DbName, <<"_design/foo">>),
-        ?assert(is_pid(IndexerPid)),
-        ?assert(is_process_alive(IndexerPid)),
-        ?assertEqual(2, count_db_refs(DbName)),
-
-        create_doc(DbName, "doc1001"),
-        Rows0 = query_view(DbName, "foo", "bar"),
-        check_rows_value(Rows0, null),
-        ?assertEqual(4, length(Rows0)),
-        ?assertEqual(2, count_db_refs(DbName)),
-        ?assert(is_process_alive(IndexerPid)),
-
-        update_design_doc(DbName,  <<"_design/foo">>, <<"bar">>),
-        {ok, NewIndexerPid} = couch_index_server:get_index(
-            couch_mrview_index, DbName, <<"_design/foo">>),
-        ?assert(is_pid(NewIndexerPid)),
-        ?assert(is_process_alive(NewIndexerPid)),
-        ?assertNotEqual(IndexerPid, NewIndexerPid),
-        ?assertEqual(2, count_db_refs(DbName)),
-
-        Rows1 = query_view(DbName, "foo", "bar", ok),
-        ?assertEqual(0, length(Rows1)),
-        Rows2 = query_view(DbName, "foo", "bar"),
-        check_rows_value(Rows2, 1),
-        ?assertEqual(4, length(Rows2)),
-
-        MonRef0 = erlang:monitor(process, IndexerPid),
-        receive
-            {'DOWN', MonRef0, _, _, _} ->
-                ok
-        after ?TIMEOUT ->
-            erlang:error(
-                {assertion_failed,
-                 [{module, ?MODULE}, {line, ?LINE},
-                  {reason, "old view group is not dead after ddoc update"}]})
-        end,
-
-        MonRef1 = erlang:monitor(process, NewIndexerPid),
-        ok = couch_server:delete(DbName, [?ADMIN_USER]),
-        receive
-            {'DOWN', MonRef1, _, _, _} ->
-                ok
-        after ?TIMEOUT ->
-            erlang:error(
-                {assertion_failed,
-                 [{module, ?MODULE}, {line, ?LINE},
-                  {reason, "new view group did not die after DB deletion"}]})
-        end
-    end).
-
-couchdb_1283() ->
-    ?_test(begin
-        ok = couch_config:set("couchdb", "max_dbs_open", "3", false),
-        ok = couch_config:set("couchdb", "delayed_commits", "false", false),
-
-        {ok, MDb1} = couch_db:create(?tempdb(), [?ADMIN_USER]),
-        DDoc = couch_doc:from_json_obj({[
-            {<<"_id">>, <<"_design/foo">>},
-            {<<"language">>, <<"javascript">>},
-            {<<"views">>, {[
-                {<<"foo">>, {[
-                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
-                ]}},
-                {<<"foo2">>, {[
-                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
-                ]}},
-                {<<"foo3">>, {[
-                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
-                ]}},
-                {<<"foo4">>, {[
-                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
-                ]}},
-                {<<"foo5">>, {[
-                    {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
-                ]}}
-            ]}}
-        ]}),
-        {ok, _} = couch_db:update_doc(MDb1, DDoc, []),
-        ok = populate_db(MDb1, 100, 100),
-        query_view(MDb1#db.name, "foo", "foo"),
-        ok = couch_db:close(MDb1),
-
-        {ok, Db1} = couch_db:create(?tempdb(), [?ADMIN_USER]),
-        ok = couch_db:close(Db1),
-        {ok, Db2} = couch_db:create(?tempdb(), [?ADMIN_USER]),
-        ok = couch_db:close(Db2),
-        {ok, Db3} = couch_db:create(?tempdb(), [?ADMIN_USER]),
-        ok = couch_db:close(Db3),
-
-        Writer1 = spawn_writer(Db1#db.name),
-        Writer2 = spawn_writer(Db2#db.name),
-
-        ?assert(is_process_alive(Writer1)),
-        ?assert(is_process_alive(Writer2)),
-
-        ?assertEqual(ok, get_writer_status(Writer1)),
-        ?assertEqual(ok, get_writer_status(Writer2)),
-
-        {ok, MonRef} = couch_mrview:compact(MDb1#db.name, <<"_design/foo">>,
-                                            [monitor]),
-
-        Writer3 = spawn_writer(Db3#db.name),
-        ?assert(is_process_alive(Writer3)),
-        ?assertEqual({error, all_dbs_active}, get_writer_status(Writer3)),
-
-        ?assert(is_process_alive(Writer1)),
-        ?assert(is_process_alive(Writer2)),
-        ?assert(is_process_alive(Writer3)),
-
-        receive
-            {'DOWN', MonRef, process, _, Reason} ->
-                ?assertEqual(normal, Reason)
-        after ?TIMEOUT ->
-            erlang:error(
-                {assertion_failed,
-                 [{module, ?MODULE}, {line, ?LINE},
-                  {reason, "Failure compacting view group"}]})
-        end,
-
-        ?assertEqual(ok, writer_try_again(Writer3)),
-        ?assertEqual(ok, get_writer_status(Writer3)),
-
-        ?assert(is_process_alive(Writer1)),
-        ?assert(is_process_alive(Writer2)),
-        ?assert(is_process_alive(Writer3)),
-
-        ?assertEqual(ok, stop_writer(Writer1)),
-        ?assertEqual(ok, stop_writer(Writer2)),
-        ?assertEqual(ok, stop_writer(Writer3))
-    end).
-
-create_doc(DbName, DocId) when is_list(DocId) ->
-    create_doc(DbName, ?l2b(DocId));
-create_doc(DbName, DocId) when is_binary(DocId) ->
-    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
-    Doc666 = couch_doc:from_json_obj({[
-        {<<"_id">>, DocId},
-        {<<"value">>, 999}
-    ]}),
-    {ok, _} = couch_db:update_docs(Db, [Doc666]),
-    couch_db:ensure_full_commit(Db),
-    couch_db:close(Db).
-
-create_docs(DbName) ->
-    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
-    Doc1 = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"doc1">>},
-        {<<"value">>, 1}
-
-    ]}),
-    Doc2 = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"doc2">>},
-        {<<"value">>, 2}
-
-    ]}),
-    Doc3 = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"doc3">>},
-        {<<"value">>, 3}
-
-    ]}),
-    {ok, _} = couch_db:update_docs(Db, [Doc1, Doc2, Doc3]),
-    couch_db:ensure_full_commit(Db),
-    couch_db:close(Db).
-
-populate_db(Db, BatchSize, N) when N > 0 ->
-    Docs = lists:map(
-        fun(_) ->
-            couch_doc:from_json_obj({[
-                {<<"_id">>, couch_uuids:new()},
-                {<<"value">>, base64:encode(crypto:rand_bytes(1000))}
-            ]})
-        end,
-        lists:seq(1, BatchSize)),
-    {ok, _} = couch_db:update_docs(Db, Docs, []),
-    populate_db(Db, BatchSize, N - length(Docs));
-populate_db(_Db, _, _) ->
-    ok.
-
-create_design_doc(DbName, DDName, ViewName) ->
-    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
-    DDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, DDName},
-        {<<"language">>, <<"javascript">>},
-        {<<"views">>, {[
-            {ViewName, {[
-                {<<"map">>, <<"function(doc) { emit(doc.value, null); }">>}
-            ]}}
-        ]}}
-    ]}),
-    {ok, Rev} = couch_db:update_doc(Db, DDoc, []),
-    couch_db:ensure_full_commit(Db),
-    couch_db:close(Db),
-    Rev.
-
-update_design_doc(DbName, DDName, ViewName) ->
-    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
-    {ok, Doc} = couch_db:open_doc(Db, DDName, [?ADMIN_USER]),
-    {Props} = couch_doc:to_json_obj(Doc, []),
-    Rev = couch_util:get_value(<<"_rev">>, Props),
-    DDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, DDName},
-        {<<"_rev">>, Rev},
-        {<<"language">>, <<"javascript">>},
-        {<<"views">>, {[
-            {ViewName, {[
-                {<<"map">>, <<"function(doc) { emit(doc.value, 1); }">>}
-            ]}}
-        ]}}
-    ]}),
-    {ok, NewRev} = couch_db:update_doc(Db, DDoc, [?ADMIN_USER]),
-    couch_db:ensure_full_commit(Db),
-    couch_db:close(Db),
-    NewRev.
-
-delete_design_doc(DbName, DDName, Rev) ->
-    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
-    DDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, DDName},
-        {<<"_rev">>, couch_doc:rev_to_str(Rev)},
-        {<<"_deleted">>, true}
-    ]}),
-    {ok, _} = couch_db:update_doc(Db, DDoc, [Rev]),
-    couch_db:close(Db).
-
-db_url(DbName) ->
-    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
-    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
-    "http://" ++ Addr ++ ":" ++ Port ++ "/" ++ ?b2l(DbName).
-
-query_view(DbName, DDoc, View) ->
-    query_view(DbName, DDoc, View, false).
-
-query_view(DbName, DDoc, View, Stale) ->
-    {ok, Code, _Headers, Body} = test_request:get(
-        db_url(DbName) ++ "/_design/" ++ DDoc ++ "/_view/" ++ View
-        ++ case Stale of
-               false -> [];
-               _ -> "?stale=" ++ atom_to_list(Stale)
-           end),
-    ?assertEqual(200, Code),
-    {Props} = ejson:decode(Body),
-    couch_util:get_value(<<"rows">>, Props, []).
-
-check_rows_value(Rows, Value) ->
-    lists:foreach(
-        fun({Row}) ->
-            ?assertEqual(Value, couch_util:get_value(<<"value">>, Row))
-        end, Rows).
-
-view_cleanup(DbName) ->
-    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
-    couch_mrview:cleanup(Db),
-    couch_db:close(Db).
-
-get_db_ref_counter(DbName) ->
-    {ok, #db{fd_ref_counter = Ref} = Db} = couch_db:open_int(DbName, []),
-    ok = couch_db:close(Db),
-    Ref.
-
-count_db_refs(DbName) ->
-    Ref = get_db_ref_counter(DbName),
-    % have to sleep a bit to let couchdb cleanup all refs and leave only
-    % active ones. otherwise the related tests will randomly fail due to
-    % count number mismatch
-    timer:sleep(200),
-    couch_ref_counter:count(Ref).
-
-count_index_files(DbName) ->
-    % call server to fetch the index files
-    RootDir = couch_config:get("couchdb", "view_index_dir"),
-    length(filelib:wildcard(RootDir ++ "/." ++
-        binary_to_list(DbName) ++ "_design"++"/mrview/*")).
-
-has_doc(DocId1, Rows) ->
-    DocId = iolist_to_binary(DocId1),
-    lists:any(fun({R}) -> lists:member({<<"id">>, DocId}, R) end, Rows).
-
-backup_db_file(DbName) ->
-    DbDir = couch_config:get("couchdb", "database_dir"),
-    DbFile = filename:join([DbDir, ?b2l(DbName) ++ ".couch"]),
-    {ok, _} = file:copy(DbFile, DbFile ++ ".backup"),
-    ok.
-
-restore_backup_db_file(DbName) ->
-    DbDir = couch_config:get("couchdb", "database_dir"),
-    stop(whereis(couch_server_sup)),
-    DbFile = filename:join([DbDir, ?b2l(DbName) ++ ".couch"]),
-    ok = file:delete(DbFile),
-    ok = file:rename(DbFile ++ ".backup", DbFile),
-    start(),
-    ok.
-
-compact_db(DbName) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    {ok, _} = couch_db:start_compact(Db),
-    ok = couch_db:close(Db),
-    wait_db_compact_done(DbName, 10).
-
-wait_db_compact_done(_DbName, 0) ->
-    erlang:error({assertion_failed,
-                  [{module, ?MODULE},
-                   {line, ?LINE},
-                   {reason, "DB compaction failed to finish"}]});
-wait_db_compact_done(DbName, N) ->
-    {ok, Db} = couch_db:open_int(DbName, []),
-    ok = couch_db:close(Db),
-    case is_pid(Db#db.compactor_pid) of
-    false ->
-        ok;
-    true ->
-        ok = timer:sleep(?DELAY),
-        wait_db_compact_done(DbName, N - 1)
-    end.
-
-compact_view_group(DbName, DDocId) when is_list(DDocId) ->
-    compact_view_group(DbName, ?l2b("_design/" ++ DDocId));
-compact_view_group(DbName, DDocId) when is_binary(DDocId) ->
-    ok = couch_mrview:compact(DbName, DDocId),
-    wait_view_compact_done(DbName, DDocId, 10).
-
-wait_view_compact_done(_DbName, _DDocId, 0) ->
-    erlang:error({assertion_failed,
-                  [{module, ?MODULE},
-                   {line, ?LINE},
-                   {reason, "DB compaction failed to finish"}]});
-wait_view_compact_done(DbName, DDocId, N) ->
-    {ok, Code, _Headers, Body} = test_request:get(
-        db_url(DbName) ++ "/" ++ ?b2l(DDocId) ++ "/_info"),
-    ?assertEqual(200, Code),
-    {Info} = ejson:decode(Body),
-    {IndexInfo} = couch_util:get_value(<<"view_index">>, Info),
-    CompactRunning = couch_util:get_value(<<"compact_running">>, IndexInfo),
-    case CompactRunning of
-        false ->
-            ok;
-        true ->
-            ok = timer:sleep(?DELAY),
-            wait_view_compact_done(DbName, DDocId, N - 1)
-    end.
-
-spawn_writer(DbName) ->
-    Parent = self(),
-    spawn(fun() ->
-        process_flag(priority, high),
-        writer_loop(DbName, Parent)
-    end).
-
-get_writer_status(Writer) ->
-    Ref = make_ref(),
-    Writer ! {get_status, Ref},
-    receive
-        {db_open, Ref} ->
-            ok;
-        {db_open_error, Error, Ref} ->
-            Error
-    after ?TIMEOUT ->
-        timeout
-    end.
-
-writer_try_again(Writer) ->
-    Ref = make_ref(),
-    Writer ! {try_again, Ref},
-    receive
-        {ok, Ref} ->
-            ok
-    after ?TIMEOUT ->
-        timeout
-    end.
-
-stop_writer(Writer) ->
-    Ref = make_ref(),
-    Writer ! {stop, Ref},
-    receive
-        {ok, Ref} ->
-            ok
-    after ?TIMEOUT ->
-        erlang:error({assertion_failed,
-                      [{module, ?MODULE},
-                       {line, ?LINE},
-                       {reason, "Timeout on stopping process"}]})
-    end.
-
-writer_loop(DbName, Parent) ->
-    case couch_db:open_int(DbName, []) of
-        {ok, Db} ->
-            writer_loop_1(Db, Parent);
-        Error ->
-            writer_loop_2(DbName, Parent, Error)
-    end.
-
-writer_loop_1(Db, Parent) ->
-    receive
-        {get_status, Ref} ->
-            Parent ! {db_open, Ref},
-            writer_loop_1(Db, Parent);
-        {stop, Ref} ->
-            ok = couch_db:close(Db),
-            Parent ! {ok, Ref}
-    end.
-
-writer_loop_2(DbName, Parent, Error) ->
-    receive
-        {get_status, Ref} ->
-            Parent ! {db_open_error, Error, Ref},
-            writer_loop_2(DbName, Parent, Error);
-        {try_again, Ref} ->
-            Parent ! {ok, Ref},
-            writer_loop(DbName, Parent)
-    end.
-
-read_header(File) ->
-    {ok, Fd} = couch_file:open(File),
-    {ok, {_Sig, Header}} = couch_file:read_header(Fd),
-    couch_file:close(Fd),
-    Header.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/3b835456c235b1827e012e25666152f3.view
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/3b835456c235b1827e012e25666152f3.view b/test/couchdb/fixtures/3b835456c235b1827e012e25666152f3.view
deleted file mode 100644
index 9c67648..0000000
Binary files a/test/couchdb/fixtures/3b835456c235b1827e012e25666152f3.view and /dev/null differ

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/couch_stats_aggregates.cfg
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/couch_stats_aggregates.cfg b/test/couchdb/fixtures/couch_stats_aggregates.cfg
deleted file mode 100644
index 30e475d..0000000
--- a/test/couchdb/fixtures/couch_stats_aggregates.cfg
+++ /dev/null
@@ -1,19 +0,0 @@
-% Licensed to the Apache Software Foundation (ASF) under one
-% or more contributor license agreements.  See the NOTICE file
-% distributed with this work for additional information
-% regarding copyright ownership.  The ASF licenses this file
-% to you under the Apache License, Version 2.0 (the
-% "License"); you may not use this file except in compliance
-% with the License.  You may obtain a copy of the License at
-% 
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing,
-% software distributed under the License is distributed on an
-% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-% KIND, either express or implied.  See the License for the
-% specific language governing permissions and limitations
-% under the License.
-
-{testing, stuff, "yay description"}.
-{number, '11', "randomosity"}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/couch_stats_aggregates.ini
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/couch_stats_aggregates.ini b/test/couchdb/fixtures/couch_stats_aggregates.ini
deleted file mode 100644
index cc5cd21..0000000
--- a/test/couchdb/fixtures/couch_stats_aggregates.ini
+++ /dev/null
@@ -1,20 +0,0 @@
-; Licensed to the Apache Software Foundation (ASF) under one
-; or more contributor license agreements.  See the NOTICE file
-; distributed with this work for additional information
-; regarding copyright ownership.  The ASF licenses this file
-; to you under the Apache License, Version 2.0 (the
-; "License"); you may not use this file except in compliance
-; with the License.  You may obtain a copy of the License at
-; 
-;   http://www.apache.org/licenses/LICENSE-2.0
-;
-; Unless required by applicable law or agreed to in writing,
-; software distributed under the License is distributed on an
-; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-; KIND, either express or implied.  See the License for the
-; specific language governing permissions and limitations
-; under the License.
-
-[stats]
-rate = 10000000 ; We call collect_sample in testing
-samples = [0, 1]

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/logo.png
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/logo.png b/test/couchdb/fixtures/logo.png
deleted file mode 100644
index d21ac02..0000000
Binary files a/test/couchdb/fixtures/logo.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/os_daemon_bad_perm.sh
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_bad_perm.sh b/test/couchdb/fixtures/os_daemon_bad_perm.sh
deleted file mode 100644
index 345c8b4..0000000
--- a/test/couchdb/fixtures/os_daemon_bad_perm.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/sh -e
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-# 
-#   http://www.apache.org/licenses/LICENSE-2.0
-# 
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-# 
-# Please do not make this file executable as that's the error being tested.
-
-sleep 5

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/os_daemon_can_reboot.sh
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_can_reboot.sh b/test/couchdb/fixtures/os_daemon_can_reboot.sh
deleted file mode 100755
index 5bc10e8..0000000
--- a/test/couchdb/fixtures/os_daemon_can_reboot.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh -e
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-# 
-#   http://www.apache.org/licenses/LICENSE-2.0
-# 
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-sleep 2

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/os_daemon_configer.escript
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_configer.escript b/test/couchdb/fixtures/os_daemon_configer.escript
deleted file mode 100755
index d437423..0000000
--- a/test/couchdb/fixtures/os_daemon_configer.escript
+++ /dev/null
@@ -1,101 +0,0 @@
-#! /usr/bin/env escript
-
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--include("../couch_eunit.hrl").
-
-
-read() ->
-    case io:get_line('') of
-        eof ->
-            stop;
-        Data ->
-            ejson:decode(Data)
-    end.
-
-write(Mesg) ->
-    Data = iolist_to_binary(ejson:encode(Mesg)),
-    io:format(binary_to_list(Data) ++ "\n", []).
-
-get_cfg(Section) ->
-    write([<<"get">>, Section]),
-    read().
-
-get_cfg(Section, Name) ->
-    write([<<"get">>, Section, Name]),
-    read().
-
-log(Mesg) ->
-    write([<<"log">>, Mesg]).
-
-log(Mesg, Level) ->
-    write([<<"log">>, Mesg, {[{<<"level">>, Level}]}]).
-
-test_get_cfg1() ->
-    Path = list_to_binary(?FILE),
-    FileName = list_to_binary(filename:basename(?FILE)),
-    {[{FileName, Path}]} = get_cfg(<<"os_daemons">>).
-
-test_get_cfg2() ->
-    Path = list_to_binary(?FILE),
-    FileName = list_to_binary(filename:basename(?FILE)),
-    Path = get_cfg(<<"os_daemons">>, FileName),
-    <<"sequential">> = get_cfg(<<"uuids">>, <<"algorithm">>).
-
-
-test_get_unknown_cfg() ->
-    {[]} = get_cfg(<<"aal;3p4">>),
-    null = get_cfg(<<"aal;3p4">>, <<"313234kjhsdfl">>).
-
-test_log() ->
-    log(<<"foobar!">>),
-    log(<<"some stuff!">>, <<"debug">>),
-    log(2),
-    log(true),
-    write([<<"log">>, <<"stuff">>, 2]),
-    write([<<"log">>, 3, null]),
-    write([<<"log">>, [1, 2], {[{<<"level">>, <<"debug">>}]}]),
-    write([<<"log">>, <<"true">>, {[]}]).
-
-do_tests() ->
-    test_get_cfg1(),
-    test_get_cfg2(),
-    test_get_unknown_cfg(),
-    test_log(),
-    loop(io:read("")).
-
-loop({ok, _}) ->
-    loop(io:read(""));
-loop(eof) ->
-    init:stop();
-loop({error, _Reason}) ->
-    init:stop().
-
-main([]) ->
-    init_code_path(),
-    couch_config:start_link(?CONFIG_CHAIN),
-    couch_drv:start_link(),
-    do_tests().
-
-init_code_path() ->
-    Paths = [
-        "couchdb",
-        "ejson",
-        "erlang-oauth",
-        "ibrowse",
-        "mochiweb",
-        "snappy"
-    ],
-    lists:foreach(fun(Name) ->
-        code:add_patha(filename:join([?BUILDDIR, "src", Name]))
-    end, Paths).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/os_daemon_die_on_boot.sh
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_die_on_boot.sh b/test/couchdb/fixtures/os_daemon_die_on_boot.sh
deleted file mode 100755
index 256ee79..0000000
--- a/test/couchdb/fixtures/os_daemon_die_on_boot.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh -e
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-# 
-#   http://www.apache.org/licenses/LICENSE-2.0
-# 
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-exit 1

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/os_daemon_die_quickly.sh
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_die_quickly.sh b/test/couchdb/fixtures/os_daemon_die_quickly.sh
deleted file mode 100755
index f5a1368..0000000
--- a/test/couchdb/fixtures/os_daemon_die_quickly.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh -e
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-# 
-#   http://www.apache.org/licenses/LICENSE-2.0
-# 
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-sleep 1

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/os_daemon_looper.escript
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_looper.escript b/test/couchdb/fixtures/os_daemon_looper.escript
deleted file mode 100755
index 73974e9..0000000
--- a/test/couchdb/fixtures/os_daemon_looper.escript
+++ /dev/null
@@ -1,26 +0,0 @@
-#! /usr/bin/env escript
-
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
-loop() ->
-    loop(io:read("")).
-
-loop({ok, _}) ->
-    loop(io:read(""));
-loop(eof) ->
-    stop;
-loop({error, Reason}) ->
-    throw({error, Reason}).
-
-main([]) ->
-    loop().

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/fixtures/test.couch
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/test.couch b/test/couchdb/fixtures/test.couch
deleted file mode 100644
index 32c79af..0000000
Binary files a/test/couchdb/fixtures/test.couch and /dev/null differ

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/json_stream_parse_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/json_stream_parse_tests.erl b/test/couchdb/json_stream_parse_tests.erl
deleted file mode 100644
index 92303b6..0000000
--- a/test/couchdb/json_stream_parse_tests.erl
+++ /dev/null
@@ -1,151 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(json_stream_parse_tests).
-
--include("couch_eunit.hrl").
-
--define(CASES,
-    [
-        {1, "1", "integer numeric literial"},
-        {3.1416, "3.14160", "float numeric literal"},  % text representation may truncate, trail zeroes
-        {-1, "-1", "negative integer numeric literal"},
-        {-3.1416, "-3.14160", "negative float numeric literal"},
-        {12.0e10, "1.20000e+11", "float literal in scientific notation"},
-        {1.234E+10, "1.23400e+10", "another float literal in scientific notation"},
-        {-1.234E-10, "-1.23400e-10", "negative float literal in scientific notation"},
-        {10.0, "1.0e+01", "yet another float literal in scientific notation"},
-        {123.456, "1.23456E+2", "yet another float literal in scientific notation"},
-        {10.0, "1e1", "yet another float literal in scientific notation"},
-        {<<"foo">>, "\"foo\"", "string literal"},
-        {<<"foo", 5, "bar">>, "\"foo\\u0005bar\"", "string literal with \\u0005"},
-        {<<"">>, "\"\"", "empty string literal"},
-        {<<"\n\n\n">>, "\"\\n\\n\\n\"", "only new lines literal"},
-        {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\"",
-            "only white spaces string literal"},
-        {null, "null", "null literal"},
-        {true, "true", "true literal"},
-        {false, "false", "false literal"},
-        {<<"null">>, "\"null\"", "null string literal"},
-        {<<"true">>, "\"true\"", "true string literal"},
-        {<<"false">>, "\"false\"", "false string literal"},
-        {{[]}, "{}", "empty object literal"},
-        {{[{<<"foo">>, <<"bar">>}]}, "{\"foo\":\"bar\"}",
-            "simple object literal"},
-        {{[{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]},
-            "{\"foo\":\"bar\",\"baz\":123}", "another simple object literal"},
-        {[], "[]", "empty array literal"},
-        {[[]], "[[]]", "empty array literal inside a single element array literal"},
-        {[1, <<"foo">>], "[1,\"foo\"]", "simple non-empty array literal"},
-        {[1199344435545.0, 1], "[1199344435545.0,1]",
-             "another simple non-empty array literal"},
-        {[false, true, 321, null], "[false, true, 321, null]", "array of literals"},
-        {{[{<<"foo">>, [123]}]}, "{\"foo\":[123]}",
-             "object literal with an array valued property"},
-        {{[{<<"foo">>, {[{<<"bar">>, true}]}}]},
-            "{\"foo\":{\"bar\":true}}", "nested object literal"},
-        {{[{<<"foo">>, []}, {<<"bar">>, {[{<<"baz">>, true}]}},
-                {<<"alice">>, <<"bob">>}]},
-            "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}",
-            "complex object literal"},
-        {[-123, <<"foo">>, {[{<<"bar">>, []}]}, null],
-            "[-123,\"foo\",{\"bar\":[]},null]",
-            "complex array literal"}
-    ]
-).
-
-
-raw_json_input_test_() ->
-    Tests = lists:map(
-        fun({EJson, JsonString, Desc}) ->
-            {Desc,
-             ?_assert(equiv(EJson, json_stream_parse:to_ejson(JsonString)))}
-        end, ?CASES),
-    {"Tests with raw JSON string as the input", Tests}.
-
-one_byte_data_fun_test_() ->
-    Tests = lists:map(
-        fun({EJson, JsonString, Desc}) ->
-            DataFun = fun() -> single_byte_data_fun(JsonString) end,
-            {Desc,
-             ?_assert(equiv(EJson, json_stream_parse:to_ejson(DataFun)))}
-        end, ?CASES),
-    {"Tests with a 1 byte output data function as the input", Tests}.
-
-test_multiple_bytes_data_fun_test_() ->
-    Tests = lists:map(
-        fun({EJson, JsonString, Desc}) ->
-            DataFun = fun() -> multiple_bytes_data_fun(JsonString) end,
-            {Desc,
-             ?_assert(equiv(EJson, json_stream_parse:to_ejson(DataFun)))}
-        end, ?CASES),
-    {"Tests with a multiple bytes output data function as the input", Tests}.
-
-
-%% Test for equivalence of Erlang terms.
-%% Due to arbitrary order of construction, equivalent objects might
-%% compare unequal as erlang terms, so we need to carefully recurse
-%% through aggregates (tuples and objects).
-equiv({Props1}, {Props2}) ->
-    equiv_object(Props1, Props2);
-equiv(L1, L2) when is_list(L1), is_list(L2) ->
-    equiv_list(L1, L2);
-equiv(N1, N2) when is_number(N1), is_number(N2) ->
-    N1 == N2;
-equiv(B1, B2) when is_binary(B1), is_binary(B2) ->
-    B1 == B2;
-equiv(true, true) ->
-    true;
-equiv(false, false) ->
-    true;
-equiv(null, null) ->
-    true.
-
-%% Object representation and traversal order is unknown.
-%% Use the sledgehammer and sort property lists.
-equiv_object(Props1, Props2) ->
-    L1 = lists:keysort(1, Props1),
-    L2 = lists:keysort(1, Props2),
-    Pairs = lists:zip(L1, L2),
-    true = lists:all(
-        fun({{K1, V1}, {K2, V2}}) ->
-            equiv(K1, K2) andalso equiv(V1, V2)
-        end,
-        Pairs).
-
-%% Recursively compare tuple elements for equivalence.
-equiv_list([], []) ->
-    true;
-equiv_list([V1 | L1], [V2 | L2]) ->
-    equiv(V1, V2) andalso equiv_list(L1, L2).
-
-single_byte_data_fun([]) ->
-    done;
-single_byte_data_fun([H | T]) ->
-    {<<H>>, fun() -> single_byte_data_fun(T) end}.
-
-multiple_bytes_data_fun([]) ->
-    done;
-multiple_bytes_data_fun(L) ->
-    N = crypto:rand_uniform(0, 7),
-    {Part, Rest} = split(L, N),
-    {list_to_binary(Part), fun() -> multiple_bytes_data_fun(Rest) end}.
-
-split(L, N) when length(L) =< N ->
-    {L, []};
-split(L, N) ->
-    take(N, L, []).
-
-take(0, L, Acc) ->
-    {lists:reverse(Acc), L};
-take(N, [H|L], Acc) ->
-    take(N - 1, L, [H | Acc]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/test_request.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/test_request.erl b/test/couchdb/test_request.erl
deleted file mode 100644
index 68e4956..0000000
--- a/test/couchdb/test_request.erl
+++ /dev/null
@@ -1,75 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(test_request).
-
--export([get/1, get/2, get/3]).
--export([put/2, put/3]).
--export([options/1, options/2, options/3]).
--export([request/3, request/4]).
-
-get(Url) ->
-    request(get, Url, []).
-
-get(Url, Headers) ->
-    request(get, Url, Headers).
-get(Url, Headers, Opts) ->
-    request(get, Url, Headers, [], Opts).
-
-
-put(Url, Body) ->
-    request(put, Url, [], Body).
-
-put(Url, Headers, Body) ->
-    request(put, Url, Headers, Body).
-
-
-options(Url) ->
-    request(options, Url, []).
-
-options(Url, Headers) ->
-    request(options, Url, Headers).
-
-options(Url, Headers, Opts) ->
-    request(options, Url, Headers, [], Opts).
-
-
-request(Method, Url, Headers) ->
-    request(Method, Url, Headers, []).
-
-request(Method, Url, Headers, Body) ->
-    request(Method, Url, Headers, Body, [], 3).
-
-request(Method, Url, Headers, Body, Opts) ->
-    request(Method, Url, Headers, Body, Opts, 3).
-
-request(_Method, _Url, _Headers, _Body, _Opts, 0) ->
-    {error, request_failed};
-request(Method, Url, Headers, Body, Opts, N) ->
-    case code:is_loaded(ibrowse) of
-        false ->
-            {ok, _} = ibrowse:start();
-        _ ->
-            ok
-    end,
-    case ibrowse:send_req(Url, Headers, Method, Body, Opts) of
-        {ok, Code0, RespHeaders, RespBody0} ->
-            Code = list_to_integer(Code0),
-            RespBody = iolist_to_binary(RespBody0),
-            {ok, Code, RespHeaders, RespBody};
-        {error, {'EXIT', {normal, _}}} ->
-            % Connection closed right after a successful request that
-            % used the same connection.
-            request(Method, Url, Headers, Body, N - 1);
-        Error ->
-            Error
-    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/test_web.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/test_web.erl b/test/couchdb/test_web.erl
deleted file mode 100644
index 1de2cd1..0000000
--- a/test/couchdb/test_web.erl
+++ /dev/null
@@ -1,112 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(test_web).
--behaviour(gen_server).
-
--include("couch_eunit.hrl").
-
--export([start_link/0, stop/0, loop/1, get_port/0, set_assert/1, check_last/0]).
--export([init/1, terminate/2, code_change/3]).
--export([handle_call/3, handle_cast/2, handle_info/2]).
-
--define(SERVER, test_web_server).
--define(HANDLER, test_web_handler).
--define(DELAY, 500).
-
-start_link() ->
-    gen_server:start({local, ?HANDLER}, ?MODULE, [], []),
-    mochiweb_http:start([
-        {name, ?SERVER},
-        {loop, {?MODULE, loop}},
-        {port, 0}
-    ]).
-
-loop(Req) ->
-    %?debugFmt("Handling request: ~p", [Req]),
-    case gen_server:call(?HANDLER, {check_request, Req}) of
-        {ok, RespInfo} ->
-            {ok, Req:respond(RespInfo)};
-        {raw, {Status, Headers, BodyChunks}} ->
-            Resp = Req:start_response({Status, Headers}),
-            lists:foreach(fun(C) -> Resp:send(C) end, BodyChunks),
-            erlang:put(mochiweb_request_force_close, true),
-            {ok, Resp};
-        {chunked, {Status, Headers, BodyChunks}} ->
-            Resp = Req:respond({Status, Headers, chunked}),
-            timer:sleep(?DELAY),
-            lists:foreach(fun(C) -> Resp:write_chunk(C) end, BodyChunks),
-            Resp:write_chunk([]),
-            {ok, Resp};
-        {error, Reason} ->
-            ?debugFmt("Error: ~p", [Reason]),
-            Body = lists:flatten(io_lib:format("Error: ~p", [Reason])),
-            {ok, Req:respond({200, [], Body})}
-    end.
-
-get_port() ->
-    mochiweb_socket_server:get(?SERVER, port).
-
-set_assert(Fun) ->
-    ?assertEqual(ok, gen_server:call(?HANDLER, {set_assert, Fun})).
-
-check_last() ->
-    gen_server:call(?HANDLER, last_status).
-
-init(_) ->
-    {ok, nil}.
-
-terminate(_Reason, _State) ->
-    ok.
-
-stop() ->
-    gen_server:cast(?SERVER, stop).
-
-
-handle_call({check_request, Req}, _From, State) when is_function(State, 1) ->
-    Resp2 = case (catch State(Req)) of
-        {ok, Resp} ->
-            {reply, {ok, Resp}, was_ok};
-        {raw, Resp} ->
-            {reply, {raw, Resp}, was_ok};
-        {chunked, Resp} ->
-            {reply, {chunked, Resp}, was_ok};
-        Error ->
-            {reply, {error, Error}, not_ok}
-    end,
-    Req:cleanup(),
-    Resp2;
-handle_call({check_request, _Req}, _From, _State) ->
-    {reply, {error, no_assert_function}, not_ok};
-handle_call(last_status, _From, State) when is_atom(State) ->
-    {reply, State, nil};
-handle_call(last_status, _From, State) ->
-    {reply, {error, not_checked}, State};
-handle_call({set_assert, Fun}, _From, nil) ->
-    {reply, ok, Fun};
-handle_call({set_assert, _}, _From, State) ->
-    {reply, {error, assert_function_set}, State};
-handle_call(Msg, _From, State) ->
-    {reply, {ignored, Msg}, State}.
-
-handle_cast(stop, State) ->
-    {stop, normal, State};
-handle_cast(Msg, State) ->
-    ?debugFmt("Ignoring cast message: ~p", [Msg]),
-    {noreply, State}.
-
-handle_info(Msg, State) ->
-    ?debugFmt("Ignoring info message: ~p", [Msg]),
-    {noreply, State}.
-
-code_change(_OldVsn, State, _Extra) ->
-    {ok, State}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_attachments_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_attachments_tests.erl b/test/couchdb_attachments_tests.erl
new file mode 100644
index 0000000..cf59785
--- /dev/null
+++ b/test/couchdb_attachments_tests.erl
@@ -0,0 +1,638 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_attachments_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(COMPRESSION_LEVEL, 8).
+-define(ATT_BIN_NAME, <<"logo.png">>).
+-define(ATT_TXT_NAME, <<"file.erl">>).
+-define(FIXTURE_PNG, filename:join([?FIXTURESDIR, "logo.png"])).
+-define(FIXTURE_TXT, ?FILE).
+-define(TIMEOUT, 1000).
+-define(TIMEOUT_EUNIT, 10).
+-define(TIMEWAIT, 100).
+-define(i2l(I), integer_to_list(I)).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    % ensure in default compression settings for attachments_compression_tests
+    couch_config:set("attachments", "compression_level",
+                     ?i2l(?COMPRESSION_LEVEL), false),
+    couch_config:set("attachments", "compressible_types", "text/*", false),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, []),
+    ok = couch_db:close(Db),
+    Addr = couch_config:get("httpd", "bind_address", any),
+    Port = mochiweb_socket_server:get(couch_httpd, port),
+    Host = Addr ++ ":" ++ ?i2l(Port),
+    {Host, ?b2l(DbName)}.
+
+setup({binary, standalone}) ->
+    {Host, DbName} = setup(),
+        setup_att(fun create_standalone_png_att/2, Host, DbName, ?FIXTURE_PNG);
+setup({text, standalone}) ->
+    {Host, DbName} = setup(),
+    setup_att(fun create_standalone_text_att/2, Host, DbName, ?FIXTURE_TXT);
+setup({binary, inline}) ->
+    {Host, DbName} = setup(),
+    setup_att(fun create_inline_png_att/2, Host, DbName, ?FIXTURE_PNG);
+setup({text, inline}) ->
+    {Host, DbName} = setup(),
+    setup_att(fun create_inline_text_att/2, Host, DbName, ?FIXTURE_TXT);
+setup(compressed) ->
+    {Host, DbName} = setup(),
+    setup_att(fun create_already_compressed_att/2, Host, DbName, ?FIXTURE_TXT).
+setup_att(Fun, Host, DbName, File) ->
+    HttpHost = "http://" ++ Host,
+    AttUrl = Fun(HttpHost, DbName),
+    {ok, Data} = file:read_file(File),
+    DocUrl = string:join([HttpHost, DbName, "doc"], "/"),
+    Helpers = {DbName, DocUrl, AttUrl},
+    {Data, Helpers}.
+
+teardown(_, {_, {DbName, _, _}}) ->
+    teardown(DbName).
+
+teardown({_, DbName}) ->
+    teardown(DbName);
+teardown(DbName) ->
+    ok = couch_server:delete(?l2b(DbName), []),
+    ok.
+
+
+attachments_test_() ->
+    {
+        "Attachments tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [
+                attachments_md5_tests(),
+                attachments_compression_tests()
+            ]
+        }
+    }.
+
+attachments_md5_tests() ->
+    {
+        "Attachments MD5 tests",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_upload_attachment_without_md5/1,
+                fun should_upload_attachment_by_chunks_without_md5/1,
+                fun should_upload_attachment_with_valid_md5_header/1,
+                fun should_upload_attachment_by_chunks_with_valid_md5_header/1,
+                fun should_upload_attachment_by_chunks_with_valid_md5_trailer/1,
+                fun should_reject_attachment_with_invalid_md5/1,
+                fun should_reject_chunked_attachment_with_invalid_md5/1,
+                fun should_reject_chunked_attachment_with_invalid_md5_trailer/1
+            ]
+        }
+    }.
+
+attachments_compression_tests() ->
+    Funs = [
+         fun should_get_att_without_accept_gzip_encoding/2,
+         fun should_get_att_with_accept_gzip_encoding/2,
+         fun should_get_att_with_accept_deflate_encoding/2,
+         fun should_return_406_response_on_unsupported_encoding/2,
+         fun should_get_doc_with_att_data/2,
+         fun should_get_doc_with_att_data_stub/2
+    ],
+    {
+        "Attachments compression tests",
+        [
+            {
+                "Created via Attachments API",
+                created_attachments_compression_tests(standalone, Funs)
+            },
+            {
+                "Created inline via Document API",
+                created_attachments_compression_tests(inline, Funs)
+            },
+            {
+                "Created already been compressed via Attachments API",
+                {
+                    foreachx,
+                    fun setup/1, fun teardown/2,
+                    [{compressed, Fun} || Fun <- Funs]
+                }
+            },
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_not_create_compressed_att_with_deflate_encoding/1,
+                    fun should_not_create_compressed_att_with_compress_encoding/1,
+                    fun should_create_compressible_att_with_ctype_params/1
+                ]
+            }
+        ]
+    }.
+
+created_attachments_compression_tests(Mod, Funs) ->
+    [
+        {
+            "Compressiable attachments",
+            {
+                foreachx,
+                fun setup/1, fun teardown/2,
+                [{{text, Mod}, Fun} || Fun <- Funs]
+            }
+        },
+        {
+            "Uncompressiable attachments",
+            {
+                foreachx,
+                fun setup/1, fun teardown/2,
+                [{{binary, Mod}, Fun} || Fun <- Funs]
+            }
+        }
+    ].
+
+
+
+should_upload_attachment_without_md5({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        Body = "We all live in a yellow submarine!",
+        Headers = [
+            {"Content-Length", "34"},
+            {"Content-Type", "text/plain"},
+            {"Host", Host}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(201, Code),
+        ?assertEqual(true, get_json(Json, [<<"ok">>]))
+    end).
+
+should_upload_attachment_by_chunks_without_md5({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        AttData = <<"We all live in a yellow submarine!">>,
+        <<Part1:21/binary, Part2:13/binary>> = AttData,
+        Body = chunked_body([Part1, Part2]),
+        Headers = [
+            {"Content-Type", "text/plain"},
+            {"Transfer-Encoding", "chunked"},
+            {"Host", Host}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(201, Code),
+        ?assertEqual(true, get_json(Json, [<<"ok">>]))
+    end).
+
+should_upload_attachment_with_valid_md5_header({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        Body = "We all live in a yellow submarine!",
+        Headers = [
+            {"Content-Length", "34"},
+            {"Content-Type", "text/plain"},
+            {"Content-MD5", ?b2l(base64:encode(couch_util:md5(Body)))},
+            {"Host", Host}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(201, Code),
+        ?assertEqual(true, get_json(Json, [<<"ok">>]))
+    end).
+
+should_upload_attachment_by_chunks_with_valid_md5_header({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        AttData = <<"We all live in a yellow submarine!">>,
+        <<Part1:21/binary, Part2:13/binary>> = AttData,
+        Body = chunked_body([Part1, Part2]),
+        Headers = [
+            {"Content-Type", "text/plain"},
+            {"Content-MD5", ?b2l(base64:encode(couch_util:md5(AttData)))},
+            {"Host", Host},
+            {"Transfer-Encoding", "chunked"}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(201, Code),
+        ?assertEqual(true, get_json(Json, [<<"ok">>]))
+    end).
+
+should_upload_attachment_by_chunks_with_valid_md5_trailer({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        AttData = <<"We all live in a yellow submarine!">>,
+        <<Part1:21/binary, Part2:13/binary>> = AttData,
+        Body = [chunked_body([Part1, Part2]),
+                "Content-MD5: ", base64:encode(couch_util:md5(AttData)),
+                "\r\n"],
+        Headers = [
+            {"Content-Type", "text/plain"},
+            {"Host", Host},
+            {"Trailer", "Content-MD5"},
+            {"Transfer-Encoding", "chunked"}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(201, Code),
+        ?assertEqual(true, get_json(Json, [<<"ok">>]))
+    end).
+
+should_reject_attachment_with_invalid_md5({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        Body = "We all live in a yellow submarine!",
+        Headers = [
+            {"Content-Length", "34"},
+            {"Content-Type", "text/plain"},
+            {"Content-MD5", ?b2l(base64:encode(<<"foobar!">>))},
+            {"Host", Host}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(400, Code),
+        ?assertEqual(<<"content_md5_mismatch">>,
+                     get_json(Json, [<<"error">>]))
+    end).
+
+
+should_reject_chunked_attachment_with_invalid_md5({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        AttData = <<"We all live in a yellow submarine!">>,
+        <<Part1:21/binary, Part2:13/binary>> = AttData,
+        Body = chunked_body([Part1, Part2]),
+        Headers = [
+            {"Content-Type", "text/plain"},
+            {"Content-MD5", ?b2l(base64:encode(<<"foobar!">>))},
+            {"Host", Host},
+            {"Transfer-Encoding", "chunked"}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(400, Code),
+        ?assertEqual(<<"content_md5_mismatch">>,
+                     get_json(Json, [<<"error">>]))
+    end).
+
+should_reject_chunked_attachment_with_invalid_md5_trailer({Host, DbName}) ->
+    ?_test(begin
+        AttUrl = string:join(["", DbName, ?docid(), "readme.txt"], "/"),
+        AttData = <<"We all live in a yellow submarine!">>,
+        <<Part1:21/binary, Part2:13/binary>> = AttData,
+        Body = [chunked_body([Part1, Part2]),
+                "Content-MD5: ", base64:encode(<<"foobar!">>),
+                "\r\n"],
+        Headers = [
+            {"Content-Type", "text/plain"},
+            {"Host", Host},
+            {"Trailer", "Content-MD5"},
+            {"Transfer-Encoding", "chunked"}
+        ],
+        {ok, Code, Json} = request("PUT", AttUrl, Headers, Body),
+        ?assertEqual(400, Code),
+        ?assertEqual(<<"content_md5_mismatch">>, get_json(Json, [<<"error">>]))
+    end).
+
+should_get_att_without_accept_gzip_encoding(_, {Data, {_, _, AttUrl}}) ->
+    ?_test(begin
+        {ok, Code, Headers, Body} = test_request:get(AttUrl),
+        ?assertEqual(200, Code),
+        ?assertNot(lists:member({"Content-Encoding", "gzip"}, Headers)),
+        ?assertEqual(Data, iolist_to_binary(Body))
+    end).
+
+should_get_att_with_accept_gzip_encoding(compressed, {Data, {_, _, AttUrl}}) ->
+    ?_test(begin
+        {ok, Code, Headers, Body} = test_request:get(
+            AttUrl, [{"Accept-Encoding", "gzip"}]),
+        ?assertEqual(200, Code),
+        ?assert(lists:member({"Content-Encoding", "gzip"}, Headers)),
+        ?assertEqual(Data, zlib:gunzip(iolist_to_binary(Body)))
+    end);
+should_get_att_with_accept_gzip_encoding({text, _}, {Data, {_, _, AttUrl}}) ->
+    ?_test(begin
+        {ok, Code, Headers, Body} = test_request:get(
+            AttUrl, [{"Accept-Encoding", "gzip"}]),
+        ?assertEqual(200, Code),
+        ?assert(lists:member({"Content-Encoding", "gzip"}, Headers)),
+        ?assertEqual(Data, zlib:gunzip(iolist_to_binary(Body)))
+    end);
+should_get_att_with_accept_gzip_encoding({binary, _}, {Data, {_, _, AttUrl}}) ->
+    ?_test(begin
+        {ok, Code, Headers, Body} = test_request:get(
+            AttUrl, [{"Accept-Encoding", "gzip"}]),
+        ?assertEqual(200, Code),
+        ?assertEqual(undefined,
+                     couch_util:get_value("Content-Encoding", Headers)),
+        ?assertEqual(Data, iolist_to_binary(Body))
+    end).
+
+should_get_att_with_accept_deflate_encoding(_, {Data, {_, _, AttUrl}}) ->
+    ?_test(begin
+        {ok, Code, Headers, Body} = test_request:get(
+            AttUrl, [{"Accept-Encoding", "deflate"}]),
+        ?assertEqual(200, Code),
+        ?assertEqual(undefined,
+                     couch_util:get_value("Content-Encoding", Headers)),
+        ?assertEqual(Data, iolist_to_binary(Body))
+    end).
+
+should_return_406_response_on_unsupported_encoding(_, {_, {_, _, AttUrl}}) ->
+    ?_assertEqual(406,
+        begin
+            {ok, Code, _, _} = test_request:get(
+                AttUrl, [{"Accept-Encoding", "deflate, *;q=0"}]),
+            Code
+        end).
+
+should_get_doc_with_att_data(compressed, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?attachments=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        AttJson = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
+        AttData = couch_util:get_nested_json_value(
+            AttJson, [<<"data">>]),
+        ?assertEqual(
+            <<"text/plain">>,
+            couch_util:get_nested_json_value(AttJson,[<<"content_type">>])),
+        ?assertEqual(Data, base64:decode(AttData))
+    end);
+should_get_doc_with_att_data({text, _}, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?attachments=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        AttJson = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
+        AttData = couch_util:get_nested_json_value(
+            AttJson, [<<"data">>]),
+        ?assertEqual(
+            <<"text/plain">>,
+            couch_util:get_nested_json_value(AttJson,[<<"content_type">>])),
+        ?assertEqual(Data, base64:decode(AttData))
+    end);
+should_get_doc_with_att_data({binary, _}, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?attachments=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        AttJson = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_BIN_NAME]),
+        AttData = couch_util:get_nested_json_value(
+            AttJson, [<<"data">>]),
+        ?assertEqual(
+            <<"image/png">>,
+            couch_util:get_nested_json_value(AttJson,[<<"content_type">>])),
+        ?assertEqual(Data, base64:decode(AttData))
+    end).
+
+should_get_doc_with_att_data_stub(compressed, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?att_encoding_info=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        {AttJson} = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
+        ?assertEqual(<<"gzip">>,
+                     couch_util:get_value(<<"encoding">>, AttJson)),
+        AttLength = couch_util:get_value(<<"length">>, AttJson),
+        EncLength = couch_util:get_value(<<"encoded_length">>, AttJson),
+        ?assertEqual(AttLength, EncLength),
+        ?assertEqual(iolist_size(zlib:gzip(Data)), AttLength)
+    end);
+should_get_doc_with_att_data_stub({text, _}, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?att_encoding_info=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        {AttJson} = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
+        ?assertEqual(<<"gzip">>,
+                     couch_util:get_value(<<"encoding">>, AttJson)),
+        AttEncLength = iolist_size(gzip(Data)),
+        ?assertEqual(AttEncLength,
+                     couch_util:get_value(<<"encoded_length">>, AttJson)),
+        ?assertEqual(byte_size(Data),
+                     couch_util:get_value(<<"length">>, AttJson))
+    end);
+should_get_doc_with_att_data_stub({binary, _}, {Data, {_, DocUrl, _}}) ->
+    ?_test(begin
+        Url = DocUrl ++ "?att_encoding_info=true",
+        {ok, Code, _, Body} = test_request:get(
+            Url, [{"Accept", "application/json"}]),
+        ?assertEqual(200, Code),
+        Json = ejson:decode(Body),
+        {AttJson} = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_BIN_NAME]),
+        ?assertEqual(undefined,
+                     couch_util:get_value(<<"encoding">>, AttJson)),
+        ?assertEqual(undefined,
+                     couch_util:get_value(<<"encoded_length">>, AttJson)),
+        ?assertEqual(byte_size(Data),
+                     couch_util:get_value(<<"length">>, AttJson))
+    end).
+
+should_not_create_compressed_att_with_deflate_encoding({Host, DbName}) ->
+    ?_assertEqual(415,
+        begin
+            HttpHost = "http://" ++ Host,
+            AttUrl = string:join([HttpHost, DbName, ?docid(), "file.txt"], "/"),
+            {ok, Data} = file:read_file(?FIXTURE_TXT),
+            Body = zlib:compress(Data),
+            Headers = [
+                {"Content-Encoding", "deflate"},
+                {"Content-Type", "text/plain"}
+            ],
+            {ok, Code, _, _} = test_request:put(AttUrl, Headers, Body),
+            Code
+        end).
+
+should_not_create_compressed_att_with_compress_encoding({Host, DbName}) ->
+    % Note: As of OTP R13B04, it seems there's no LZW compression
+    % (i.e. UNIX compress utility implementation) lib in OTP.
+    % However there's a simple working Erlang implementation at:
+    % http://scienceblogs.com/goodmath/2008/01/simple_lempelziv_compression_i.php
+    ?_assertEqual(415,
+        begin
+            HttpHost = "http://" ++ Host,
+            AttUrl = string:join([HttpHost, DbName, ?docid(), "file.txt"], "/"),
+            {ok, Data} = file:read_file(?FIXTURE_TXT),
+            Headers = [
+                {"Content-Encoding", "compress"},
+                {"Content-Type", "text/plain"}
+            ],
+            {ok, Code, _, _} = test_request:put(AttUrl, Headers, Data),
+            Code
+        end).
+
+should_create_compressible_att_with_ctype_params({Host, DbName}) ->
+    {timeout, ?TIMEOUT_EUNIT, ?_test(begin
+        HttpHost = "http://" ++ Host,
+        DocUrl = string:join([HttpHost, DbName, ?docid()], "/"),
+        AttUrl = string:join([DocUrl, ?b2l(?ATT_TXT_NAME)], "/"),
+        {ok, Data} = file:read_file(?FIXTURE_TXT),
+        Headers = [{"Content-Type", "text/plain; charset=UTF-8"}],
+        {ok, Code0, _, _} = test_request:put(AttUrl, Headers, Data),
+        ?assertEqual(201, Code0),
+
+        {ok, Code1, _, Body} = test_request:get(
+            DocUrl ++ "?att_encoding_info=true"),
+        ?assertEqual(200, Code1),
+        Json = ejson:decode(Body),
+        {AttJson} = couch_util:get_nested_json_value(
+            Json, [<<"_attachments">>, ?ATT_TXT_NAME]),
+        ?assertEqual(<<"gzip">>,
+                     couch_util:get_value(<<"encoding">>, AttJson)),
+        AttEncLength = iolist_size(gzip(Data)),
+        ?assertEqual(AttEncLength,
+                     couch_util:get_value(<<"encoded_length">>, AttJson)),
+        ?assertEqual(byte_size(Data),
+                     couch_util:get_value(<<"length">>, AttJson))
+    end)}.
+
+
+get_json(Json, Path) ->
+    couch_util:get_nested_json_value(Json, Path).
+
+to_hex(Val) ->
+    to_hex(Val, []).
+
+to_hex(0, Acc) ->
+    Acc;
+to_hex(Val, Acc) ->
+    to_hex(Val div 16, [hex_char(Val rem 16) | Acc]).
+
+hex_char(V) when V < 10 -> $0 + V;
+hex_char(V) -> $A + V - 10.
+
+chunked_body(Chunks) ->
+    chunked_body(Chunks, []).
+
+chunked_body([], Acc) ->
+    iolist_to_binary(lists:reverse(Acc, "0\r\n"));
+chunked_body([Chunk | Rest], Acc) ->
+    Size = to_hex(size(Chunk)),
+    chunked_body(Rest, ["\r\n", Chunk, "\r\n", Size | Acc]).
+
+get_socket() ->
+    Options = [binary, {packet, 0}, {active, false}],
+    Addr = couch_config:get("httpd", "bind_address", any),
+    Port = mochiweb_socket_server:get(couch_httpd, port),
+    {ok, Sock} = gen_tcp:connect(Addr, Port, Options),
+    Sock.
+
+request(Method, Url, Headers, Body) ->
+    RequestHead = [Method, " ", Url, " HTTP/1.1"],
+    RequestHeaders = [[string:join([Key, Value], ": "), "\r\n"]
+                      || {Key, Value} <- Headers],
+    Request = [RequestHead, "\r\n", RequestHeaders, "\r\n", Body, "\r\n"],
+    Sock = get_socket(),
+    gen_tcp:send(Sock, list_to_binary(lists:flatten(Request))),
+    timer:sleep(?TIMEWAIT),  % must wait to receive complete response
+    {ok, R} = gen_tcp:recv(Sock, 0),
+    gen_tcp:close(Sock),
+    [Header, Body1] = re:split(R, "\r\n\r\n", [{return, binary}]),
+    {ok, {http_response, _, Code, _}, _} =
+        erlang:decode_packet(http, Header, []),
+    Json = ejson:decode(Body1),
+    {ok, Code, Json}.
+
+create_standalone_text_att(Host, DbName) ->
+    {ok, Data} = file:read_file(?FIXTURE_TXT),
+    Url = string:join([Host, DbName, "doc", ?b2l(?ATT_TXT_NAME)], "/"),
+    {ok, Code, _Headers, _Body} = test_request:put(
+        Url, [{"Content-Type", "text/plain"}], Data),
+    ?assertEqual(201, Code),
+    Url.
+
+create_standalone_png_att(Host, DbName) ->
+    {ok, Data} = file:read_file(?FIXTURE_PNG),
+    Url = string:join([Host, DbName, "doc", ?b2l(?ATT_BIN_NAME)], "/"),
+    {ok, Code, _Headers, _Body} = test_request:put(
+        Url, [{"Content-Type", "image/png"}], Data),
+    ?assertEqual(201, Code),
+    Url.
+
+create_inline_text_att(Host, DbName) ->
+    {ok, Data} = file:read_file(?FIXTURE_TXT),
+    Url = string:join([Host, DbName, "doc"], "/"),
+    Doc = {[
+        {<<"_attachments">>, {[
+            {?ATT_TXT_NAME, {[
+                {<<"content_type">>, <<"text/plain">>},
+                {<<"data">>, base64:encode(Data)}
+            ]}
+        }]}}
+    ]},
+    {ok, Code, _Headers, _Body} = test_request:put(
+        Url, [{"Content-Type", "application/json"}], ejson:encode(Doc)),
+    ?assertEqual(201, Code),
+    string:join([Url, ?b2l(?ATT_TXT_NAME)], "/").
+
+create_inline_png_att(Host, DbName) ->
+    {ok, Data} = file:read_file(?FIXTURE_PNG),
+    Url = string:join([Host, DbName, "doc"], "/"),
+    Doc = {[
+        {<<"_attachments">>, {[
+            {?ATT_BIN_NAME, {[
+                {<<"content_type">>, <<"image/png">>},
+                {<<"data">>, base64:encode(Data)}
+            ]}
+        }]}}
+    ]},
+    {ok, Code, _Headers, _Body} = test_request:put(
+        Url, [{"Content-Type", "application/json"}], ejson:encode(Doc)),
+    ?assertEqual(201, Code),
+    string:join([Url, ?b2l(?ATT_BIN_NAME)], "/").
+
+create_already_compressed_att(Host, DbName) ->
+    {ok, Data} = file:read_file(?FIXTURE_TXT),
+    Url = string:join([Host, DbName, "doc", ?b2l(?ATT_TXT_NAME)], "/"),
+    {ok, Code, _Headers, _Body} = test_request:put(
+        Url, [{"Content-Type", "text/plain"}, {"Content-Encoding", "gzip"}],
+        zlib:gzip(Data)),
+    ?assertEqual(201, Code),
+    Url.
+
+gzip(Data) ->
+    Z = zlib:open(),
+    ok = zlib:deflateInit(Z, ?COMPRESSION_LEVEL, deflated, 16 + 15, 8, default),
+    zlib:deflate(Z, Data),
+    Last = zlib:deflate(Z, [], finish),
+    ok = zlib:deflateEnd(Z),
+    ok = zlib:close(Z),
+    Last.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb_compaction_daemon.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_compaction_daemon.erl b/test/couchdb_compaction_daemon.erl
new file mode 100644
index 0000000..725a97b
--- /dev/null
+++ b/test/couchdb_compaction_daemon.erl
@@ -0,0 +1,231 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_compaction_daemon).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(DELAY, 100).
+-define(TIMEOUT, 30000).
+-define(TIMEOUT_S, ?TIMEOUT div 1000).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    couch_config:set("compaction_daemon", "check_interval", "3", false),
+    couch_config:set("compaction_daemon", "min_file_size", "100000", false),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    create_design_doc(Db),
+    ok = couch_db:close(Db),
+    DbName.
+
+teardown(DbName) ->
+    Configs = couch_config:get("compactions"),
+    lists:foreach(
+        fun({Key, _}) ->
+            ok = couch_config:delete("compactions", Key, false)
+        end,
+        Configs),
+    couch_server:delete(DbName, [?ADMIN_USER]),
+    ok.
+
+
+compaction_daemon_test_() ->
+    {
+        "Compaction daemon tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_compact_by_default_rule/1,
+                    fun should_compact_by_dbname_rule/1
+                ]
+            }
+        }
+    }.
+
+
+should_compact_by_default_rule(DbName) ->
+    {timeout, ?TIMEOUT_S, ?_test(begin
+        {ok, Db} = couch_db:open_int(DbName, []),
+        populate(DbName, 70, 70, 200 * 1024),
+
+        {_, DbFileSize} = get_db_frag(DbName),
+        {_, ViewFileSize} = get_view_frag(DbName),
+
+        ok = couch_config:set("compactions", "_default",
+            "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"70%\"}]",
+            false),
+
+        ok = timer:sleep(4000), % something >= check_interval
+        wait_compaction_finished(DbName),
+        ok = couch_config:delete("compactions", "_default", false),
+
+        {DbFrag2, DbFileSize2} = get_db_frag(DbName),
+        {ViewFrag2, ViewFileSize2} = get_view_frag(DbName),
+
+        ?assert(DbFrag2 < 70),
+        ?assert(ViewFrag2 < 70),
+
+        ?assert(DbFileSize > DbFileSize2),
+        ?assert(ViewFileSize > ViewFileSize2),
+
+        ?assert(couch_db:is_idle(Db)),
+        ok = couch_db:close(Db)
+    end)}.
+
+should_compact_by_dbname_rule(DbName) ->
+    {timeout, ?TIMEOUT_S, ?_test(begin
+        {ok, Db} = couch_db:open_int(DbName, []),
+        populate(DbName, 70, 70, 200 * 1024),
+
+        {_, DbFileSize} = get_db_frag(DbName),
+        {_, ViewFileSize} = get_view_frag(DbName),
+
+        ok = couch_config:set("compactions", ?b2l(DbName),
+            "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"70%\"}]",
+            false),
+
+        ok = timer:sleep(4000), % something >= check_interval
+        wait_compaction_finished(DbName),
+        ok = couch_config:delete("compactions", ?b2l(DbName), false),
+
+        {DbFrag2, DbFileSize2} = get_db_frag(DbName),
+        {ViewFrag2, ViewFileSize2} = get_view_frag(DbName),
+
+        ?assert(DbFrag2 < 70),
+        ?assert(ViewFrag2 < 70),
+
+        ?assert(DbFileSize > DbFileSize2),
+        ?assert(ViewFileSize > ViewFileSize2),
+
+        ?assert(couch_db:is_idle(Db)),
+        ok = couch_db:close(Db)
+    end)}.
+
+
+create_design_doc(Db) ->
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"_design/foo">>},
+        {<<"language">>, <<"javascript">>},
+        {<<"views">>, {[
+            {<<"foo">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}},
+            {<<"foo2">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}},
+            {<<"foo3">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}}
+        ]}}
+    ]}),
+    {ok, _} = couch_db:update_docs(Db, [DDoc]),
+    {ok, _} = couch_db:ensure_full_commit(Db),
+    ok.
+
+populate(DbName, DbFrag, ViewFrag, MinFileSize) ->
+    {CurDbFrag, DbFileSize} = get_db_frag(DbName),
+    {CurViewFrag, ViewFileSize} = get_view_frag(DbName),
+    populate(DbName, DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag,
+             lists:min([DbFileSize, ViewFileSize])).
+
+populate(_Db, DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag, FileSize)
+    when CurDbFrag >= DbFrag, CurViewFrag >= ViewFrag, FileSize >= MinFileSize ->
+    ok;
+populate(DbName, DbFrag, ViewFrag, MinFileSize, _, _, _) ->
+    update(DbName),
+    {CurDbFrag, DbFileSize} = get_db_frag(DbName),
+    {CurViewFrag, ViewFileSize} = get_view_frag(DbName),
+    populate(DbName, DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag,
+             lists:min([DbFileSize, ViewFileSize])).
+
+update(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    lists:foreach(fun(_) ->
+        Doc = couch_doc:from_json_obj({[{<<"_id">>, couch_uuids:new()}]}),
+        {ok, _} = couch_db:update_docs(Db, [Doc]),
+        query_view(Db#db.name)
+    end, lists:seq(1, 200)),
+    couch_db:close(Db).
+
+db_url(DbName) ->
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    "http://" ++ Addr ++ ":" ++ Port ++ "/" ++ ?b2l(DbName).
+
+query_view(DbName) ->
+    {ok, Code, _Headers, _Body} = test_request:get(
+        db_url(DbName) ++ "/_design/foo/_view/foo"),
+    ?assertEqual(200, Code).
+
+get_db_frag(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, Info} = couch_db:get_db_info(Db),
+    couch_db:close(Db),
+    FileSize = couch_util:get_value(disk_size, Info),
+    DataSize = couch_util:get_value(data_size, Info),
+    {round((FileSize - DataSize) / FileSize * 100), FileSize}.
+
+get_view_frag(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, Info} = couch_mrview:get_info(Db, <<"_design/foo">>),
+    couch_db:close(Db),
+    FileSize = couch_util:get_value(disk_size, Info),
+    DataSize = couch_util:get_value(data_size, Info),
+    {round((FileSize - DataSize) / FileSize * 100), FileSize}.
+
+wait_compaction_finished(DbName) ->
+    Parent = self(),
+    Loop = spawn_link(fun() -> wait_loop(DbName, Parent) end),
+    receive
+        {done, Loop} ->
+            ok
+    after ?TIMEOUT ->
+        erlang:error(
+            {assertion_failed,
+             [{module, ?MODULE}, {line, ?LINE},
+              {reason, "Compaction timeout"}]})
+    end.
+
+wait_loop(DbName, Parent) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, DbInfo} = couch_db:get_db_info(Db),
+    {ok, ViewInfo} = couch_mrview:get_info(Db, <<"_design/foo">>),
+    couch_db:close(Db),
+    case (couch_util:get_value(compact_running, ViewInfo) =:= true) orelse
+        (couch_util:get_value(compact_running, DbInfo) =:= true) of
+        false ->
+            Parent ! {done, self()};
+        true ->
+            ok = timer:sleep(?DELAY),
+            wait_loop(DbName, Parent)
+    end.


[09/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 171-os-daemons-config.t etap test suite to eunit

Merged into couchdb_os_daemons_tests suite.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/aeca06f6
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/aeca06f6
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/aeca06f6

Branch: refs/heads/1963-eunit-bigcouch
Commit: aeca06f6a459e8da381ad28a0b69ec5086670bf3
Parents: a3eecf2
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Jun 3 14:10:25 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couch_auth_cache_tests.erl         |  40 +++-----
 test/couchdb/couchdb_os_daemons_tests.erl       |  21 ++++
 .../couchdb/fixtures/os_daemon_configer.escript | 101 +++++++++++++++++++
 3 files changed, 138 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/aeca06f6/test/couchdb/couch_auth_cache_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_auth_cache_tests.erl b/test/couchdb/couch_auth_cache_tests.erl
index 34f7127..3b2321c 100644
--- a/test/couchdb/couch_auth_cache_tests.erl
+++ b/test/couchdb/couch_auth_cache_tests.erl
@@ -74,47 +74,43 @@ should_get_nil_on_missed_cache(_) ->
     ?_assertEqual(nil, couch_auth_cache:get_user_creds("joe")).
 
 should_get_right_password_hash(DbName) ->
-    ?_assert(begin
+    ?_test(begin
         PasswordHash = hash_password("pass1"),
         {ok, _} = update_user_doc(DbName, "joe", "pass1"),
         Creds = couch_auth_cache:get_user_creds("joe"),
         ?assertEqual(PasswordHash,
-                      couch_util:get_value(<<"password_sha">>, Creds)),
-        true
+                      couch_util:get_value(<<"password_sha">>, Creds))
     end).
 
 should_ensure_doc_hash_equals_cached_one(DbName) ->
-    ?_assert(begin
+    ?_test(begin
         {ok, _} = update_user_doc(DbName, "joe", "pass1"),
         Creds = couch_auth_cache:get_user_creds("joe"),
 
         CachedHash = couch_util:get_value(<<"password_sha">>, Creds),
         StoredHash = get_user_doc_password_sha(DbName, "joe"),
-        ?assertEqual(StoredHash, CachedHash),
-        true
+        ?assertEqual(StoredHash, CachedHash)
     end).
 
 should_update_password(DbName) ->
-    ?_assert(begin
+    ?_test(begin
         PasswordHash = hash_password("pass2"),
         {ok, Rev} = update_user_doc(DbName, "joe", "pass1"),
         {ok, _} = update_user_doc(DbName, "joe", "pass2", Rev),
         Creds = couch_auth_cache:get_user_creds("joe"),
         ?assertEqual(PasswordHash,
-                      couch_util:get_value(<<"password_sha">>, Creds)),
-        true
+                      couch_util:get_value(<<"password_sha">>, Creds))
     end).
 
 should_cleanup_cache_after_userdoc_deletion(DbName) ->
-    ?_assert(begin
+    ?_test(begin
         {ok, _} = update_user_doc(DbName, "joe", "pass1"),
         delete_user_doc(DbName, "joe"),
-        ?assertEqual(nil, couch_auth_cache:get_user_creds("joe")),
-        true
+        ?assertEqual(nil, couch_auth_cache:get_user_creds("joe"))
     end).
 
 should_restore_cache_after_userdoc_recreation(DbName) ->
-    ?_assert(begin
+    ?_test(begin
         PasswordHash = hash_password("pass5"),
         {ok, _} = update_user_doc(DbName, "joe", "pass1"),
         delete_user_doc(DbName, "joe"),
@@ -124,22 +120,20 @@ should_restore_cache_after_userdoc_recreation(DbName) ->
         Creds = couch_auth_cache:get_user_creds("joe"),
 
         ?assertEqual(PasswordHash,
-                      couch_util:get_value(<<"password_sha">>, Creds)),
-        true
+                      couch_util:get_value(<<"password_sha">>, Creds))
     end).
 
 should_drop_cache_on_auth_db_change(DbName) ->
-    ?_assert(begin
+    ?_test(begin
         {ok, _} = update_user_doc(DbName, "joe", "pass1"),
         full_commit(DbName),
         couch_config:set("couch_httpd_auth", "authentication_db",
                          ?b2l(?tempdb()), false),
-        ?assertEqual(nil, couch_auth_cache:get_user_creds("joe")),
-        true
+        ?assertEqual(nil, couch_auth_cache:get_user_creds("joe"))
     end).
 
 should_restore_cache_on_auth_db_change(DbName) ->
-    ?_assert(begin
+    ?_test(begin
         PasswordHash = hash_password("pass1"),
         {ok, _} = update_user_doc(DbName, "joe", "pass1"),
         Creds = couch_auth_cache:get_user_creds("joe"),
@@ -157,20 +151,18 @@ should_restore_cache_on_auth_db_change(DbName) ->
 
         Creds = couch_auth_cache:get_user_creds("joe"),
         ?assertEqual(PasswordHash,
-                      couch_util:get_value(<<"password_sha">>, Creds)),
-        true
+                      couch_util:get_value(<<"password_sha">>, Creds))
     end).
 
 should_recover_cache_after_shutdown(DbName) ->
-    ?_assert(begin
+    ?_test(begin
         PasswordHash = hash_password("pass2"),
         {ok, Rev0} = update_user_doc(DbName, "joe", "pass1"),
         {ok, Rev1} = update_user_doc(DbName, "joe", "pass2", Rev0),
         full_commit(DbName),
         shutdown_db(DbName),
         {ok, Rev1} = get_doc_rev(DbName, "joe"),
-        ?assertEqual(PasswordHash, get_user_doc_password_sha(DbName, "joe")),
-        true
+        ?assertEqual(PasswordHash, get_user_doc_password_sha(DbName, "joe"))
     end).
 
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/aeca06f6/test/couchdb/couchdb_os_daemons_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_os_daemons_tests.erl b/test/couchdb/couchdb_os_daemons_tests.erl
index 1f9f6be..dd07e82 100644
--- a/test/couchdb/couchdb_os_daemons_tests.erl
+++ b/test/couchdb/couchdb_os_daemons_tests.erl
@@ -26,6 +26,7 @@
     buf=[]
 }).
 
+-define(DAEMON_CONFIGER, "os_daemon_configer.escript").
 -define(DAEMON_LOOPER, "os_daemon_looper.escript").
 -define(DELAY, 100).
 -define(TIMEOUT, 1000).
@@ -75,6 +76,17 @@ os_daemons_test_() ->
         }
     }.
 
+configuration_reader_test_() ->
+    {
+        "OS Daemon requests CouchDB configuration",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{?DAEMON_CONFIGER,
+              fun should_read_write_config_settings_by_daemon/2}]
+        }
+    }.
+
 
 should_check_daemon(DName, _) ->
     ?_test(begin
@@ -133,6 +145,15 @@ should_keep_alive_one_daemon_on_killing_other(DName, _) ->
         check_daemon(T, DName)
     end).
 
+should_read_write_config_settings_by_daemon(DName, _) ->
+    ?_test(begin
+        % have to wait till daemon run all his tests
+        % see daemon's script for more info
+        timer:sleep(?TIMEOUT),
+        {ok, [D]} = couch_os_daemons:info([table]),
+        check_daemon(D, DName)
+    end).
+
 
 check_daemon(D) ->
     check_daemon(D, D#daemon.name).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/aeca06f6/test/couchdb/fixtures/os_daemon_configer.escript
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_configer.escript b/test/couchdb/fixtures/os_daemon_configer.escript
new file mode 100755
index 0000000..d437423
--- /dev/null
+++ b/test/couchdb/fixtures/os_daemon_configer.escript
@@ -0,0 +1,101 @@
+#! /usr/bin/env escript
+
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-include("../couch_eunit.hrl").
+
+
+read() ->
+    case io:get_line('') of
+        eof ->
+            stop;
+        Data ->
+            ejson:decode(Data)
+    end.
+
+write(Mesg) ->
+    Data = iolist_to_binary(ejson:encode(Mesg)),
+    io:format(binary_to_list(Data) ++ "\n", []).
+
+get_cfg(Section) ->
+    write([<<"get">>, Section]),
+    read().
+
+get_cfg(Section, Name) ->
+    write([<<"get">>, Section, Name]),
+    read().
+
+log(Mesg) ->
+    write([<<"log">>, Mesg]).
+
+log(Mesg, Level) ->
+    write([<<"log">>, Mesg, {[{<<"level">>, Level}]}]).
+
+test_get_cfg1() ->
+    Path = list_to_binary(?FILE),
+    FileName = list_to_binary(filename:basename(?FILE)),
+    {[{FileName, Path}]} = get_cfg(<<"os_daemons">>).
+
+test_get_cfg2() ->
+    Path = list_to_binary(?FILE),
+    FileName = list_to_binary(filename:basename(?FILE)),
+    Path = get_cfg(<<"os_daemons">>, FileName),
+    <<"sequential">> = get_cfg(<<"uuids">>, <<"algorithm">>).
+
+
+test_get_unknown_cfg() ->
+    {[]} = get_cfg(<<"aal;3p4">>),
+    null = get_cfg(<<"aal;3p4">>, <<"313234kjhsdfl">>).
+
+test_log() ->
+    log(<<"foobar!">>),
+    log(<<"some stuff!">>, <<"debug">>),
+    log(2),
+    log(true),
+    write([<<"log">>, <<"stuff">>, 2]),
+    write([<<"log">>, 3, null]),
+    write([<<"log">>, [1, 2], {[{<<"level">>, <<"debug">>}]}]),
+    write([<<"log">>, <<"true">>, {[]}]).
+
+do_tests() ->
+    test_get_cfg1(),
+    test_get_cfg2(),
+    test_get_unknown_cfg(),
+    test_log(),
+    loop(io:read("")).
+
+loop({ok, _}) ->
+    loop(io:read(""));
+loop(eof) ->
+    init:stop();
+loop({error, _Reason}) ->
+    init:stop().
+
+main([]) ->
+    init_code_path(),
+    couch_config:start_link(?CONFIG_CHAIN),
+    couch_drv:start_link(),
+    do_tests().
+
+init_code_path() ->
+    Paths = [
+        "couchdb",
+        "ejson",
+        "erlang-oauth",
+        "ibrowse",
+        "mochiweb",
+        "snappy"
+    ],
+    lists:foreach(fun(Name) ->
+        code:add_patha(filename:join([?BUILDDIR, "src", Name]))
+    end, Paths).


[32/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Switch to using test_util:{start,stop}_couch


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/1db13965
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/1db13965
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/1db13965

Branch: refs/heads/1963-eunit-bigcouch
Commit: 1db1396512513052af3b5efbb70d254e042610a0
Parents: 86f1d34
Author: Russell Branca <ch...@apache.org>
Authored: Mon Aug 11 15:40:05 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:23:39 2014 -0700

----------------------------------------------------------------------
 test/couch_auth_cache_tests.erl         |  2 +-
 test/couch_changes_tests.erl            | 16 +---------------
 test/couch_db_tests.erl                 |  7 ++-----
 test/couch_util_tests.erl               |  4 ++--
 test/couchdb_attachments_tests.erl      | 16 +++-------------
 test/couchdb_compaction_daemon.erl      | 16 +++-------------
 test/couchdb_cors_tests.erl             | 16 +++-------------
 test/couchdb_csp_tests.erl              | 16 +---------------
 test/couchdb_file_compression_tests.erl | 16 +---------------
 test/couchdb_http_proxy_tests.erl       |  8 ++------
 test/couchdb_os_proc_pool.erl           | 16 +++-------------
 test/couchdb_update_conflicts_tests.erl | 16 +++-------------
 test/couchdb_vhosts_tests.erl           | 18 ++----------------
 test/couchdb_views_tests.erl            | 20 +++-----------------
 14 files changed, 30 insertions(+), 157 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couch_auth_cache_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_auth_cache_tests.erl b/test/couch_auth_cache_tests.erl
index b2c43c3..371cb63 100644
--- a/test/couch_auth_cache_tests.erl
+++ b/test/couch_auth_cache_tests.erl
@@ -36,7 +36,7 @@ couch_auth_cache_test_() ->
         "CouchDB auth cache tests",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun test_util:start_couch/0, fun test_util:stop_couch/1,
             {
                 foreach,
                 fun setup/0, fun teardown/1,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couch_changes_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_changes_tests.erl b/test/couch_changes_tests.erl
index 7fd4c55..3ae1b52 100644
--- a/test/couch_changes_tests.erl
+++ b/test/couch_changes_tests.erl
@@ -26,20 +26,6 @@
 }).
 
 
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
 setup() ->
     DbName = ?tempdb(),
     {ok, Db} = create_db(DbName),
@@ -71,7 +57,7 @@ changes_test_() ->
         "Changes feeed",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun test_util:start_couch/0, fun test_util:stop_couch/1,
             [
                 filter_by_doc_id(),
                 filter_by_design(),

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couch_db_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_db_tests.erl b/test/couch_db_tests.erl
index d1fdf06..90f0537 100644
--- a/test/couch_db_tests.erl
+++ b/test/couch_db_tests.erl
@@ -18,20 +18,17 @@
 
 
 setup() ->
-    {ok, _} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    ok = test_util:start_couch(),
     couch_config:set("log", "include_sasl", "false", false),
     ok.
 
-teardown(_) ->
-    couch_server_sup:stop().
-
 
 create_delete_db_test_()->
     {
         "Database create/delete tests",
         {
             setup,
-            fun setup/0, fun teardown/1,
+            fun setup/0, fun test_util:stop_couch/1,
             fun(_) ->
                 [should_create_db(),
                  should_delete_db(),

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couch_util_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_util_tests.erl b/test/couch_util_tests.erl
index 17e6942..c5689af 100644
--- a/test/couch_util_tests.erl
+++ b/test/couch_util_tests.erl
@@ -23,13 +23,13 @@ setup() ->
     %% On other hand, we cannot unload driver here due to
     %% {error, not_loaded_by_this_process} while it is. Any ideas is welcome.
     %%
-    couch_server_sup:start_link(?CONFIG_CHAIN),
+    ok = test_util:start_couch(),
     %% couch_config:start_link(?CONFIG_CHAIN),
     %% {ok, _} = couch_drv:start_link(),
     ok.
 
 teardown(_) ->
-    couch_server_sup:stop(),
+    ok = test_util:stop_couch(),
     %% couch_config:stop(),
     %% erl_ddll:unload_driver(couch_icu_driver),
     ok.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couchdb_attachments_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_attachments_tests.erl b/test/couchdb_attachments_tests.erl
index 70587f8..0391822 100644
--- a/test/couchdb_attachments_tests.erl
+++ b/test/couchdb_attachments_tests.erl
@@ -27,22 +27,12 @@
 
 
 start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    ok = test_util:start_couch(),
     % ensure in default compression settings for attachments_compression_tests
     couch_config:set("attachments", "compression_level",
                      ?i2l(?COMPRESSION_LEVEL), false),
     couch_config:set("attachments", "compressible_types", "text/*", false),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
+    ok.
 
 setup() ->
     DbName = ?tempdb(),
@@ -91,7 +81,7 @@ attachments_test_() ->
         "Attachments tests",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun start/0, fun test_util:stop_couch/1,
             [
                 attachments_md5_tests(),
                 attachments_compression_tests()

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couchdb_compaction_daemon.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_compaction_daemon.erl b/test/couchdb_compaction_daemon.erl
index 39e6118..de166a4 100644
--- a/test/couchdb_compaction_daemon.erl
+++ b/test/couchdb_compaction_daemon.erl
@@ -22,20 +22,10 @@
 
 
 start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    ok = test_util:start_couch(),
     couch_config:set("compaction_daemon", "check_interval", "3", false),
     couch_config:set("compaction_daemon", "min_file_size", "100000", false),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
+    ok.
 
 setup() ->
     DbName = ?tempdb(),
@@ -60,7 +50,7 @@ compaction_daemon_test_() ->
         "Compaction daemon tests",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun start/0, fun test_util:stop_couch/1,
             {
                 foreach,
                 fun setup/0, fun teardown/1,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couchdb_cors_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_cors_tests.erl b/test/couchdb_cors_tests.erl
index 9f927c4..336d5ee 100644
--- a/test/couchdb_cors_tests.erl
+++ b/test/couchdb_cors_tests.erl
@@ -23,20 +23,10 @@
 
 
 start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    ok = test_util:start_couch(),
     ok = couch_config:set("httpd", "enable_cors", "true", false),
     ok = couch_config:set("vhosts", "example.com", "/", false),
-    Pid.
-
-stop(Pid) ->
-    couch_server_sup:stop(),
-    erlang:monitor(process, Pid),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
+    ok.
 
 setup() ->
     DbName = ?tempdb(),
@@ -92,7 +82,7 @@ cors_test_() ->
         "CORS (COUCHDB-431)",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun start/0, fun test_util:stop_couch/1,
             [
                 cors_tests(Funs),
                 vhost_cors_tests(Funs),

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couchdb_csp_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_csp_tests.erl b/test/couchdb_csp_tests.erl
index 9733ad4..0626a08 100644
--- a/test/couchdb_csp_tests.erl
+++ b/test/couchdb_csp_tests.erl
@@ -17,20 +17,6 @@
 -define(TIMEOUT, 1000).
 
 
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    couch_server_sup:stop(),
-    erlang:monitor(process, Pid),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
 setup() ->
     ok = couch_config:set("csp", "enable", "true", false),
     Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
@@ -46,7 +32,7 @@ csp_test_() ->
         "Content Security Policy tests",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun test_util:start_couch/0, fun test_util:stop_couch/1,
             {
                 foreach,
                 fun setup/0, fun teardown/1,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couchdb_file_compression_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_file_compression_tests.erl b/test/couchdb_file_compression_tests.erl
index dc3f939..5573dd8 100644
--- a/test/couchdb_file_compression_tests.erl
+++ b/test/couchdb_file_compression_tests.erl
@@ -21,20 +21,6 @@
 -define(TIMEOUT, 30000).
 
 
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
 setup() ->
     couch_config:set("couchdb", "file_compression", "none", false),
     DbName = ?tempdb(),
@@ -65,7 +51,7 @@ couch_auth_cache_test_() ->
         "CouchDB file compression tests",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun test_util:start_couch/0, fun test_util:stop_couch/1,
             {
                 foreach,
                 fun setup/0, fun teardown/1,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couchdb_http_proxy_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_http_proxy_tests.erl b/test/couchdb_http_proxy_tests.erl
index 262eb15..0c554ff 100644
--- a/test/couchdb_http_proxy_tests.erl
+++ b/test/couchdb_http_proxy_tests.erl
@@ -31,17 +31,13 @@ start() ->
     % we have to write any config changes to temp ini file to not loose them
     % when supervisor will kill all children due to reaching restart threshold
     % (each httpd_global_handlers changes causes couch_httpd restart)
-    couch_server_sup:start_link(?CONFIG_CHAIN ++ [?CONFIG_FIXTURE_TEMP]),
+    ok = test_util:start_couch(test_util:config_files() ++ [?CONFIG_FIXTURE_TEMP]),
     % 49151 is IANA Reserved, let's assume no one is listening there
     couch_config:set("httpd_global_handlers", "_error",
         "{couch_httpd_proxy, handle_proxy_req, <<\"http://127.0.0.1:49151/\">>}"
     ),
     ok.
 
-stop(_) ->
-    couch_server_sup:stop(),
-    ok.
-
 setup() ->
     {ok, Pid} = test_web:start_link(),
     Value = lists:flatten(io_lib:format(
@@ -68,7 +64,7 @@ http_proxy_test_() ->
         "HTTP Proxy handler tests",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun start/0, fun test_util:stop_couch/1,
             {
                 foreach,
                 fun setup/0, fun teardown/1,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couchdb_os_proc_pool.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_os_proc_pool.erl b/test/couchdb_os_proc_pool.erl
index 1a37bd6..ae07d58 100644
--- a/test/couchdb_os_proc_pool.erl
+++ b/test/couchdb_os_proc_pool.erl
@@ -19,19 +19,9 @@
 
 
 start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    ok = test_util:start_couch(),
     couch_config:set("query_server_config", "os_process_limit", "3", false),
-    Pid.
-
-stop(Pid) ->
-    couch_server_sup:stop(),
-    erlang:monitor(process, Pid),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
+    ok.
 
 
 os_proc_pool_test_() ->
@@ -39,7 +29,7 @@ os_proc_pool_test_() ->
         "OS processes pool tests",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun start/0, fun test_util:stop_couch/1,
             [
                 should_block_new_proc_on_full_pool(),
                 should_free_slot_on_proc_unexpected_exit()

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couchdb_update_conflicts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_update_conflicts_tests.erl b/test/couchdb_update_conflicts_tests.erl
index adf1e5c..2450313 100644
--- a/test/couchdb_update_conflicts_tests.erl
+++ b/test/couchdb_update_conflicts_tests.erl
@@ -23,19 +23,9 @@
 
 
 start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    ok = test_util:start_couch(),
     couch_config:set("couchdb", "delayed_commits", "true", false),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
+    ok.
 
 setup() ->
     DbName = ?tempdb(),
@@ -61,7 +51,7 @@ view_indexes_cleanup_test_() ->
         "Update conflicts",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun start/0, fun test_util:stop_couch/1,
             [
                 concurrent_updates(),
                 couchdb_188()

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couchdb_vhosts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_vhosts_tests.erl b/test/couchdb_vhosts_tests.erl
index fc55c1c..7432022 100644
--- a/test/couchdb_vhosts_tests.erl
+++ b/test/couchdb_vhosts_tests.erl
@@ -20,20 +20,6 @@
 -define(iofmt(S, A), lists:flatten(io_lib:format(S, A))).
 
 
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
 setup() ->
     DbName = ?tempdb(),
     {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
@@ -111,7 +97,7 @@ vhosts_test_() ->
         "Virtual Hosts rewrite tests",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun test_util:start_couch/0, fun test_util:stop_couch/1,
             {
                 foreach,
                 fun setup/0, fun teardown/1,
@@ -138,7 +124,7 @@ oauth_test_() ->
         "Virtual Hosts OAuth tests",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun test_util:start_couch/0, fun test_util:stop_couch/1,
             {
                 foreach,
                 fun setup_oauth/0, fun teardown/1,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1db13965/test/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_views_tests.erl b/test/couchdb_views_tests.erl
index 24b5e5b..6efa7a1 100644
--- a/test/couchdb_views_tests.erl
+++ b/test/couchdb_views_tests.erl
@@ -21,20 +21,6 @@
 -define(TIMEOUT, 1000).
 
 
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
 setup() ->
     DbName = ?tempdb(),
     {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
@@ -65,7 +51,7 @@ view_indexes_cleanup_test_() ->
         "View indexes cleanup",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun test_util:start_couch/0, fun test_util:stop_couch/1,
             {
                 foreach,
                 fun setup/0, fun teardown/1,
@@ -83,7 +69,7 @@ view_group_db_leaks_test_() ->
         "View group db leaks",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun test_util:start_couch/0, fun test_util:stop_couch/1,
             {
                 foreach,
                 fun setup_with_docs/0, fun teardown/1,
@@ -100,7 +86,7 @@ view_group_shutdown_test_() ->
         "View group shutdown",
         {
             setup,
-            fun start/0, fun stop/1,
+            fun test_util:start_couch/0, fun test_util:stop_couch/1,
             [couchdb_1283()]
         }
     }.


[08/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 172-os-daemons-errors.t etap test suite to eunit

Merged into couchdb_os_daemons_tests suite.
Removed errors redirection to /dev/null to explicitly signal that
permissions are set correctly.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/1334a672
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/1334a672
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/1334a672

Branch: refs/heads/1963-eunit-bigcouch
Commit: 1334a672a56af9cc050d9cf12b9c63feac50cbee
Parents: aeca06f
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Jun 3 15:54:33 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_os_daemons_tests.erl      | 64 ++++++++++++++++++++-
 test/couchdb/fixtures/os_daemon_bad_perm.sh    | 17 ++++++
 test/couchdb/fixtures/os_daemon_can_reboot.sh  | 15 +++++
 test/couchdb/fixtures/os_daemon_die_on_boot.sh | 15 +++++
 test/couchdb/fixtures/os_daemon_die_quickly.sh | 15 +++++
 5 files changed, 125 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1334a672/test/couchdb/couchdb_os_daemons_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_os_daemons_tests.erl b/test/couchdb/couchdb_os_daemons_tests.erl
index dd07e82..aa949c9 100644
--- a/test/couchdb/couchdb_os_daemons_tests.erl
+++ b/test/couchdb/couchdb_os_daemons_tests.erl
@@ -28,6 +28,10 @@
 
 -define(DAEMON_CONFIGER, "os_daemon_configer.escript").
 -define(DAEMON_LOOPER, "os_daemon_looper.escript").
+-define(DAEMON_BAD_PERM, "os_daemon_bad_perm.sh").
+-define(DAEMON_CAN_REBOOT, "os_daemon_can_reboot.sh").
+-define(DAEMON_DIE_ON_BOOT, "os_daemon_die_on_boot.sh").
+-define(DAEMON_DIE_QUICKLY, "os_daemon_die_quickly.sh").
 -define(DELAY, 100).
 -define(TIMEOUT, 1000).
 
@@ -84,6 +88,20 @@ configuration_reader_test_() ->
             fun setup/1, fun teardown/2,
             [{?DAEMON_CONFIGER,
               fun should_read_write_config_settings_by_daemon/2}]
+
+        }
+    }.
+
+error_test_() ->
+    {
+        "OS Daemon process error tests",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{?DAEMON_BAD_PERM, fun should_fail_due_to_lack_of_permissions/2},
+             {?DAEMON_DIE_ON_BOOT, fun should_die_on_boot/2},
+             {?DAEMON_DIE_QUICKLY, fun should_die_quickly/2},
+             {?DAEMON_CAN_REBOOT, fun should_not_being_halted/2}]
         }
     }.
 
@@ -154,13 +172,57 @@ should_read_write_config_settings_by_daemon(DName, _) ->
         check_daemon(D, DName)
     end).
 
+should_fail_due_to_lack_of_permissions(DName, _) ->
+    ?_test(should_halts(DName, 1000)).
+
+should_die_on_boot(DName, _) ->
+    ?_test(should_halts(DName, 1000)).
+
+should_die_quickly(DName, _) ->
+    ?_test(should_halts(DName, 4000)).
+
+should_not_being_halted(DName, _) ->
+    ?_test(begin
+        timer:sleep(1000),
+        {ok, [D1]} = couch_os_daemons:info([table]),
+        check_daemon(D1, DName, 0),
+
+        % Should reboot every two seconds. We're at 1s, so wait
+        % until 3s to be in the middle of the next invocation's
+        % life span.
+
+        timer:sleep(2000),
+        {ok, [D2]} = couch_os_daemons:info([table]),
+        check_daemon(D2, DName, 1),
+
+        % If the kill command changed, that means we rebooted the process.
+        ?assertNotEqual(D1#daemon.kill, D2#daemon.kill)
+    end).
+
+should_halts(DName, Time) ->
+    timer:sleep(Time),
+    {ok, [D]} = couch_os_daemons:info([table]),
+    check_dead(D, DName),
+    couch_config:delete("os_daemons", DName, false).
 
 check_daemon(D) ->
     check_daemon(D, D#daemon.name).
 
 check_daemon(D, Name) ->
+    check_daemon(D, Name, 0).
+
+check_daemon(D, Name, Errs) ->
     ?assert(is_port(D#daemon.port)),
     ?assertEqual(Name, D#daemon.name),
     ?assertNotEqual(undefined, D#daemon.kill),
-    ?assertEqual([], D#daemon.errors),
+    ?assertEqual(running, D#daemon.status),
+    ?assertEqual(Errs, length(D#daemon.errors)),
     ?assertEqual([], D#daemon.buf).
+
+check_dead(D, Name) ->
+    ?assert(is_port(D#daemon.port)),
+    ?assertEqual(Name, D#daemon.name),
+    ?assertNotEqual(undefined, D#daemon.kill),
+    ?assertEqual(halted, D#daemon.status),
+    ?assertEqual(nil, D#daemon.errors),
+    ?assertEqual(nil, D#daemon.buf).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1334a672/test/couchdb/fixtures/os_daemon_bad_perm.sh
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_bad_perm.sh b/test/couchdb/fixtures/os_daemon_bad_perm.sh
new file mode 100644
index 0000000..345c8b4
--- /dev/null
+++ b/test/couchdb/fixtures/os_daemon_bad_perm.sh
@@ -0,0 +1,17 @@
+#!/bin/sh -e
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+# 
+# Please do not make this file executable as that's the error being tested.
+
+sleep 5

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1334a672/test/couchdb/fixtures/os_daemon_can_reboot.sh
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_can_reboot.sh b/test/couchdb/fixtures/os_daemon_can_reboot.sh
new file mode 100755
index 0000000..5bc10e8
--- /dev/null
+++ b/test/couchdb/fixtures/os_daemon_can_reboot.sh
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+sleep 2

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1334a672/test/couchdb/fixtures/os_daemon_die_on_boot.sh
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_die_on_boot.sh b/test/couchdb/fixtures/os_daemon_die_on_boot.sh
new file mode 100755
index 0000000..256ee79
--- /dev/null
+++ b/test/couchdb/fixtures/os_daemon_die_on_boot.sh
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+exit 1

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1334a672/test/couchdb/fixtures/os_daemon_die_quickly.sh
----------------------------------------------------------------------
diff --git a/test/couchdb/fixtures/os_daemon_die_quickly.sh b/test/couchdb/fixtures/os_daemon_die_quickly.sh
new file mode 100755
index 0000000..f5a1368
--- /dev/null
+++ b/test/couchdb/fixtures/os_daemon_die_quickly.sh
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+sleep 1


[28/42] Move files out of test/couchdb into top level test/ folder

Posted by ch...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_key_tree_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_key_tree_tests.erl b/test/couch_key_tree_tests.erl
new file mode 100644
index 0000000..753ecc4
--- /dev/null
+++ b/test/couch_key_tree_tests.erl
@@ -0,0 +1,380 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_key_tree_tests).
+
+-include("couch_eunit.hrl").
+
+-define(DEPTH, 10).
+
+
+key_tree_merge_test_()->
+    {
+        "Key tree merge",
+        [
+            should_merge_with_empty_tree(),
+            should_merge_reflexive(),
+            should_merge_prefix_of_a_tree_with_tree(),
+            should_produce_conflict_on_merge_with_unrelated_branch(),
+            should_merge_reflexive_for_child_nodes(),
+            should_merge_tree_to_itself(),
+            should_merge_tree_of_odd_length(),
+            should_merge_tree_with_stem(),
+            should_merge_with_stem_at_deeper_level(),
+            should_merge_with_stem_at_deeper_level_with_deeper_paths(),
+            should_merge_single_tree_with_deeper_stem(),
+            should_merge_tree_with_large_stem(),
+            should_merge_stems(),
+            should_create_conflicts_on_merge(),
+            should_create_no_conflicts_on_merge(),
+            should_ignore_conflicting_branch()
+        ]
+    }.
+
+key_tree_missing_leaves_test_()->
+    {
+        "Missing tree leaves",
+        [
+            should_not_find_missing_leaves(),
+            should_find_missing_leaves()
+        ]
+    }.
+
+key_tree_remove_leaves_test_()->
+    {
+        "Remove tree leaves",
+        [
+            should_have_no_effect_on_removing_no_leaves(),
+            should_have_no_effect_on_removing_non_existant_branch(),
+            should_remove_leaf(),
+            should_produce_empty_tree_on_removing_all_leaves(),
+            should_have_no_effect_on_removing_non_existant_node(),
+            should_produce_empty_tree_on_removing_last_leaf()
+        ]
+    }.
+
+key_tree_get_leaves_test_()->
+    {
+        "Leaves retrieving",
+        [
+            should_extract_subtree(),
+            should_extract_subsubtree(),
+            should_gather_non_existant_leaf(),
+            should_gather_leaf(),
+            shoul_gather_multiple_leaves(),
+            should_retrieve_full_key_path(),
+            should_retrieve_full_key_path_for_node(),
+            should_retrieve_leaves_with_parent_node(),
+            should_retrieve_all_leaves()
+        ]
+    }.
+
+key_tree_leaf_counting_test_()->
+    {
+        "Leaf counting",
+        [
+            should_have_no_leaves_for_empty_tree(),
+            should_have_single_leaf_for_tree_with_single_node(),
+            should_have_two_leaves_for_tree_with_chindler_siblings(),
+            should_not_affect_on_leaf_counting_for_stemmed_tree()
+        ]
+    }.
+
+key_tree_stemming_test_()->
+    {
+        "Stemming",
+        [
+            should_have_no_effect_for_stemming_more_levels_than_exists(),
+            should_return_one_deepest_node(),
+            should_return_two_deepest_nodes()
+        ]
+    }.
+
+
+should_merge_with_empty_tree()->
+    One = {1, {"1","foo",[]}},
+    ?_assertEqual({[One], no_conflicts},
+                  couch_key_tree:merge([], One, ?DEPTH)).
+
+should_merge_reflexive()->
+    One = {1, {"1","foo",[]}},
+    ?_assertEqual({[One], no_conflicts},
+                  couch_key_tree:merge([One], One, ?DEPTH)).
+
+should_merge_prefix_of_a_tree_with_tree()->
+    One = {1, {"1","foo",[]}},
+    TwoSibs = [{1, {"1","foo",[]}},
+               {1, {"2","foo",[]}}],
+    ?_assertEqual({TwoSibs, no_conflicts},
+                  couch_key_tree:merge(TwoSibs, One, ?DEPTH)).
+
+should_produce_conflict_on_merge_with_unrelated_branch()->
+    TwoSibs = [{1, {"1","foo",[]}},
+               {1, {"2","foo",[]}}],
+    Three = {1, {"3","foo",[]}},
+    ThreeSibs = [{1, {"1","foo",[]}},
+                 {1, {"2","foo",[]}},
+                 {1, {"3","foo",[]}}],
+    ?_assertEqual({ThreeSibs, conflicts},
+                  couch_key_tree:merge(TwoSibs, Three, ?DEPTH)).
+
+should_merge_reflexive_for_child_nodes()->
+    TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}},
+    ?_assertEqual({[TwoChild], no_conflicts},
+                  couch_key_tree:merge([TwoChild], TwoChild, ?DEPTH)).
+
+should_merge_tree_to_itself()->
+    TwoChildSibs = {1, {"1","foo", [{"1a", "bar", []},
+                                    {"1b", "bar", []}]}},
+    ?_assertEqual({[TwoChildSibs], no_conflicts},
+                  couch_key_tree:merge([TwoChildSibs], TwoChildSibs, ?DEPTH)).
+
+should_merge_tree_of_odd_length()->
+    TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}},
+    TwoChildSibs = {1, {"1","foo", [{"1a", "bar", []},
+                                    {"1b", "bar", []}]}},
+    TwoChildPlusSibs = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]},
+                                        {"1b", "bar", []}]}},
+
+    ?_assertEqual({[TwoChildPlusSibs], no_conflicts},
+                  couch_key_tree:merge([TwoChild], TwoChildSibs, ?DEPTH)).
+
+should_merge_tree_with_stem()->
+    Stemmed = {2, {"1a", "bar", []}},
+    TwoChildSibs = {1, {"1","foo", [{"1a", "bar", []},
+                                    {"1b", "bar", []}]}},
+
+    ?_assertEqual({[TwoChildSibs], no_conflicts},
+                  couch_key_tree:merge([TwoChildSibs], Stemmed, ?DEPTH)).
+
+should_merge_with_stem_at_deeper_level()->
+    Stemmed = {3, {"1bb", "boo", []}},
+    TwoChildSibs = {1, {"1","foo", [{"1a", "bar", []},
+                                    {"1b", "bar", [{"1bb", "boo", []}]}]}},
+    ?_assertEqual({[TwoChildSibs], no_conflicts},
+                  couch_key_tree:merge([TwoChildSibs], Stemmed, ?DEPTH)).
+
+should_merge_with_stem_at_deeper_level_with_deeper_paths()->
+    Stemmed = {3, {"1bb", "boo", []}},
+    StemmedTwoChildSibs = [{2,{"1a", "bar", []}},
+                           {2,{"1b", "bar", [{"1bb", "boo", []}]}}],
+    ?_assertEqual({StemmedTwoChildSibs, no_conflicts},
+                  couch_key_tree:merge(StemmedTwoChildSibs, Stemmed, ?DEPTH)).
+
+should_merge_single_tree_with_deeper_stem()->
+    Stemmed = {3, {"1aa", "bar", []}},
+    TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}},
+    ?_assertEqual({[TwoChild], no_conflicts},
+                  couch_key_tree:merge([TwoChild], Stemmed, ?DEPTH)).
+
+should_merge_tree_with_large_stem()->
+    Stemmed = {2, {"1a", "bar", [{"1aa", "bar", []}]}},
+    TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}},
+    ?_assertEqual({[TwoChild], no_conflicts},
+                  couch_key_tree:merge([TwoChild], Stemmed, ?DEPTH)).
+
+should_merge_stems()->
+    StemmedA = {2, {"1a", "bar", [{"1aa", "bar", []}]}},
+    StemmedB = {3, {"1aa", "bar", []}},
+    ?_assertEqual({[StemmedA], no_conflicts},
+                  couch_key_tree:merge([StemmedA], StemmedB, ?DEPTH)).
+
+should_create_conflicts_on_merge()->
+    OneChild = {1, {"1","foo",[{"1a", "bar", []}]}},
+    Stemmed = {3, {"1aa", "bar", []}},
+    ?_assertEqual({[OneChild, Stemmed], conflicts},
+                  couch_key_tree:merge([OneChild], Stemmed, ?DEPTH)).
+
+should_create_no_conflicts_on_merge()->
+    OneChild = {1, {"1","foo",[{"1a", "bar", []}]}},
+    Stemmed = {3, {"1aa", "bar", []}},
+    TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}},
+    ?_assertEqual({[TwoChild], no_conflicts},
+                  couch_key_tree:merge([OneChild, Stemmed], TwoChild, ?DEPTH)).
+
+should_ignore_conflicting_branch()->
+    %% this test is based on couch-902-test-case2.py
+    %% foo has conflicts from replication at depth two
+    %% foo3 is the current value
+    Foo = {1, {"foo",
+               "val1",
+               [{"foo2","val2",[]},
+                {"foo3", "val3", []}
+               ]}},
+    %% foo now has an attachment added, which leads to foo4 and val4
+    %% off foo3
+    Bar = {1, {"foo",
+               [],
+               [{"foo3",
+                 [],
+                 [{"foo4","val4",[]}
+                  ]}]}},
+    %% this is what the merge returns
+    %% note that it ignore the conflicting branch as there's no match
+    FooBar = {1, {"foo",
+               "val1",
+               [{"foo2","val2",[]},
+                {"foo3", "val3", [{"foo4","val4",[]}]}
+               ]}},
+    {
+        "COUCHDB-902",
+        ?_assertEqual({[FooBar], no_conflicts},
+                      couch_key_tree:merge([Foo], Bar, ?DEPTH))
+    }.
+
+should_not_find_missing_leaves()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual([],
+                  couch_key_tree:find_missing(TwoChildSibs,
+                                              [{0,"1"}, {1,"1a"}])).
+
+should_find_missing_leaves()->
+    Stemmed1 = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+    Stemmed2 = [{2, {"1aa", "bar", []}}],
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    [
+        ?_assertEqual(
+            [{0, "10"}, {100, "x"}],
+            couch_key_tree:find_missing(
+                TwoChildSibs,
+                [{0,"1"}, {0, "10"}, {1,"1a"}, {100, "x"}])),
+        ?_assertEqual(
+            [{0, "1"}, {100, "x"}],
+            couch_key_tree:find_missing(
+                Stemmed1,
+                [{0,"1"}, {1,"1a"}, {100, "x"}])),
+        ?_assertEqual(
+            [{0, "1"}, {1,"1a"}, {100, "x"}],
+            couch_key_tree:find_missing(
+                Stemmed2,
+                [{0,"1"}, {1,"1a"}, {100, "x"}]))
+    ].
+
+should_have_no_effect_on_removing_no_leaves()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({TwoChildSibs, []},
+                  couch_key_tree:remove_leafs(TwoChildSibs,
+                                              [])).
+
+should_have_no_effect_on_removing_non_existant_branch()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({TwoChildSibs, []},
+                  couch_key_tree:remove_leafs(TwoChildSibs,
+                                              [{0, "1"}])).
+
+should_remove_leaf()->
+    OneChild = [{0, {"1","foo",[{"1a", "bar", []}]}}],
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({OneChild, [{1, "1b"}]},
+                  couch_key_tree:remove_leafs(TwoChildSibs,
+                                              [{1, "1b"}])).
+
+should_produce_empty_tree_on_removing_all_leaves()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({[], [{1, "1b"}, {1, "1a"}]},
+                  couch_key_tree:remove_leafs(TwoChildSibs,
+                                              [{1, "1b"}, {1, "1a"}])).
+
+should_have_no_effect_on_removing_non_existant_node()->
+    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+    ?_assertEqual({Stemmed, []},
+                  couch_key_tree:remove_leafs(Stemmed,
+                                              [{1, "1a"}])).
+
+should_produce_empty_tree_on_removing_last_leaf()->
+    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+    ?_assertEqual({[], [{2, "1aa"}]},
+                  couch_key_tree:remove_leafs(Stemmed,
+                                              [{2, "1aa"}])).
+
+should_extract_subtree()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({[{"foo", {0, ["1"]}}],[]},
+                  couch_key_tree:get(TwoChildSibs, [{0, "1"}])).
+
+should_extract_subsubtree()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({[{"bar", {1, ["1a", "1"]}}],[]},
+                  couch_key_tree:get(TwoChildSibs, [{1, "1a"}])).
+
+should_gather_non_existant_leaf()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({[],[{0, "x"}]},
+                  couch_key_tree:get_key_leafs(TwoChildSibs, [{0, "x"}])).
+
+should_gather_leaf()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({[{"bar", {1, ["1a","1"]}}],[]},
+                  couch_key_tree:get_key_leafs(TwoChildSibs, [{1, "1a"}])).
+
+shoul_gather_multiple_leaves()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({[{"bar", {1, ["1a","1"]}},{"bar",{1, ["1b","1"]}}],[]},
+                  couch_key_tree:get_key_leafs(TwoChildSibs, [{0, "1"}])).
+
+should_retrieve_full_key_path()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({[{0,[{"1", "foo"}]}],[]},
+                  couch_key_tree:get_full_key_paths(TwoChildSibs, [{0, "1"}])).
+
+should_retrieve_full_key_path_for_node()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual({[{1,[{"1a", "bar"},{"1", "foo"}]}],[]},
+                  couch_key_tree:get_full_key_paths(TwoChildSibs, [{1, "1a"}])).
+
+should_retrieve_leaves_with_parent_node()->
+    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    [
+        ?_assertEqual([{2, [{"1aa", "bar"},{"1a", "bar"}]}],
+                      couch_key_tree:get_all_leafs_full(Stemmed)),
+        ?_assertEqual([{1, [{"1a", "bar"},{"1", "foo"}]},
+                       {1, [{"1b", "bar"},{"1", "foo"}]}],
+                      couch_key_tree:get_all_leafs_full(TwoChildSibs))
+    ].
+
+should_retrieve_all_leaves()->
+    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    [
+        ?_assertEqual([{"bar", {2, ["1aa","1a"]}}],
+                      couch_key_tree:get_all_leafs(Stemmed)),
+        ?_assertEqual([{"bar", {1, ["1a", "1"]}}, {"bar", {1, ["1b","1"]}}],
+                      couch_key_tree:get_all_leafs(TwoChildSibs))
+    ].
+
+should_have_no_leaves_for_empty_tree()->
+    ?_assertEqual(0, couch_key_tree:count_leafs([])).
+
+should_have_single_leaf_for_tree_with_single_node()->
+    ?_assertEqual(1, couch_key_tree:count_leafs([{0, {"1","foo",[]}}])).
+
+should_have_two_leaves_for_tree_with_chindler_siblings()->
+    TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}],
+    ?_assertEqual(2, couch_key_tree:count_leafs(TwoChildSibs)).
+
+should_not_affect_on_leaf_counting_for_stemmed_tree()->
+    ?_assertEqual(1, couch_key_tree:count_leafs([{2, {"1bb", "boo", []}}])).
+
+should_have_no_effect_for_stemming_more_levels_than_exists()->
+    TwoChild = [{0, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}],
+    ?_assertEqual(TwoChild, couch_key_tree:stem(TwoChild, 3)).
+
+should_return_one_deepest_node()->
+    TwoChild = [{0, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}],
+    Stemmed = [{2, {"1aa", "bar", []}}],
+    ?_assertEqual(Stemmed, couch_key_tree:stem(TwoChild, 1)).
+
+should_return_two_deepest_nodes()->
+    TwoChild = [{0, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}],
+    Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}],
+    ?_assertEqual(Stemmed, couch_key_tree:stem(TwoChild, 2)).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_passwords_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_passwords_tests.erl b/test/couch_passwords_tests.erl
new file mode 100644
index 0000000..116265c
--- /dev/null
+++ b/test/couch_passwords_tests.erl
@@ -0,0 +1,54 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_passwords_tests).
+
+-include("couch_eunit.hrl").
+
+
+pbkdf2_test_()->
+    {"PBKDF2",
+     [
+         {"Iterations: 1, length: 20",
+          ?_assertEqual(
+              {ok, <<"0c60c80f961f0e71f3a9b524af6012062fe037a6">>},
+              couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 1, 20))},
+
+         {"Iterations: 2, length: 20",
+          ?_assertEqual(
+              {ok, <<"ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957">>},
+              couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 2, 20))},
+
+         {"Iterations: 4096, length: 20",
+          ?_assertEqual(
+              {ok, <<"4b007901b765489abead49d926f721d065a429c1">>},
+              couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 4096, 20))},
+
+         {"Iterations: 4096, length: 25",
+          ?_assertEqual(
+              {ok, <<"3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038">>},
+              couch_passwords:pbkdf2(<<"passwordPASSWORDpassword">>,
+                                     <<"saltSALTsaltSALTsaltSALTsaltSALTsalt">>,
+                                     4096, 25))},
+         {"Null byte",
+          ?_assertEqual(
+              {ok, <<"56fa6aa75548099dcc37d7f03425e0c3">>},
+              couch_passwords:pbkdf2(<<"pass\0word">>,
+                                     <<"sa\0lt">>,
+                                     4096, 16))},
+
+         {timeout, 180,  %% this may runs too long on slow hosts
+          {"Iterations: 16777216 - this may take some time",
+           ?_assertEqual(
+               {ok, <<"eefe3d61cd4da4e4e9945b3d6ba2158c2634e984">>},
+               couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 16777216, 20)
+           )}}]}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_ref_counter_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_ref_counter_tests.erl b/test/couch_ref_counter_tests.erl
new file mode 100644
index 0000000..b7e97b4
--- /dev/null
+++ b/test/couch_ref_counter_tests.erl
@@ -0,0 +1,107 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_ref_counter_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(TIMEOUT, 1000).
+
+
+setup() ->
+    {ok, RefCtr} = couch_ref_counter:start([]),
+    ChildPid = spawn(fun() -> loop() end),
+    {RefCtr, ChildPid}.
+
+teardown({_, ChildPid}) ->
+    erlang:monitor(process, ChildPid),
+    ChildPid ! close,
+    wait().
+
+
+couch_ref_counter_test_() ->
+    {
+        "CouchDB reference counter tests",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_initialize_with_calling_process_as_referrer/1,
+                fun should_ignore_unknown_pid/1,
+                fun should_increment_counter_on_pid_add/1,
+                fun should_not_increase_counter_on_readding_same_pid/1,
+                fun should_drop_ref_for_double_added_pid/1,
+                fun should_decrement_counter_on_pid_drop/1,
+                fun should_add_after_drop/1,
+                fun should_decrement_counter_on_process_exit/1
+
+            ]
+        }
+    }.
+
+
+should_initialize_with_calling_process_as_referrer({RefCtr, _}) ->
+    ?_assertEqual(1, couch_ref_counter:count(RefCtr)).
+
+should_ignore_unknown_pid({RefCtr, ChildPid}) ->
+    ?_assertEqual(ok, couch_ref_counter:drop(RefCtr, ChildPid)).
+
+should_increment_counter_on_pid_add({RefCtr, ChildPid}) ->
+    couch_ref_counter:add(RefCtr, ChildPid),
+    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
+
+should_not_increase_counter_on_readding_same_pid({RefCtr, ChildPid}) ->
+    couch_ref_counter:add(RefCtr, ChildPid),
+    couch_ref_counter:add(RefCtr, ChildPid),
+    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
+
+should_drop_ref_for_double_added_pid({RefCtr, ChildPid}) ->
+    couch_ref_counter:add(RefCtr, ChildPid),
+    couch_ref_counter:add(RefCtr, ChildPid),
+    couch_ref_counter:drop(RefCtr, ChildPid),
+    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
+
+should_decrement_counter_on_pid_drop({RefCtr, ChildPid}) ->
+    couch_ref_counter:add(RefCtr, ChildPid),
+    couch_ref_counter:drop(RefCtr, ChildPid),
+    ?_assertEqual(1, couch_ref_counter:count(RefCtr)).
+
+should_add_after_drop({RefCtr, ChildPid}) ->
+    couch_ref_counter:add(RefCtr, ChildPid),
+    couch_ref_counter:drop(RefCtr, ChildPid),
+    couch_ref_counter:add(RefCtr, ChildPid),
+    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
+
+should_decrement_counter_on_process_exit({RefCtr, ChildPid}) ->
+    ?_assertEqual(1,
+        begin
+            couch_ref_counter:add(RefCtr, ChildPid),
+            erlang:monitor(process, ChildPid),
+            ChildPid ! close,
+            wait(),
+            couch_ref_counter:count(RefCtr)
+        end).
+
+
+loop() ->
+    receive
+        close -> ok
+    end.
+
+wait() ->
+    receive
+        {'DOWN', _, _, _, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw(timeout_error)
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_stats_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_stats_tests.erl b/test/couch_stats_tests.erl
new file mode 100644
index 0000000..d156449
--- /dev/null
+++ b/test/couch_stats_tests.erl
@@ -0,0 +1,412 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_stats_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(STATS_CFG_FIXTURE,
+    filename:join([?FIXTURESDIR, "couch_stats_aggregates.cfg"])).
+-define(STATS_INI_FIXTURE,
+    filename:join([?FIXTURESDIR, "couch_stats_aggregates.ini"])).
+-define(TIMEOUT, 1000).
+-define(TIMEWAIT, 500).
+
+
+setup_collector() ->
+    couch_stats_collector:start(),
+    ok.
+
+setup_aggregator(_) ->
+    {ok, Pid} = couch_config:start_link([?STATS_INI_FIXTURE]),
+    {ok, _} = couch_stats_collector:start(),
+    {ok, _} = couch_stats_aggregator:start(?STATS_CFG_FIXTURE),
+    Pid.
+
+teardown_collector(_) ->
+    couch_stats_collector:stop(),
+    ok.
+
+teardown_aggregator(_, Pid) ->
+    couch_stats_aggregator:stop(),
+    couch_stats_collector:stop(),
+    erlang:monitor(process, Pid),
+    couch_config:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, config_stop})
+    end,
+    ok.
+
+
+couch_stats_collector_test_() ->
+    {
+        "CouchDB stats collector tests",
+        {
+            foreach,
+            fun setup_collector/0, fun teardown_collector/1,
+            [
+                should_increment_counter(),
+                should_decrement_counter(),
+                should_increment_and_decrement_counter(),
+                should_record_absolute_values(),
+                should_clear_absolute_values(),
+                should_track_process_count(),
+                should_increment_counter_multiple_times_per_pid(),
+                should_decrement_counter_on_process_exit(),
+                should_decrement_for_each_track_process_count_call_on_exit(),
+                should_return_all_counters_and_absolute_values(),
+                should_return_incremental_counters(),
+                should_return_absolute_values()
+            ]
+        }
+    }.
+
+couch_stats_aggregator_test_() ->
+    Funs = [
+        fun should_init_empty_aggregate/2,
+        fun should_get_empty_aggregate/2,
+        fun should_change_stats_on_values_add/2,
+        fun should_change_stats_for_all_times_on_values_add/2,
+        fun should_change_stats_on_values_change/2,
+        fun should_change_stats_for_all_times_on_values_change/2,
+        fun should_not_remove_data_after_some_time_for_0_sample/2,
+        fun should_remove_data_after_some_time_for_other_samples/2
+    ],
+    {
+        "CouchDB stats aggregator tests",
+        [
+            {
+                "Absolute values",
+                {
+                    foreachx,
+                    fun setup_aggregator/1, fun teardown_aggregator/2,
+                    [{absolute, Fun} || Fun <- Funs]
+                }
+            },
+            {
+                "Counters",
+                {
+                    foreachx,
+                    fun setup_aggregator/1, fun teardown_aggregator/2,
+                    [{counter, Fun} || Fun <- Funs]
+                }
+            }
+        ]
+    }.
+
+
+should_increment_counter() ->
+    ?_assertEqual(100,
+        begin
+            AddCount = fun() -> couch_stats_collector:increment(foo) end,
+            repeat(AddCount, 100),
+            couch_stats_collector:get(foo)
+        end).
+
+should_decrement_counter() ->
+    ?_assertEqual(67,
+        begin
+            AddCount = fun() -> couch_stats_collector:increment(foo) end,
+            RemCount = fun() -> couch_stats_collector:decrement(foo) end,
+            repeat(AddCount, 100),
+            repeat(RemCount, 33),
+            couch_stats_collector:get(foo)
+        end).
+
+should_increment_and_decrement_counter() ->
+    ?_assertEqual(0,
+        begin
+            AddCount = fun() -> couch_stats_collector:increment(foo) end,
+            RemCount = fun() -> couch_stats_collector:decrement(foo) end,
+            repeat(AddCount, 100),
+            repeat(RemCount, 25),
+            repeat(AddCount, 10),
+            repeat(RemCount, 5),
+            repeat(RemCount, 80),
+            couch_stats_collector:get(foo)
+        end).
+
+should_record_absolute_values() ->
+    ?_assertEqual(lists:seq(1, 15),
+        begin
+            lists:map(fun(Val) ->
+                couch_stats_collector:record(bar, Val)
+            end, lists:seq(1, 15)),
+            couch_stats_collector:get(bar)
+        end).
+
+should_clear_absolute_values() ->
+    ?_assertEqual(nil,
+        begin
+            lists:map(fun(Val) ->
+                couch_stats_collector:record(bar, Val)
+            end, lists:seq(1, 15)),
+            couch_stats_collector:clear(bar),
+            couch_stats_collector:get(bar)
+        end).
+
+should_track_process_count() ->
+    ?_assertMatch({_, 1}, spawn_and_count(1)).
+
+should_increment_counter_multiple_times_per_pid() ->
+    ?_assertMatch({_, 3}, spawn_and_count(3)).
+
+should_decrement_counter_on_process_exit() ->
+    ?_assertEqual(2,
+        begin
+            {Pid, 1} = spawn_and_count(1),
+            spawn_and_count(2),
+            RefMon = erlang:monitor(process, Pid),
+            Pid ! sepuku,
+            receive
+                {'DOWN', RefMon, _, _, _} -> ok
+            after ?TIMEOUT ->
+                throw(timeout)
+            end,
+            % sleep for awhile to let collector handle the updates
+            % suddenly, it couldn't notice process death instantly
+            timer:sleep(?TIMEWAIT),
+            couch_stats_collector:get(hoopla)
+        end).
+
+should_decrement_for_each_track_process_count_call_on_exit() ->
+    ?_assertEqual(2,
+        begin
+            {_, 2} = spawn_and_count(2),
+            {Pid, 6} = spawn_and_count(4),
+            RefMon = erlang:monitor(process, Pid),
+            Pid ! sepuku,
+            receive
+                {'DOWN', RefMon, _, _, _} -> ok
+            after ?TIMEOUT ->
+                throw(timeout)
+            end,
+            timer:sleep(?TIMEWAIT),
+            couch_stats_collector:get(hoopla)
+        end).
+
+should_return_all_counters_and_absolute_values() ->
+    ?_assertEqual([{bar,[1.0,0.0]}, {foo,1}],
+        begin
+            couch_stats_collector:record(bar, 0.0),
+            couch_stats_collector:record(bar, 1.0),
+            couch_stats_collector:increment(foo),
+            lists:sort(couch_stats_collector:all())
+        end).
+
+should_return_incremental_counters() ->
+    ?_assertEqual([{foo,1}],
+        begin
+            couch_stats_collector:record(bar, 0.0),
+            couch_stats_collector:record(bar, 1.0),
+            couch_stats_collector:increment(foo),
+            lists:sort(couch_stats_collector:all(incremental))
+        end).
+
+should_return_absolute_values() ->
+    ?_assertEqual([{bar,[1.0,0.0]}, {zing, "Z"}],
+        begin
+            couch_stats_collector:record(bar, 0.0),
+            couch_stats_collector:record(bar, 1.0),
+            couch_stats_collector:record(zing, 90),
+            couch_stats_collector:increment(foo),
+            lists:sort(couch_stats_collector:all(absolute))
+        end).
+
+should_init_empty_aggregate(absolute, _) ->
+    {Aggs} = couch_stats_aggregator:all(),
+    ?_assertEqual({[{'11', make_agg(<<"randomosity">>,
+                                    null, null, null, null, null)}]},
+                  couch_util:get_value(number, Aggs));
+should_init_empty_aggregate(counter, _) ->
+    {Aggs} = couch_stats_aggregator:all(),
+    ?_assertEqual({[{stuff, make_agg(<<"yay description">>,
+                                     null, null, null, null, null)}]},
+                  couch_util:get_value(testing, Aggs)).
+
+should_get_empty_aggregate(absolute, _) ->
+    ?_assertEqual(make_agg(<<"randomosity">>, null, null, null, null, null),
+             couch_stats_aggregator:get_json({number, '11'}));
+should_get_empty_aggregate(counter, _) ->
+    ?_assertEqual(make_agg(<<"yay description">>, null, null, null, null, null),
+             couch_stats_aggregator:get_json({testing, stuff})).
+
+should_change_stats_on_values_add(absolute, _) ->
+    lists:foreach(fun(X) ->
+        couch_stats_collector:record({number, 11}, X)
+    end, lists:seq(0, 10)),
+    couch_stats_aggregator:collect_sample(),
+    ?_assertEqual(make_agg(<<"randomosity">>, 5.0, 5.0, null, 5.0, 5.0),
+                  couch_stats_aggregator:get_json({number, 11}));
+should_change_stats_on_values_add(counter, _) ->
+    lists:foreach(fun(_) ->
+        couch_stats_collector:increment({testing, stuff})
+    end, lists:seq(1, 100)),
+    couch_stats_aggregator:collect_sample(),
+    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 100.0, null, 100, 100),
+                  couch_stats_aggregator:get_json({testing, stuff})).
+
+should_change_stats_for_all_times_on_values_add(absolute, _) ->
+    lists:foreach(fun(X) ->
+        couch_stats_collector:record({number, 11}, X)
+    end, lists:seq(0, 10)),
+    couch_stats_aggregator:collect_sample(),
+    ?_assertEqual(make_agg(<<"randomosity">>, 5.0, 5.0, null, 5.0, 5.0),
+                  couch_stats_aggregator:get_json({number, 11}, 1));
+should_change_stats_for_all_times_on_values_add(counter, _) ->
+    lists:foreach(fun(_) ->
+        couch_stats_collector:increment({testing, stuff})
+    end, lists:seq(1, 100)),
+    couch_stats_aggregator:collect_sample(),
+    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 100.0, null, 100, 100),
+                  couch_stats_aggregator:get_json({testing, stuff}, 1)).
+
+should_change_stats_on_values_change(absolute, _) ->
+    ?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
+        begin
+            lists:foreach(fun(X) ->
+                couch_stats_collector:record({number, 11}, X)
+            end, lists:seq(0, 10)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_collector:record({number, 11}, 15),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({number, 11})
+        end);
+should_change_stats_on_values_change(counter, _) ->
+    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 50.0, 70.711, 0, 100),
+        begin
+            lists:foreach(fun(_) ->
+                couch_stats_collector:increment({testing, stuff})
+            end, lists:seq(1, 100)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({testing, stuff})
+        end).
+
+should_change_stats_for_all_times_on_values_change(absolute, _) ->
+    ?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
+        begin
+            lists:foreach(fun(X) ->
+                couch_stats_collector:record({number, 11}, X)
+            end, lists:seq(0, 10)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_collector:record({number, 11}, 15),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({number, 11}, 1)
+        end);
+should_change_stats_for_all_times_on_values_change(counter, _) ->
+    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 50.0, 70.711, 0, 100),
+        begin
+            lists:foreach(fun(_) ->
+                couch_stats_collector:increment({testing, stuff})
+            end, lists:seq(1, 100)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({testing, stuff}, 1)
+        end).
+
+should_not_remove_data_after_some_time_for_0_sample(absolute, _) ->
+    ?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
+        begin
+            lists:foreach(fun(X) ->
+                couch_stats_collector:record({number, 11}, X)
+            end, lists:seq(0, 10)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_collector:record({number, 11}, 15),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({number, 11})
+        end);
+should_not_remove_data_after_some_time_for_0_sample(counter, _) ->
+    ?_assertEqual(make_agg(<<"yay description">>, 100.0, 33.333, 57.735, 0, 100),
+        begin
+            lists:foreach(fun(_) ->
+                couch_stats_collector:increment({testing, stuff})
+            end, lists:seq(1, 100)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({testing, stuff})
+        end).
+
+should_remove_data_after_some_time_for_other_samples(absolute, _) ->
+    ?_assertEqual(make_agg(<<"randomosity">>, 15.0, 15.0, null, 15.0, 15.0),
+        begin
+            lists:foreach(fun(X) ->
+                couch_stats_collector:record({number, 11}, X)
+            end, lists:seq(0, 10)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_collector:record({number, 11}, 15),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({number, 11}, 1)
+        end);
+should_remove_data_after_some_time_for_other_samples(counter, _) ->
+    ?_assertEqual(make_agg(<<"yay description">>, 0, 0.0, 0.0, 0, 0),
+        begin
+            lists:foreach(fun(_) ->
+                couch_stats_collector:increment({testing, stuff})
+            end, lists:seq(1, 100)),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            timer:sleep(?TIMEWAIT),
+            couch_stats_aggregator:collect_sample(),
+            couch_stats_aggregator:get_json({testing, stuff}, 1)
+        end).
+
+
+spawn_and_count(N) ->
+    Self = self(),
+    Pid = spawn(fun() ->
+        lists:foreach(
+            fun(_) ->
+                couch_stats_collector:track_process_count(hoopla)
+            end, lists:seq(1,N)),
+        Self ! reporting,
+        receive
+            sepuku -> ok
+        end
+    end),
+    receive reporting -> ok end,
+    {Pid, couch_stats_collector:get(hoopla)}.
+
+repeat(_, 0) ->
+    ok;
+repeat(Fun, Count) ->
+    Fun(),
+    repeat(Fun, Count-1).
+
+make_agg(Desc, Sum, Mean, StdDev, Min, Max) ->
+    {[
+        {description, Desc},
+        {current, Sum},
+        {sum, Sum},
+        {mean, Mean},
+        {stddev, StdDev},
+        {min, Min},
+        {max, Max}
+    ]}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_stream_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_stream_tests.erl b/test/couch_stream_tests.erl
new file mode 100644
index 0000000..335a2fe
--- /dev/null
+++ b/test/couch_stream_tests.erl
@@ -0,0 +1,100 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_stream_tests).
+
+-include("couch_eunit.hrl").
+
+
+setup() ->
+    {ok, Fd} = couch_file:open(?tempfile(), [create, overwrite]),
+    {ok, Stream} = couch_stream:open(Fd),
+    {Fd, Stream}.
+
+teardown({Fd, _}) ->
+    ok = couch_file:close(Fd).
+
+
+stream_test_() ->
+    {
+        "CouchDB stream tests",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_write/1,
+                fun should_write_consecutive/1,
+                fun should_write_empty_binary/1,
+                fun should_return_file_pointers_on_close/1,
+                fun should_return_stream_size_on_close/1,
+                fun should_return_valid_pointers/1,
+                fun should_recall_last_pointer_position/1,
+                fun should_stream_more_with_4K_chunk_size/1
+            ]
+        }
+    }.
+
+
+should_write({_, Stream}) ->
+    ?_assertEqual(ok, couch_stream:write(Stream, <<"food">>)).
+
+should_write_consecutive({_, Stream}) ->
+    couch_stream:write(Stream, <<"food">>),
+    ?_assertEqual(ok, couch_stream:write(Stream, <<"foob">>)).
+
+should_write_empty_binary({_, Stream}) ->
+    ?_assertEqual(ok, couch_stream:write(Stream, <<>>)).
+
+should_return_file_pointers_on_close({_, Stream}) ->
+    couch_stream:write(Stream, <<"foodfoob">>),
+    {Ptrs, _, _, _, _} = couch_stream:close(Stream),
+    ?_assertEqual([{0, 8}], Ptrs).
+
+should_return_stream_size_on_close({_, Stream}) ->
+    couch_stream:write(Stream, <<"foodfoob">>),
+    {_, Length, _, _, _} = couch_stream:close(Stream),
+    ?_assertEqual(8, Length).
+
+should_return_valid_pointers({Fd, Stream}) ->
+    couch_stream:write(Stream, <<"foodfoob">>),
+    {Ptrs, _, _, _, _} = couch_stream:close(Stream),
+    ?_assertEqual(<<"foodfoob">>, read_all(Fd, Ptrs)).
+
+should_recall_last_pointer_position({Fd, Stream}) ->
+    couch_stream:write(Stream, <<"foodfoob">>),
+    {_, _, _, _, _} = couch_stream:close(Stream),
+    {ok, ExpPtr} = couch_file:bytes(Fd),
+    {ok, Stream2} = couch_stream:open(Fd),
+    ZeroBits = <<0:(8 * 10)>>,
+    OneBits = <<1:(8 * 10)>>,
+    ok = couch_stream:write(Stream2, OneBits),
+    ok = couch_stream:write(Stream2, ZeroBits),
+    {Ptrs, 20, _, _, _} = couch_stream:close(Stream2),
+    [{ExpPtr, 20}] = Ptrs,
+    AllBits = iolist_to_binary([OneBits, ZeroBits]),
+    ?_assertEqual(AllBits, read_all(Fd, Ptrs)).
+
+should_stream_more_with_4K_chunk_size({Fd, _}) ->
+    {ok, Stream} = couch_stream:open(Fd, [{buffer_size, 4096}]),
+    lists:foldl(
+        fun(_, Acc) ->
+            Data = <<"a1b2c">>,
+            couch_stream:write(Stream, Data),
+            [Data | Acc]
+        end, [], lists:seq(1, 1024)),
+    ?_assertMatch({[{0, 4100}, {4106, 1020}], 5120, _, _, _},
+                  couch_stream:close(Stream)).
+
+
+read_all(Fd, PosList) ->
+    Data = couch_stream:foldl(Fd, PosList, fun(Bin, Acc) -> [Bin, Acc] end, []),
+    iolist_to_binary(Data).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_task_status_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_task_status_tests.erl b/test/couch_task_status_tests.erl
new file mode 100644
index 0000000..f71ad2b
--- /dev/null
+++ b/test/couch_task_status_tests.erl
@@ -0,0 +1,225 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_task_status_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(TIMEOUT, 1000).
+
+
+setup() ->
+    {ok, TaskStatusPid} = couch_task_status:start_link(),
+    TaskUpdaterPid = spawn(fun() -> loop() end),
+    {TaskStatusPid, TaskUpdaterPid}.
+
+teardown({TaskStatusPid, _}) ->
+    erlang:monitor(process, TaskStatusPid),
+    couch_task_status:stop(),
+    receive
+        {'DOWN', _, _, TaskStatusPid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw(timeout_error)
+    end.
+
+
+couch_task_status_test_() ->
+    {
+        "CouchDB task status updates",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_register_task/1,
+                fun should_set_task_startup_time/1,
+                fun should_have_update_time_as_startup_before_any_progress/1,
+                fun should_set_task_type/1,
+                fun should_not_register_multiple_tasks_for_same_pid/1,
+                fun should_set_task_progress/1,
+                fun should_update_task_progress/1,
+                fun should_update_time_changes_on_task_progress/1,
+                fun should_control_update_frequency/1,
+                fun should_reset_control_update_frequency/1,
+                fun should_track_multiple_tasks/1,
+                fun should_finish_task/1
+
+            ]
+        }
+    }.
+
+
+should_register_task({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?_assertEqual(1, length(couch_task_status:all())).
+
+should_set_task_startup_time({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?_assert(is_integer(get_task_prop(Pid, started_on))).
+
+should_have_update_time_as_startup_before_any_progress({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    StartTime = get_task_prop(Pid, started_on),
+    ?_assertEqual(StartTime, get_task_prop(Pid, updated_on)).
+
+should_set_task_type({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?_assertEqual(replication, get_task_prop(Pid, type)).
+
+should_not_register_multiple_tasks_for_same_pid({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?_assertEqual({add_task_error, already_registered},
+                  call(Pid, add, [{type, compaction}, {progress, 0}])).
+
+should_set_task_progress({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?_assertEqual(0, get_task_prop(Pid, progress)).
+
+should_update_task_progress({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    call(Pid, update, [{progress, 25}]),
+    ?_assertEqual(25, get_task_prop(Pid, progress)).
+
+should_update_time_changes_on_task_progress({_, Pid}) ->
+    ?_assert(
+        begin
+            ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+            ok = timer:sleep(1000),  % sleep awhile to customize update time
+            call(Pid, update, [{progress, 25}]),
+            get_task_prop(Pid, updated_on) > get_task_prop(Pid, started_on)
+        end).
+
+should_control_update_frequency({_, Pid}) ->
+    ?_assertEqual(66,
+        begin
+            ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+            call(Pid, update, [{progress, 50}]),
+            call(Pid, update_frequency, 500),
+            call(Pid, update, [{progress, 66}]),
+            call(Pid, update, [{progress, 77}]),
+            get_task_prop(Pid, progress)
+        end).
+
+should_reset_control_update_frequency({_, Pid}) ->
+    ?_assertEqual(87,
+        begin
+            ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+            call(Pid, update, [{progress, 50}]),
+            call(Pid, update_frequency, 500),
+            call(Pid, update, [{progress, 66}]),
+            call(Pid, update, [{progress, 77}]),
+            call(Pid, update_frequency, 0),
+            call(Pid, update, [{progress, 87}]),
+            get_task_prop(Pid, progress)
+        end).
+
+should_track_multiple_tasks(_) ->
+    ?_assert(run_multiple_tasks()).
+
+should_finish_task({_, Pid}) ->
+    ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+    ?assertEqual(1, length(couch_task_status:all())),
+    ok = call(Pid, done),
+    ?_assertEqual(0, length(couch_task_status:all())).
+
+
+run_multiple_tasks() ->
+    Pid1 = spawn(fun() -> loop() end),
+    Pid2 = spawn(fun() -> loop() end),
+    Pid3 = spawn(fun() -> loop() end),
+    call(Pid1, add, [{type, replication}, {progress, 0}]),
+    call(Pid2, add, [{type, compaction}, {progress, 0}]),
+    call(Pid3, add, [{type, indexer}, {progress, 0}]),
+
+    ?assertEqual(3, length(couch_task_status:all())),
+    ?assertEqual(replication, get_task_prop(Pid1, type)),
+    ?assertEqual(compaction, get_task_prop(Pid2, type)),
+    ?assertEqual(indexer, get_task_prop(Pid3, type)),
+
+    call(Pid2, update, [{progress, 33}]),
+    call(Pid3, update, [{progress, 42}]),
+    call(Pid1, update, [{progress, 11}]),
+    ?assertEqual(42, get_task_prop(Pid3, progress)),
+    call(Pid1, update, [{progress, 72}]),
+    ?assertEqual(72, get_task_prop(Pid1, progress)),
+    ?assertEqual(33, get_task_prop(Pid2, progress)),
+
+    call(Pid1, done),
+    ?assertEqual(2, length(couch_task_status:all())),
+    call(Pid3, done),
+    ?assertEqual(1, length(couch_task_status:all())),
+    call(Pid2, done),
+    ?assertEqual(0, length(couch_task_status:all())),
+
+    true.
+
+
+loop() ->
+    receive
+        {add, Props, From} ->
+            Resp = couch_task_status:add_task(Props),
+            From ! {ok, self(), Resp},
+            loop();
+        {update, Props, From} ->
+            Resp = couch_task_status:update(Props),
+            From ! {ok, self(), Resp},
+            loop();
+        {update_frequency, Msecs, From} ->
+            Resp = couch_task_status:set_update_frequency(Msecs),
+            From ! {ok, self(), Resp},
+            loop();
+        {done, From} ->
+            From ! {ok, self(), ok}
+    end.
+
+call(Pid, Command) ->
+    Pid ! {Command, self()},
+    wait(Pid).
+
+call(Pid, Command, Arg) ->
+    Pid ! {Command, Arg, self()},
+    wait(Pid).
+
+wait(Pid) ->
+    receive
+        {ok, Pid, Msg} ->
+            Msg
+    after ?TIMEOUT ->
+        throw(timeout_error)
+    end.
+
+get_task_prop(Pid, Prop) ->
+    From = list_to_binary(pid_to_list(Pid)),
+    Element = lists:foldl(
+        fun(PropList, Acc) ->
+            case couch_util:get_value(pid, PropList) of
+                From ->
+                    [PropList | Acc];
+                _ ->
+                    Acc
+            end
+        end,
+        [], couch_task_status:all()
+    ),
+    case couch_util:get_value(Prop, hd(Element), nil) of
+        nil ->
+            erlang:error({assertion_failed,
+                         [{module, ?MODULE},
+                          {line, ?LINE},
+                          {reason, "Could not get property '"
+                                   ++ couch_util:to_list(Prop)
+                                   ++ "' for task "
+                                   ++ pid_to_list(Pid)}]});
+        Value ->
+            Value
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_util_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_util_tests.erl b/test/couch_util_tests.erl
new file mode 100644
index 0000000..8e24e72
--- /dev/null
+++ b/test/couch_util_tests.erl
@@ -0,0 +1,136 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_util_tests).
+
+-include("couch_eunit.hrl").
+
+
+setup() ->
+    %% We cannot start driver from here since it becomes bounded to eunit
+    %% master process and the next couch_server_sup:start_link call will
+    %% fail because server couldn't load driver since it already is.
+    %%
+    %% On other hand, we cannot unload driver here due to
+    %% {error, not_loaded_by_this_process} while it is. Any ideas is welcome.
+    %%
+    couch_server_sup:start_link(?CONFIG_CHAIN),
+    %% couch_config:start_link(?CONFIG_CHAIN),
+    %% {ok, _} = couch_drv:start_link(),
+    ok.
+
+teardown(_) ->
+    couch_server_sup:stop(),
+    %% couch_config:stop(),
+    %% erl_ddll:unload_driver(couch_icu_driver),
+    ok.
+
+
+collation_test_() ->
+    {
+        "Collation tests",
+        [
+            {
+                setup,
+                fun setup/0, fun teardown/1,
+                [
+                    should_collate_ascii(),
+                    should_collate_non_ascii()
+                ]
+            }
+        ]
+    }.
+
+should_collate_ascii() ->
+    ?_assertEqual(1, couch_util:collate(<<"foo">>, <<"bar">>)).
+
+should_collate_non_ascii() ->
+    ?_assertEqual(-1, couch_util:collate(<<"A">>, <<"aa">>)).
+
+to_existed_atom_test() ->
+    ?assert(couch_util:to_existing_atom(true)),
+    ?assertMatch(foo, couch_util:to_existing_atom(<<"foo">>)),
+    ?assertMatch(foobarbaz, couch_util:to_existing_atom("foobarbaz")).
+
+implode_test() ->
+    ?assertEqual([1, 38, 2, 38, 3], couch_util:implode([1, 2, 3], "&")).
+
+trim_test() ->
+    lists:map(fun(S) -> ?assertEqual("foo", couch_util:trim(S)) end,
+              [" foo", "foo ", "\tfoo", " foo ", "foo\t", "foo\n", "\nfoo"]).
+
+abs_pathname_test() ->
+    {ok, Cwd} = file:get_cwd(),
+    ?assertEqual(Cwd ++ "/foo", couch_util:abs_pathname("./foo")).
+
+flush_test() ->
+    ?assertNot(couch_util:should_flush()),
+    AcquireMem = fun() ->
+        _IntsToAGazillion = lists:seq(1, 200000),
+        _LotsOfData = lists:map(fun(_) -> <<"foobar">> end,
+                                lists:seq(1, 500000)),
+        _BigBin = list_to_binary(_LotsOfData),
+
+        %% Allocation 200K tuples puts us above the memory threshold
+        %% Originally, there should be:
+        %%      ?assertNot(should_flush())
+        %% however, unlike for etap test, GC collects all allocated bits
+        %% making this conditions fail. So we have to invert the condition
+        %% since GC works, cleans the memory and everything is fine.
+        ?assertNot(couch_util:should_flush())
+    end,
+    AcquireMem(),
+
+    %% Checking to flush invokes GC
+    ?assertNot(couch_util:should_flush()).
+
+verify_test() ->
+    ?assert(couch_util:verify("It4Vooya", "It4Vooya")),
+    ?assertNot(couch_util:verify("It4VooyaX", "It4Vooya")),
+    ?assert(couch_util:verify(<<"ahBase3r">>, <<"ahBase3r">>)),
+    ?assertNot(couch_util:verify(<<"ahBase3rX">>, <<"ahBase3r">>)),
+    ?assertNot(couch_util:verify(nil, <<"ahBase3r">>)).
+
+find_in_binary_test_() ->
+    Cases = [
+        {<<"foo">>, <<"foobar">>, {exact, 0}},
+        {<<"foo">>, <<"foofoo">>, {exact, 0}},
+        {<<"foo">>, <<"barfoo">>, {exact, 3}},
+        {<<"foo">>, <<"barfo">>, {partial, 3}},
+        {<<"f">>, <<"fobarfff">>, {exact, 0}},
+        {<<"f">>, <<"obarfff">>, {exact, 4}},
+        {<<"f">>, <<"obarggf">>, {exact, 6}},
+        {<<"f">>, <<"f">>, {exact, 0}},
+        {<<"f">>, <<"g">>, not_found},
+        {<<"foo">>, <<"f">>, {partial, 0}},
+        {<<"foo">>, <<"g">>, not_found},
+        {<<"foo">>, <<"">>, not_found},
+        {<<"fofo">>, <<"foofo">>, {partial, 3}},
+        {<<"foo">>, <<"gfobarfo">>, {partial, 6}},
+        {<<"foo">>, <<"gfobarf">>, {partial, 6}},
+        {<<"foo">>, <<"gfobar">>, not_found},
+        {<<"fog">>, <<"gbarfogquiz">>, {exact, 4}},
+        {<<"ggg">>, <<"ggg">>, {exact, 0}},
+        {<<"ggg">>, <<"ggggg">>, {exact, 0}},
+        {<<"ggg">>, <<"bggg">>, {exact, 1}},
+        {<<"ggg">>, <<"bbgg">>, {partial, 2}},
+        {<<"ggg">>, <<"bbbg">>, {partial, 3}},
+        {<<"ggg">>, <<"bgbggbggg">>, {exact, 6}},
+        {<<"ggg">>, <<"bgbggb">>, not_found}
+    ],
+    lists:map(
+        fun({Needle, Haystack, Result}) ->
+            Msg = lists:flatten(io_lib:format("Looking for ~s in ~s",
+                                              [Needle, Haystack])),
+            {Msg, ?_assertMatch(Result,
+                                couch_util:find_in_binary(Needle, Haystack))}
+        end, Cases).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_uuids_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_uuids_tests.erl b/test/couch_uuids_tests.erl
new file mode 100644
index 0000000..ea1d034
--- /dev/null
+++ b/test/couch_uuids_tests.erl
@@ -0,0 +1,161 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_uuids_tests).
+
+-include("couch_eunit.hrl").
+
+-define(TIMEOUT_S, 20).
+
+
+setup() ->
+    {ok, Pid} = couch_config:start_link(?CONFIG_CHAIN),
+    erlang:monitor(process, Pid),
+    couch_uuids:start(),
+    Pid.
+
+setup(Opts) ->
+    Pid = setup(),
+    lists:foreach(
+        fun({Option, Value}) ->
+            couch_config:set("uuids", Option, Value, false)
+        end, Opts),
+    Pid.
+
+teardown(Pid) ->
+    couch_uuids:stop(),
+    couch_config:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} -> ok
+    after
+        1000 -> throw({timeout_error, config_stop})
+    end.
+
+teardown(_, Pid) ->
+    teardown(Pid).
+
+
+default_test_() ->
+    {
+        "Default UUID algorithm",
+        {
+            setup,
+            fun setup/0, fun teardown/1,
+            fun should_be_unique/1
+        }
+    }.
+
+sequential_test_() ->
+    Opts = [{"algorithm", "sequential"}],
+    Cases = [
+        fun should_be_unique/2,
+        fun should_increment_monotonically/2,
+        fun should_rollover/2
+    ],
+    {
+        "UUID algorithm: sequential",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{Opts, Fun} || Fun <- Cases]
+        }
+    }.
+
+utc_test_() ->
+    Opts = [{"algorithm", "utc_random"}],
+    Cases = [
+        fun should_be_unique/2,
+        fun should_increment_monotonically/2
+    ],
+    {
+        "UUID algorithm: utc_random",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{Opts, Fun} || Fun <- Cases]
+        }
+    }.
+
+utc_id_suffix_test_() ->
+    Opts = [{"algorithm", "utc_id"}, {"utc_id_suffix", "bozo"}],
+    Cases = [
+        fun should_be_unique/2,
+        fun should_increment_monotonically/2,
+        fun should_preserve_suffix/2
+    ],
+    {
+        "UUID algorithm: utc_id",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{Opts, Fun} || Fun <- Cases]
+        }
+    }.
+
+
+should_be_unique() ->
+    %% this one may really runs for too long on slow hosts
+    {timeout, ?TIMEOUT_S, ?_assert(test_unique(10000, [couch_uuids:new()]))}.
+should_be_unique(_) ->
+    should_be_unique().
+should_be_unique(_, _) ->
+    should_be_unique().
+
+should_increment_monotonically(_, _) ->
+    ?_assert(couch_uuids:new() < couch_uuids:new()).
+
+should_rollover(_, _) ->
+    ?_test(begin
+        UUID = binary_to_list(couch_uuids:new()),
+        Prefix = element(1, lists:split(26, UUID)),
+        N = gen_until_pref_change(Prefix, 0),
+        ?assert(N >= 5000 andalso N =< 11000)
+    end).
+
+should_preserve_suffix(_, _) ->
+    ?_test(begin
+        UUID = binary_to_list(couch_uuids:new()),
+        Suffix = get_suffix(UUID),
+        ?assert(test_same_suffix(10000, Suffix))
+    end).
+
+
+test_unique(0, _) ->
+    true;
+test_unique(N, UUIDs) ->
+    UUID = couch_uuids:new(),
+    ?assertNot(lists:member(UUID, UUIDs)),
+    test_unique(N - 1, [UUID| UUIDs]).
+
+get_prefix(UUID) ->
+    element(1, lists:split(26, binary_to_list(UUID))).
+
+gen_until_pref_change(_, Count) when Count > 8251 ->
+    Count;
+gen_until_pref_change(Prefix, N) ->
+    case get_prefix(couch_uuids:new()) of
+        Prefix -> gen_until_pref_change(Prefix, N + 1);
+        _ -> N
+    end.
+
+get_suffix(UUID) when is_binary(UUID) ->
+    get_suffix(binary_to_list(UUID));
+get_suffix(UUID) ->
+    element(2, lists:split(14, UUID)).
+
+test_same_suffix(0, _) ->
+    true;
+test_same_suffix(N, Suffix) ->
+    case get_suffix(couch_uuids:new()) of
+        Suffix -> test_same_suffix(N - 1, Suffix);
+        _ -> false
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couch_work_queue_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_work_queue_tests.erl b/test/couch_work_queue_tests.erl
new file mode 100644
index 0000000..8a463b5
--- /dev/null
+++ b/test/couch_work_queue_tests.erl
@@ -0,0 +1,393 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_work_queue_tests).
+
+-include("couch_eunit.hrl").
+
+-define(TIMEOUT, 100).
+
+
+setup(Opts) ->
+    {ok, Q} = couch_work_queue:new(Opts),
+    Producer = spawn_producer(Q),
+    Consumer = spawn_consumer(Q),
+    {Q, Producer, Consumer}.
+
+setup_max_items() ->
+    setup([{max_items, 3}]).
+
+setup_max_size() ->
+    setup([{max_size, 160}]).
+
+setup_max_items_and_size() ->
+    setup([{max_size, 160}, {max_items, 3}]).
+
+setup_multi_workers() ->
+    {Q, Producer, Consumer1} = setup([{max_size, 160},
+                                      {max_items, 3},
+                                      {multi_workers, true}]),
+    Consumer2 = spawn_consumer(Q),
+    Consumer3 = spawn_consumer(Q),
+    {Q, Producer, [Consumer1, Consumer2, Consumer3]}.
+
+teardown({Q, Producer, Consumers}) when is_list(Consumers) ->
+    % consume all to unblock and let producer/consumer stop without timeout
+    [consume(Consumer, all) || Consumer <- Consumers],
+
+    ok = close_queue(Q),
+    ok = stop(Producer, "producer"),
+    R = [stop(Consumer, "consumer") || Consumer <- Consumers],
+    R = [ok || _ <- Consumers],
+    ok;
+teardown({Q, Producer, Consumer}) ->
+    teardown({Q, Producer, [Consumer]}).
+
+
+single_consumer_test_() ->
+    {
+        "Single producer and consumer",
+        [
+            {
+                "Queue with 3 max items",
+                {
+                    foreach,
+                    fun setup_max_items/0, fun teardown/1,
+                    single_consumer_max_item_count() ++ common_cases()
+                }
+            },
+            {
+                "Queue with max size of 160 bytes",
+                {
+                    foreach,
+                    fun setup_max_size/0, fun teardown/1,
+                    single_consumer_max_size() ++ common_cases()
+                }
+            },
+            {
+                "Queue with max size of 160 bytes and 3 max items",
+                {
+                    foreach,
+                    fun setup_max_items_and_size/0, fun teardown/1,
+                    single_consumer_max_items_and_size() ++ common_cases()
+                }
+            }
+        ]
+    }.
+
+multiple_consumers_test_() ->
+    {
+        "Single producer and multiple consumers",
+        [
+            {
+                "Queue with max size of 160 bytes and 3 max items",
+                {
+                    foreach,
+                    fun setup_multi_workers/0, fun teardown/1,
+                    common_cases() ++ multiple_consumers()
+                }
+
+            }
+        ]
+    }.
+
+common_cases()->
+    [
+        fun should_block_consumer_on_dequeue_from_empty_queue/1,
+        fun should_consume_right_item/1,
+        fun should_timeout_on_close_non_empty_queue/1,
+        fun should_not_block_producer_for_non_empty_queue_after_close/1,
+        fun should_be_closed/1
+    ].
+
+single_consumer_max_item_count()->
+    [
+        fun should_have_no_items_for_new_queue/1,
+        fun should_block_producer_on_full_queue_count/1,
+        fun should_receive_first_queued_item/1,
+        fun should_consume_multiple_items/1,
+        fun should_consume_all/1
+    ].
+
+single_consumer_max_size()->
+    [
+        fun should_have_zero_size_for_new_queue/1,
+        fun should_block_producer_on_full_queue_size/1,
+        fun should_increase_queue_size_on_produce/1,
+        fun should_receive_first_queued_item/1,
+        fun should_consume_multiple_items/1,
+        fun should_consume_all/1
+    ].
+
+single_consumer_max_items_and_size() ->
+    single_consumer_max_item_count() ++ single_consumer_max_size().
+
+multiple_consumers() ->
+    [
+        fun should_have_zero_size_for_new_queue/1,
+        fun should_have_no_items_for_new_queue/1,
+        fun should_increase_queue_size_on_produce/1
+    ].
+
+
+should_have_no_items_for_new_queue({Q, _, _}) ->
+    ?_assertEqual(0, couch_work_queue:item_count(Q)).
+
+should_have_zero_size_for_new_queue({Q, _, _}) ->
+    ?_assertEqual(0, couch_work_queue:size(Q)).
+
+should_block_consumer_on_dequeue_from_empty_queue({_, _, Consumers}) when is_list(Consumers) ->
+    [consume(C, 2) || C <- Consumers],
+    Pongs = [ping(C) || C <- Consumers],
+    ?_assertEqual([timeout, timeout, timeout], Pongs);
+should_block_consumer_on_dequeue_from_empty_queue({_, _, Consumer}) ->
+    consume(Consumer, 1),
+    Pong = ping(Consumer),
+    ?_assertEqual(timeout, Pong).
+
+should_consume_right_item({Q, Producer, Consumers}) when is_list(Consumers) ->
+    [consume(C, 3) || C <- Consumers],
+
+    Item1 = produce(Producer, 10),
+    ok = ping(Producer),
+    ?assertEqual(0, couch_work_queue:item_count(Q)),
+    ?assertEqual(0, couch_work_queue:size(Q)),
+
+    Item2 = produce(Producer, 10),
+    ok = ping(Producer),
+    ?assertEqual(0, couch_work_queue:item_count(Q)),
+    ?assertEqual(0, couch_work_queue:size(Q)),
+
+    Item3 = produce(Producer, 10),
+    ok = ping(Producer),
+    ?assertEqual(0, couch_work_queue:item_count(Q)),
+    ?assertEqual(0, couch_work_queue:size(Q)),
+
+    R = [{ping(C), Item}
+         || {C, Item} <- lists:zip(Consumers, [Item1, Item2, Item3])],
+
+    ?_assertEqual([{ok, Item1}, {ok, Item2}, {ok, Item3}], R);
+should_consume_right_item({_, Producer, Consumer}) ->
+    consume(Consumer, 1),
+    Item = produce(Producer, 10),
+    produce(Producer, 20),
+    ok = ping(Producer),
+    ok = ping(Consumer),
+    {ok, Items} = last_consumer_items(Consumer),
+    ?_assertEqual([Item], Items).
+
+should_increase_queue_size_on_produce({Q, Producer, _}) ->
+    produce(Producer, 50),
+    ok = ping(Producer),
+    Count1 = couch_work_queue:item_count(Q),
+    Size1 = couch_work_queue:size(Q),
+
+    produce(Producer, 10),
+    Count2 = couch_work_queue:item_count(Q),
+    Size2 = couch_work_queue:size(Q),
+
+    ?_assertEqual([{Count1, Size1}, {Count2, Size2}], [{1, 50}, {2, 60}]).
+
+should_block_producer_on_full_queue_count({Q, Producer, _}) ->
+    produce(Producer, 10),
+    ?assertEqual(1, couch_work_queue:item_count(Q)),
+    ok = ping(Producer),
+
+    produce(Producer, 15),
+    ?assertEqual(2, couch_work_queue:item_count(Q)),
+    ok = ping(Producer),
+
+    produce(Producer, 20),
+    ?assertEqual(3, couch_work_queue:item_count(Q)),
+    Pong = ping(Producer),
+
+    ?_assertEqual(timeout, Pong).
+
+should_block_producer_on_full_queue_size({Q, Producer, _}) ->
+    produce(Producer, 100),
+    ok = ping(Producer),
+    ?assertEqual(1, couch_work_queue:item_count(Q)),
+    ?assertEqual(100, couch_work_queue:size(Q)),
+
+    produce(Producer, 110),
+    Pong = ping(Producer),
+    ?assertEqual(2, couch_work_queue:item_count(Q)),
+    ?assertEqual(210, couch_work_queue:size(Q)),
+
+    ?_assertEqual(timeout, Pong).
+
+should_consume_multiple_items({_, Producer, Consumer}) ->
+    Item1 = produce(Producer, 10),
+    ok = ping(Producer),
+
+    Item2 = produce(Producer, 15),
+    ok = ping(Producer),
+
+    consume(Consumer, 2),
+
+    {ok, Items} = last_consumer_items(Consumer),
+    ?_assertEqual([Item1, Item2], Items).
+
+should_receive_first_queued_item({Q, Producer, Consumer}) ->
+    consume(Consumer, 100),
+    timeout = ping(Consumer),
+
+    Item = produce(Producer, 11),
+    ok = ping(Producer),
+
+    ok = ping(Consumer),
+    ?assertEqual(0, couch_work_queue:item_count(Q)),
+
+    {ok, Items} = last_consumer_items(Consumer),
+    ?_assertEqual([Item], Items).
+
+should_consume_all({_, Producer, Consumer}) ->
+    Item1 = produce(Producer, 10),
+    Item2 = produce(Producer, 15),
+    Item3 = produce(Producer, 20),
+
+    consume(Consumer, all),
+
+    {ok, Items} = last_consumer_items(Consumer),
+    ?_assertEqual([Item1, Item2, Item3], Items).
+
+should_timeout_on_close_non_empty_queue({Q, Producer, _}) ->
+    produce(Producer, 1),
+    Status = close_queue(Q),
+
+    ?_assertEqual(timeout, Status).
+
+should_not_block_producer_for_non_empty_queue_after_close({Q, Producer, _}) ->
+    produce(Producer, 1),
+    close_queue(Q),
+    Pong = ping(Producer),
+    Size = couch_work_queue:size(Q),
+    Count = couch_work_queue:item_count(Q),
+
+    ?_assertEqual({ok, 1, 1}, {Pong, Size, Count}).
+
+should_be_closed({Q, _, Consumers}) when is_list(Consumers) ->
+    ok = close_queue(Q),
+
+    [consume(C, 1) || C <- Consumers],
+
+    LastConsumerItems = [last_consumer_items(C) || C <- Consumers],
+    ItemsCount = couch_work_queue:item_count(Q),
+    Size = couch_work_queue:size(Q),
+
+    ?_assertEqual({[closed, closed, closed], closed, closed},
+                  {LastConsumerItems, ItemsCount, Size});
+should_be_closed({Q, _, Consumer}) ->
+    ok = close_queue(Q),
+
+    consume(Consumer, 1),
+
+    LastConsumerItems = last_consumer_items(Consumer),
+    ItemsCount = couch_work_queue:item_count(Q),
+    Size = couch_work_queue:size(Q),
+
+    ?_assertEqual({closed, closed, closed},
+                  {LastConsumerItems, ItemsCount, Size}).
+
+
+close_queue(Q) ->
+    ok = couch_work_queue:close(Q),
+    MonRef = erlang:monitor(process, Q),
+    receive
+        {'DOWN', MonRef, process, Q, _Reason} -> ok
+    after ?TIMEOUT ->
+        erlang:demonitor(MonRef),
+        timeout
+    end.
+
+spawn_consumer(Q) ->
+    Parent = self(),
+    spawn(fun() -> consumer_loop(Parent, Q, nil) end).
+
+consumer_loop(Parent, Q, PrevItem) ->
+    receive
+        {stop, Ref} ->
+            Parent ! {ok, Ref};
+        {ping, Ref} ->
+            Parent ! {pong, Ref},
+            consumer_loop(Parent, Q, PrevItem);
+        {last_item, Ref} ->
+            Parent ! {item, Ref, PrevItem},
+            consumer_loop(Parent, Q, PrevItem);
+        {consume, N} ->
+            Result = couch_work_queue:dequeue(Q, N),
+            consumer_loop(Parent, Q, Result)
+    end.
+
+spawn_producer(Q) ->
+    Parent = self(),
+    spawn(fun() -> producer_loop(Parent, Q) end).
+
+producer_loop(Parent, Q) ->
+    receive
+        {stop, Ref} ->
+            Parent ! {ok, Ref};
+        {ping, Ref} ->
+            Parent ! {pong, Ref},
+            producer_loop(Parent, Q);
+        {produce, Ref, Size} ->
+            Item = crypto:rand_bytes(Size),
+            Parent ! {item, Ref, Item},
+            ok = couch_work_queue:queue(Q, Item),
+            producer_loop(Parent, Q)
+    end.
+
+consume(Consumer, N) ->
+    Consumer ! {consume, N}.
+
+last_consumer_items(Consumer) ->
+    Ref = make_ref(),
+    Consumer ! {last_item, Ref},
+    receive
+        {item, Ref, Items} ->
+            Items
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+produce(Producer, Size) ->
+    Ref = make_ref(),
+    Producer ! {produce, Ref, Size},
+    receive
+        {item, Ref, Item} ->
+            Item
+    after ?TIMEOUT ->
+        erlang:error({assertion_failed,
+                      [{module, ?MODULE},
+                       {line, ?LINE},
+                       {reason, "Timeout asking producer to produce an item"}]})
+    end.
+
+ping(Pid) ->
+    Ref = make_ref(),
+    Pid ! {ping, Ref},
+    receive
+        {pong, Ref} ->
+            ok
+    after ?TIMEOUT ->
+        timeout
+    end.
+
+stop(Pid, Name) ->
+    Ref = make_ref(),
+    Pid ! {stop, Ref},
+    receive
+        {ok, Ref} -> ok
+    after ?TIMEOUT ->
+        ?debugMsg("Timeout stopping " ++ Name),
+        timeout
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e40ca37c/test/couchdb/couch_auth_cache_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_auth_cache_tests.erl b/test/couchdb/couch_auth_cache_tests.erl
deleted file mode 100644
index 3b2321c..0000000
--- a/test/couchdb/couch_auth_cache_tests.erl
+++ /dev/null
@@ -1,238 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_auth_cache_tests).
-
--include("couch_eunit.hrl").
--include_lib("couchdb/couch_db.hrl").
-
--define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
--define(SALT, <<"SALT">>).
--define(TIMEOUT, 1000).
-
-
-start() ->
-    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
-    Pid.
-
-stop(Pid) ->
-    erlang:monitor(process, Pid),
-    couch_server_sup:stop(),
-    receive
-        {'DOWN', _, _, Pid, _} ->
-            ok
-    after ?TIMEOUT ->
-        throw({timeout, server_stop})
-    end.
-
-setup() ->
-    DbName = ?tempdb(),
-    couch_config:set("couch_httpd_auth", "authentication_db",
-                     ?b2l(DbName), false),
-    DbName.
-
-teardown(DbName) ->
-    ok = couch_server:delete(DbName, [?ADMIN_USER]),
-    ok.
-
-
-couch_auth_cache_test_() ->
-    {
-        "CouchDB auth cache tests",
-        {
-            setup,
-            fun start/0, fun stop/1,
-            {
-                foreach,
-                fun setup/0, fun teardown/1,
-                [
-                    fun should_get_nil_on_missed_cache/1,
-                    fun should_get_right_password_hash/1,
-                    fun should_ensure_doc_hash_equals_cached_one/1,
-                    fun should_update_password/1,
-                    fun should_cleanup_cache_after_userdoc_deletion/1,
-                    fun should_restore_cache_after_userdoc_recreation/1,
-                    fun should_drop_cache_on_auth_db_change/1,
-                    fun should_restore_cache_on_auth_db_change/1,
-                    fun should_recover_cache_after_shutdown/1
-                ]
-            }
-        }
-    }.
-
-
-should_get_nil_on_missed_cache(_) ->
-    ?_assertEqual(nil, couch_auth_cache:get_user_creds("joe")).
-
-should_get_right_password_hash(DbName) ->
-    ?_test(begin
-        PasswordHash = hash_password("pass1"),
-        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
-        Creds = couch_auth_cache:get_user_creds("joe"),
-        ?assertEqual(PasswordHash,
-                      couch_util:get_value(<<"password_sha">>, Creds))
-    end).
-
-should_ensure_doc_hash_equals_cached_one(DbName) ->
-    ?_test(begin
-        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
-        Creds = couch_auth_cache:get_user_creds("joe"),
-
-        CachedHash = couch_util:get_value(<<"password_sha">>, Creds),
-        StoredHash = get_user_doc_password_sha(DbName, "joe"),
-        ?assertEqual(StoredHash, CachedHash)
-    end).
-
-should_update_password(DbName) ->
-    ?_test(begin
-        PasswordHash = hash_password("pass2"),
-        {ok, Rev} = update_user_doc(DbName, "joe", "pass1"),
-        {ok, _} = update_user_doc(DbName, "joe", "pass2", Rev),
-        Creds = couch_auth_cache:get_user_creds("joe"),
-        ?assertEqual(PasswordHash,
-                      couch_util:get_value(<<"password_sha">>, Creds))
-    end).
-
-should_cleanup_cache_after_userdoc_deletion(DbName) ->
-    ?_test(begin
-        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
-        delete_user_doc(DbName, "joe"),
-        ?assertEqual(nil, couch_auth_cache:get_user_creds("joe"))
-    end).
-
-should_restore_cache_after_userdoc_recreation(DbName) ->
-    ?_test(begin
-        PasswordHash = hash_password("pass5"),
-        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
-        delete_user_doc(DbName, "joe"),
-        ?assertEqual(nil, couch_auth_cache:get_user_creds("joe")),
-
-        {ok, _} = update_user_doc(DbName, "joe", "pass5"),
-        Creds = couch_auth_cache:get_user_creds("joe"),
-
-        ?assertEqual(PasswordHash,
-                      couch_util:get_value(<<"password_sha">>, Creds))
-    end).
-
-should_drop_cache_on_auth_db_change(DbName) ->
-    ?_test(begin
-        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
-        full_commit(DbName),
-        couch_config:set("couch_httpd_auth", "authentication_db",
-                         ?b2l(?tempdb()), false),
-        ?assertEqual(nil, couch_auth_cache:get_user_creds("joe"))
-    end).
-
-should_restore_cache_on_auth_db_change(DbName) ->
-    ?_test(begin
-        PasswordHash = hash_password("pass1"),
-        {ok, _} = update_user_doc(DbName, "joe", "pass1"),
-        Creds = couch_auth_cache:get_user_creds("joe"),
-        full_commit(DbName),
-
-        DbName1 = ?tempdb(),
-        couch_config:set("couch_httpd_auth", "authentication_db",
-                         ?b2l(DbName1), false),
-
-        {ok, _} = update_user_doc(DbName1, "joe", "pass5"),
-        full_commit(DbName1),
-
-        couch_config:set("couch_httpd_auth", "authentication_db",
-                         ?b2l(DbName), false),
-
-        Creds = couch_auth_cache:get_user_creds("joe"),
-        ?assertEqual(PasswordHash,
-                      couch_util:get_value(<<"password_sha">>, Creds))
-    end).
-
-should_recover_cache_after_shutdown(DbName) ->
-    ?_test(begin
-        PasswordHash = hash_password("pass2"),
-        {ok, Rev0} = update_user_doc(DbName, "joe", "pass1"),
-        {ok, Rev1} = update_user_doc(DbName, "joe", "pass2", Rev0),
-        full_commit(DbName),
-        shutdown_db(DbName),
-        {ok, Rev1} = get_doc_rev(DbName, "joe"),
-        ?assertEqual(PasswordHash, get_user_doc_password_sha(DbName, "joe"))
-    end).
-
-
-update_user_doc(DbName, UserName, Password) ->
-    update_user_doc(DbName, UserName, Password, nil).
-
-update_user_doc(DbName, UserName, Password, Rev) ->
-    User = iolist_to_binary(UserName),
-    Doc = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"org.couchdb.user:", User/binary>>},
-        {<<"name">>, User},
-        {<<"type">>, <<"user">>},
-        {<<"salt">>, ?SALT},
-        {<<"password_sha">>, hash_password(Password)},
-        {<<"roles">>, []}
-    ] ++ case Rev of
-            nil -> [];
-            _ ->   [{<<"_rev">>, Rev}]
-         end
-    }),
-    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
-    {ok, NewRev} = couch_db:update_doc(AuthDb, Doc, []),
-    ok = couch_db:close(AuthDb),
-    {ok, couch_doc:rev_to_str(NewRev)}.
-
-hash_password(Password) ->
-    ?l2b(couch_util:to_hex(crypto:sha(iolist_to_binary([Password, ?SALT])))).
-
-shutdown_db(DbName) ->
-    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
-    ok = couch_db:close(AuthDb),
-    couch_util:shutdown_sync(AuthDb#db.main_pid),
-    ok = timer:sleep(1000).
-
-get_doc_rev(DbName, UserName) ->
-    DocId = iolist_to_binary([<<"org.couchdb.user:">>, UserName]),
-    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
-    UpdateRev =
-    case couch_db:open_doc(AuthDb, DocId, []) of
-    {ok, Doc} ->
-        {Props} = couch_doc:to_json_obj(Doc, []),
-        couch_util:get_value(<<"_rev">>, Props);
-    {not_found, missing} ->
-        nil
-    end,
-    ok = couch_db:close(AuthDb),
-    {ok, UpdateRev}.
-
-get_user_doc_password_sha(DbName, UserName) ->
-    DocId = iolist_to_binary([<<"org.couchdb.user:">>, UserName]),
-    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
-    {ok, Doc} = couch_db:open_doc(AuthDb, DocId, []),
-    ok = couch_db:close(AuthDb),
-    {Props} = couch_doc:to_json_obj(Doc, []),
-    couch_util:get_value(<<"password_sha">>, Props).
-
-delete_user_doc(DbName, UserName) ->
-    DocId = iolist_to_binary([<<"org.couchdb.user:">>, UserName]),
-    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
-    {ok, Doc} = couch_db:open_doc(AuthDb, DocId, []),
-    {Props} = couch_doc:to_json_obj(Doc, []),
-    DeletedDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, DocId},
-        {<<"_rev">>, couch_util:get_value(<<"_rev">>, Props)},
-        {<<"_deleted">>, true}
-    ]}),
-    {ok, _} = couch_db:update_doc(AuthDb, DeletedDoc, []),
-    ok = couch_db:close(AuthDb).
-
-full_commit(DbName) ->
-    {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_USER]),
-    {ok, _} = couch_db:ensure_full_commit(AuthDb),
-    ok = couch_db:close(AuthDb).


[11/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 200-view-group-no-db-leaks.t etap test suite to eunit

Merged into couchdb_views_tests suite. Apply minor refactor changes.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/c98d19fe
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/c98d19fe
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/c98d19fe

Branch: refs/heads/1963-eunit-bigcouch
Commit: c98d19fe2672ba840c3834df444ab047a3fe94c2
Parents: 97aa93a
Author: Alexander Shorin <kx...@apache.org>
Authored: Thu Jun 5 23:03:31 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_views_tests.erl | 240 +++++++++++++++++++++++++++---
 1 file changed, 223 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c98d19fe/test/couchdb/couchdb_views_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_views_tests.erl b/test/couchdb/couchdb_views_tests.erl
index d10b567..77ee4f5 100644
--- a/test/couchdb/couchdb_views_tests.erl
+++ b/test/couchdb/couchdb_views_tests.erl
@@ -16,8 +16,10 @@
 -include_lib("couchdb/couch_db.hrl").
 
 -define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(DELAY, 100).
 -define(TIMEOUT, 1000).
 
+
 start() ->
     {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
     Pid.
@@ -34,7 +36,8 @@ stop(Pid) ->
 
 setup() ->
     DbName = ?tempdb(),
-    {ok, _} = couch_db:create(DbName, [?ADMIN_USER]),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    ok = couch_db:close(Db),
     FooRev = create_design_doc(DbName, <<"_design/foo">>, <<"bar">>),
     query_view(DbName, "foo", "bar"),
     BooRev = create_design_doc(DbName, <<"_design/boo">>, <<"baz">>),
@@ -43,7 +46,8 @@ setup() ->
 
 setup_with_docs() ->
     DbName = ?tempdb(),
-    {ok, _} = couch_db:create(DbName, [?ADMIN_USER]),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    ok = couch_db:close(Db),
     create_docs(DbName),
     create_design_doc(DbName, <<"_design/foo">>, <<"bar">>),
     DbName.
@@ -51,7 +55,7 @@ setup_with_docs() ->
 teardown({DbName, _}) ->
     teardown(DbName);
 teardown(DbName) when is_binary(DbName) ->
-    ok = couch_server:delete(DbName, [?ADMIN_USER]),
+    couch_server:delete(DbName, [?ADMIN_USER]),
     ok.
 
 
@@ -73,17 +77,33 @@ view_indexes_cleanup_test_() ->
         }
     }.
 
+view_group_db_leaks_test_() ->
+    {
+        "View group db leaks",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup_with_docs/0, fun teardown/1,
+                [
+                    fun couchdb_1138/1,
+                    fun couchdb_1309/1
+                ]
+            }
+        }
+    }.
+
+
 should_not_remember_docs_in_index_after_backup_restore_test() ->
     %% COUCHDB-640
     start(),
     DbName = setup_with_docs(),
 
     ok = backup_db_file(DbName),
-    create_doc(DbName),
+    create_doc(DbName, "doc666"),
 
-    Body0 = query_view(DbName, "foo", "bar"),
-    ViewJson0 = ejson:decode(Body0),
-    Rows0 = couch_util:get_nested_json_value(ViewJson0, [<<"rows">>]),
+    Rows0 = query_view(DbName, "foo", "bar"),
     ?assert(has_doc("doc1", Rows0)),
     ?assert(has_doc("doc2", Rows0)),
     ?assert(has_doc("doc3", Rows0)),
@@ -91,9 +111,7 @@ should_not_remember_docs_in_index_after_backup_restore_test() ->
 
     restore_backup_db_file(DbName),
 
-    Body1 = query_view(DbName, "foo", "bar"),
-    ViewJson1 = ejson:decode(Body1),
-    Rows1 = couch_util:get_nested_json_value(ViewJson1, [<<"rows">>]),
+    Rows1 = query_view(DbName, "foo", "bar"),
     ?assert(has_doc("doc1", Rows1)),
     ?assert(has_doc("doc2", Rows1)),
     ?assert(has_doc("doc3", Rows1)),
@@ -118,13 +136,106 @@ should_cleanup_all_index_files({DbName, {FooRev, BooRev}})->
     view_cleanup(DbName),
     ?_assertEqual(0, count_index_files(DbName)).
 
-
-create_doc(DbName) ->
+couchdb_1138(DbName) ->
+    ?_test(begin
+        {ok, IndexerPid} = couch_index_server:get_index(
+            couch_mrview_index, DbName, <<"_design/foo">>),
+        ?assert(is_pid(IndexerPid)),
+        ?assert(is_process_alive(IndexerPid)),
+        ?assertEqual(2, count_db_refs(DbName)),
+
+        Rows0 = query_view(DbName, "foo", "bar"),
+        ?assertEqual(3, length(Rows0)),
+        ?assertEqual(2, count_db_refs(DbName)),
+        ?assert(is_process_alive(IndexerPid)),
+
+        create_doc(DbName, "doc1000"),
+        Rows1 = query_view(DbName, "foo", "bar"),
+        ?assertEqual(4, length(Rows1)),
+        ?assertEqual(2, count_db_refs(DbName)),
+        ?assert(is_process_alive(IndexerPid)),
+
+        Ref1 = get_db_ref_counter(DbName),
+        compact_db(DbName),
+        Ref2 = get_db_ref_counter(DbName),
+        ?assertEqual(2, couch_ref_counter:count(Ref2)),
+        ?assertNotEqual(Ref2, Ref1),
+        ?assertNot(is_process_alive(Ref1)),
+        ?assert(is_process_alive(IndexerPid)),
+
+        compact_view_group(DbName, "foo"),
+        ?assertEqual(2, count_db_refs(DbName)),
+        Ref3 = get_db_ref_counter(DbName),
+        ?assertEqual(Ref3, Ref2),
+        ?assert(is_process_alive(IndexerPid)),
+
+        create_doc(DbName, "doc1001"),
+        Rows2 = query_view(DbName, "foo", "bar"),
+        ?assertEqual(5, length(Rows2)),
+        ?assertEqual(2, count_db_refs(DbName)),
+        ?assert(is_process_alive(IndexerPid))
+    end).
+
+couchdb_1309(DbName) ->
+    ?_test(begin
+        {ok, IndexerPid} = couch_index_server:get_index(
+            couch_mrview_index, DbName, <<"_design/foo">>),
+        ?assert(is_pid(IndexerPid)),
+        ?assert(is_process_alive(IndexerPid)),
+        ?assertEqual(2, count_db_refs(DbName)),
+
+        create_doc(DbName, "doc1001"),
+        Rows0 = query_view(DbName, "foo", "bar"),
+        check_rows_value(Rows0, null),
+        ?assertEqual(4, length(Rows0)),
+        ?assertEqual(2, count_db_refs(DbName)),
+        ?assert(is_process_alive(IndexerPid)),
+
+        update_design_doc(DbName,  <<"_design/foo">>, <<"bar">>),
+        {ok, NewIndexerPid} = couch_index_server:get_index(
+            couch_mrview_index, DbName, <<"_design/foo">>),
+        ?assert(is_pid(NewIndexerPid)),
+        ?assert(is_process_alive(NewIndexerPid)),
+        ?assertNotEqual(IndexerPid, NewIndexerPid),
+        ?assertEqual(2, count_db_refs(DbName)),
+
+        Rows1 = query_view(DbName, "foo", "bar", ok),
+        ?assertEqual(0, length(Rows1)),
+        Rows2 = query_view(DbName, "foo", "bar"),
+        check_rows_value(Rows2, 1),
+        ?assertEqual(4, length(Rows2)),
+
+        MonRef0 = erlang:monitor(process, IndexerPid),
+        receive
+            {'DOWN', MonRef0, _, _, _} ->
+                ok
+        after ?TIMEOUT ->
+            erlang:error(
+                {assertion_failed,
+                 [{module, ?MODULE}, {line, ?LINE},
+                  {reason, "old view group is not dead after ddoc update"}]})
+        end,
+
+        MonRef1 = erlang:monitor(process, NewIndexerPid),
+        ok = couch_server:delete(DbName, [?ADMIN_USER]),
+        receive
+            {'DOWN', MonRef1, _, _, _} ->
+                ok
+        after ?TIMEOUT ->
+            erlang:error(
+                {assertion_failed,
+                 [{module, ?MODULE}, {line, ?LINE},
+                  {reason, "new view group did not die after DB deletion"}]})
+        end
+    end).
+
+create_doc(DbName, DocId) when is_list(DocId) ->
+    create_doc(DbName, ?l2b(DocId));
+create_doc(DbName, DocId) when is_binary(DocId) ->
     {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
     Doc666 = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"doc666">>},
+        {<<"_id">>, DocId},
         {<<"value">>, 999}
-
     ]}),
     {ok, _} = couch_db:update_docs(Db, [Doc666]),
     couch_db:ensure_full_commit(Db),
@@ -158,7 +269,7 @@ create_design_doc(DbName, DDName, ViewName) ->
         {<<"language">>, <<"javascript">>},
         {<<"views">>, {[
             {ViewName, {[
-                {<<"map">>, <<"function(doc) { emit(doc.value, 1); }">>}
+                {<<"map">>, <<"function(doc) { emit(doc.value, null); }">>}
             ]}}
         ]}}
     ]}),
@@ -167,6 +278,26 @@ create_design_doc(DbName, DDName, ViewName) ->
     couch_db:close(Db),
     Rev.
 
+update_design_doc(DbName, DDName, ViewName) ->
+    {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
+    {ok, Doc} = couch_db:open_doc(Db, DDName, [?ADMIN_USER]),
+    {Props} = couch_doc:to_json_obj(Doc, []),
+    Rev = couch_util:get_value(<<"_rev">>, Props),
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, DDName},
+        {<<"_rev">>, Rev},
+        {<<"language">>, <<"javascript">>},
+        {<<"views">>, {[
+            {ViewName, {[
+                {<<"map">>, <<"function(doc) { emit(doc.value, 1); }">>}
+            ]}}
+        ]}}
+    ]}),
+    {ok, NewRev} = couch_db:update_doc(Db, DDoc, [?ADMIN_USER]),
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db),
+    NewRev.
+
 delete_design_doc(DbName, DDName, Rev) ->
     {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
     DDoc = couch_doc:from_json_obj({[
@@ -183,16 +314,43 @@ db_url(DbName) ->
     "http://" ++ Addr ++ ":" ++ Port ++ "/" ++ ?b2l(DbName).
 
 query_view(DbName, DDoc, View) ->
+    query_view(DbName, DDoc, View, false).
+
+query_view(DbName, DDoc, View, Stale) ->
     {ok, Code, _Headers, Body} = test_request:get(
-        db_url(DbName) ++ "/_design/" ++ DDoc ++ "/_view/" ++ View),
+        db_url(DbName) ++ "/_design/" ++ DDoc ++ "/_view/" ++ View
+        ++ case Stale of
+               false -> [];
+               _ -> "?stale=" ++ atom_to_list(Stale)
+           end),
     ?assertEqual(200, Code),
-    Body.
+    {Props} = ejson:decode(Body),
+    couch_util:get_value(<<"rows">>, Props, []).
+
+check_rows_value(Rows, Value) ->
+    lists:foreach(
+        fun({Row}) ->
+            ?assertEqual(Value, couch_util:get_value(<<"value">>, Row))
+        end, Rows).
 
 view_cleanup(DbName) ->
     {ok, Db} = couch_db:open(DbName, [?ADMIN_USER]),
     couch_mrview:cleanup(Db),
     couch_db:close(Db).
 
+get_db_ref_counter(DbName) ->
+    {ok, #db{fd_ref_counter = Ref} = Db} = couch_db:open_int(DbName, []),
+    ok = couch_db:close(Db),
+    Ref.
+
+count_db_refs(DbName) ->
+    Ref = get_db_ref_counter(DbName),
+    % have to sleep a bit to let couchdb cleanup all refs and leave only
+    % active ones. otherwise the related tests will randomly fail due to
+    % count number mismatch
+    timer:sleep(200),
+    couch_ref_counter:count(Ref).
+
 count_index_files(DbName) ->
     % call server to fetch the index files
     RootDir = couch_config:get("couchdb", "view_index_dir"),
@@ -217,3 +375,51 @@ restore_backup_db_file(DbName) ->
     ok = file:rename(DbFile ++ ".backup", DbFile),
     start(),
     ok.
+
+compact_db(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, _} = couch_db:start_compact(Db),
+    ok = couch_db:close(Db),
+    wait_db_compact_done(DbName, 10).
+
+wait_db_compact_done(_DbName, 0) ->
+    erlang:error({assertion_failed,
+                  [{module, ?MODULE},
+                   {line, ?LINE},
+                   {reason, "DB compaction failed to finish"}]});
+wait_db_compact_done(DbName, N) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    ok = couch_db:close(Db),
+    case is_pid(Db#db.compactor_pid) of
+    false ->
+        ok;
+    true ->
+        ok = timer:sleep(?DELAY),
+        wait_db_compact_done(DbName, N - 1)
+    end.
+
+compact_view_group(DbName, DDocId) when is_list(DDocId) ->
+    compact_view_group(DbName, ?l2b("_design/" ++ DDocId));
+compact_view_group(DbName, DDocId) when is_binary(DDocId) ->
+    ok = couch_mrview:compact(DbName, DDocId),
+    wait_view_compact_done(DbName, DDocId, 10).
+
+wait_view_compact_done(_DbName, _DDocId, 0) ->
+    erlang:error({assertion_failed,
+                  [{module, ?MODULE},
+                   {line, ?LINE},
+                   {reason, "DB compaction failed to finish"}]});
+wait_view_compact_done(DbName, DDocId, N) ->
+    {ok, Code, _Headers, Body} = test_request:get(
+        db_url(DbName) ++ "/" ++ ?b2l(DDocId) ++ "/_info"),
+    ?assertEqual(200, Code),
+    {Info} = ejson:decode(Body),
+    {IndexInfo} = couch_util:get_value(<<"view_index">>, Info),
+    CompactRunning = couch_util:get_value(<<"compact_running">>, IndexInfo),
+    case CompactRunning of
+        false ->
+            ok;
+        true ->
+            ok = timer:sleep(?DELAY),
+            wait_view_compact_done(DbName, DDocId, N - 1)
+    end.


[03/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 160-vhosts.t etap test suite to eunit

Split Rewrite and OAuth tests.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/7b3ecbe3
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/7b3ecbe3
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/7b3ecbe3

Branch: refs/heads/1963-eunit-bigcouch
Commit: 7b3ecbe30cf223fb1a44ffd99ba1a798aaaa3f0f
Parents: 4206af5
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Jun 3 10:58:58 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_vhosts_tests.erl | 441 +++++++++++++++++++++++++++++
 test/couchdb/test_request.erl         |  15 +-
 2 files changed, 451 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/7b3ecbe3/test/couchdb/couchdb_vhosts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_vhosts_tests.erl b/test/couchdb/couchdb_vhosts_tests.erl
new file mode 100644
index 0000000..94b1957
--- /dev/null
+++ b/test/couchdb/couchdb_vhosts_tests.erl
@@ -0,0 +1,441 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_vhosts_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(ADMIN_USER, {user_ctx, #user_ctx{roles=[<<"_admin">>]}}).
+-define(TIMEOUT, 1000).
+-define(iofmt(S, A), lists:flatten(io_lib:format(S, A))).
+
+
+start() ->
+    {ok, Pid} = couch_server_sup:start_link(?CONFIG_CHAIN),
+    Pid.
+
+stop(Pid) ->
+    erlang:monitor(process, Pid),
+    couch_server_sup:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, server_stop})
+    end.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+    Doc = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"doc1">>},
+        {<<"value">>, 666}
+    ]}),
+
+    Doc1 = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"_design/doc1">>},
+        {<<"shows">>, {[
+            {<<"test">>, <<"function(doc, req) {
+            return { json: {
+                    requested_path: '/' + req.requested_path.join('/'),
+                    path: '/' + req.path.join('/')}};}">>}
+        ]}},
+        {<<"rewrites">>, [
+            {[
+                {<<"from">>, <<"/">>},
+                {<<"to">>, <<"_show/test">>}
+            ]}
+        ]}
+    ]}),
+    {ok, _} = couch_db:update_docs(Db, [Doc, Doc1]),
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db),
+
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    Url = "http://" ++ Addr ++ ":" ++ Port,
+    {Url, ?b2l(DbName)}.
+
+setup_oauth() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER]),
+
+    couch_config:set("couch_httpd_auth", "authentication_db",
+                     ?b2l(?tempdb()), false),
+    couch_config:set("oauth_token_users", "otoksec1", "joe", false),
+    couch_config:set("oauth_consumer_secrets", "consec1", "foo", false),
+    couch_config:set("oauth_token_secrets", "otoksec1", "foobar", false),
+    couch_config:set("couch_httpd_auth", "require_valid_user", "true", false),
+
+    ok = couch_config:set(
+        "vhosts", "oauth-example.com",
+        "/" ++ ?b2l(DbName) ++ "/_design/test/_rewrite/foobar", false),
+
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"_design/test">>},
+        {<<"language">>, <<"javascript">>},
+        {<<"rewrites">>, [
+            {[
+                {<<"from">>, <<"foobar">>},
+                {<<"to">>, <<"_info">>}
+            ]}
+        ]}
+    ]}),
+    {ok, _} = couch_db:update_doc(Db, DDoc, []),
+
+    couch_db:ensure_full_commit(Db),
+    couch_db:close(Db),
+
+    Addr = couch_config:get("httpd", "bind_address", "127.0.0.1"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    Url = "http://" ++ Addr ++ ":" ++ Port,
+    {Url, ?b2l(DbName)}.
+
+teardown({_, DbName}) ->
+    ok = couch_server:delete(?l2b(DbName), []),
+    ok.
+
+
+vhosts_test_() ->
+    {
+        "Virtual Hosts rewrite tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_return_database_info/1,
+                    fun should_return_revs_info/1,
+                    fun should_serve_utils_for_vhost/1,
+                    fun should_return_virtual_request_path_field_in_request/1,
+                    fun should_return_real_request_path_field_in_request/1,
+                    fun should_match_wildcard_vhost/1,
+                    fun should_return_db_info_for_wildcard_vhost_for_custom_db/1,
+                    fun should_replace_rewrite_variables_for_db_and_doc/1,
+                    fun should_return_db_info_for_vhost_with_resource/1,
+                    fun should_return_revs_info_for_vhost_with_resource/1,
+                    fun should_return_db_info_for_vhost_with_wildcard_resource/1,
+                    fun should_return_path_for_vhost_with_wildcard_host/1
+                ]
+            }
+        }
+    }.
+
+oauth_test_() ->
+    {
+        "Virtual Hosts OAuth tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup_oauth/0, fun teardown/1,
+                [
+                    fun should_require_auth/1,
+                    fun should_succeed_oauth/1,
+                    fun should_fail_oauth_with_wrong_credentials/1
+                ]
+            }
+        }
+    }.
+
+
+should_return_database_info({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
+        case test_request:get(Url, [], [{host_header, "example.com"}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_revs_info({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
+        case test_request:get(Url ++ "/doc1?revs_info=true", [],
+                              [{host_header, "example.com"}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"_revs_info">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_serve_utils_for_vhost({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "example.com", "/" ++ DbName, false),
+        case test_request:get(Url ++ "/_utils/index.html", [],
+                              [{host_header, "example.com"}]) of
+            {ok, _, _, Body} ->
+                ?assertMatch(<<"<!DOCTYPE html>", _/binary>>, Body);
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_virtual_request_path_field_in_request({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "example1.com",
+                              "/" ++ DbName ++ "/_design/doc1/_rewrite/",
+                              false),
+        case test_request:get(Url, [], [{host_header, "example1.com"}]) of
+            {ok, _, _, Body} ->
+                {Json} = ejson:decode(Body),
+                ?assertEqual(<<"/">>,
+                             proplists:get_value(<<"requested_path">>, Json));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_real_request_path_field_in_request({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "example1.com",
+                              "/" ++ DbName ++ "/_design/doc1/_rewrite/",
+                              false),
+        case test_request:get(Url, [], [{host_header, "example1.com"}]) of
+            {ok, _, _, Body} ->
+                {Json} = ejson:decode(Body),
+                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
+                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_match_wildcard_vhost({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "*.example.com",
+                              "/" ++ DbName ++ "/_design/doc1/_rewrite", false),
+        case test_request:get(Url, [], [{host_header, "test.example.com"}]) of
+            {ok, _, _, Body} ->
+                {Json} = ejson:decode(Body),
+                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
+                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_db_info_for_wildcard_vhost_for_custom_db({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", ":dbname.example1.com",
+                              "/:dbname", false),
+        Host = DbName ++ ".example1.com",
+        case test_request:get(Url, [], [{host_header, Host}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_replace_rewrite_variables_for_db_and_doc({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts",":appname.:dbname.example1.com",
+                              "/:dbname/_design/:appname/_rewrite/", false),
+        Host = "doc1." ++ DbName ++ ".example1.com",
+        case test_request:get(Url, [], [{host_header, Host}]) of
+            {ok, _, _, Body} ->
+                {Json} = ejson:decode(Body),
+                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
+                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_db_info_for_vhost_with_resource({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts",
+                              "example.com/test", "/" ++ DbName, false),
+        ReqUrl = Url ++ "/test",
+        case test_request:get(ReqUrl, [], [{host_header, "example.com"}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+
+should_return_revs_info_for_vhost_with_resource({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts",
+                              "example.com/test", "/" ++ DbName, false),
+        ReqUrl = Url ++ "/test/doc1?revs_info=true",
+        case test_request:get(ReqUrl, [], [{host_header, "example.com"}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"_revs_info">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_db_info_for_vhost_with_wildcard_resource({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "*.example2.com/test", "/*", false),
+        ReqUrl = Url ++ "/test",
+        Host = DbName ++ ".example2.com",
+        case test_request:get(ReqUrl, [], [{host_header, Host}]) of
+            {ok, _, _, Body} ->
+                {JsonBody} = ejson:decode(Body),
+                ?assert(proplists:is_defined(<<"db_name">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_return_path_for_vhost_with_wildcard_host({Url, DbName}) ->
+    ?_test(begin
+        ok = couch_config:set("vhosts", "*/test1",
+                              "/" ++ DbName ++ "/_design/doc1/_show/test",
+                              false),
+        case test_request:get(Url ++ "/test1") of
+            {ok, _, _, Body} ->
+                {Json} = ejson:decode(Body),
+                Path = ?l2b("/" ++ DbName ++ "/_design/doc1/_show/test"),
+                ?assertEqual(Path, proplists:get_value(<<"path">>, Json));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_require_auth({Url, _}) ->
+    ?_test(begin
+        case test_request:get(Url, [], [{host_header, "oauth-example.com"}]) of
+            {ok, Code, _, Body} ->
+                ?assertEqual(401, Code),
+                {JsonBody} = ejson:decode(Body),
+                ?assertEqual(<<"unauthorized">>,
+                             couch_util:get_value(<<"error">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_succeed_oauth({Url, _}) ->
+    ?_test(begin
+        AuthDbName = couch_config:get("couch_httpd_auth", "authentication_db"),
+        JoeDoc = couch_doc:from_json_obj({[
+            {<<"_id">>, <<"org.couchdb.user:joe">>},
+            {<<"type">>, <<"user">>},
+            {<<"name">>, <<"joe">>},
+            {<<"roles">>, []},
+            {<<"password_sha">>, <<"fe95df1ca59a9b567bdca5cbaf8412abd6e06121">>},
+            {<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4001a73">>}
+        ]}),
+        {ok, AuthDb} = couch_db:open_int(?l2b(AuthDbName), [?ADMIN_USER]),
+        {ok, _} = couch_db:update_doc(AuthDb, JoeDoc, [?ADMIN_USER]),
+
+        Host = "oauth-example.com",
+        Consumer = {"consec1", "foo", hmac_sha1},
+        SignedParams = oauth:sign(
+            "GET", "http://" ++ Host ++ "/", [], Consumer, "otoksec1", "foobar"),
+        OAuthUrl = oauth:uri(Url, SignedParams),
+
+        case test_request:get(OAuthUrl, [], [{host_header, Host}]) of
+            {ok, Code, _, Body} ->
+                ?assertEqual(200, Code),
+                {JsonBody} = ejson:decode(Body),
+                ?assertEqual(<<"test">>,
+                             couch_util:get_value(<<"name">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).
+
+should_fail_oauth_with_wrong_credentials({Url, _}) ->
+    ?_test(begin
+        AuthDbName = couch_config:get("couch_httpd_auth", "authentication_db"),
+        JoeDoc = couch_doc:from_json_obj({[
+            {<<"_id">>, <<"org.couchdb.user:joe">>},
+            {<<"type">>, <<"user">>},
+            {<<"name">>, <<"joe">>},
+            {<<"roles">>, []},
+            {<<"password_sha">>, <<"fe95df1ca59a9b567bdca5cbaf8412abd6e06121">>},
+            {<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4001a73">>}
+        ]}),
+        {ok, AuthDb} = couch_db:open_int(?l2b(AuthDbName), [?ADMIN_USER]),
+        {ok, _} = couch_db:update_doc(AuthDb, JoeDoc, [?ADMIN_USER]),
+
+        Host = "oauth-example.com",
+        Consumer = {"consec1", "bad_secret", hmac_sha1},
+        SignedParams = oauth:sign(
+            "GET", "http://" ++ Host ++ "/", [], Consumer, "otoksec1", "foobar"),
+        OAuthUrl = oauth:uri(Url, SignedParams),
+
+        case test_request:get(OAuthUrl, [], [{host_header, Host}]) of
+            {ok, Code, _, Body} ->
+                ?assertEqual(401, Code),
+                {JsonBody} = ejson:decode(Body),
+                ?assertEqual(<<"unauthorized">>,
+                             couch_util:get_value(<<"error">>, JsonBody));
+            Else ->
+                erlang:error({assertion_failed,
+                             [{module, ?MODULE},
+                              {line, ?LINE},
+                              {reason, ?iofmt("Request failed: ~p", [Else])}]})
+        end
+    end).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/7b3ecbe3/test/couchdb/test_request.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/test_request.erl b/test/couchdb/test_request.erl
index d6725ea..36871b4 100644
--- a/test/couchdb/test_request.erl
+++ b/test/couchdb/test_request.erl
@@ -12,7 +12,7 @@
 
 -module(test_request).
 
--export([get/1, get/2, put/2, put/3]).
+-export([get/1, get/2, get/3, put/2, put/3]).
 -export([request/3, request/4]).
 
 get(Url) ->
@@ -20,6 +20,8 @@ get(Url) ->
 
 get(Url, Headers) ->
     request(get, Url, Headers).
+get(Url, Headers, Opts) ->
+    request(get, Url, Headers, [], Opts).
 
 
 put(Url, Body) ->
@@ -33,18 +35,21 @@ request(Method, Url, Headers) ->
     request(Method, Url, Headers, []).
 
 request(Method, Url, Headers, Body) ->
-    request(Method, Url, Headers, Body, 3).
+    request(Method, Url, Headers, Body, [], 3).
 
-request(_Method, _Url, _Headers, _Body, 0) ->
+request(Method, Url, Headers, Body, Opts) ->
+    request(Method, Url, Headers, Body, Opts, 3).
+
+request(_Method, _Url, _Headers, _Body, _Opts, 0) ->
     {error, request_failed};
-request(Method, Url, Headers, Body, N) ->
+request(Method, Url, Headers, Body, Opts, N) ->
     case code:is_loaded(ibrowse) of
         false ->
             {ok, _} = ibrowse:start();
         _ ->
             ok
     end,
-    case ibrowse:send_req(Url, Headers, Method, Body) of
+    case ibrowse:send_req(Url, Headers, Method, Body, Opts) of
         {ok, Code0, RespHeaders, RespBody0} ->
             Code = list_to_integer(Code0),
             RespBody = iolist_to_binary(RespBody0),


[06/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 100-ref-counter.t etap test suite to eunit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/7d7a7421
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/7d7a7421
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/7d7a7421

Branch: refs/heads/1963-eunit-bigcouch
Commit: 7d7a7421b9f41c3e017622dc534ca4017876e009
Parents: 1667f9c
Author: Alexander Shorin <kx...@apache.org>
Authored: Mon May 26 20:59:45 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couch_ref_counter_tests.erl | 107 ++++++++++++++++++++++++++
 1 file changed, 107 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/7d7a7421/test/couchdb/couch_ref_counter_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couch_ref_counter_tests.erl b/test/couchdb/couch_ref_counter_tests.erl
new file mode 100644
index 0000000..b7e97b4
--- /dev/null
+++ b/test/couchdb/couch_ref_counter_tests.erl
@@ -0,0 +1,107 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_ref_counter_tests).
+
+-include("couch_eunit.hrl").
+-include_lib("couchdb/couch_db.hrl").
+
+-define(TIMEOUT, 1000).
+
+
+setup() ->
+    {ok, RefCtr} = couch_ref_counter:start([]),
+    ChildPid = spawn(fun() -> loop() end),
+    {RefCtr, ChildPid}.
+
+teardown({_, ChildPid}) ->
+    erlang:monitor(process, ChildPid),
+    ChildPid ! close,
+    wait().
+
+
+couch_ref_counter_test_() ->
+    {
+        "CouchDB reference counter tests",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [
+                fun should_initialize_with_calling_process_as_referrer/1,
+                fun should_ignore_unknown_pid/1,
+                fun should_increment_counter_on_pid_add/1,
+                fun should_not_increase_counter_on_readding_same_pid/1,
+                fun should_drop_ref_for_double_added_pid/1,
+                fun should_decrement_counter_on_pid_drop/1,
+                fun should_add_after_drop/1,
+                fun should_decrement_counter_on_process_exit/1
+
+            ]
+        }
+    }.
+
+
+should_initialize_with_calling_process_as_referrer({RefCtr, _}) ->
+    ?_assertEqual(1, couch_ref_counter:count(RefCtr)).
+
+should_ignore_unknown_pid({RefCtr, ChildPid}) ->
+    ?_assertEqual(ok, couch_ref_counter:drop(RefCtr, ChildPid)).
+
+should_increment_counter_on_pid_add({RefCtr, ChildPid}) ->
+    couch_ref_counter:add(RefCtr, ChildPid),
+    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
+
+should_not_increase_counter_on_readding_same_pid({RefCtr, ChildPid}) ->
+    couch_ref_counter:add(RefCtr, ChildPid),
+    couch_ref_counter:add(RefCtr, ChildPid),
+    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
+
+should_drop_ref_for_double_added_pid({RefCtr, ChildPid}) ->
+    couch_ref_counter:add(RefCtr, ChildPid),
+    couch_ref_counter:add(RefCtr, ChildPid),
+    couch_ref_counter:drop(RefCtr, ChildPid),
+    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
+
+should_decrement_counter_on_pid_drop({RefCtr, ChildPid}) ->
+    couch_ref_counter:add(RefCtr, ChildPid),
+    couch_ref_counter:drop(RefCtr, ChildPid),
+    ?_assertEqual(1, couch_ref_counter:count(RefCtr)).
+
+should_add_after_drop({RefCtr, ChildPid}) ->
+    couch_ref_counter:add(RefCtr, ChildPid),
+    couch_ref_counter:drop(RefCtr, ChildPid),
+    couch_ref_counter:add(RefCtr, ChildPid),
+    ?_assertEqual(2, couch_ref_counter:count(RefCtr)).
+
+should_decrement_counter_on_process_exit({RefCtr, ChildPid}) ->
+    ?_assertEqual(1,
+        begin
+            couch_ref_counter:add(RefCtr, ChildPid),
+            erlang:monitor(process, ChildPid),
+            ChildPid ! close,
+            wait(),
+            couch_ref_counter:count(RefCtr)
+        end).
+
+
+loop() ->
+    receive
+        close -> ok
+    end.
+
+wait() ->
+    receive
+        {'DOWN', _, _, _, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw(timeout_error)
+    end.


[40/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Rename couch_server_sup to couch_sup in couch module tests


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/98e4a994
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/98e4a994
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/98e4a994

Branch: refs/heads/1963-eunit-bigcouch
Commit: 98e4a99443a91157738f598f728ae195da5a314b
Parents: 863ee8f
Author: Russell Branca <ch...@apache.org>
Authored: Thu Aug 21 13:02:54 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 13:49:08 2014 -0700

----------------------------------------------------------------------
 test/couchdb_modules_load_tests.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/98e4a994/test/couchdb_modules_load_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb_modules_load_tests.erl b/test/couchdb_modules_load_tests.erl
index c4fa40f..d1c6376 100644
--- a/test/couchdb_modules_load_tests.erl
+++ b/test/couchdb_modules_load_tests.erl
@@ -52,7 +52,7 @@ should_load_modules() ->
         couch_os_process,
         couch_query_servers,
         couch_server,
-        couch_server_sup,
+        couch_sup,
         couch_stats_aggregator,
         couch_stats_collector,
         couch_stream,


[37/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
WIP: Switch to using test_util:start_config


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/d444897a
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/d444897a
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/d444897a

Branch: refs/heads/1963-eunit-bigcouch
Commit: d444897adc576a082bbaa01cdfe28f655b73c08f
Parents: 097c7f1
Author: Russell Branca <ch...@apache.org>
Authored: Fri Aug 15 13:01:56 2014 -0700
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:25:34 2014 -0700

----------------------------------------------------------------------
 test/couch_doc_json_tests.erl | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/d444897a/test/couch_doc_json_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_doc_json_tests.erl b/test/couch_doc_json_tests.erl
index ef5d0f2..64dea96 100644
--- a/test/couch_doc_json_tests.erl
+++ b/test/couch_doc_json_tests.erl
@@ -17,12 +17,12 @@
 
 
 setup() ->
-    config:start_link(?CONFIG_CHAIN),
+    {ok, Pid} = test_util:start_config(?CONFIG_CHAIN),
     config:set("attachments", "compression_level", "0", false),
-    ok.
+    Pid.
 
 teardown(_) ->
-    config:stop().
+    test_util:stop_config().
 
 
 json_doc_test_() ->


[02/42] couch commit: updated refs/heads/1963-eunit-bigcouch to c3f3807

Posted by ch...@apache.org.
Port 180-http-proxy.t etap test suite to eunit


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/f8dd4b28
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/f8dd4b28
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/f8dd4b28

Branch: refs/heads/1963-eunit-bigcouch
Commit: f8dd4b288f0cf27b38cdcd18e3334f30ee880da6
Parents: 1334a67
Author: Alexander Shorin <kx...@apache.org>
Authored: Wed Jun 4 12:54:44 2014 +0400
Committer: Russell Branca <ch...@apache.org>
Committed: Tue Aug 26 11:21:39 2014 -0700

----------------------------------------------------------------------
 test/couchdb/couchdb_http_proxy_tests.erl | 462 +++++++++++++++++++++++++
 test/couchdb/test_web.erl                 | 112 ++++++
 2 files changed, 574 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/f8dd4b28/test/couchdb/couchdb_http_proxy_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_http_proxy_tests.erl b/test/couchdb/couchdb_http_proxy_tests.erl
new file mode 100644
index 0000000..acb1974
--- /dev/null
+++ b/test/couchdb/couchdb_http_proxy_tests.erl
@@ -0,0 +1,462 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_http_proxy_tests).
+
+-include("couch_eunit.hrl").
+
+-record(req, {method=get, path="", headers=[], body="", opts=[]}).
+
+-define(CONFIG_FIXTURE_TEMP,
+    begin
+        FileName = filename:join([?TEMPDIR, ?tempfile() ++ ".ini"]),
+        {ok, Fd} = file:open(FileName, write),
+        ok = file:truncate(Fd),
+        ok = file:close(Fd),
+        FileName
+    end).
+-define(TIMEOUT, 5000).
+
+
+start() ->
+    % we have to write any config changes to temp ini file to not loose them
+    % when supervisor will kill all children due to reaching restart threshold
+    % (each httpd_global_handlers changes causes couch_httpd restart)
+    couch_server_sup:start_link(?CONFIG_CHAIN ++ [?CONFIG_FIXTURE_TEMP]),
+    % 49151 is IANA Reserved, let's assume no one is listening there
+    couch_config:set("httpd_global_handlers", "_error",
+        "{couch_httpd_proxy, handle_proxy_req, <<\"http://127.0.0.1:49151/\">>}"
+    ),
+    ok.
+
+stop(_) ->
+    couch_server_sup:stop(),
+    ok.
+
+setup() ->
+    {ok, Pid} = test_web:start_link(),
+    Value = lists:flatten(io_lib:format(
+        "{couch_httpd_proxy, handle_proxy_req, ~p}",
+        [list_to_binary(proxy_url())])),
+    couch_config:set("httpd_global_handlers", "_test", Value),
+    % let couch_httpd restart
+    timer:sleep(100),
+    Pid.
+
+teardown(Pid) ->
+    erlang:monitor(process, Pid),
+    test_web:stop(),
+    receive
+        {'DOWN', _, _, Pid, _} ->
+            ok
+    after ?TIMEOUT ->
+        throw({timeout, test_web_stop})
+    end.
+
+
+http_proxy_test_() ->
+    {
+        "HTTP Proxy handler tests",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            {
+                foreach,
+                fun setup/0, fun teardown/1,
+                [
+                    fun should_proxy_basic_request/1,
+                    fun should_return_alternative_status/1,
+                    fun should_respect_trailing_slash/1,
+                    fun should_proxy_headers/1,
+                    fun should_proxy_host_header/1,
+                    fun should_pass_headers_back/1,
+                    fun should_use_same_protocol_version/1,
+                    fun should_proxy_body/1,
+                    fun should_proxy_body_back/1,
+                    fun should_proxy_chunked_body/1,
+                    fun should_proxy_chunked_body_back/1,
+                    fun should_rewrite_location_header/1,
+                    fun should_not_rewrite_external_locations/1,
+                    fun should_rewrite_relative_location/1,
+                    fun should_refuse_connection_to_backend/1
+                ]
+            }
+
+        }
+    }.
+
+
+should_proxy_basic_request(_) ->
+    Remote = fun(Req) ->
+        'GET' = Req:get(method),
+        "/" = Req:get(path),
+        0 = Req:get(body_length),
+        <<>> = Req:recv_body(),
+        {ok, {200, [{"Content-Type", "text/plain"}], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    ?_test(check_request(#req{}, Remote, Local)).
+
+should_return_alternative_status(_) ->
+    Remote = fun(Req) ->
+        "/alternate_status" = Req:get(path),
+        {ok, {201, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "201", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{path = "/alternate_status"},
+    ?_test(check_request(Req, Remote, Local)).
+
+should_respect_trailing_slash(_) ->
+    Remote = fun(Req) ->
+        "/trailing_slash/" = Req:get(path),
+        {ok, {200, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{path="/trailing_slash/"},
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_headers(_) ->
+    Remote = fun(Req) ->
+        "/passes_header" = Req:get(path),
+        "plankton" = Req:get_header_value("X-CouchDB-Ralph"),
+        {ok, {200, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{
+        path="/passes_header",
+        headers=[{"X-CouchDB-Ralph", "plankton"}]
+    },
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_host_header(_) ->
+    Remote = fun(Req) ->
+        "/passes_host_header" = Req:get(path),
+        "www.google.com" = Req:get_header_value("Host"),
+        {ok, {200, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{
+        path="/passes_host_header",
+        headers=[{"Host", "www.google.com"}]
+    },
+    ?_test(check_request(Req, Remote, Local)).
+
+should_pass_headers_back(_) ->
+    Remote = fun(Req) ->
+        "/passes_header_back" = Req:get(path),
+        {ok, {200, [{"X-CouchDB-Plankton", "ralph"}], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", Headers, "ok"}) ->
+            lists:member({"X-CouchDB-Plankton", "ralph"}, Headers);
+        (_) ->
+            false
+    end,
+    Req = #req{path="/passes_header_back"},
+    ?_test(check_request(Req, Remote, Local)).
+
+should_use_same_protocol_version(_) ->
+    Remote = fun(Req) ->
+        "/uses_same_version" = Req:get(path),
+        {1, 0} = Req:get(version),
+        {ok, {200, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "200", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{
+        path="/uses_same_version",
+        opts=[{http_vsn, {1, 0}}]
+    },
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_body(_) ->
+    Remote = fun(Req) ->
+        'PUT' = Req:get(method),
+        "/passes_body" = Req:get(path),
+        <<"Hooray!">> = Req:recv_body(),
+        {ok, {201, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "201", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{
+        method=put,
+        path="/passes_body",
+        body="Hooray!"
+    },
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_body_back(_) ->
+    BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
+    Remote = fun(Req) ->
+        'GET' = Req:get(method),
+        "/passes_eof_body" = Req:get(path),
+        {raw, {200, [{"Connection", "close"}], BodyChunks}}
+    end,
+    Local = fun
+        ({ok, "200", _, "foobarbazinga"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{path="/passes_eof_body"},
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_chunked_body(_) ->
+    BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
+    Remote = fun(Req) ->
+        'POST' = Req:get(method),
+        "/passes_chunked_body" = Req:get(path),
+        RecvBody = fun
+            ({Length, Chunk}, [Chunk | Rest]) ->
+                Length = size(Chunk),
+                Rest;
+            ({0, []}, []) ->
+                ok
+        end,
+        ok = Req:stream_body(1024 * 1024, RecvBody, BodyChunks),
+        {ok, {201, [], "ok"}}
+    end,
+    Local = fun
+        ({ok, "201", _, "ok"}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{
+        method=post,
+        path="/passes_chunked_body",
+        headers=[{"Transfer-Encoding", "chunked"}],
+        body=chunked_body(BodyChunks)
+    },
+    ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_chunked_body_back(_) ->
+    ?_test(begin
+        Remote = fun(Req) ->
+            'GET' = Req:get(method),
+            "/passes_chunked_body_back" = Req:get(path),
+            BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
+            {chunked, {200, [{"Transfer-Encoding", "chunked"}], BodyChunks}}
+        end,
+        Req = #req{
+            path="/passes_chunked_body_back",
+            opts=[{stream_to, self()}]
+        },
+
+        Resp = check_request(Req, Remote, no_local),
+        ?assertMatch({ibrowse_req_id, _}, Resp),
+        {_, ReqId} = Resp,
+
+        % Grab headers from response
+        receive
+            {ibrowse_async_headers, ReqId, "200", Headers} ->
+                ?assertEqual("chunked",
+                             proplists:get_value("Transfer-Encoding", Headers)),
+            ibrowse:stream_next(ReqId)
+        after 1000 ->
+            throw({error, timeout})
+        end,
+
+        ?assertEqual(<<"foobarbazinga">>, recv_body(ReqId, [])),
+        ?assertEqual(was_ok, test_web:check_last())
+    end).
+
+should_refuse_connection_to_backend(_) ->
+    Local = fun
+        ({ok, "500", _, _}) ->
+            true;
+        (_) ->
+            false
+    end,
+    Req = #req{opts=[{url, server_url("/_error")}]},
+    ?_test(check_request(Req, no_remote, Local)).
+
+should_rewrite_location_header(_) ->
+    {
+        "Testing location header rewrites",
+        do_rewrite_tests([
+            {"Location", proxy_url() ++ "/foo/bar",
+                         server_url() ++ "/foo/bar"},
+            {"Content-Location", proxy_url() ++ "/bing?q=2",
+                                 server_url() ++ "/bing?q=2"},
+            {"Uri", proxy_url() ++ "/zip#frag",
+                    server_url() ++ "/zip#frag"},
+            {"Destination", proxy_url(),
+                            server_url() ++ "/"}
+        ])
+    }.
+
+should_not_rewrite_external_locations(_) ->
+    {
+        "Testing no rewrite of external locations",
+        do_rewrite_tests([
+            {"Location", external_url() ++ "/search",
+                         external_url() ++ "/search"},
+            {"Content-Location", external_url() ++ "/s?q=2",
+                                 external_url() ++ "/s?q=2"},
+            {"Uri", external_url() ++ "/f#f",
+                    external_url() ++ "/f#f"},
+            {"Destination", external_url() ++ "/f?q=2#f",
+                            external_url() ++ "/f?q=2#f"}
+        ])
+    }.
+
+should_rewrite_relative_location(_) ->
+    {
+        "Testing relative rewrites",
+        do_rewrite_tests([
+            {"Location", "/foo",
+                         server_url() ++ "/foo"},
+            {"Content-Location", "bar",
+                                 server_url() ++ "/bar"},
+            {"Uri", "/zing?q=3",
+                    server_url() ++ "/zing?q=3"},
+            {"Destination", "bing?q=stuff#yay",
+                            server_url() ++ "/bing?q=stuff#yay"}
+        ])
+    }.
+
+
+do_rewrite_tests(Tests) ->
+    lists:map(fun({Header, Location, Url}) ->
+        should_rewrite_header(Header, Location, Url)
+    end, Tests).
+
+should_rewrite_header(Header, Location, Url) ->
+    Remote = fun(Req) ->
+        "/rewrite_test" = Req:get(path),
+        {ok, {302, [{Header, Location}], "ok"}}
+    end,
+    Local = fun
+        ({ok, "302", Headers, "ok"}) ->
+            ?assertEqual(Url, couch_util:get_value(Header, Headers)),
+            true;
+        (E) ->
+            ?debugFmt("~p", [E]),
+            false
+    end,
+    Req = #req{path="/rewrite_test"},
+    {Header, ?_test(check_request(Req, Remote, Local))}.
+
+
+server_url() ->
+    server_url("/_test").
+
+server_url(Resource) ->
+    Addr = couch_config:get("httpd", "bind_address"),
+    Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+    lists:concat(["http://", Addr, ":", Port, Resource]).
+
+proxy_url() ->
+    "http://127.0.0.1:" ++ integer_to_list(test_web:get_port()).
+
+external_url() ->
+    "https://google.com".
+
+check_request(Req, Remote, Local) ->
+    case Remote of
+        no_remote ->
+            ok;
+        _ ->
+            test_web:set_assert(Remote)
+    end,
+    Url = case proplists:lookup(url, Req#req.opts) of
+        none ->
+            server_url() ++ Req#req.path;
+        {url, DestUrl} ->
+            DestUrl
+    end,
+    Opts = [{headers_as_is, true} | Req#req.opts],
+    Resp =ibrowse:send_req(
+        Url, Req#req.headers, Req#req.method, Req#req.body, Opts
+    ),
+    %?debugFmt("ibrowse response: ~p", [Resp]),
+    case Local of
+        no_local ->
+            ok;
+        _ ->
+            ?assert(Local(Resp))
+    end,
+    case {Remote, Local} of
+        {no_remote, _} ->
+            ok;
+        {_, no_local} ->
+            ok;
+        _ ->
+            ?assertEqual(was_ok, test_web:check_last())
+    end,
+    Resp.
+
+chunked_body(Chunks) ->
+    chunked_body(Chunks, []).
+
+chunked_body([], Acc) ->
+    iolist_to_binary(lists:reverse(Acc, "0\r\n\r\n"));
+chunked_body([Chunk | Rest], Acc) ->
+    Size = to_hex(size(Chunk)),
+    chunked_body(Rest, ["\r\n", Chunk, "\r\n", Size | Acc]).
+
+to_hex(Val) ->
+    to_hex(Val, []).
+
+to_hex(0, Acc) ->
+    Acc;
+to_hex(Val, Acc) ->
+    to_hex(Val div 16, [hex_char(Val rem 16) | Acc]).
+
+hex_char(V) when V < 10 -> $0 + V;
+hex_char(V) -> $A + V - 10.
+
+recv_body(ReqId, Acc) ->
+    receive
+        {ibrowse_async_response, ReqId, Data} ->
+            recv_body(ReqId, [Data | Acc]);
+        {ibrowse_async_response_end, ReqId} ->
+            iolist_to_binary(lists:reverse(Acc));
+        Else ->
+            throw({error, unexpected_mesg, Else})
+    after ?TIMEOUT ->
+        throw({error, timeout})
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/f8dd4b28/test/couchdb/test_web.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/test_web.erl b/test/couchdb/test_web.erl
new file mode 100644
index 0000000..1de2cd1
--- /dev/null
+++ b/test/couchdb/test_web.erl
@@ -0,0 +1,112 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(test_web).
+-behaviour(gen_server).
+
+-include("couch_eunit.hrl").
+
+-export([start_link/0, stop/0, loop/1, get_port/0, set_assert/1, check_last/0]).
+-export([init/1, terminate/2, code_change/3]).
+-export([handle_call/3, handle_cast/2, handle_info/2]).
+
+-define(SERVER, test_web_server).
+-define(HANDLER, test_web_handler).
+-define(DELAY, 500).
+
+start_link() ->
+    gen_server:start({local, ?HANDLER}, ?MODULE, [], []),
+    mochiweb_http:start([
+        {name, ?SERVER},
+        {loop, {?MODULE, loop}},
+        {port, 0}
+    ]).
+
+loop(Req) ->
+    %?debugFmt("Handling request: ~p", [Req]),
+    case gen_server:call(?HANDLER, {check_request, Req}) of
+        {ok, RespInfo} ->
+            {ok, Req:respond(RespInfo)};
+        {raw, {Status, Headers, BodyChunks}} ->
+            Resp = Req:start_response({Status, Headers}),
+            lists:foreach(fun(C) -> Resp:send(C) end, BodyChunks),
+            erlang:put(mochiweb_request_force_close, true),
+            {ok, Resp};
+        {chunked, {Status, Headers, BodyChunks}} ->
+            Resp = Req:respond({Status, Headers, chunked}),
+            timer:sleep(?DELAY),
+            lists:foreach(fun(C) -> Resp:write_chunk(C) end, BodyChunks),
+            Resp:write_chunk([]),
+            {ok, Resp};
+        {error, Reason} ->
+            ?debugFmt("Error: ~p", [Reason]),
+            Body = lists:flatten(io_lib:format("Error: ~p", [Reason])),
+            {ok, Req:respond({200, [], Body})}
+    end.
+
+get_port() ->
+    mochiweb_socket_server:get(?SERVER, port).
+
+set_assert(Fun) ->
+    ?assertEqual(ok, gen_server:call(?HANDLER, {set_assert, Fun})).
+
+check_last() ->
+    gen_server:call(?HANDLER, last_status).
+
+init(_) ->
+    {ok, nil}.
+
+terminate(_Reason, _State) ->
+    ok.
+
+stop() ->
+    gen_server:cast(?SERVER, stop).
+
+
+handle_call({check_request, Req}, _From, State) when is_function(State, 1) ->
+    Resp2 = case (catch State(Req)) of
+        {ok, Resp} ->
+            {reply, {ok, Resp}, was_ok};
+        {raw, Resp} ->
+            {reply, {raw, Resp}, was_ok};
+        {chunked, Resp} ->
+            {reply, {chunked, Resp}, was_ok};
+        Error ->
+            {reply, {error, Error}, not_ok}
+    end,
+    Req:cleanup(),
+    Resp2;
+handle_call({check_request, _Req}, _From, _State) ->
+    {reply, {error, no_assert_function}, not_ok};
+handle_call(last_status, _From, State) when is_atom(State) ->
+    {reply, State, nil};
+handle_call(last_status, _From, State) ->
+    {reply, {error, not_checked}, State};
+handle_call({set_assert, Fun}, _From, nil) ->
+    {reply, ok, Fun};
+handle_call({set_assert, _}, _From, State) ->
+    {reply, {error, assert_function_set}, State};
+handle_call(Msg, _From, State) ->
+    {reply, {ignored, Msg}, State}.
+
+handle_cast(stop, State) ->
+    {stop, normal, State};
+handle_cast(Msg, State) ->
+    ?debugFmt("Ignoring cast message: ~p", [Msg]),
+    {noreply, State}.
+
+handle_info(Msg, State) ->
+    ?debugFmt("Ignoring info message: ~p", [Msg]),
+    {noreply, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.