From e4ae7f5182b88cd60f0332f2a1b12ac54d3493df Mon Sep 17 00:00:00 2001 From: Alexander Shorin Date: Tue, 13 Jan 2015 05:25:24 +0300 Subject: [PATCH] Goodbye, etap! --- test/etap/001-load.t | 86 --- test/etap/002-icu-driver.t | 33 - test/etap/010-file-basics.t | 114 --- test/etap/011-file-headers.t | 152 ---- test/etap/020-btree-basics.t | 265 ------- test/etap/021-btree-reductions.t | 237 ------ test/etap/022-emsort-basics.t | 72 -- test/etap/030-doc-from-json.t | 232 ------ test/etap/031-doc-to-json.t | 193 ----- test/etap/040-util.t | 80 -- test/etap/041-uuid-gen.t | 130 ---- test/etap/042-work-queue.t | 500 ------------ test/etap/043-find-in-binary.t | 68 -- test/etap/050-stream.t | 87 --- test/etap/060-kt-merging.t | 175 ----- test/etap/061-kt-missing-leaves.t | 65 -- test/etap/062-kt-remove-leaves.t | 69 -- test/etap/063-kt-get-leaves.t | 98 --- test/etap/064-kt-counting.t | 46 -- test/etap/065-kt-stemming.t | 42 - test/etap/070-couch-db.t | 74 -- test/etap/071-couchdb-rapid-cycle.t | 74 -- test/etap/072-cleanup.t | 126 --- test/etap/073-changes.t | 550 ------------- test/etap/074-doc-update-conflicts.t | 208 ----- test/etap/075-auth-cache.t | 274 ------- test/etap/076-file-compression.t | 183 ----- .../etap/077-couch-db-fast-db-delete-create.t | 50 -- test/etap/080-config-get-set.t | 128 --- test/etap/081-config-override.1.ini | 22 - test/etap/081-config-override.2.ini | 22 - test/etap/081-config-override.t | 208 ----- test/etap/082-config-register.t | 79 -- test/etap/083-config-no-files.t | 53 -- test/etap/090-task-status.t | 279 ------- test/etap/120-stats-collect.t | 150 ---- test/etap/121-stats-aggregates.cfg | 19 - test/etap/121-stats-aggregates.ini | 20 - test/etap/121-stats-aggregates.t | 162 ---- test/etap/130-attachments-md5.t | 248 ------ test/etap/140-attachment-comp.t | 728 ------------------ test/etap/150-invalid-view-seq.t | 172 ----- test/etap/160-vhosts.t | 371 --------- test/etap/170-os-daemons.es | 26 - test/etap/170-os-daemons.t | 114 --- test/etap/171-os-daemons-config.es | 85 -- test/etap/171-os-daemons-config.t | 72 -- test/etap/172-os-daemon-errors.1.sh | 17 - test/etap/172-os-daemon-errors.2.sh | 15 - test/etap/172-os-daemon-errors.3.sh | 15 - test/etap/172-os-daemon-errors.4.sh | 15 - test/etap/172-os-daemon-errors.t | 126 --- test/etap/173-os-daemon-cfg-register.t | 116 --- test/etap/180-http-proxy.ini | 20 - test/etap/180-http-proxy.t | 366 --------- test/etap/190-json-stream-parse.t | 184 ----- test/etap/200-view-group-no-db-leaks.t | 271 ------- test/etap/201-view-group-shutdown.t | 291 ------- test/etap/210-os-proc-pool.t | 163 ---- test/etap/220-compaction-daemon.t | 225 ------ test/etap/230-pbkfd2.t | 38 - test/etap/231-cors.t | 430 ----------- test/etap/232-csp.t | 85 -- test/etap/250-upgrade-legacy-view-files.t | 168 ---- test/etap/etap.erl | 614 --------------- .../3b835456c235b1827e012e25666152f3.view | Bin 4192 -> 0 bytes test/etap/fixtures/test.couch | Bin 16482 -> 0 bytes test/etap/run.tpl | 32 - test/etap/test_cfg_register.c | 31 - test/etap/test_util.erl.in | 115 --- test/etap/test_web.erl | 99 --- 71 files changed, 10677 deletions(-) delete mode 100755 test/etap/001-load.t delete mode 100755 test/etap/002-icu-driver.t delete mode 100755 test/etap/010-file-basics.t delete mode 100755 test/etap/011-file-headers.t delete mode 100755 test/etap/020-btree-basics.t delete mode 100755 test/etap/021-btree-reductions.t delete mode 100755 test/etap/022-emsort-basics.t delete mode 100755 test/etap/030-doc-from-json.t delete mode 100755 test/etap/031-doc-to-json.t delete mode 100755 test/etap/040-util.t delete mode 100755 test/etap/041-uuid-gen.t delete mode 100755 test/etap/042-work-queue.t delete mode 100755 test/etap/043-find-in-binary.t delete mode 100755 test/etap/050-stream.t delete mode 100755 test/etap/060-kt-merging.t delete mode 100755 test/etap/061-kt-missing-leaves.t delete mode 100755 test/etap/062-kt-remove-leaves.t delete mode 100755 test/etap/063-kt-get-leaves.t delete mode 100755 test/etap/064-kt-counting.t delete mode 100755 test/etap/065-kt-stemming.t delete mode 100755 test/etap/070-couch-db.t delete mode 100755 test/etap/071-couchdb-rapid-cycle.t delete mode 100755 test/etap/072-cleanup.t delete mode 100755 test/etap/073-changes.t delete mode 100755 test/etap/074-doc-update-conflicts.t delete mode 100755 test/etap/075-auth-cache.t delete mode 100755 test/etap/076-file-compression.t delete mode 100644 test/etap/077-couch-db-fast-db-delete-create.t delete mode 100755 test/etap/080-config-get-set.t delete mode 100644 test/etap/081-config-override.1.ini delete mode 100644 test/etap/081-config-override.2.ini delete mode 100755 test/etap/081-config-override.t delete mode 100755 test/etap/082-config-register.t delete mode 100755 test/etap/083-config-no-files.t delete mode 100755 test/etap/090-task-status.t delete mode 100755 test/etap/120-stats-collect.t delete mode 100644 test/etap/121-stats-aggregates.cfg delete mode 100644 test/etap/121-stats-aggregates.ini delete mode 100755 test/etap/121-stats-aggregates.t delete mode 100755 test/etap/130-attachments-md5.t delete mode 100755 test/etap/140-attachment-comp.t delete mode 100755 test/etap/150-invalid-view-seq.t delete mode 100755 test/etap/160-vhosts.t delete mode 100755 test/etap/170-os-daemons.es delete mode 100755 test/etap/170-os-daemons.t delete mode 100755 test/etap/171-os-daemons-config.es delete mode 100755 test/etap/171-os-daemons-config.t delete mode 100644 test/etap/172-os-daemon-errors.1.sh delete mode 100755 test/etap/172-os-daemon-errors.2.sh delete mode 100755 test/etap/172-os-daemon-errors.3.sh delete mode 100755 test/etap/172-os-daemon-errors.4.sh delete mode 100755 test/etap/172-os-daemon-errors.t delete mode 100755 test/etap/173-os-daemon-cfg-register.t delete mode 100644 test/etap/180-http-proxy.ini delete mode 100755 test/etap/180-http-proxy.t delete mode 100755 test/etap/190-json-stream-parse.t delete mode 100755 test/etap/200-view-group-no-db-leaks.t delete mode 100755 test/etap/201-view-group-shutdown.t delete mode 100755 test/etap/210-os-proc-pool.t delete mode 100755 test/etap/220-compaction-daemon.t delete mode 100644 test/etap/230-pbkfd2.t delete mode 100644 test/etap/231-cors.t delete mode 100644 test/etap/232-csp.t delete mode 100644 test/etap/250-upgrade-legacy-view-files.t delete mode 100644 test/etap/etap.erl delete mode 100644 test/etap/fixtures/3b835456c235b1827e012e25666152f3.view delete mode 100644 test/etap/fixtures/test.couch delete mode 100644 test/etap/run.tpl delete mode 100644 test/etap/test_cfg_register.c delete mode 100644 test/etap/test_util.erl.in delete mode 100644 test/etap/test_web.erl diff --git a/test/etap/001-load.t b/test/etap/001-load.t deleted file mode 100755 index 3645f53328b..00000000000 --- a/test/etap/001-load.t +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -% Test that we can load each module. - -main(_) -> - test_util:init_code_path(), - Modules = [ - couch, - couch_app, - couch_auth_cache, - couch_btree, - couch_changes, - couch_compaction_daemon, - couch_compress, - couch_config, - couch_config_writer, - couch_db, - couch_db_update_notifier, - couch_db_update_notifier_sup, - couch_db_updater, - couch_doc, - couch_drv, - % Fails unless couch_config gen_server is started. - % couch_ejson_compare, - couch_emsort, - couch_event_sup, - couch_external_manager, - couch_external_server, - couch_file, - couch_httpd, - couch_httpd_auth, - couch_httpd_cors, - couch_httpd_db, - couch_httpd_external, - couch_httpd_misc_handlers, - couch_httpd_oauth, - couch_httpd_proxy, - couch_httpd_rewrite, - couch_httpd_stats_handlers, - couch_httpd_vhost, - couch_key_tree, - couch_log, - couch_lru, - couch_native_process, - couch_os_daemons, - couch_os_process, - couch_passwords, - couch_primary_sup, - couch_proc_manager, - couch_query_servers, - couch_secondary_sup, - couch_server, - couch_stats_aggregator, - couch_stats_collector, - couch_stream, - couch_sup, - couch_task_status, - couch_users_db, - couch_util, - couch_uuids, - couch_work_queue, - json_stream_parse - ], - - etap:plan(length(Modules)), - lists:foreach( - fun(Module) -> - etap:loaded_ok( - Module, - lists:concat(["Loaded: ", Module]) - ) - end, Modules), - etap:end_tests(). diff --git a/test/etap/002-icu-driver.t b/test/etap/002-icu-driver.t deleted file mode 100755 index 2ce47d92b78..00000000000 --- a/test/etap/002-icu-driver.t +++ /dev/null @@ -1,33 +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. - -main(_) -> - test_util:init_code_path(), - application:start(config), - etap:plan(3), - etap:is( - element(1, couch_drv:start_link()), - ok, - "Started couch_icu_driver." - ), - etap:is( - couch_util:collate(<<"foo">>, <<"bar">>), - 1, - "Can collate stuff" - ), - etap:is( - couch_util:collate(<<"A">>, <<"aa">>), - -1, - "Collate's non-ascii style." - ), - etap:end_tests(). diff --git a/test/etap/010-file-basics.t b/test/etap/010-file-basics.t deleted file mode 100755 index a33771779a6..00000000000 --- a/test/etap/010-file-basics.t +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -% 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. - --define(etap_match(Got, Expected, Desc), - etap:fun_is(fun(XXXXXX) -> - case XXXXXX of Expected -> true; _ -> false end - end, Got, Desc)). - -filename() -> test_util:build_file("test/etap/temp.010"). - -main(_) -> - test_util:init_code_path(), - etap:plan(19), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - timer:sleep(1000), - etap:bail() - end, - ok. - -test() -> - etap:is({error, enoent}, couch_file:open("not a real file"), - "Opening a non-existant file should return an enoent error."), - - etap:fun_is( - fun({ok, _}) -> true; (_) -> false end, - couch_file:open(filename() ++ ".1", [create, invalid_option]), - "Invalid flags to open are ignored." - ), - - {ok, Fd} = couch_file:open(filename() ++ ".0", [create, overwrite]), - etap:ok(is_pid(Fd), - "Returned file descriptor is a Pid"), - - etap:is({ok, 0}, couch_file:bytes(Fd), - "Newly created files have 0 bytes."), - - ?etap_match(couch_file:append_term(Fd, foo), {ok, 0, _}, - "Appending a term returns the previous end of file position."), - - {ok, Size} = couch_file:bytes(Fd), - etap:is_greater(Size, 0, - "Writing a term increased the file size."), - - ?etap_match(couch_file:append_binary(Fd, <<"fancy!">>), {ok, Size, _}, - "Appending a binary returns the current file size."), - - etap:is({ok, foo}, couch_file:pread_term(Fd, 0), - "Reading the first term returns what we wrote: foo"), - - etap:is({ok, <<"fancy!">>}, couch_file:pread_binary(Fd, Size), - "Reading back the binary returns what we wrote: <<\"fancy\">>."), - - etap:is({ok, couch_compress:compress(foo, snappy)}, - couch_file:pread_binary(Fd, 0), - "Reading a binary at a term position returns the term as binary." - ), - - {ok, BinPos, _} = couch_file:append_binary(Fd, <<131,100,0,3,102,111,111>>), - etap:is({ok, foo}, couch_file:pread_term(Fd, BinPos), - "Reading a term from a written binary term representation succeeds."), - - BigBin = list_to_binary(lists:duplicate(100000, 0)), - {ok, BigBinPos, _} = couch_file:append_binary(Fd, BigBin), - etap:is({ok, BigBin}, couch_file:pread_binary(Fd, BigBinPos), - "Reading a large term from a written representation succeeds."), - - ok = couch_file:write_header(Fd, hello), - etap:is({ok, hello}, couch_file:read_header(Fd), - "Reading a header succeeds."), - - {ok, BigBinPos2, _} = couch_file:append_binary(Fd, BigBin), - etap:is({ok, BigBin}, couch_file:pread_binary(Fd, BigBinPos2), - "Reading a large term from a written representation succeeds 2."), - - % append_binary == append_iolist? - % Possible bug in pread_iolist or iolist() -> append_binary - {ok, IOLPos, _} = couch_file:append_binary(Fd, ["foo", $m, <<"bam">>]), - {ok, IoList} = couch_file:pread_iolist(Fd, IOLPos), - etap:is(<<"foombam">>, iolist_to_binary(IoList), - "Reading an results in a binary form of the written iolist()"), - - % XXX: How does on test fsync? - etap:is(ok, couch_file:sync(Fd), - "Syncing does not cause an error."), - - etap:is(ok, couch_file:truncate(Fd, Size), - "Truncating a file succeeds."), - - %etap:is(eof, (catch couch_file:pread_binary(Fd, Size)), - % "Reading data that was truncated fails.") - etap:skip(fun() -> ok end, - "No idea how to test reading beyond EOF"), - - etap:is({ok, foo}, couch_file:pread_term(Fd, 0), - "Truncating does not affect data located before the truncation mark."), - - etap:is(ok, couch_file:close(Fd), - "Files close properly."), - ok. diff --git a/test/etap/011-file-headers.t b/test/etap/011-file-headers.t deleted file mode 100755 index a26b0320768..00000000000 --- a/test/etap/011-file-headers.t +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -%%! -pa ./src/couchdb -sasl errlog_type error -boot start_sasl -noshell - -% 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. - -filename() -> test_util:build_file("test/etap/temp.011"). -sizeblock() -> 4096. % Need to keep this in sync with couch_file.erl - -main(_) -> - test_util:init_code_path(), - {S1, S2, S3} = now(), - random:seed(S1, S2, S3), - - etap:plan(18), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail() - end, - ok. - -test() -> - {ok, Fd} = couch_file:open(filename(), [create,overwrite]), - - etap:is({ok, 0}, couch_file:bytes(Fd), - "File should be initialized to contain zero bytes."), - - etap:is(ok, couch_file:write_header(Fd, {<<"some_data">>, 32}), - "Writing a header succeeds."), - - {ok, Size1} = couch_file:bytes(Fd), - etap:is_greater(Size1, 0, - "Writing a header allocates space in the file."), - - etap:is({ok, {<<"some_data">>, 32}}, couch_file:read_header(Fd), - "Reading the header returns what we wrote."), - - etap:is(ok, couch_file:write_header(Fd, [foo, <<"more">>]), - "Writing a second header succeeds."), - - {ok, Size2} = couch_file:bytes(Fd), - etap:is_greater(Size2, Size1, - "Writing a second header allocates more space."), - - etap:is({ok, [foo, <<"more">>]}, couch_file:read_header(Fd), - "Reading the second header does not return the first header."), - - % Delete the second header. - ok = couch_file:truncate(Fd, Size1), - - etap:is({ok, {<<"some_data">>, 32}}, couch_file:read_header(Fd), - "Reading the header after a truncation returns a previous header."), - - couch_file:write_header(Fd, [foo, <<"more">>]), - etap:is({ok, Size2}, couch_file:bytes(Fd), - "Rewriting the same second header returns the same second size."), - - couch_file:write_header(Fd, erlang:make_tuple(5000, <<"CouchDB">>)), - etap:is( - couch_file:read_header(Fd), - {ok, erlang:make_tuple(5000, <<"CouchDB">>)}, - "Headers larger than the block size can be saved (COUCHDB-1319)" - ), - - ok = couch_file:close(Fd), - - % Now for the fun stuff. Try corrupting the second header and see - % if we recover properly. - - % Destroy the 0x1 byte that marks a header - check_header_recovery(fun(CouchFd, RawFd, Expect, HeaderPos) -> - etap:isnt(Expect, couch_file:read_header(CouchFd), - "Should return a different header before corruption."), - file:pwrite(RawFd, HeaderPos, <<0>>), - etap:is(Expect, couch_file:read_header(CouchFd), - "Corrupting the byte marker should read the previous header.") - end), - - % Corrupt the size. - check_header_recovery(fun(CouchFd, RawFd, Expect, HeaderPos) -> - etap:isnt(Expect, couch_file:read_header(CouchFd), - "Should return a different header before corruption."), - % +1 for 0x1 byte marker - file:pwrite(RawFd, HeaderPos+1, <<10/integer>>), - etap:is(Expect, couch_file:read_header(CouchFd), - "Corrupting the size should read the previous header.") - end), - - % Corrupt the MD5 signature - check_header_recovery(fun(CouchFd, RawFd, Expect, HeaderPos) -> - etap:isnt(Expect, couch_file:read_header(CouchFd), - "Should return a different header before corruption."), - % +5 = +1 for 0x1 byte and +4 for term size. - file:pwrite(RawFd, HeaderPos+5, <<"F01034F88D320B22">>), - etap:is(Expect, couch_file:read_header(CouchFd), - "Corrupting the MD5 signature should read the previous header.") - end), - - % Corrupt the data - check_header_recovery(fun(CouchFd, RawFd, Expect, HeaderPos) -> - etap:isnt(Expect, couch_file:read_header(CouchFd), - "Should return a different header before corruption."), - % +21 = +1 for 0x1 byte, +4 for term size and +16 for MD5 sig - file:pwrite(RawFd, HeaderPos+21, <<"some data goes here!">>), - etap:is(Expect, couch_file:read_header(CouchFd), - "Corrupting the header data should read the previous header.") - end), - - ok. - -check_header_recovery(CheckFun) -> - {ok, Fd} = couch_file:open(filename(), [create,overwrite]), - {ok, RawFd} = file:open(filename(), [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 sizeblock()) * sizeblock()}; -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). - diff --git a/test/etap/020-btree-basics.t b/test/etap/020-btree-basics.t deleted file mode 100755 index b0fb2d28c46..00000000000 --- a/test/etap/020-btree-basics.t +++ /dev/null @@ -1,265 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -%%! -pa ./src/couchdb -sasl errlog_type error -boot start_sasl -noshell - -% 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. - -filename() -> test_util:build_file("test/etap/temp.020"). -rows() -> 250. - --record(btree, { - fd, - root, - extract_kv, - assemble_kv, - less, - reduce, - compression -}). - -main(_) -> - test_util:init_code_path(), - etap:plan(75), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail() - end, - ok. - -%% @todo Determine if this number should be greater to see if the btree was -%% broken into multiple nodes. AKA "How do we appropiately detect if multiple -%% nodes were created." -test()-> - Sorted = [{Seq, random:uniform()} || Seq <- lists:seq(1, rows())], - etap:ok(test_kvs(Sorted), "Testing sorted keys"), - etap:ok(test_kvs(lists:reverse(Sorted)), "Testing reversed sorted keys"), - etap:ok(test_kvs(shuffle(Sorted)), "Testing shuffled keys."), - ok. - -test_kvs(KeyValues) -> - ReduceFun = fun - (reduce, KVs) -> - length(KVs); - (rereduce, Reds) -> - lists:sum(Reds) - end, - - Keys = [K || {K, _} <- KeyValues], - - {ok, Fd} = couch_file:open(filename(), [create,overwrite]), - {ok, Btree} = couch_btree:open(nil, Fd, [{compression, none}]), - etap:ok(is_record(Btree, btree), "Created btree is really a btree record"), - etap:is(Btree#btree.fd, Fd, "Btree#btree.fd is set correctly."), - etap:is(Btree#btree.root, nil, "Btree#btree.root is set correctly."), - etap:is(0, couch_btree:size(Btree), "Empty btrees have a 0 size."), - - Btree1 = couch_btree:set_options(Btree, [{reduce, ReduceFun}]), - etap:is(Btree1#btree.reduce, ReduceFun, "Reduce function was set"), - {ok, _, EmptyRes} = couch_btree:foldl(Btree1, fun(_, X) -> {ok, X+1} end, 0), - etap:is(EmptyRes, 0, "Folding over an empty btree"), - - {ok, Btree2} = couch_btree:add_remove(Btree1, KeyValues, []), - etap:ok(test_btree(Btree2, KeyValues), - "Adding all keys at once returns a complete btree."), - - etap:is((couch_btree:size(Btree2) > 0), true, - "Non empty btrees have a size > 0."), - etap:is((couch_btree:size(Btree2) =< couch_file:bytes(Fd)), true, - "Btree size is <= file size."), - - etap:fun_is( - fun - ({ok, {kp_node, _}}) -> true; - (_) -> false - end, - couch_file:pread_term(Fd, element(1, Btree2#btree.root)), - "Btree root pointer is a kp_node." - ), - - {ok, Btree3} = couch_btree:add_remove(Btree2, [], Keys), - etap:ok(test_btree(Btree3, []), - "Removing all keys at once returns an empty btree."), - - etap:is(0, couch_btree:size(Btree3), - "After removing all keys btree size is 0."), - - {Btree4, _} = lists:foldl(fun(KV, {BtAcc, PrevSize}) -> - {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [KV], []), - case couch_btree:size(BtAcc2) > PrevSize of - true -> - ok; - false -> - etap:bail("After inserting a value, btree size did not increase.") - end, - {BtAcc2, couch_btree:size(BtAcc2)} - end, {Btree3, couch_btree:size(Btree3)}, KeyValues), - - etap:ok(test_btree(Btree4, KeyValues), - "Adding all keys one at a time returns a complete btree."), - etap:is((couch_btree:size(Btree4) > 0), true, - "Non empty btrees have a size > 0."), - - {Btree5, _} = lists:foldl(fun({K, _}, {BtAcc, PrevSize}) -> - {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [], [K]), - case couch_btree:size(BtAcc2) < PrevSize of - true -> - ok; - false -> - etap:bail("After removing a key, btree size did not decrease.") - end, - {BtAcc2, couch_btree:size(BtAcc2)} - end, {Btree4, couch_btree:size(Btree4)}, KeyValues), - etap:ok(test_btree(Btree5, []), - "Removing all keys one at a time returns an empty btree."), - etap:is(0, couch_btree:size(Btree5), - "After removing all keys, one by one, btree size is 0."), - - KeyValuesRev = lists:reverse(KeyValues), - {Btree6, _} = lists:foldl(fun(KV, {BtAcc, PrevSize}) -> - {ok, BtAcc2} = couch_btree:add_remove(BtAcc, [KV], []), - case couch_btree:size(BtAcc2) > PrevSize of - true -> - ok; - false -> - etap:is(false, true, - "After inserting a value, btree size did not increase.") - end, - {BtAcc2, couch_btree:size(BtAcc2)} - end, {Btree5, couch_btree:size(Btree5)}, KeyValuesRev), - etap:ok(test_btree(Btree6, KeyValues), - "Adding all keys in reverse order returns a complete btree."), - - {_, 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), - - etap:ok(test_add_remove(Btree6, Rem2Keys0, Rem2Keys1), - "Add/Remove every other key."), - - etap:ok(test_add_remove(Btree6, Rem2Keys1, Rem2Keys0), - "Add/Remove opposite every other key."), - - Size1 = couch_btree:size(Btree6), - {ok, Btree7} = couch_btree:add_remove(Btree6, [], [K||{K,_}<-Rem2Keys1]), - Size2 = couch_btree:size(Btree7), - etap:is((Size2 < Size1), true, "Btree size decreased"), - {ok, Btree8} = couch_btree:add_remove(Btree7, [], [K||{K,_}<-Rem2Keys0]), - Size3 = couch_btree:size(Btree8), - etap:is((Size3 < Size2), true, "Btree size decreased"), - etap:is(Size3, 0, "Empty btree has size 0."), - etap:ok(test_btree(Btree8, []), - "Removing both halves of every other key returns an empty btree."), - - %% Third chunk (close out) - etap:is(couch_file:close(Fd), ok, "closing out"), - true. - -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. - -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. diff --git a/test/etap/021-btree-reductions.t b/test/etap/021-btree-reductions.t deleted file mode 100755 index e80ac2ded34..00000000000 --- a/test/etap/021-btree-reductions.t +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -%%! -pa ./src/couchdb -sasl errlog_type error -boot start_sasl -noshell - -% 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. - -filename() -> "./test/etap/temp.021". -rows() -> 1000. - -main(_) -> - test_util:init_code_path(), - etap:plan(20), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail() - end, - ok. - -test()-> - ReduceFun = fun - (reduce, KVs) -> length(KVs); - (rereduce, Reds) -> lists:sum(Reds) - end, - - {ok, Fd} = couch_file:open(filename(), [create,overwrite]), - {ok, Btree} = couch_btree:open(nil, Fd, [{reduce, ReduceFun}]), - - % Create a list, of {"even", Value} or {"odd", Value} pairs. - {_, 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())), - - {ok, Btree2} = couch_btree:add_remove(Btree, EvenOddKVs, []), - - GroupFun = fun({K1, _}, {K2, _}) -> K1 == K2 end, - FoldFun = fun(GroupedKey, Unreduced, Acc) -> - {ok, [{GroupedKey, couch_btree:final_reduce(Btree2, Unreduced)} | Acc]} - end, - - {SK1, EK1} = {{"even", -1}, {"even", foo}}, - {SK2, EK2} = {{"odd", -1}, {"odd", foo}}, - - etap:fun_is( - fun - ({ok, [{{"odd", _}, 500}, {{"even", _}, 500}]}) -> - true; - (_) -> - false - end, - couch_btree:fold_reduce(Btree2, FoldFun, [], [{key_group_fun, GroupFun}]), - "Reduction works with no specified direction, startkey, or endkey." - ), - - etap:fun_is( - fun - ({ok, [{{"odd", _}, 500}, {{"even", _}, 500}]}) -> - true; - (_) -> - false - end, - couch_btree:fold_reduce(Btree2, FoldFun, [], [{key_group_fun, GroupFun}, {dir, fwd}]), - "Reducing forward works with no startkey or endkey." - ), - - etap:fun_is( - fun - ({ok, [{{"even", _}, 500}, {{"odd", _}, 500}]}) -> - true; - (_) -> - false - end, - couch_btree:fold_reduce(Btree2, FoldFun, [], [{key_group_fun, GroupFun}, {dir, rev}]), - "Reducing backwards works with no startkey or endkey." - ), - - etap:fun_is( - fun - ({ok, [{{"odd", _}, 500}, {{"even", _}, 500}]}) -> - true; - (_) -> - false - end, - couch_btree:fold_reduce(Btree2, FoldFun, [], [{dir, fwd}, {key_group_fun, GroupFun}, {start_key, SK1}, {end_key, EK2}]), - "Reducing works over the entire range with startkey and endkey set." - ), - - etap:fun_is( - fun - ({ok, [{{"even", _}, 500}]}) -> true; - (_) -> false - end, - couch_btree:fold_reduce(Btree2, FoldFun, [], [{dir, fwd}, {key_group_fun, GroupFun}, {start_key, SK1}, {end_key, EK1}]), - "Reducing forward over first half works with a startkey and endkey." - ), - - etap:fun_is( - fun - ({ok, [{{"odd", _}, 500}]}) -> true; - (_) -> false - end, - couch_btree:fold_reduce(Btree2, FoldFun, [], [{dir, fwd}, {key_group_fun, GroupFun}, {start_key, SK2}, {end_key, EK2}]), - "Reducing forward over second half works with second startkey and endkey" - ), - - etap:fun_is( - fun - ({ok, [{{"odd", _}, 500}]}) -> true; - (_) -> false - end, - couch_btree:fold_reduce(Btree2, FoldFun, [], [{dir, rev}, {key_group_fun, GroupFun}, {start_key, EK2}, {end_key, SK2}]), - "Reducing in reverse works after swapping the startkey and endkey." - ), - - etap:fun_is( - fun - ({ok, [{{"even", _}, 500}, {{"odd", _}, 500}]}) -> - true; - (_) -> - false - end, - couch_btree:fold_reduce(Btree2, FoldFun, [], [{dir, rev}, {key_group_fun, GroupFun}, {start_key, EK2}, {end_key, SK1}]), - "Reducing in reverse results in reversed accumulator." - ), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, fwd}, {key_group_fun, GroupFun}, - {start_key, {"even", 0}}, {end_key, {"odd", rows() + 1}} - ]), - {ok, [{{"odd", 1}, 500}, {{"even", 2}, 500}]}, - "Right fold reduce value for whole range with inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, fwd}, {key_group_fun, GroupFun}, - {start_key, {"even", 0}}, {end_key_gt, {"odd", 999}} - ]), - {ok, [{{"odd", 1}, 499}, {{"even", 2}, 500}]}, - "Right fold reduce value for whole range without inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, rev}, {key_group_fun, GroupFun}, - {start_key, {"odd", 999}}, {end_key, {"even", 2}} - ]), - {ok, [{{"even", 1000}, 500}, {{"odd", 999}, 500}]}, - "Right fold reduce value for whole reversed range with inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, rev}, {key_group_fun, GroupFun}, - {start_key, {"odd", 999}}, {end_key_gt, {"even", 2}} - ]), - {ok, [{{"even", 1000}, 499}, {{"odd", 999}, 500}]}, - "Right fold reduce value for whole reversed range without inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, fwd}, {key_group_fun, GroupFun}, - {start_key, {"even", 0}}, {end_key, {"odd", 499}} - ]), - {ok, [{{"odd", 1}, 250}, {{"even", 2}, 500}]}, - "Right fold reduce value for first half with inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, fwd}, {key_group_fun, GroupFun}, - {start_key, {"even", 0}}, {end_key_gt, {"odd", 499}} - ]), - {ok, [{{"odd", 1}, 249}, {{"even", 2}, 500}]}, - "Right fold reduce value for first half without inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, rev}, {key_group_fun, GroupFun}, - {start_key, {"odd", 999}}, {end_key, {"even", 500}} - ]), - {ok, [{{"even", 1000}, 251}, {{"odd", 999}, 500}]}, - "Right fold reduce value for first half reversed with inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, rev}, {key_group_fun, GroupFun}, - {start_key, {"odd", 999}}, {end_key_gt, {"even", 500}} - ]), - {ok, [{{"even", 1000}, 250}, {{"odd", 999}, 500}]}, - "Right fold reduce value for first half reversed without inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, fwd}, {key_group_fun, GroupFun}, - {start_key, {"even", 500}}, {end_key, {"odd", 999}} - ]), - {ok, [{{"odd", 1}, 500}, {{"even", 500}, 251}]}, - "Right fold reduce value for second half with inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, fwd}, {key_group_fun, GroupFun}, - {start_key, {"even", 500}}, {end_key_gt, {"odd", 999}} - ]), - {ok, [{{"odd", 1}, 499}, {{"even", 500}, 251}]}, - "Right fold reduce value for second half without inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, rev}, {key_group_fun, GroupFun}, - {start_key, {"odd", 501}}, {end_key, {"even", 2}} - ]), - {ok, [{{"even", 1000}, 500}, {{"odd", 501}, 251}]}, - "Right fold reduce value for second half reversed with inclusive end key"), - - etap:is( - couch_btree:fold_reduce(Btree2, FoldFun, [], [ - {dir, rev}, {key_group_fun, GroupFun}, - {start_key, {"odd", 501}}, {end_key_gt, {"even", 2}} - ]), - {ok, [{{"even", 1000}, 499}, {{"odd", 501}, 251}]}, - "Right fold reduce value for second half reversed without inclusive end key"), - - couch_file:close(Fd). diff --git a/test/etap/022-emsort-basics.t b/test/etap/022-emsort-basics.t deleted file mode 100755 index 1f212f4bb34..00000000000 --- a/test/etap/022-emsort-basics.t +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -%%! -pa ./src/couchdb -sasl errlog_type error -boot start_sasl -noshell - -% 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. - -filename() -> "./test/etap/temp.022". -group() -> 100. -rows() -> 1000. -total() -> group() * rows(). - -main(_) -> - test_util:init_code_path(), - etap:plan(4), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - timer:sleep(1000), - etap:bail() - end, - ok. - -test()-> - {ok, Fd} = couch_file:open(filename(), [create,overwrite]), - - test_basic(Fd), - - couch_file:close(Fd). - -test_basic(Fd) -> - {ok, Ems} = couch_emsort:open(Fd), - Ems1 = lists:foldl(fun(_, EAcc0) -> - KVs = [{random:uniform(), val_here} || _ <- lists:seq(1, group())], - {ok, EAcc1} = couch_emsort:add(EAcc0, KVs), - EAcc1 - end, Ems, lists:seq(1, rows())), - - {ok, Iter1} = couch_emsort:sort(Ems1), - {Bad1, Total1} = read_sorted(Iter1, {-1, 0, 0}), - etap:is(Bad1, 0, "No rows out of order."), - etap:is(Total1, total(), "Found exactly as many rows as expected."), - - {ok, Ems2} = couch_emsort:merge(Ems1), - {ok, Iter2} = couch_emsort:iter(Ems2), - {Bad2, Total2} = read_sorted(Iter2, {-1, 0, 0}), - etap:is(Bad2, 0, "No rows out of order."), - etap:is(Total2, total(), "Found exactly as many rows as expected."), - - ok. - -read_sorted(Iter, {Prev, Bad, Count}) -> - case couch_emsort:next(Iter) of - {ok, {K, _}, NextIter} when K > Prev -> - read_sorted(NextIter, {K, Bad, Count+1}); - {ok, {K, _}, NextIter} -> - read_sorted(NextIter, {K, Bad+1, Count+1}); - finished -> - {Bad, Count} - end. - diff --git a/test/etap/030-doc-from-json.t b/test/etap/030-doc-from-json.t deleted file mode 100755 index 1a11d8dcba4..00000000000 --- a/test/etap/030-doc-from-json.t +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -%%! -pa ./src/couchdb -pa ./src/mochiweb -sasl errlog_type false -noshell - -% 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("couch/include/couch_db.hrl"). - -main(_) -> - test_util:init_code_path(), - etap:plan(26), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail() - end, - ok. - -test() -> - application:start(config), - config:set("attachments", "compression_level", "0", false), - ok = test_from_json_success(), - ok = test_from_json_errors(), - ok. - -test_from_json_success() -> - 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=[ - couch_att:new([ - {name, <<"my_attachment.fu">>}, - {data, stub}, - {type, <<"application/awesome">>}, - {att_len, 45}, - {disk_len, 45}, - {revpos, undefined} - ]), - couch_att:new([ - {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:foreach(fun({EJson, Expect, Mesg}) -> - etap:is(couch_doc:from_json_obj(EJson), Expect, Mesg) - end, Cases), - ok. - -test_from_json_errors() -> - 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:foreach(fun - ({EJson, Expect, Mesg}) -> - Error = (catch couch_doc:from_json_obj(EJson)), - etap:is(Error, Expect, Mesg); - ({EJson, Mesg}) -> - try - couch_doc:from_json_obj(EJson), - etap:ok(false, "Conversion failed to raise an exception.") - catch - _:_ -> etap:ok(true, Mesg) - end - end, Cases), - ok. diff --git a/test/etap/031-doc-to-json.t b/test/etap/031-doc-to-json.t deleted file mode 100755 index d4227e7dd4b..00000000000 --- a/test/etap/031-doc-to-json.t +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -%%! -pa ./src/couchdb -pa ./src/mochiweb -sasl errlog_type false -noshell - -% 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("couch/include/couch_db.hrl"). - -main(_) -> - test_util:init_code_path(), - etap:plan(12), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail() - end, - ok. - -test() -> - application:start(config), - config:set("attachments", "compression_level", "0", false), - ok = test_to_json_success(), - ok. - -test_to_json_success() -> - 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=[ - couch_att:new([ - {name, <<"big.xml">>}, - {type, <<"xml/sucks">>}, - {data, fun() -> ok end}, - {revpos, 1}, - {att_len, 400}, - {disk_len, 400} - ]), - couch_att:new([ - {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=[ - couch_att:new([ - {name, <<"stuff.txt">>}, - {type, <<"text/plain">>}, - {data, fun() -> <<"diet pepsi">> end}, - {revpos, 1}, - {att_len, 10}, - {disk_len, 10} - ]), - couch_att:new([ - {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:foreach(fun - ({Doc, EJson, Mesg}) -> - etap:is(couch_doc:to_json_obj(Doc, []), EJson, Mesg); - ({Options, Doc, EJson, Mesg}) -> - etap:is(couch_doc:to_json_obj(Doc, Options), EJson, Mesg) - end, Cases), - ok. - diff --git a/test/etap/040-util.t b/test/etap/040-util.t deleted file mode 100755 index d57a32ed205..00000000000 --- a/test/etap/040-util.t +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - application:start(crypto), - - etap:plan(14), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - % to_existing_atom - etap:is(true, couch_util:to_existing_atom(true), "An atom is an atom."), - etap:is(foo, couch_util:to_existing_atom(<<"foo">>), - "A binary foo is the atom foo."), - etap:is(foobarbaz, couch_util:to_existing_atom("foobarbaz"), - "A list of atoms is one munged atom."), - - % implode - etap:is([1, 38, 2, 38, 3], couch_util:implode([1,2,3],"&"), - "use & as separator in list."), - - % trim - Strings = [" foo", "foo ", "\tfoo", " foo ", "foo\t", "foo\n", "\nfoo"], - etap:ok(lists:all(fun(S) -> couch_util:trim(S) == "foo" end, Strings), - "everything here trimmed should be foo."), - - % abs_pathname - {ok, Cwd} = file:get_cwd(), - etap:is(Cwd ++ "/foo", couch_util:abs_pathname("./foo"), - "foo is in this directory."), - - % should_flush - etap:ok(not couch_util:should_flush(), - "Not using enough memory to flush."), - AcquireMem = fun() -> - _IntsToAGazillion = lists:seq(1, 200000), - _LotsOfData = lists:map( - fun(Int) -> {Int, <<"foobar">>} end, - lists:seq(1, 500000)), - etap:ok(couch_util:should_flush(), - "Allocation 200K tuples puts us above the memory threshold.") - end, - AcquireMem(), - - etap:ok(not couch_util:should_flush(), - "Checking to flush invokes GC."), - - % verify - etap:is(true, couch_util:verify("It4Vooya", "It4Vooya"), - "String comparison."), - etap:is(false, couch_util:verify("It4VooyaX", "It4Vooya"), - "String comparison (unequal lengths)."), - etap:is(true, couch_util:verify(<<"ahBase3r">>, <<"ahBase3r">>), - "Binary comparison."), - etap:is(false, couch_util:verify(<<"ahBase3rX">>, <<"ahBase3r">>), - "Binary comparison (unequal lengths)."), - etap:is(false, couch_util:verify(nil, <<"ahBase3r">>), - "Binary comparison with atom."), - - ok. diff --git a/test/etap/041-uuid-gen.t b/test/etap/041-uuid-gen.t deleted file mode 100755 index da1ded11aa4..00000000000 --- a/test/etap/041-uuid-gen.t +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -% Run tests and wait for the gen_servers to shutdown -run_test(Config, Test) -> - lists:foreach(fun({Key, Value}) -> - config:set("uuids", Key, Value, false) - end, Config), - couch_uuids:start(), - Test(), - couch_uuids:stop(). - -main(_) -> - test_util:init_code_path(), - application:start(crypto), - application:start(config), - etap:plan(9), - - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - TestUnique = fun() -> - etap:is( - test_unique(10000, couch_uuids:new()), - true, - "Can generate 10K unique IDs" - ) - end, - run_test([{"algorithm", "random"}], TestUnique), - run_test([{"algorithm", "sequential"}], TestUnique), - run_test([{"algorithm", "utc_random"}], TestUnique), - run_test([{"algorithm", "utc_id"}, {"utc_id_suffix", "bozo"}], TestUnique), - - TestMonotonic = fun () -> - etap:is( - couch_uuids:new() < couch_uuids:new(), - true, - "should produce monotonically increasing ids" - ) - end, - run_test([{"algorithm", "sequential"}], TestMonotonic), - run_test([{"algorithm", "utc_random"}], TestMonotonic), - run_test([{"algorithm", "utc_id"}, {"utc_id_suffix", "bozo"}], - TestMonotonic), - - % Pretty sure that the average of a uniform distribution is the - % midpoint of the range. Thus, to exceed a threshold, we need - % approximately Total / (Range/2 + RangeMin) samples. - % - % In our case this works out to be 8194. (0xFFF000 / 0x7FF) - % These tests just fudge the limits for a good generator at 25% - % in either direction. Technically it should be possible to generate - % bounds that will show if your random number generator is not - % sufficiently random but I hated statistics in school. - TestRollOver = fun() -> - UUID = binary_to_list(couch_uuids:new()), - Prefix = element(1, lists:split(26, UUID)), - N = gen_until_pref_change(Prefix,0), - etap:diag("N is: ~p~n",[N]), - etap:is( - N >= 5000 andalso N =< 11000, - true, - "should roll over every so often." - ) - end, - run_test([{"algorithm", "sequential"}], TestRollOver), - - TestSuffix = fun() -> - UUID = binary_to_list(couch_uuids:new()), - Suffix = get_suffix(UUID), - etap:is( - test_same_suffix(100, Suffix), - true, - "utc_id ids should have the same suffix." - ) - end, - run_test([{"algorithm", "utc_id"}, {"utc_id_suffix", "bozo"}], TestSuffix). - -test_unique(0, _) -> - true; -test_unique(N, UUID) -> - case couch_uuids:new() of - UUID -> - etap:diag("N: ~p~n", [N]), - false; - Else -> test_unique(N-1, Else) - end. - -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. diff --git a/test/etap/042-work-queue.t b/test/etap/042-work-queue.t deleted file mode 100755 index 8594a6f8787..00000000000 --- a/test/etap/042-work-queue.t +++ /dev/null @@ -1,500 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -% 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. - -main(_) -> - test_util:init_code_path(), - - etap:plan(155), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - - -test() -> - ok = crypto:start(), - test_single_consumer_max_item_count(), - test_single_consumer_max_size(), - test_single_consumer_max_item_count_and_size(), - test_multiple_consumers(), - ok. - - -test_single_consumer_max_item_count() -> - etap:diag("Spawning a queue with 3 max items, 1 producer and 1 consumer"), - - {ok, Q} = couch_work_queue:new([{max_items, 3}]), - Producer = spawn_producer(Q), - Consumer = spawn_consumer(Q), - - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - - consume(Consumer, 1), - etap:is(ping(Consumer), timeout, - "Consumer blocked when attempting to dequeue 1 item from empty queue"), - - Item1 = produce(Producer, 10), - etap:is(ping(Producer), ok, "Producer not blocked"), - - etap:is(ping(Consumer), ok, "Consumer unblocked"), - etap:is(last_consumer_items(Consumer), {ok, [Item1]}, - "Consumer received the right item"), - - Item2 = produce(Producer, 20), - etap:is(ping(Producer), ok, "Producer not blocked with non full queue"), - etap:is(couch_work_queue:item_count(Q), 1, "Queue item count is 1"), - - Item3 = produce(Producer, 15), - etap:is(ping(Producer), ok, "Producer not blocked with non full queue"), - etap:is(couch_work_queue:item_count(Q), 2, "Queue item count is 2"), - - Item4 = produce(Producer, 3), - etap:is(couch_work_queue:item_count(Q), 3, "Queue item count is 3"), - etap:is(ping(Producer), timeout, "Producer blocked with full queue"), - - consume(Consumer, 2), - etap:is(ping(Consumer), ok, - "Consumer not blocked when attempting to dequeue 2 items from queue"), - etap:is(last_consumer_items(Consumer), {ok, [Item2, Item3]}, - "Consumer received the right items"), - etap:is(couch_work_queue:item_count(Q), 1, "Queue item count is 1"), - - consume(Consumer, 2), - etap:is(ping(Consumer), ok, - "Consumer not blocked when attempting to dequeue 2 items from queue"), - etap:is(last_consumer_items(Consumer), {ok, [Item4]}, - "Consumer received the right item"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - - consume(Consumer, 100), - etap:is(ping(Consumer), timeout, - "Consumer blocked when attempting to dequeue 100 items from empty queue"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - - Item5 = produce(Producer, 11), - etap:is(ping(Producer), ok, "Producer not blocked with empty queue"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - - Item6 = produce(Producer, 19), - etap:is(ping(Producer), ok, "Producer not blocked with non full queue"), - etap:is(couch_work_queue:item_count(Q), 1, "Queue item count is 1"), - - Item7 = produce(Producer, 2), - etap:is(ping(Producer), ok, "Producer not blocked with non full queue"), - etap:is(couch_work_queue:item_count(Q), 2, "Queue item count is 2"), - - Item8 = produce(Producer, 33), - etap:is(ping(Producer), timeout, "Producer blocked with full queue"), - etap:is(couch_work_queue:item_count(Q), 3, "Queue item count is 3"), - - etap:is(ping(Consumer), ok, "Consumer unblocked"), - etap:is(last_consumer_items(Consumer), {ok, [Item5]}, - "Consumer received the first queued item"), - etap:is(couch_work_queue:item_count(Q), 3, "Queue item count is 3"), - - consume(Consumer, all), - etap:is(ping(Consumer), ok, - "Consumer not blocked when attempting to dequeue all items from queue"), - etap:is(last_consumer_items(Consumer), {ok, [Item6, Item7, Item8]}, - "Consumer received all queued items"), - - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - - etap:is(close_queue(Q), ok, "Closed queue"), - consume(Consumer, 1), - etap:is(last_consumer_items(Consumer), closed, "Consumer got closed queue"), - etap:is(couch_work_queue:item_count(Q), closed, "Queue closed"), - etap:is(couch_work_queue:size(Q), closed, "Queue closed"), - - stop(Producer, "producer"), - stop(Consumer, "consumer"). - - - -test_single_consumer_max_size() -> - etap:diag("Spawning a queue with max size of 160 bytes, " - "1 producer and 1 consumer"), - - {ok, Q} = couch_work_queue:new([{max_size, 160}]), - Producer = spawn_producer(Q), - Consumer = spawn_consumer(Q), - - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - consume(Consumer, 1), - etap:is(ping(Consumer), timeout, - "Consumer blocked when attempting to dequeue 1 item from empty queue"), - - Item1 = produce(Producer, 50), - etap:is(ping(Producer), ok, "Producer not blocked"), - - etap:is(ping(Consumer), ok, "Consumer unblocked"), - etap:is(last_consumer_items(Consumer), {ok, [Item1]}, - "Consumer received the right item"), - - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - Item2 = produce(Producer, 50), - etap:is(ping(Producer), ok, "Producer not blocked"), - etap:is(couch_work_queue:item_count(Q), 1, "Queue item count is 1"), - etap:is(couch_work_queue:size(Q), 50, "Queue size is 50 bytes"), - - Item3 = produce(Producer, 50), - etap:is(ping(Producer), ok, "Producer not blocked"), - etap:is(couch_work_queue:item_count(Q), 2, "Queue item count is 2"), - etap:is(couch_work_queue:size(Q), 100, "Queue size is 100 bytes"), - - Item4 = produce(Producer, 61), - etap:is(ping(Producer), timeout, "Producer blocked"), - etap:is(couch_work_queue:item_count(Q), 3, "Queue item count is 3"), - etap:is(couch_work_queue:size(Q), 161, "Queue size is 161 bytes"), - - consume(Consumer, 1), - etap:is(ping(Consumer), ok, - "Consumer not blocked when attempting to dequeue 1 item from full queue"), - etap:is(last_consumer_items(Consumer), {ok, [Item2]}, - "Consumer received the right item"), - etap:is(couch_work_queue:item_count(Q), 2, "Queue item count is 2"), - etap:is(couch_work_queue:size(Q), 111, "Queue size is 111 bytes"), - - Item5 = produce(Producer, 20), - etap:is(ping(Producer), ok, "Producer not blocked"), - etap:is(couch_work_queue:item_count(Q), 3, "Queue item count is 3"), - etap:is(couch_work_queue:size(Q), 131, "Queue size is 131 bytes"), - - Item6 = produce(Producer, 40), - etap:is(ping(Producer), timeout, "Producer blocked"), - etap:is(couch_work_queue:item_count(Q), 4, "Queue item count is 4"), - etap:is(couch_work_queue:size(Q), 171, "Queue size is 171 bytes"), - - etap:is(close_queue(Q), timeout, - "Timeout when trying to close non-empty queue"), - - consume(Consumer, 2), - etap:is(ping(Consumer), ok, - "Consumer not blocked when attempting to dequeue 2 items from full queue"), - etap:is(last_consumer_items(Consumer), {ok, [Item3, Item4]}, - "Consumer received the right items"), - etap:is(couch_work_queue:item_count(Q), 2, "Queue item count is 2"), - etap:is(couch_work_queue:size(Q), 60, "Queue size is 60 bytes"), - - etap:is(close_queue(Q), timeout, - "Timeout when trying to close non-empty queue"), - - consume(Consumer, all), - etap:is(ping(Consumer), ok, - "Consumer not blocked when attempting to dequeue all items from queue"), - etap:is(last_consumer_items(Consumer), {ok, [Item5, Item6]}, - "Consumer received the right items"), - - etap:is(couch_work_queue:item_count(Q), closed, "Queue closed"), - etap:is(couch_work_queue:size(Q), closed, "Queue closed"), - - consume(Consumer, all), - etap:is(last_consumer_items(Consumer), closed, "Consumer got closed queue"), - - stop(Producer, "producer"), - stop(Consumer, "consumer"). - - -test_single_consumer_max_item_count_and_size() -> - etap:diag("Spawning a queue with 3 max items, max size of 200 bytes, " - "1 producer and 1 consumer"), - - {ok, Q} = couch_work_queue:new([{max_items, 3}, {max_size, 200}]), - Producer = spawn_producer(Q), - Consumer = spawn_consumer(Q), - - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - Item1 = produce(Producer, 100), - etap:is(ping(Producer), ok, "Producer not blocked"), - etap:is(couch_work_queue:item_count(Q), 1, "Queue item count is 1"), - etap:is(couch_work_queue:size(Q), 100, "Queue size is 100 bytes"), - - Item2 = produce(Producer, 110), - etap:is(ping(Producer), timeout, - "Producer blocked when queue size >= max_size"), - etap:is(couch_work_queue:item_count(Q), 2, "Queue item count is 2"), - etap:is(couch_work_queue:size(Q), 210, "Queue size is 210 bytes"), - - consume(Consumer, all), - etap:is(ping(Consumer), ok, - "Consumer not blocked when attempting to dequeue all items from queue"), - etap:is(last_consumer_items(Consumer), {ok, [Item1, Item2]}, - "Consumer received the right items"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - etap:is(ping(Producer), ok, "Producer not blocked anymore"), - - Item3 = produce(Producer, 10), - etap:is(ping(Producer), ok, "Producer not blocked"), - etap:is(couch_work_queue:item_count(Q), 1, "Queue item count is 1"), - etap:is(couch_work_queue:size(Q), 10, "Queue size is 10 bytes"), - - Item4 = produce(Producer, 4), - etap:is(ping(Producer), ok, "Producer not blocked"), - etap:is(couch_work_queue:item_count(Q), 2, "Queue item count is 2"), - etap:is(couch_work_queue:size(Q), 14, "Queue size is 14 bytes"), - - Item5 = produce(Producer, 2), - etap:is(ping(Producer), timeout, - "Producer blocked when queue item count = max_items"), - etap:is(couch_work_queue:item_count(Q), 3, "Queue item count is 3"), - etap:is(couch_work_queue:size(Q), 16, "Queue size is 16 bytes"), - - consume(Consumer, 1), - etap:is(ping(Consumer), ok, - "Consumer not blocked when attempting to dequeue 1 item from queue"), - etap:is(last_consumer_items(Consumer), {ok, [Item3]}, - "Consumer received 1 item"), - etap:is(couch_work_queue:item_count(Q), 2, "Queue item count is 2"), - etap:is(couch_work_queue:size(Q), 6, "Queue size is 6 bytes"), - - etap:is(close_queue(Q), timeout, - "Timeout when trying to close non-empty queue"), - - consume(Consumer, 1), - etap:is(ping(Consumer), ok, - "Consumer not blocked when attempting to dequeue 1 item from queue"), - etap:is(last_consumer_items(Consumer), {ok, [Item4]}, - "Consumer received 1 item"), - etap:is(couch_work_queue:item_count(Q), 1, "Queue item count is 1"), - etap:is(couch_work_queue:size(Q), 2, "Queue size is 2 bytes"), - - Item6 = produce(Producer, 50), - etap:is(ping(Producer), ok, - "Producer not blocked when queue is not full and already received" - " a close request"), - etap:is(couch_work_queue:item_count(Q), 2, "Queue item count is 2"), - etap:is(couch_work_queue:size(Q), 52, "Queue size is 52 bytes"), - - consume(Consumer, all), - etap:is(ping(Consumer), ok, - "Consumer not blocked when attempting to dequeue all items from queue"), - etap:is(last_consumer_items(Consumer), {ok, [Item5, Item6]}, - "Consumer received all queued items"), - - etap:is(couch_work_queue:item_count(Q), closed, "Queue closed"), - etap:is(couch_work_queue:size(Q), closed, "Queue closed"), - - consume(Consumer, 1), - etap:is(last_consumer_items(Consumer), closed, "Consumer got closed queue"), - - stop(Producer, "producer"), - stop(Consumer, "consumer"). - - -test_multiple_consumers() -> - etap:diag("Spawning a queue with 3 max items, max size of 200 bytes, " - "1 producer and 3 consumers"), - - {ok, Q} = couch_work_queue:new( - [{max_items, 3}, {max_size, 200}, {multi_workers, true}]), - Producer = spawn_producer(Q), - Consumer1 = spawn_consumer(Q), - Consumer2 = spawn_consumer(Q), - Consumer3 = spawn_consumer(Q), - - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - consume(Consumer1, 1), - etap:is(ping(Consumer1), timeout, - "Consumer 1 blocked when attempting to dequeue 1 item from empty queue"), - consume(Consumer2, 2), - etap:is(ping(Consumer2), timeout, - "Consumer 2 blocked when attempting to dequeue 2 items from empty queue"), - consume(Consumer3, 1), - etap:is(ping(Consumer3), timeout, - "Consumer 3 blocked when attempting to dequeue 1 item from empty queue"), - - Item1 = produce(Producer, 50), - etap:is(ping(Producer), ok, "Producer not blocked"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - Item2 = produce(Producer, 50), - etap:is(ping(Producer), ok, "Producer not blocked"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - Item3 = produce(Producer, 50), - etap:is(ping(Producer), ok, "Producer not blocked"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - etap:is(ping(Consumer1), ok, "Consumer 1 unblocked"), - etap:is(last_consumer_items(Consumer1), {ok, [Item1]}, - "Consumer 1 received 1 item"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - etap:is(ping(Consumer2), ok, "Consumer 2 unblocked"), - etap:is(last_consumer_items(Consumer2), {ok, [Item2]}, - "Consumer 2 received 1 item"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - etap:is(ping(Consumer3), ok, "Consumer 3 unblocked"), - etap:is(last_consumer_items(Consumer3), {ok, [Item3]}, - "Consumer 3 received 1 item"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - consume(Consumer1, 1), - etap:is(ping(Consumer1), timeout, - "Consumer 1 blocked when attempting to dequeue 1 item from empty queue"), - consume(Consumer2, 2), - etap:is(ping(Consumer2), timeout, - "Consumer 2 blocked when attempting to dequeue 1 item from empty queue"), - consume(Consumer3, 1), - etap:is(ping(Consumer3), timeout, - "Consumer 3 blocked when attempting to dequeue 1 item from empty queue"), - - Item4 = produce(Producer, 50), - etap:is(ping(Producer), ok, "Producer not blocked"), - etap:is(couch_work_queue:item_count(Q), 0, "Queue item count is 0"), - etap:is(couch_work_queue:size(Q), 0, "Queue size is 0 bytes"), - - etap:is(close_queue(Q), ok, "Closed queue"), - - etap:is(ping(Consumer1), ok, "Consumer 1 unblocked"), - etap:is(last_consumer_items(Consumer1), {ok, [Item4]}, - "Consumer 1 received 1 item"), - - etap:is(couch_work_queue:item_count(Q), closed, "Queue closed"), - etap:is(couch_work_queue:size(Q), closed, "Queue closed"), - - etap:is(ping(Consumer2), ok, "Consumer 2 unblocked"), - etap:is(last_consumer_items(Consumer2), closed, - "Consumer 2 received 'closed' atom"), - - etap:is(ping(Consumer3), ok, "Consumer 3 unblocked"), - etap:is(last_consumer_items(Consumer3), closed, - "Consumer 3 received 'closed' atom"), - - stop(Producer, "producer"), - stop(Consumer1, "consumer 1"), - stop(Consumer2, "consumer 2"), - stop(Consumer3, "consumer 3"). - - -close_queue(Q) -> - ok = couch_work_queue:close(Q), - MonRef = erlang:monitor(process, Q), - receive - {'DOWN', MonRef, process, Q, _Reason} -> - etap:diag("Queue closed") - after 3000 -> - 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 3000 -> - timeout - end. - - -produce(Producer, Size) -> - Ref = make_ref(), - Producer ! {produce, Ref, Size}, - receive - {item, Ref, Item} -> - Item - after 3000 -> - etap:bail("Timeout asking producer to produce an item") - end. - - -ping(Pid) -> - Ref = make_ref(), - Pid ! {ping, Ref}, - receive - {pong, Ref} -> - ok - after 3000 -> - timeout - end. - - -stop(Pid, Name) -> - Ref = make_ref(), - Pid ! {stop, Ref}, - receive - {ok, Ref} -> - etap:diag("Stopped " ++ Name) - after 3000 -> - etap:bail("Timeout stopping " ++ Name) - end. diff --git a/test/etap/043-find-in-binary.t b/test/etap/043-find-in-binary.t deleted file mode 100755 index dca1d9c7220..00000000000 --- a/test/etap/043-find-in-binary.t +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - - etap:plan(length(cases())), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - - -test() -> - lists:foreach(fun({Needle, Haystack, Result}) -> - try - Msg = io_lib:format("Looking for ~s in ~s", [Needle, Haystack]), - etap:is(couch_util:find_in_binary(Needle, Haystack), Result, Msg) - catch _T:_R -> - etap:diag("~p", [{_T, _R}]) - end - end, cases()), - ok. - - -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} - ]. diff --git a/test/etap/050-stream.t b/test/etap/050-stream.t deleted file mode 100755 index 0251f002b83..00000000000 --- a/test/etap/050-stream.t +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - etap:plan(13), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -read_all(Fd, PosList) -> - Data = couch_stream:foldl(Fd, PosList, fun(Bin, Acc) -> [Bin, Acc] end, []), - iolist_to_binary(Data). - -test() -> - {ok, Fd} = couch_file:open("test/etap/temp.050", [create,overwrite]), - {ok, Stream} = couch_stream:open(Fd), - - etap:is(ok, couch_stream:write(Stream, <<"food">>), - "Writing to streams works."), - - etap:is(ok, couch_stream:write(Stream, <<"foob">>), - "Consecutive writing to streams works."), - - etap:is(ok, couch_stream:write(Stream, <<>>), - "Writing an empty binary does nothing."), - - {Ptrs, Length, _, _, _} = couch_stream:close(Stream), - etap:is(Ptrs, [{0, 8}], "Close returns the file pointers."), - etap:is(Length, 8, "Close also returns the number of bytes written."), - etap:is(<<"foodfoob">>, read_all(Fd, Ptrs), "Returned pointers are valid."), - - % Remember where we expect the pointer to be. - {ok, ExpPtr} = couch_file:bytes(Fd), - {ok, Stream2} = couch_stream:open(Fd), - OneBits = <<1:(8*10)>>, - etap:is(ok, couch_stream:write(Stream2, OneBits), - "Successfully wrote 79 zero bits and 1 one bit."), - - ZeroBits = <<0:(8*10)>>, - etap:is(ok, couch_stream:write(Stream2, ZeroBits), - "Successfully wrote 80 0 bits."), - - {Ptrs2, Length2, _, _, _} = couch_stream:close(Stream2), - etap:is(Ptrs2, [{ExpPtr, 20}], "Closing stream returns the file pointers."), - etap:is(Length2, 20, "Length written is 160 bytes."), - - AllBits = iolist_to_binary([OneBits,ZeroBits]), - etap:is(AllBits, read_all(Fd, Ptrs2), "Returned pointers are valid."), - - % Stream more the 4K chunk size. - {ok, ExpPtr2} = couch_file:bytes(Fd), - {ok, Stream3} = couch_stream:open(Fd, [{buffer_size, 4096}]), - lists:foldl(fun(_, Acc) -> - Data = <<"a1b2c">>, - couch_stream:write(Stream3, Data), - [Data | Acc] - end, [], lists:seq(1, 1024)), - {Ptrs3, Length3, _, _, _} = couch_stream:close(Stream3), - - % 4095 because of 5 * 4096 rem 5 (last write before exceeding threshold) - % + 5 puts us over the threshold - % + 4 bytes for the term_to_binary adding a length header - % + 1 byte every 4K for tail append headers - SecondPtr = ExpPtr2 + 4095 + 5 + 4 + 1, - etap:is(Ptrs3, [{ExpPtr2, 4100}, {SecondPtr, 1020}], "Pointers every 4K bytes."), - etap:is(Length3, 5120, "Wrote the expected 5K bytes."), - - couch_file:close(Fd), - ok. diff --git a/test/etap/060-kt-merging.t b/test/etap/060-kt-merging.t deleted file mode 100755 index e0b1a9c368c..00000000000 --- a/test/etap/060-kt-merging.t +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - etap:plan(16), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - One = {1, {"1","foo",[]}}, - - etap:is( - {[One], new_leaf}, - couch_key_tree:merge([], One, 10), - "The empty tree is the identity for merge." - ), - etap:is( - {[One], internal_node}, - couch_key_tree:merge([One], One, 10), - "Merging is reflexive." - ), - - TwoSibs = [{1, {"1","foo",[]}}, - {1, {"2","foo",[]}}], - - etap:is( - {TwoSibs, internal_node}, - couch_key_tree:merge(TwoSibs, One, 10), - "Merging a prefix of a tree with the tree yields the tree." - ), - - Three = {1, {"3","foo",[]}}, - ThreeSibs = [{1, {"1","foo",[]}}, - {1, {"2","foo",[]}}, - {1, {"3","foo",[]}}], - - etap:is( - {ThreeSibs, new_branch}, - couch_key_tree:merge(TwoSibs, Three, 10), - "Merging a third unrelated branch leads to a conflict." - ), - - - TwoChild = {1, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}, - - etap:is( - {[TwoChild], internal_node}, - couch_key_tree:merge([TwoChild], TwoChild, 10), - "Merging two children is still reflexive." - ), - - TwoChildSibs = {1, {"1","foo", [{"1a", "bar", []}, - {"1b", "bar", []}]}}, - - Stemmed1b = {2, {"1a", "bar", []}}, - etap:is( - {[TwoChildSibs], internal_node}, - couch_key_tree:merge([TwoChildSibs], Stemmed1b, 10), - "Merging a tree with a stem." - ), - - TwoChildSibs2 = {1, {"1","foo", [{"1a", "bar", []}, - {"1b", "bar", [{"1bb", "boo", []}]}]}}, - Stemmed1bb = {3, {"1bb", "boo", []}}, - etap:is( - {[TwoChildSibs2], internal_node}, - couch_key_tree:merge([TwoChildSibs2], Stemmed1bb, 10), - "Merging a stem at a deeper level." - ), - - StemmedTwoChildSibs2 = [{2,{"1a", "bar", []}}, - {2,{"1b", "bar", [{"1bb", "boo", []}]}}], - - etap:is( - {StemmedTwoChildSibs2, internal_node}, - couch_key_tree:merge(StemmedTwoChildSibs2, Stemmed1bb, 10), - "Merging a stem at a deeper level against paths at deeper levels." - ), - - Stemmed1aa = {3, {"1aa", "bar", []}}, - etap:is( - {[TwoChild], internal_node}, - couch_key_tree:merge([TwoChild], Stemmed1aa, 10), - "Merging a single tree with a deeper stem." - ), - - Stemmed1a = {2, {"1a", "bar", [{"1aa", "bar", []}]}}, - etap:is( - {[TwoChild], internal_node}, - couch_key_tree:merge([TwoChild], Stemmed1a, 10), - "Merging a larger stem." - ), - - etap:is( - {[Stemmed1a], internal_node}, - couch_key_tree:merge([Stemmed1a], Stemmed1aa, 10), - "More merging." - ), - - OneChild = {1, {"1","foo",[{"1a", "bar", []}]}}, - Expect1 = [OneChild, Stemmed1aa], - etap:is( - {Expect1, new_branch}, - couch_key_tree:merge([OneChild], Stemmed1aa, 10), - "Merging should create conflicts." - ), - - etap:is( - {[TwoChild], new_leaf}, - couch_key_tree:merge(Expect1, TwoChild, 10), - "Merge should have no conflicts." - ), - - %% 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",[]}]} - ]}}, - - etap:is( - {[FooBar], new_leaf}, - couch_key_tree:merge([Foo],Bar,10), - "Merging trees with conflicts ought to behave." - ), - - etap:is( - {[TwoChild], internal_node}, - couch_key_tree:merge([TwoChild],One,10), - "Merging tree with already-existing path." - ), - - etap:is( - {[TwoChildSibs], internal_node}, - couch_key_tree:merge([TwoChildSibs],One,10), - "Merging tree with already-existing path with siblings." - ), - - ok. diff --git a/test/etap/061-kt-missing-leaves.t b/test/etap/061-kt-missing-leaves.t deleted file mode 100755 index d60b4db8de9..00000000000 --- a/test/etap/061-kt-missing-leaves.t +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - etap:plan(4), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}], - Stemmed1 = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}], - Stemmed2 = [{2, {"1aa", "bar", []}}], - - etap:is( - [], - couch_key_tree:find_missing(TwoChildSibs, [{0,"1"}, {1,"1a"}]), - "Look for missing keys." - ), - - etap:is( - [{0, "10"}, {100, "x"}], - couch_key_tree:find_missing( - TwoChildSibs, - [{0,"1"}, {0, "10"}, {1,"1a"}, {100, "x"}] - ), - "Look for missing keys." - ), - - etap:is( - [{0, "1"}, {100, "x"}], - couch_key_tree:find_missing( - Stemmed1, - [{0,"1"}, {1,"1a"}, {100, "x"}] - ), - "Look for missing keys." - ), - etap:is( - [{0, "1"}, {1,"1a"}, {100, "x"}], - couch_key_tree:find_missing( - Stemmed2, - [{0,"1"}, {1,"1a"}, {100, "x"}] - ), - "Look for missing keys." - ), - - ok. diff --git a/test/etap/062-kt-remove-leaves.t b/test/etap/062-kt-remove-leaves.t deleted file mode 100755 index 745a00be7a6..00000000000 --- a/test/etap/062-kt-remove-leaves.t +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - etap:plan(6), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - OneChild = [{0, {"1","foo",[{"1a", "bar", []}]}}], - TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}], - Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}], - - etap:is( - {TwoChildSibs, []}, - couch_key_tree:remove_leafs(TwoChildSibs, []), - "Removing no leaves has no effect on the tree." - ), - - etap:is( - {TwoChildSibs, []}, - couch_key_tree:remove_leafs(TwoChildSibs, [{0, "1"}]), - "Removing a non-existant branch has no effect." - ), - - etap:is( - {OneChild, [{1, "1b"}]}, - couch_key_tree:remove_leafs(TwoChildSibs, [{1, "1b"}]), - "Removing a leaf removes the leaf." - ), - - etap:is( - {[], [{1, "1b"},{1, "1a"}]}, - couch_key_tree:remove_leafs(TwoChildSibs, [{1, "1a"}, {1, "1b"}]), - "Removing all leaves returns an empty tree." - ), - - etap:is( - {Stemmed, []}, - couch_key_tree:remove_leafs(Stemmed, [{1, "1a"}]), - "Removing a non-existant node has no effect." - ), - - etap:is( - {[], [{2, "1aa"}]}, - couch_key_tree:remove_leafs(Stemmed, [{2, "1aa"}]), - "Removing the last leaf returns an empty tree." - ), - - ok. diff --git a/test/etap/063-kt-get-leaves.t b/test/etap/063-kt-get-leaves.t deleted file mode 100755 index 6d4e8007d2b..00000000000 --- a/test/etap/063-kt-get-leaves.t +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - etap:plan(11), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}], - Stemmed = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}], - - etap:is( - {[{"foo", {0, ["1"]}}],[]}, - couch_key_tree:get(TwoChildSibs, [{0, "1"}]), - "extract a subtree." - ), - - etap:is( - {[{"bar", {1, ["1a", "1"]}}],[]}, - couch_key_tree:get(TwoChildSibs, [{1, "1a"}]), - "extract a subtree." - ), - - etap:is( - {[],[{0,"x"}]}, - couch_key_tree:get_key_leafs(TwoChildSibs, [{0, "x"}]), - "gather up the leaves." - ), - - etap:is( - {[{"bar", {1, ["1a","1"]}}],[]}, - couch_key_tree:get_key_leafs(TwoChildSibs, [{1, "1a"}]), - "gather up the leaves." - ), - - etap:is( - {[{"bar", {1, ["1a","1"]}},{"bar",{1, ["1b","1"]}}],[]}, - couch_key_tree:get_key_leafs(TwoChildSibs, [{0, "1"}]), - "gather up the leaves." - ), - - etap:is( - {[{0,[{"1", "foo"}]}],[]}, - couch_key_tree:get_full_key_paths(TwoChildSibs, [{0, "1"}]), - "retrieve full key paths." - ), - - etap:is( - {[{1,[{"1a", "bar"},{"1", "foo"}]}],[]}, - couch_key_tree:get_full_key_paths(TwoChildSibs, [{1, "1a"}]), - "retrieve full key paths." - ), - - etap:is( - [{2, [{"1aa", "bar"},{"1a", "bar"}]}], - couch_key_tree:get_all_leafs_full(Stemmed), - "retrieve all leaves." - ), - - etap:is( - [{1, [{"1a", "bar"},{"1", "foo"}]}, {1, [{"1b", "bar"},{"1", "foo"}]}], - couch_key_tree:get_all_leafs_full(TwoChildSibs), - "retrieve all the leaves." - ), - - etap:is( - [{"bar", {2, ["1aa","1a"]}}], - couch_key_tree:get_all_leafs(Stemmed), - "retrieve all leaves." - ), - - etap:is( - [{"bar", {1, ["1a", "1"]}}, {"bar", {1, ["1b","1"]}}], - couch_key_tree:get_all_leafs(TwoChildSibs), - "retrieve all the leaves." - ), - - ok. diff --git a/test/etap/064-kt-counting.t b/test/etap/064-kt-counting.t deleted file mode 100755 index f182d28700d..00000000000 --- a/test/etap/064-kt-counting.t +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - etap:plan(4), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - EmptyTree = [], - One = [{0, {"1","foo",[]}}], - TwoChildSibs = [{0, {"1","foo", [{"1a", "bar", []}, {"1b", "bar", []}]}}], - Stemmed = [{2, {"1bb", "boo", []}}], - - etap:is(0, couch_key_tree:count_leafs(EmptyTree), - "Empty trees have no leaves."), - - etap:is(1, couch_key_tree:count_leafs(One), - "Single node trees have a single leaf."), - - etap:is(2, couch_key_tree:count_leafs(TwoChildSibs), - "Two children siblings counted as two leaves."), - - etap:is(1, couch_key_tree:count_leafs(Stemmed), - "Stemming does not affect leaf counting."), - - ok. diff --git a/test/etap/065-kt-stemming.t b/test/etap/065-kt-stemming.t deleted file mode 100755 index 6e781c1d382..00000000000 --- a/test/etap/065-kt-stemming.t +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - etap:plan(3), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - TwoChild = [{0, {"1","foo", [{"1a", "bar", [{"1aa", "bar", []}]}]}}], - Stemmed1 = [{1, {"1a", "bar", [{"1aa", "bar", []}]}}], - Stemmed2 = [{2, {"1aa", "bar", []}}], - - etap:is(TwoChild, couch_key_tree:stem(TwoChild, 3), - "Stemming more levels than what exists does nothing."), - - etap:is(Stemmed1, couch_key_tree:stem(TwoChild, 2), - "Stemming with a depth of two returns the deepest two nodes."), - - etap:is(Stemmed2, couch_key_tree:stem(TwoChild, 1), - "Stemming to a depth of one returns the deepest node."), - - ok. diff --git a/test/etap/070-couch-db.t b/test/etap/070-couch-db.t deleted file mode 100755 index 5fa9344139c..00000000000 --- a/test/etap/070-couch-db.t +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - - etap:plan(4), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - timer:sleep(1000), - etap:bail(Other) - end, - ok. - -test() -> - test_util:start_couch(), - - couch_db:create(<<"etap-test-db">>, []), - {ok, AllDbs} = couch_server:all_databases(), - etap:ok(lists:member(<<"etap-test-db">>, AllDbs), "Database was created."), - - couch_server:delete(<<"etap-test-db">>, []), - {ok, AllDbs2} = couch_server:all_databases(), - etap:ok(not lists:member(<<"etap-test-db">>, AllDbs2), - "Database was deleted."), - - gen_server:call(couch_server, {set_max_dbs_open, 3}), - MkDbName = fun(Int) -> list_to_binary("lru-" ++ integer_to_list(Int)) end, - - lists:foreach(fun(Int) -> - {ok, TestDbs} = couch_server:all_databases(), - ok = case lists:member(MkDbName(Int), TestDbs) of - true -> couch_server:delete(MkDbName(Int), []); - _ -> ok - end, - {ok, Db} = couch_db:create(MkDbName(Int), []), - ok = couch_db:close(Db) - end, lists:seq(1, 6)), - - {ok, AllDbs3} = couch_server:all_databases(), - NumCreated = lists:foldl(fun(Int, Acc) -> - true = lists:member(MkDbName(Int), AllDbs3), - Acc+1 - end, 0, lists:seq(1, 6)), - etap:is(6, NumCreated, "Created all databases."), - - lists:foreach(fun(Int) -> - ok = couch_server:delete(MkDbName(Int), []) - end, lists:seq(1, 6)), - - {ok, AllDbs4} = couch_server:all_databases(), - NumDeleted = lists:foldl(fun(Int, Acc) -> - false = lists:member(MkDbName(Int), AllDbs4), - Acc+1 - end, 0, lists:seq(1, 6)), - etap:is(6, NumDeleted, "Deleted all databases."), - - ok = test_util:stop_couch(), - ok. diff --git a/test/etap/071-couchdb-rapid-cycle.t b/test/etap/071-couchdb-rapid-cycle.t deleted file mode 100755 index 2e8648350d4..00000000000 --- a/test/etap/071-couchdb-rapid-cycle.t +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - - etap:plan(2), - try - ok = test() - catch T:R -> - Stack = erlang:get_stacktrace(), - etap:diag("Test died abnormally: ~p~n ~p", [{T, R}, Stack]), - timer:sleep(250), - etap:bail(io_lib:format("Error: ~p", [{T, R}])) - end, - ok. - -dbname() -> <<"etap-test-db">>. - -test() -> - ok = test_util:start_couch(), - timer:sleep(500), - - couch_server:delete(dbname(), []), - etap:diag("Test starting"), - timer:sleep(250), - - Pid = spawn_link(fun() -> open_loop() end), - - Result = lists:foldl(fun(_, N) -> - {ok, Db} = couch_db:create(dbname(), []), - ok = couch_db:close(Db), - ok = couch_server:delete(dbname(), []), - N + 1 - end, 0, lists:seq(1, 100)), - - etap:is(Result, 100, "Cycled the database 1000 times successfully."), - etap:is(is_process_alive(Pid), true, "The open loop lives"), - - Pid ! {self(), close}, - receive - {Pid, ok} -> ok - after 1000 -> - open_loop_didnt_die - end. - -open_loop() -> - receive - {Parent, close} -> - Parent ! {self(), ok} - after 0 -> - case couch_db:open_int(dbname(), []) of - {ok, Db} -> - ok = couch_db:close(Db); - {not_found, no_db_file} -> - ok; - Other -> - etap:diag("WHIBBLE? ~p", [Other]) - end, - open_loop() - end. - diff --git a/test/etap/072-cleanup.t b/test/etap/072-cleanup.t deleted file mode 100755 index ddd8ca8f966..00000000000 --- a/test/etap/072-cleanup.t +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --define(TEST_DB, <<"etap-test-db">>). - --record(user_ctx, { - name = null, - roles = [], - handler -}). - --define(ADMIN_USER, #user_ctx{roles=[<<"_admin">>]}). - -main(_) -> - test_util:init_code_path(), - - etap:plan(7), - try test() of - ok -> - etap:end_tests() - catch - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - timer:sleep(1000), - etap:bail(Other) - end, - ok. - -test() -> - - ok = test_util:start_couch(), - couch_server:delete(?TEST_DB, []), - timer:sleep(1000), - - couch_db:create(?TEST_DB, []), - - {ok, AllDbs} = couch_server:all_databases(), - etap:ok(lists:member(?TEST_DB, AllDbs), "Database was created."), - - FooRev = create_design_doc(<<"_design/foo">>, <<"bar">>), - query_view("foo", "bar"), - - BoozRev = create_design_doc(<<"_design/booz">>, <<"baz">>), - query_view("booz", "baz"), - - {ok, _Db} = couch_db:open(?TEST_DB, [{user_ctx, ?ADMIN_USER}]), - view_cleanup(), - etap:is(count_index_files(), 2, - "Two index files before any deletions."), - - delete_design_doc(<<"_design/foo">>, FooRev), - view_cleanup(), - etap:is(count_index_files(), 1, - "One index file after first deletion and cleanup."), - - delete_design_doc(<<"_design/booz">>, BoozRev), - view_cleanup(), - etap:is(count_index_files(), 0, - "No index files after second deletion and cleanup."), - - couch_server:delete(?TEST_DB, []), - {ok, AllDbs2} = couch_server:all_databases(), - etap:ok(not lists:member(?TEST_DB, AllDbs2), - "Database was deleted."), - ok. - -create_design_doc(DDName, ViewName) -> - {ok, Db} = couch_db:open(?TEST_DB, [{user_ctx, ?ADMIN_USER}]), - DDoc = couch_doc:from_json_obj({[ - {<<"_id">>, DDName}, - {<<"language">>, <<"javascript">>}, - {<<"views">>, {[ - {ViewName, {[ - {<<"map">>, <<"function(doc) { emit(doc.value, 1); }">>} - ]}} - ]}} - ]}), - {ok, Rev} = couch_db:update_doc(Db, DDoc, []), - couch_db:ensure_full_commit(Db), - couch_db:close(Db), - Rev. - -delete_design_doc(DDName, Rev) -> - {ok, Db} = couch_db:open(?TEST_DB, [{user_ctx, ?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() -> - Addr = 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(?TEST_DB). - -query_view(DDoc, View) -> - {ok, Code, _Headers, _Body} = test_util:request( - db_url() ++ "/_design/" ++ DDoc ++ "/_view/" ++ View, [], get), - etap:is(Code, 200, "Built view index for " ++ DDoc ++ "."), - ok. - -view_cleanup() -> - {ok, Db} = couch_db:open(?TEST_DB, [{user_ctx, ?ADMIN_USER}]), - couch_mrview:cleanup(Db), - couch_db:close(Db). - -count_index_files() -> - % call server to fetch the index files - RootDir = config:get("couchdb", "view_index_dir"), - length(filelib:wildcard(RootDir ++ "/." ++ - binary_to_list(?TEST_DB) ++ "_design"++"/mrview/*")). diff --git a/test/etap/073-changes.t b/test/etap/073-changes.t deleted file mode 100755 index bd81964eeee..00000000000 --- a/test/etap/073-changes.t +++ /dev/null @@ -1,550 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -% 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. - -% Verify that compacting databases that are being used as the source or -% target of a replication doesn't affect the replication and that the -% replication doesn't hold their reference counters forever. - --mode(compile). - --record(user_ctx, { - name = null, - roles = [], - handler -}). - --record(changes_args, { - feed = "normal", - dir = fwd, - since = 0, - limit = 1000000000000000, - style = main_only, - heartbeat, - timeout, - filter = "", - filter_fun, - filter_args = [], - include_docs = false, - doc_options = [], - conflicts = false, - db_open_options = [] -}). - --record(row, { - id, - seq, - deleted = false -}). - - -test_db_name() -> <<"couch_test_changes">>. - - -main(_) -> - test_util:run(43, fun() -> test() end). - - -test() -> - test_util:start_couch(), - - test_by_doc_ids(), - test_by_doc_ids_with_since(), - test_by_doc_ids_continuous(), - test_design_docs_only(), - test_heartbeat(), - - ok. - - -test_by_doc_ids() -> - create_db(test_db_name()), - - {ok, _Rev1} = save_doc({[{<<"_id">>, <<"doc1">>}]}), - {ok, _Rev2} = save_doc({[{<<"_id">>, <<"doc2">>}]}), - {ok, Rev3} = save_doc({[{<<"_id">>, <<"doc3">>}]}), - {ok, _Rev4} = save_doc({[{<<"_id">>, <<"doc4">>}]}), - {ok, _Rev5} = save_doc({[{<<"_id">>, <<"doc5">>}]}), - {ok, _Rev3_2} = save_doc({[{<<"_id">>, <<"doc3">>}, {<<"_rev">>, Rev3}]}), - {ok, _Rev6} = save_doc({[{<<"_id">>, <<"doc6">>}]}), - {ok, _Rev7} = save_doc({[{<<"_id">>, <<"doc7">>}]}), - {ok, _Rev8} = save_doc({[{<<"_id">>, <<"doc8">>}]}), - - etap:diag("Folding changes in ascending order with _doc_ids filter"), - ChangesArgs = #changes_args{ - filter = "_doc_ids" - }, - DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>], - Req = {json_req, {[{<<"doc_ids">>, DocIds}]}}, - Consumer = spawn_consumer(test_db_name(), ChangesArgs, Req), - - {Rows, LastSeq} = wait_finished(Consumer), - {ok, Db2} = couch_db:open_int(test_db_name(), []), - UpSeq = couch_db:get_update_seq(Db2), - couch_db:close(Db2), - etap:is(length(Rows), 2, "Received 2 changes rows"), - etap:is(LastSeq, UpSeq, "LastSeq is same as database update seq number"), - [#row{seq = Seq1, id = Id1}, #row{seq = Seq2, id = Id2}] = Rows, - etap:is(Id1, <<"doc4">>, "First row is for doc doc4"), - etap:is(Seq1, 4, "First row has seq 4"), - etap:is(Id2, <<"doc3">>, "Second row is for doc doc3"), - etap:is(Seq2, 6, "Second row has seq 6"), - - stop(Consumer), - etap:diag("Folding changes in descending order with _doc_ids filter"), - ChangesArgs2 = #changes_args{ - filter = "_doc_ids", - dir = rev - }, - Consumer2 = spawn_consumer(test_db_name(), ChangesArgs2, Req), - - {Rows2, LastSeq2} = wait_finished(Consumer2), - etap:is(length(Rows2), 2, "Received 2 changes rows"), - etap:is(LastSeq2, 4, "LastSeq is 4"), - [#row{seq = Seq1_2, id = Id1_2}, #row{seq = Seq2_2, id = Id2_2}] = Rows2, - etap:is(Id1_2, <<"doc3">>, "First row is for doc doc3"), - etap:is(Seq1_2, 6, "First row has seq 4"), - etap:is(Id2_2, <<"doc4">>, "Second row is for doc doc4"), - etap:is(Seq2_2, 4, "Second row has seq 6"), - - stop(Consumer2), - delete_db(). - - -test_by_doc_ids_with_since() -> - create_db(test_db_name()), - - {ok, _Rev1} = save_doc({[{<<"_id">>, <<"doc1">>}]}), - {ok, _Rev2} = save_doc({[{<<"_id">>, <<"doc2">>}]}), - {ok, Rev3} = save_doc({[{<<"_id">>, <<"doc3">>}]}), - {ok, _Rev4} = save_doc({[{<<"_id">>, <<"doc4">>}]}), - {ok, _Rev5} = save_doc({[{<<"_id">>, <<"doc5">>}]}), - {ok, Rev3_2} = save_doc({[{<<"_id">>, <<"doc3">>}, {<<"_rev">>, Rev3}]}), - {ok, _Rev6} = save_doc({[{<<"_id">>, <<"doc6">>}]}), - {ok, _Rev7} = save_doc({[{<<"_id">>, <<"doc7">>}]}), - {ok, _Rev8} = save_doc({[{<<"_id">>, <<"doc8">>}]}), - - ChangesArgs = #changes_args{ - filter = "_doc_ids", - since = 5 - }, - DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>], - Req = {json_req, {[{<<"doc_ids">>, DocIds}]}}, - Consumer = spawn_consumer(test_db_name(), ChangesArgs, Req), - - {Rows, LastSeq} = wait_finished(Consumer), - {ok, Db2} = couch_db:open_int(test_db_name(), []), - UpSeq = couch_db:get_update_seq(Db2), - couch_db:close(Db2), - etap:is(LastSeq, UpSeq, "LastSeq is same as database update seq number"), - etap:is(length(Rows), 1, "Received 1 changes rows"), - [#row{seq = Seq1, id = Id1}] = Rows, - etap:is(Id1, <<"doc3">>, "First row is for doc doc3"), - etap:is(Seq1, 6, "First row has seq 6"), - - stop(Consumer), - - ChangesArgs2 = #changes_args{ - filter = "_doc_ids", - since = 6 - }, - Consumer2 = spawn_consumer(test_db_name(), ChangesArgs2, Req), - - {Rows2, LastSeq2} = wait_finished(Consumer2), - {ok, Db3} = couch_db:open_int(test_db_name(), []), - UpSeq2 = couch_db:get_update_seq(Db3), - couch_db:close(Db3), - etap:is(LastSeq2, UpSeq2, "LastSeq is same as database update seq number"), - etap:is(length(Rows2), 0, "Received 0 change rows"), - - stop(Consumer2), - - {ok, _Rev3_3} = save_doc( - {[{<<"_id">>, <<"doc3">>}, {<<"_deleted">>, true}, {<<"_rev">>, Rev3_2}]}), - - ChangesArgs3 = #changes_args{ - filter = "_doc_ids", - since = 9 - }, - Consumer3 = spawn_consumer(test_db_name(), ChangesArgs3, Req), - - {Rows3, LastSeq3} = wait_finished(Consumer3), - {ok, Db4} = couch_db:open_int(test_db_name(), []), - UpSeq3 = couch_db:get_update_seq(Db4), - couch_db:close(Db4), - etap:is(LastSeq3, UpSeq3, "LastSeq is same as database update seq number"), - etap:is(length(Rows3), 1, "Received 1 changes rows"), - etap:is( - [#row{seq = LastSeq3, id = <<"doc3">>, deleted = true}], - Rows3, - "Received row with doc3 deleted"), - - stop(Consumer3), - - delete_db(). - - -test_by_doc_ids_continuous() -> - create_db(test_db_name()), - - {ok, _Rev1} = save_doc({[{<<"_id">>, <<"doc1">>}]}), - {ok, _Rev2} = save_doc({[{<<"_id">>, <<"doc2">>}]}), - {ok, Rev3} = save_doc({[{<<"_id">>, <<"doc3">>}]}), - {ok, Rev4} = save_doc({[{<<"_id">>, <<"doc4">>}]}), - {ok, _Rev5} = save_doc({[{<<"_id">>, <<"doc5">>}]}), - {ok, Rev3_2} = save_doc({[{<<"_id">>, <<"doc3">>}, {<<"_rev">>, Rev3}]}), - {ok, _Rev6} = save_doc({[{<<"_id">>, <<"doc6">>}]}), - {ok, _Rev7} = save_doc({[{<<"_id">>, <<"doc7">>}]}), - {ok, _Rev8} = save_doc({[{<<"_id">>, <<"doc8">>}]}), - - ChangesArgs = #changes_args{ - filter = "_doc_ids", - feed = "continuous" - }, - DocIds = [<<"doc3">>, <<"doc4">>, <<"doc9999">>], - Req = {json_req, {[{<<"doc_ids">>, DocIds}]}}, - Consumer = spawn_consumer(test_db_name(), ChangesArgs, Req), - - pause(Consumer), - Rows = get_rows(Consumer), - - etap:is(length(Rows), 2, "Received 2 changes rows"), - [#row{seq = Seq1, id = Id1}, #row{seq = Seq2, id = Id2}] = Rows, - etap:is(Id1, <<"doc4">>, "First row is for doc doc4"), - etap:is(Seq1, 4, "First row has seq 4"), - etap:is(Id2, <<"doc3">>, "Second row is for doc doc3"), - etap:is(Seq2, 6, "Second row has seq 6"), - - clear_rows(Consumer), - {ok, _Rev9} = save_doc({[{<<"_id">>, <<"doc9">>}]}), - {ok, _Rev10} = save_doc({[{<<"_id">>, <<"doc10">>}]}), - unpause(Consumer), - pause(Consumer), - etap:is(get_rows(Consumer), [], "No new rows"), - - {ok, Rev4_2} = save_doc({[{<<"_id">>, <<"doc4">>}, {<<"_rev">>, Rev4}]}), - {ok, _Rev11} = save_doc({[{<<"_id">>, <<"doc11">>}]}), - {ok, _Rev4_3} = save_doc({[{<<"_id">>, <<"doc4">>}, {<<"_rev">>, Rev4_2}]}), - {ok, _Rev12} = save_doc({[{<<"_id">>, <<"doc12">>}]}), - {ok, Rev3_3} = save_doc({[{<<"_id">>, <<"doc3">>}, {<<"_rev">>, Rev3_2}]}), - unpause(Consumer), - pause(Consumer), - - NewRows = get_rows(Consumer), - etap:is(length(NewRows), 2, "Received 2 new rows"), - [Row14, Row16] = NewRows, - etap:is(Row14#row.seq, 14, "First row has seq 14"), - etap:is(Row14#row.id, <<"doc4">>, "First row is for doc doc4"), - etap:is(Row16#row.seq, 16, "Second row has seq 16"), - etap:is(Row16#row.id, <<"doc3">>, "Second row is for doc doc3"), - - clear_rows(Consumer), - {ok, _Rev3_4} = save_doc({[{<<"_id">>, <<"doc3">>}, {<<"_rev">>, Rev3_3}]}), - unpause(Consumer), - pause(Consumer), - etap:is(get_rows(Consumer), [#row{seq = 17, id = <<"doc3">>}], - "Got row for seq 17, doc doc3"), - - unpause(Consumer), - stop(Consumer), - delete_db(). - - -test_design_docs_only() -> - create_db(test_db_name()), - - {ok, _Rev1} = save_doc({[{<<"_id">>, <<"doc1">>}]}), - {ok, _Rev2} = save_doc({[{<<"_id">>, <<"doc2">>}]}), - {ok, Rev3} = save_doc({[{<<"_id">>, <<"_design/foo">>}]}), - - ChangesArgs = #changes_args{ - filter = "_design" - }, - Consumer = spawn_consumer(test_db_name(), ChangesArgs, {json_req, null}), - - {Rows, LastSeq} = wait_finished(Consumer), - {ok, Db2} = couch_db:open_int(test_db_name(), []), - UpSeq = couch_db:get_update_seq(Db2), - couch_db:close(Db2), - - etap:is(LastSeq, UpSeq, "LastSeq is same as database update seq number"), - etap:is(length(Rows), 1, "Received 1 changes rows"), - etap:is(Rows, [#row{seq = 3, id = <<"_design/foo">>}], "Received row with ddoc"), - - stop(Consumer), - - {ok, _Rev3_2} = save_doc( - {[{<<"_id">>, <<"_design/foo">>}, {<<"_rev">>, Rev3}, - {<<"_deleted">>, true}]}), - - Consumer2 = spawn_consumer(test_db_name(), ChangesArgs, {json_req, null}), - - {Rows2, LastSeq2} = wait_finished(Consumer2), - UpSeq2 = UpSeq + 1, - - etap:is(LastSeq2, UpSeq2, "LastSeq is same as database update seq number"), - etap:is(length(Rows2), 1, "Received 1 changes rows"), - etap:is( - Rows2, - [#row{seq = 4, id = <<"_design/foo">>, deleted = true}], - "Received row with deleted ddoc"), - - stop(Consumer2), - delete_db(). - -test_heartbeat() -> - create_db(test_db_name()), - - {ok, _} = save_doc({[ - {<<"_id">>, <<"_design/foo">>}, - {<<"language">>, <<"javascript">>}, - {<<"filters">>, {[ - {<<"foo">>, <<"function(doc) { if ((doc._id == 'doc10') || - (doc._id == 'doc11') || - (doc._id == 'doc12')) { - return true; - } else { - return false; - }}">> - }]}} - ]}), - - ChangesArgs = #changes_args{ - filter = "foo/foo", - feed = "continuous", - timeout = 10000, - heartbeat = 1000 - }, - Consumer = spawn_consumer(test_db_name(), ChangesArgs, {json_req, null}), - - {ok, _Rev1} = save_doc({[{<<"_id">>, <<"doc1">>}]}), - timer:sleep(200), - {ok, _Rev2} = save_doc({[{<<"_id">>, <<"doc2">>}]}), - timer:sleep(200), - {ok, _Rev3} = save_doc({[{<<"_id">>, <<"doc3">>}]}), - timer:sleep(200), - {ok, _Rev4} = save_doc({[{<<"_id">>, <<"doc4">>}]}), - timer:sleep(200), - {ok, _Rev5} = save_doc({[{<<"_id">>, <<"doc5">>}]}), - timer:sleep(200), - {ok, _Rev6} = save_doc({[{<<"_id">>, <<"doc6">>}]}), - timer:sleep(200), - {ok, _Rev7} = save_doc({[{<<"_id">>, <<"doc7">>}]}), - timer:sleep(200), - {ok, _Rev8} = save_doc({[{<<"_id">>, <<"doc8">>}]}), - timer:sleep(200), - {ok, _Rev9} = save_doc({[{<<"_id">>, <<"doc9">>}]}), - Heartbeats = get_heartbeats(Consumer), - etap:is(Heartbeats, 2, "Received 2 heartbeats now"), - {ok, _Rev10} = save_doc({[{<<"_id">>, <<"doc10">>}]}), - timer:sleep(200), - {ok, _Rev11} = save_doc({[{<<"_id">>, <<"doc11">>}]}), - timer:sleep(200), - {ok, _Rev12} = save_doc({[{<<"_id">>, <<"doc12">>}]}), - Heartbeats2 = get_heartbeats(Consumer), - etap:is(Heartbeats2, 3, "Received 3 heartbeats now"), - Rows = get_rows(Consumer), - etap:is(length(Rows), 3, "Received 3 changes rows"), - - {ok, _Rev13} = save_doc({[{<<"_id">>, <<"doc13">>}]}), - timer:sleep(200), - {ok, _Rev14} = save_doc({[{<<"_id">>, <<"doc14">>}]}), - timer:sleep(200), - Heartbeats3 = get_heartbeats(Consumer), - etap:is(Heartbeats3, 6, "Received 6 heartbeats now"), - stop(Consumer), - delete_db(). - - -db() -> - {ok, Db} = couch_db:reopen(get(current_db)), - Db. - - -save_doc(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}, - receive - {rows, Ref, Rows} -> - Rows - after 3000 -> - etap:bail("Timeout getting rows from consumer") - end. - -get_heartbeats(Consumer) -> - Ref = make_ref(), - Consumer ! {get_heartbeats, Ref}, - receive - {hearthbeats, Ref, HeartBeats} -> - HeartBeats - after 3000 -> - etap:bail("Timeout getting heartbeats from consumer") - end. - - -clear_rows(Consumer) -> - Ref = make_ref(), - Consumer ! {reset, Ref}, - receive - {ok, Ref} -> - ok - after 3000 -> - etap:bail("Timeout clearing consumer rows") - end. - - -stop(Consumer) -> - Ref = make_ref(), - Consumer ! {stop, Ref}, - receive - {ok, Ref} -> - ok - after 3000 -> - etap:bail("Timeout stopping consumer") - end. - - -pause(Consumer) -> - Ref = make_ref(), - Consumer ! {pause, Ref}, - receive - {paused, Ref} -> - ok - after 3000 -> - etap:bail("Timeout pausing consumer") - end. - - -unpause(Consumer) -> - Ref = make_ref(), - Consumer ! {continue, Ref}, - receive - {ok, Ref} -> - ok - after 3000 -> - etap:bail("Timeout unpausing consumer") - end. - - -wait_finished(_Consumer) -> - receive - {consumer_finished, Rows, LastSeq} -> - {Rows, LastSeq} - after 30000 -> - etap:bail("Timeout waiting for consumer to finish") - end. - - -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}) - 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) -> - Options = [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}, overwrite], - {ok, Db} = couch_db:create(DbName, Options), - put(current_db, Db). - - -delete_db() -> - Db = db(), - Options = [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}], - ok = couch_server:delete(couch_db:name(Db), Options), - put(current_db, undefined). diff --git a/test/etap/074-doc-update-conflicts.t b/test/etap/074-doc-update-conflicts.t deleted file mode 100755 index a7468e85698..00000000000 --- a/test/etap/074-doc-update-conflicts.t +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -% 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. - --record(user_ctx, { - name = null, - roles = [], - handler -}). - --define(i2l(I), integer_to_list(I)). - -test_db_name() -> <<"couch_test_update_conflicts">>. - - -main(_) -> - test_util:run(25, fun() -> test() end). - - -test() -> - test_util:start_couch(), - config:set("couchdb", "delayed_commits", "true", false), - - lists:foreach( - fun(NumClients) -> test_concurrent_doc_update(NumClients) end, - [100, 500, 1000]), - - test_bulk_delete_create(), - - ok. - - -% Verify that if multiple clients try to update the same document -% simultaneously, only one of them will get success response and all -% the other ones will get a conflict error. Also validate that the -% client which got the success response got its document version -% persisted into the database. -test_concurrent_doc_update(NumClients) -> - {ok, Db} = create_db(test_db_name()), - Doc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"foobar">>}, - {<<"value">>, 0} - ]}), - {ok, Rev} = couch_db:update_doc(Db, Doc, []), - ok = couch_db:close(Db), - RevStr = couch_doc:rev_to_str(Rev), - etap:diag("Created first revision of test document"), - - etap:diag("Spawning " ++ ?i2l(NumClients) ++ - " clients to update the document"), - Clients = lists:map( - fun(Value) -> - ClientDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"foobar">>}, - {<<"_rev">>, RevStr}, - {<<"value">>, Value} - ]}), - Pid = spawn_client(ClientDoc), - {Value, Pid, erlang:monitor(process, Pid)} - end, - lists:seq(1, NumClients)), - - lists:foreach(fun({_, Pid, _}) -> Pid ! go end, Clients), - etap:diag("Waiting for clients to finish"), - - {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} -> - etap:bail("Client " ++ ?i2l(Value) ++ - " got update error: " ++ couch_util:to_list(Error)) - after 60000 -> - etap:bail("Timeout waiting for client " ++ ?i2l(Value) ++ " to die") - end - end, - {0, nil}, - Clients), - - etap:diag("Verifying client results"), - etap:is( - NumConflicts, - NumClients - 1, - "Got " ++ ?i2l(NumClients - 1) ++ " client conflicts"), - - {ok, Db2} = couch_db:open_int(test_db_name(), []), - {ok, Leaves} = couch_db:open_doc_revs(Db2, <<"foobar">>, all, []), - ok = couch_db:close(Db2), - etap:is(length(Leaves), 1, "Only one document revision was persisted"), - [{ok, Doc2}] = Leaves, - {JsonDoc} = couch_doc:to_json_obj(Doc2, []), - etap:is( - couch_util:get_value(<<"value">>, JsonDoc), - SavedValue, - "Persisted doc has the right value"), - - ok = timer:sleep(1000), - etap:diag("Restarting the server"), - ok = test_util:stop_couch(), - ok = timer:sleep(1000), - ok = test_util:start_couch(), - - {ok, Db3} = couch_db:open_int(test_db_name(), []), - {ok, Leaves2} = couch_db:open_doc_revs(Db3, <<"foobar">>, all, []), - ok = couch_db:close(Db3), - etap:is(length(Leaves2), 1, "Only one document revision was persisted"), - [{ok, Doc3}] = Leaves, - etap:is(Doc3, Doc2, "Got same document after server restart"), - - delete_db(Db3). - - -% COUCHDB-188 -test_bulk_delete_create() -> - {ok, Db} = create_db(test_db_name()), - Doc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"foobar">>}, - {<<"value">>, 0} - ]}), - {ok, Rev} = couch_db:update_doc(Db, Doc, []), - - DeletedDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"foobar">>}, - {<<"_rev">>, couch_doc:rev_to_str(Rev)}, - {<<"_deleted">>, true} - ]}), - NewDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"foobar">>}, - {<<"value">>, 666} - ]}), - - {ok, Db2} = couch_db:reopen(Db), - {ok, Results} = couch_db:update_docs(Db2, [DeletedDoc, NewDoc], []), - ok = couch_db:close(Db2), - - etap:is(length([ok || {ok, _} <- Results]), 2, - "Deleted and non-deleted versions got an ok reply"), - - [{ok, Rev1}, {ok, Rev2}] = Results, - {ok, Db3} = couch_db:open_int(test_db_name(), []), - - {ok, [{ok, Doc1}]} = couch_db:open_doc_revs( - Db3, <<"foobar">>, [Rev1], [conflicts, deleted_conflicts]), - {ok, [{ok, Doc2}]} = couch_db:open_doc_revs( - Db3, <<"foobar">>, [Rev2], [conflicts, deleted_conflicts]), - ok = couch_db:close(Db3), - - {Doc1Props} = couch_doc:to_json_obj(Doc1, []), - {Doc2Props} = couch_doc:to_json_obj(Doc2, []), - - etap:is(couch_util:get_value(<<"_deleted">>, Doc1Props), true, - "Document was deleted"), - etap:is(couch_util:get_value(<<"_deleted">>, Doc2Props), undefined, - "New document not flagged as deleted"), - etap:is(couch_util:get_value(<<"value">>, Doc2Props), 666, - "New leaf revision has the right value"), - etap:is(couch_util:get_value(<<"_conflicts">>, Doc1Props), undefined, - "Deleted document has no conflicts"), - etap:is(couch_util:get_value(<<"_deleted_conflicts">>, Doc1Props), undefined, - "Deleted document has no deleted conflicts"), - etap:is(couch_util:get_value(<<"_conflicts">>, Doc2Props), undefined, - "New leaf revision doesn't have conflicts"), - etap:is(couch_util:get_value(<<"_deleted_conflicts">>, Doc2Props), undefined, - "New leaf revision doesn't have deleted conflicts"), - - etap:is(element(1, Rev1), 2, "Deleted revision has position 2"), - etap:is(element(1, Rev2), 1, "New leaf revision has position 1"), - - delete_db(Db2). - - -spawn_client(Doc) -> - spawn(fun() -> - {ok, Db} = couch_db:open_int(test_db_name(), []), - 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). - - -create_db(DbName) -> - couch_db:create( - DbName, - [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}, overwrite]). - - -delete_db(Db) -> - ok = couch_server:delete( - couch_db:name(Db), [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}]). diff --git a/test/etap/075-auth-cache.t b/test/etap/075-auth-cache.t deleted file mode 100755 index 4d79134db59..00000000000 --- a/test/etap/075-auth-cache.t +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -% 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. - --record(user_ctx, { - name = null, - roles = [], - handler -}). - --record(db, { - main_pid = nil, - compactor_pid = nil, - instance_start_time, % number of microsecs since jan 1 1970 as a binary string - fd, - fd_monitor, - header, - committed_update_seq, - id_tree, - seq_tree, - local_tree, - update_seq, - name, - filepath, - validate_doc_funs = [], - security = [], - security_ptr = nil, - user_ctx = #user_ctx{}, - waiting_delayed_commit = nil, - revs_limit = 1000, - fsync_options = [], - options = [], - compression, - before_doc_update = nil, % nil | fun(Doc, Db) -> NewDoc - after_doc_read = nil % nil | fun(Doc, Db) -> NewDoc -}). - -auth_db_name() -> <<"couch_test_auth_db">>. -auth_db_2_name() -> <<"couch_test_auth_db_2">>. -salt() -> <<"SALT">>. - - -main(_) -> - test_util:init_code_path(), - - etap:plan(19), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - - -test() -> - test_util:start_couch(), - OrigName = config:get("couch_httpd_auth", "authentication_db"), - config:set( - "couch_httpd_auth", "authentication_db", - binary_to_list(auth_db_name()), false), - delete_db(auth_db_name()), - delete_db(auth_db_2_name()), - - test_auth_db_crash(), - - config:set("couch_httpd_auth", "authentication_db", OrigName, false), - delete_db(auth_db_name()), - delete_db(auth_db_2_name()), - couch_server_sup:stop(), - ok. - - -test_auth_db_crash() -> - Creds0 = couch_auth_cache:get_user_creds("joe"), - etap:is(Creds0, nil, "Got nil when getting joe's credentials"), - - etap:diag("Adding first version of Joe's user doc"), - PasswordHash1 = hash_password("pass1"), - {ok, Rev1} = update_user_doc(auth_db_name(), "joe", "pass1"), - - Creds1 = couch_auth_cache:get_user_creds("joe"), - etap:is(is_list(Creds1), true, "Got joe's credentials from cache"), - etap:is(couch_util:get_value(<<"password_sha">>, Creds1), PasswordHash1, - "Cached credentials have the right password"), - - etap:diag("Updating Joe's user doc password"), - PasswordHash2 = hash_password("pass2"), - {ok, _Rev2} = update_user_doc(auth_db_name(), "joe", "pass2", Rev1), - - Creds2 = couch_auth_cache:get_user_creds("joe"), - etap:is(is_list(Creds2), true, "Got joe's credentials from cache"), - etap:is(couch_util:get_value(<<"password_sha">>, Creds2), PasswordHash2, - "Cached credentials have the new password"), - - etap:diag("Shutting down the auth database process"), - shutdown_db(auth_db_name()), - - {ok, UpdateRev} = get_doc_rev(auth_db_name(), "joe"), - PasswordHash3 = hash_password("pass3"), - {ok, _Rev3} = update_user_doc(auth_db_name(), "joe", "pass3", UpdateRev), - - etap:is(get_user_doc_password_sha(auth_db_name(), "joe"), - PasswordHash3, - "Latest Joe's doc revision has the new password hash"), - - Creds3 = couch_auth_cache:get_user_creds("joe"), - etap:is(is_list(Creds3), true, "Got joe's credentials from cache"), - etap:is(couch_util:get_value(<<"password_sha">>, Creds3), PasswordHash3, - "Cached credentials have the new password"), - - etap:diag("Deleting Joe's user doc"), - delete_user_doc(auth_db_name(), "joe"), - Creds4 = couch_auth_cache:get_user_creds("joe"), - etap:is(nil, Creds4, - "Joe's credentials not found in cache after user doc was deleted"), - - etap:diag("Adding new user doc for Joe"), - PasswordHash5 = hash_password("pass5"), - {ok, _NewRev1} = update_user_doc(auth_db_name(), "joe", "pass5"), - - Creds5 = couch_auth_cache:get_user_creds("joe"), - etap:is(is_list(Creds5), true, "Got joe's credentials from cache"), - etap:is(couch_util:get_value(<<"password_sha">>, Creds5), PasswordHash5, - "Cached credentials have the right password"), - - full_commit(auth_db_name()), - - etap:diag("Changing the auth database"), - config:set( - "couch_httpd_auth", "authentication_db", - binary_to_list(auth_db_2_name()), false), - ok = timer:sleep(500), - - Creds6 = couch_auth_cache:get_user_creds("joe"), - etap:is(nil, Creds6, - "Joe's credentials not found in cache after auth database changed"), - - etap:diag("Adding first version of Joe's user doc to new auth database"), - PasswordHash7 = hash_password("pass7"), - {ok, _} = update_user_doc(auth_db_2_name(), "joe", "pass7"), - - Creds7 = couch_auth_cache:get_user_creds("joe"), - etap:is(is_list(Creds7), true, "Got joe's credentials from cache"), - etap:is(couch_util:get_value(<<"password_sha">>, Creds7), PasswordHash7, - "Cached credentials have the right password"), - - etap:diag("Shutting down the auth database process"), - shutdown_db(auth_db_2_name()), - - {ok, UpdateRev2} = get_doc_rev(auth_db_2_name(), "joe"), - PasswordHash8 = hash_password("pass8"), - {ok, _Rev8} = update_user_doc(auth_db_2_name(), "joe", "pass8", UpdateRev2), - - etap:is(get_user_doc_password_sha(auth_db_2_name(), "joe"), - PasswordHash8, - "Latest Joe's doc revision has the new password hash"), - - Creds8 = couch_auth_cache:get_user_creds("joe"), - etap:is(is_list(Creds8), true, "Got joe's credentials from cache"), - etap:is(couch_util:get_value(<<"password_sha">>, Creds8), PasswordHash8, - "Cached credentials have the new password"), - - etap:diag("Changing the auth database again"), - config:set( - "couch_httpd_auth", "authentication_db", - binary_to_list(auth_db_name()), false), - ok = timer:sleep(500), - - Creds9 = couch_auth_cache:get_user_creds("joe"), - etap:is(Creds9, Creds5, - "Got same credentials as before the firt auth database change"), - etap:is(couch_util:get_value(<<"password_sha">>, Creds9), PasswordHash5, - "Cached credentials have the right password"), - ok. - - -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} = open_auth_db(DbName), - {ok, NewRev} = couch_db:update_doc(AuthDb, Doc, []), - ok = couch_db:close(AuthDb), - {ok, couch_doc:rev_to_str(NewRev)}. - - -hash_password(Password) -> - list_to_binary( - couch_util:to_hex(crypto:sha(iolist_to_binary([Password, salt()])))). - - -shutdown_db(DbName) -> - {ok, AuthDb} = open_auth_db(DbName), - 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} = open_auth_db(DbName), - 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} = open_auth_db(DbName), - {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} = open_auth_db(DbName), - {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} = open_auth_db(DbName), - {ok, _} = couch_db:ensure_full_commit(AuthDb), - ok = couch_db:close(AuthDb). - - -open_auth_db(DbName) -> - couch_db:open_int( - DbName, [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}]). - - -delete_db(Name) -> - couch_server:delete( - Name, [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}]). diff --git a/test/etap/076-file-compression.t b/test/etap/076-file-compression.t deleted file mode 100755 index 499aae509ba..00000000000 --- a/test/etap/076-file-compression.t +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -% 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. - --mode(compile). - --record(user_ctx, { - name = null, - roles = [], - handler -}). - -test_db_name() -> <<"couch_test_file_compression">>. -ddoc_id() -> <<"_design/test">>. -num_docs() -> 5000. - - -main(_) -> - test_util:init_code_path(), - - etap:plan(10), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - - -test() -> - test_util:start_couch(), - config:set("couchdb", "file_compression", "none", false), - - create_database(), - compact_db(), - compact_view(), - DbDiskSize1 = db_disk_size(), - ViewDiskSize1 = view_disk_size(), - - config:set("couchdb", "file_compression", "snappy", false), - compact_db(), - compact_view(), - DbDiskSize2 = db_disk_size(), - ViewDiskSize2 = view_disk_size(), - - etap:is(DbDiskSize2 < DbDiskSize1, true, "Database disk size decreased"), - etap:is(ViewDiskSize2 < ViewDiskSize1, true, "Index disk size decreased"), - - config:set("couchdb", "file_compression", "deflate_9", false), - compact_db(), - compact_view(), - DbDiskSize3 = db_disk_size(), - ViewDiskSize3 = view_disk_size(), - - etap:is(DbDiskSize3 < DbDiskSize2, true, "Database disk size decreased again"), - etap:is(ViewDiskSize3 < ViewDiskSize2, true, "Index disk size decreased again"), - - config:set("couchdb", "file_compression", "deflate_1", false), - compact_db(), - compact_view(), - DbDiskSize4 = db_disk_size(), - ViewDiskSize4 = view_disk_size(), - - etap:is(DbDiskSize4 > DbDiskSize3, true, "Database disk size increased"), - etap:is(ViewDiskSize4 > ViewDiskSize3, true, "Index disk size increased"), - - config:set("couchdb", "file_compression", "snappy", false), - compact_db(), - compact_view(), - DbDiskSize5 = db_disk_size(), - ViewDiskSize5 = view_disk_size(), - - etap:is(DbDiskSize5 > DbDiskSize4, true, "Database disk size increased again"), - etap:is(ViewDiskSize5 > ViewDiskSize4, true, "Index disk size increased again"), - - config:set("couchdb", "file_compression", "none", false), - compact_db(), - compact_view(), - DbDiskSize6 = db_disk_size(), - ViewDiskSize6 = view_disk_size(), - - etap:is(DbDiskSize6 > DbDiskSize5, true, "Database disk size increased again"), - etap:is(ViewDiskSize6 > ViewDiskSize5, true, "Index disk size increased again"), - - delete_db(), - couch_server_sup:stop(), - ok. - - -create_database() -> - {ok, Db} = couch_db:create( - test_db_name(), - [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}, overwrite]), - ok = populate_db(Db, num_docs()), - DDoc = couch_doc:from_json_obj({[ - {<<"_id">>, ddoc_id()}, - {<<"language">>, <<"javascript">>}, - {<<"views">>, {[ - {<<"view1">>, {[ - {<<"map">>, <<"function(doc) { emit(doc._id, doc.string); }">>} - ]}} - ]} - } - ]}), - {ok, _} = couch_db:update_doc(Db, DDoc, []), - refresh_index(), - ok = couch_db:close(Db). - - -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">>, list_to_binary(lists:duplicate(1000, $X))} - ]}) - end, - lists:seq(1, 500)), - {ok, _} = couch_db:update_docs(Db, Docs, []), - populate_db(Db, NumDocs - 500). - - -refresh_index() -> - {ok, Db} = couch_db:open_int(test_db_name(), []), - {ok, DDoc} = couch_db:open_doc(Db, ddoc_id(), [ejson_body]), - couch_mrview:query_view(Db, DDoc, <<"view1">>, [{stale, false}]), - ok = couch_db:close(Db). - - -compact_db() -> - {ok, Db} = couch_db:open_int(test_db_name(), []), - {ok, CPid} = couch_db:start_compact(Db), - case couch_db:wait_for_compaction(Db, 120000) of - timeout -> etap:bail("Timeout waiting for database compaction"); - ok -> ok - end, - ok = couch_db:close(Db). - - -compact_view() -> - {ok, MonRef} = couch_mrview:compact(test_db_name(), ddoc_id(), [monitor]), - receive - {'DOWN', MonRef, process, _CompactPid, normal} -> - ok; - {'DOWN', MonRef, process, _CompactPid, Reason} -> - etap:bail("Error compacting view group: " ++ couch_util:to_list(Reason)) - after 120000 -> - etap:bail("Timeout waiting for view group compaction") - end. - - -db_disk_size() -> - {ok, Db} = couch_db:open_int(test_db_name(), []), - {ok, Info} = couch_db:get_db_info(Db), - ok = couch_db:close(Db), - couch_util:get_value(disk_size, Info). - - -view_disk_size() -> - {ok, Db} = couch_db:open_int(test_db_name(), []), - {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). - - -delete_db() -> - ok = couch_server:delete( - test_db_name(), [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}]). diff --git a/test/etap/077-couch-db-fast-db-delete-create.t b/test/etap/077-couch-db-fast-db-delete-create.t deleted file mode 100644 index 4ba5c56fcd4..00000000000 --- a/test/etap/077-couch-db-fast-db-delete-create.t +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:run(1, fun() -> test() end). - - -loop(0) -> - ok; -loop(N) -> - ok = cycle(), - loop(N - 1). - - -cycle() -> - {ok, Db} = couch_db:create(<<"etap-test-db">>, []), - % Dirty but only less dirty than importing the #db{} record - couch_file:close(element(5, Db)), - ok = couch_server:delete(<<"etap-test-db">>, [sync]), - ok. - - -test() -> - test_util:start_couch(), - - ok = loop(1), - ok = loop(10), - ok = loop(100), - ok = loop(1000), - - % for more thorough testing: - % ok = loop(10000), - % ok = loop(100000), - % ok = loop(1000000), - % ok = loop(10000000), - - etap:is(true, true, "lots of creating and deleting of a database"), - ok. diff --git a/test/etap/080-config-get-set.t b/test/etap/080-config-get-set.t deleted file mode 100755 index d7279360dee..00000000000 --- a/test/etap/080-config-get-set.t +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -default_config() -> - test_util:build_file("etc/couchdb/default_dev.ini"). - -main(_) -> - test_util:init_code_path(), - etap:plan(12), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - application:set_env(config, ini_files, ["etc/couchdb/default_dev.ini"]), - application:start(config), - - - % Check that we can get values - - - etap:fun_is( - fun(List) -> length(List) > 0 end, - config:all(), - "Data was loaded from the INI file." - ), - - etap:fun_is( - fun(List) -> length(List) > 0 end, - config:get("daemons"), - "There are settings in the [daemons] section of the INI file." - ), - - etap:is( - config:get("httpd_design_handlers", "_view"), - "{couch_mrview_http, handle_view_req}", - "The {httpd_design_handlers, view} is the expected default." - ), - - etap:is( - config:get("httpd", "foo", "bar"), - "bar", - "Returns the default when key doesn't exist in config." - ), - - etap:is( - config:get("httpd", "foo"), - undefined, - "The default default is the atom 'undefined'." - ), - - etap:is( - config:get("httpd", "port", "bar"), - "5984", - "Only returns the default when the config setting does not exist." - ), - - - % Check that setting values works. - - - ok = config:set("log", "level", "severe", false), - - etap:is( - config:get("log", "level"), - "severe", - "Non persisted changes take effect." - ), - - etap:is( - config:get("new_section", "bizzle"), - undefined, - "Section 'new_section' does not exist." - ), - - ok = config:set("new_section", "bizzle", "bang", false), - - etap:is( - config:get("new_section", "bizzle"), - "bang", - "New section 'new_section' was created for a new key/value pair." - ), - - - % Check that deleting works - - - ok = config:delete("new_section", "bizzle", false), - etap:is( - config:get("new_section", "bizzle"), - undefined, - "Deleting sets the value to \"\"" - ), - - - % Check ge/set/delete binary strings - - ok = config:set(<<"foo">>, <<"bar">>, <<"baz">>, false), - etap:is( - config:get(<<"foo">>, <<"bar">>), - <<"baz">>, - "Can get and set with binary section and key values." - ), - ok = config:delete(<<"foo">>, <<"bar">>, false), - etap:is( - config:get(<<"foo">>, <<"bar">>), - undefined, - "Deleting with binary section/key pairs sets the value to \"\"" - ), - - ok. diff --git a/test/etap/081-config-override.1.ini b/test/etap/081-config-override.1.ini deleted file mode 100644 index 55451dade8d..00000000000 --- a/test/etap/081-config-override.1.ini +++ /dev/null @@ -1,22 +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. - -[couchdb] -max_dbs_open=10 - -[httpd] -port=4895 diff --git a/test/etap/081-config-override.2.ini b/test/etap/081-config-override.2.ini deleted file mode 100644 index 5f46357f5b7..00000000000 --- a/test/etap/081-config-override.2.ini +++ /dev/null @@ -1,22 +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. - -[httpd] -port = 80 - -[fizbang] -unicode = normalized diff --git a/test/etap/081-config-override.t b/test/etap/081-config-override.t deleted file mode 100755 index 3a97b66d455..00000000000 --- a/test/etap/081-config-override.t +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -default_config() -> - test_util:build_file("etc/couchdb/default_dev.ini"). - -local_config_1() -> - test_util:source_file("test/etap/081-config-override.1.ini"). - -local_config_2() -> - test_util:source_file("test/etap/081-config-override.2.ini"). - -local_config_write() -> - test_util:build_file("test/etap/temp.081"). - -% Run tests and wait for the config gen_server to shutdown. -run_tests(IniFiles, Tests) -> - application:set_env(config, ini_files, IniFiles), - ok = application:start(config), - Tests(), - timer:sleep(1000), - ok = application:stop(config), - ok. - -main(_) -> - test_util:init_code_path(), - etap:plan(17), - - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - - CheckStartStop = fun() -> ok end, - run_tests([default_config()], CheckStartStop), - - CheckDefaults = fun() -> - etap:is( - config:get("couchdb", "max_dbs_open"), - "100", - "{couchdb, max_dbs_open} is 100 by defualt." - ), - - etap:is( - config:get("httpd","port"), - "5984", - "{httpd, port} is 5984 by default" - ), - - etap:is( - config:get("fizbang", "unicode"), - undefined, - "{fizbang, unicode} is undefined by default" - ) - end, - - run_tests([default_config()], CheckDefaults), - - - % Check that subsequent files override values appropriately - - CheckOverride = fun() -> - etap:is( - config:get("couchdb", "max_dbs_open"), - "10", - "{couchdb, max_dbs_open} was overriden with the value 10" - ), - - etap:is( - config:get("httpd", "port"), - "4895", - "{httpd, port} was overriden with the value 4895" - ) - end, - - run_tests([default_config(), local_config_1()], CheckOverride), - - - % Check that overrides can create new sections - - CheckOverride2 = fun() -> - etap:is( - config:get("httpd", "port"), - "80", - "{httpd, port} is overriden with the value 80" - ), - - etap:is( - config:get("fizbang", "unicode"), - "normalized", - "{fizbang, unicode} was created by override INI file" - ) - end, - - run_tests([default_config(), local_config_2()], CheckOverride2), - - - % Check that values can be overriden multiple times - - CheckOverride3 = fun() -> - etap:is( - config:get("httpd", "port"), - "80", - "{httpd, port} value was taken from the last specified INI file." - ) - end, - - run_tests( - [default_config(), local_config_1(), local_config_2()], - CheckOverride3 - ), - - % Check persistence to last file. - - % Empty the file in case it exists. - {ok, Fd} = file:open(local_config_write(), write), - ok = file:truncate(Fd), - ok = file:close(Fd), - - % Open and write a value - CheckCanWrite = fun() -> - etap:is( - config:get("httpd", "port"), - "5984", - "{httpd, port} is still 5984 by default" - ), - - etap:is( - config:set("httpd", "port", "8080"), - ok, - "Writing {httpd, port} is kosher." - ), - - etap:is( - config:get("httpd", "port"), - "8080", - "{httpd, port} was updated to 8080 successfully." - ), - - etap:is( - config:delete("httpd", "bind_address"), - ok, - "Deleting {httpd, bind_address} succeeds" - ), - - etap:is( - config:get("httpd", "bind_address"), - undefined, - "{httpd, bind_address} was actually deleted." - ) - end, - - run_tests([default_config(), local_config_write()], CheckCanWrite), - - % Open and check where we don't expect persistence. - - CheckDidntWrite = fun() -> - etap:is( - config:get("httpd", "port"), - "5984", - "{httpd, port} was not persisted to the primary INI file." - ), - - etap:is( - config:get("httpd", "bind_address"), - "127.0.0.1", - "{httpd, bind_address} was not deleted form the primary INI file." - ) - end, - - run_tests([default_config()], CheckDidntWrite), - - % Open and check we have only the persistence we expect. - CheckDidWrite = fun() -> - etap:is( - config:get("httpd", "port"), - "8080", - "{httpd, port} is still 8080 after reopening the config." - ), - - etap:is( - config:get("httpd", "bind_address"), - undefined, - "{httpd, bind_address} is still \"\" after reopening." - ) - end, - - run_tests([local_config_write()], CheckDidWrite), - - ok. diff --git a/test/etap/082-config-register.t b/test/etap/082-config-register.t deleted file mode 100755 index bd09ea73559..00000000000 --- a/test/etap/082-config-register.t +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --mode(compile). --export([handle_config_change/5]). - -default_config() -> - test_util:build_file("etc/couchdb/default_dev.ini"). - -main(_) -> - test_util:init_code_path(), - etap:plan(5), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - application:set_env(config, ini_files, ["etc/couchdb/default_dev.ini"]), - ok = application:start(config), - - etap:is( - config:get("httpd", "port"), - "5984", - "{httpd, port} is 5984 by default." - ), - - ok = config:set("httpd", "port", "4895", false), - - etap:is( - config:get("httpd", "port"), - "4895", - "{httpd, port} changed to 4895" - ), - - config:listen_for_changes(?MODULE, self()), - - ok = config:set("httpd", "port", "8080", false), - - receive - {"httpd", "port", Value, false} -> - etap:is(Value, "8080", "Registered function got notification.") - after - 1000 -> - etap:fail("notification failed") - end, - - % Implicitly checking that we *don't* call the function - etap:is( - config:get("httpd", "bind_address"), - "127.0.0.1", - "{httpd, bind_address} is not '0.0.0.0'" - ), - - Msg = receive M -> M after 500 -> nil end, - etap:is(Msg, nil, "yay, no notification for get"), - - ok. - -handle_config_change(Sec, Key, Val, Persist, Pid) -> - Pid ! {Sec, Key, Val, Persist}, - {ok, Pid}. - diff --git a/test/etap/083-config-no-files.t b/test/etap/083-config-no-files.t deleted file mode 100755 index 3ad0905e39f..00000000000 --- a/test/etap/083-config-no-files.t +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - - -main(_) -> - test_util:init_code_path(), - etap:plan(3), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - application:start(config), - - etap:fun_is( - fun(KVPairs) -> length(KVPairs) == 0 end, - config:all(), - "No INI files specified returns 0 key/value pairs." - ), - - ok = config:set("httpd", "port", "80", false), - - etap:is( - config:get("httpd", "port"), - "80", - "Created a new non-persisted k/v pair." - ), - - ok = config:set("httpd", "bind_address", "127.0.0.1"), - etap:is( - config:get("httpd", "bind_address"), - "127.0.0.1", - "Asking for a persistent key/value pair doesn't choke." - ), - - ok. diff --git a/test/etap/090-task-status.t b/test/etap/090-task-status.t deleted file mode 100755 index 23115bdaabd..00000000000 --- a/test/etap/090-task-status.t +++ /dev/null @@ -1,279 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - etap:plan(28), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -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]; - _ -> - [] - end - end, - [], couch_task_status:all() - ), - case couch_util:get_value(Prop, hd(Element), nil) of - nil -> - etap:bail("Could not get property '" ++ couch_util:to_list(Prop) ++ - "' for task " ++ pid_to_list(Pid)); - Value -> - Value - end. - - -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 1000 -> - throw(timeout_error) - end. - -test() -> - {ok, TaskStatusPid} = couch_task_status:start_link(), - - TaskUpdater = fun() -> loop() end, - % create three updaters - Pid1 = spawn(TaskUpdater), - Pid2 = spawn(TaskUpdater), - Pid3 = spawn(TaskUpdater), - - ok = call(Pid1, add, [{type, replication}, {progress, 0}]), - etap:is( - length(couch_task_status:all()), - 1, - "Started a task" - ), - Task1StartTime = get_task_prop(Pid1, started_on), - etap:is( - is_integer(Task1StartTime), - true, - "Task start time is defined." - ), - etap:is( - get_task_prop(Pid1, updated_on), - Task1StartTime, - "Task's start time is the same as the update time before an update." - ), - - etap:is( - call(Pid1, add, [{type, compaction}, {progress, 0}]), - {add_task_error, already_registered}, - "Unable to register multiple tasks for a single Pid." - ), - - etap:is( - get_task_prop(Pid1, type), - replication, - "Task type is 'replication'." - ), - etap:is( - get_task_prop(Pid1, progress), - 0, - "Task progress is 0." - ), - - ok = timer:sleep(1000), - call(Pid1, update, [{progress, 25}]), - etap:is( - get_task_prop(Pid1, progress), - 25, - "Task progress is 25." - ), - etap:is( - get_task_prop(Pid1, updated_on) > Task1StartTime, - true, - "Task's last update time has increased after an update." - ), - - call(Pid2, add, [{type, compaction}, {progress, 0}]), - etap:is( - length(couch_task_status:all()), - 2, - "Started a second task." - ), - Task2StartTime = get_task_prop(Pid2, started_on), - etap:is( - is_integer(Task2StartTime), - true, - "Second task's start time is defined." - ), - etap:is( - get_task_prop(Pid2, updated_on), - Task2StartTime, - "Second task's start time is the same as the update time before an update." - ), - - etap:is( - get_task_prop(Pid2, type), - compaction, - "Second task's type is 'compaction'." - ), - etap:is( - get_task_prop(Pid2, progress), - 0, - "Second task's progress is 0." - ), - - ok = timer:sleep(1000), - call(Pid2, update, [{progress, 33}]), - etap:is( - get_task_prop(Pid2, progress), - 33, - "Second task's progress updated to 33." - ), - etap:is( - get_task_prop(Pid2, updated_on) > Task2StartTime, - true, - "Second task's last update time has increased after an update." - ), - - call(Pid3, add, [{type, indexer}, {progress, 0}]), - etap:is( - length(couch_task_status:all()), - 3, - "Registered a third task." - ), - Task3StartTime = get_task_prop(Pid3, started_on), - etap:is( - is_integer(Task3StartTime), - true, - "Third task's start time is defined." - ), - etap:is( - get_task_prop(Pid3, updated_on), - Task3StartTime, - "Third task's start time is the same as the update time before an update." - ), - - etap:is( - get_task_prop(Pid3, type), - indexer, - "Third task's type is 'indexer'." - ), - etap:is( - get_task_prop(Pid3, progress), - 0, - "Third task's progress is 0." - ), - - ok = timer:sleep(1000), - call(Pid3, update, [{progress, 50}]), - etap:is( - get_task_prop(Pid3, progress), - 50, - "Third task's progress updated to 50." - ), - etap:is( - get_task_prop(Pid3, updated_on) > Task3StartTime, - true, - "Third task's last update time has increased after an update." - ), - - call(Pid3, update_frequency, 500), - call(Pid3, update, [{progress, 66}]), - etap:is( - get_task_prop(Pid3, progress), - 66, - "Third task's progress updated to 66." - ), - - call(Pid3, update, [{progress, 67}]), - etap:is( - get_task_prop(Pid3, progress), - 66, - "Task update dropped because of frequency limit." - ), - - call(Pid3, update_frequency, 0), - call(Pid3, update, [{progress, 77}]), - etap:is( - get_task_prop(Pid3, progress), - 77, - "Task updated after reseting frequency limit." - ), - - - call(Pid1, done), - etap:is( - length(couch_task_status:all()), - 2, - "First task finished." - ), - - call(Pid2, done), - etap:is( - length(couch_task_status:all()), - 1, - "Second task finished." - ), - - call(Pid3, done), - etap:is( - length(couch_task_status:all()), - 0, - "Third task finished." - ), - - erlang:monitor(process, TaskStatusPid), - couch_task_status:stop(), - receive - {'DOWN', _, _, TaskStatusPid, _} -> - ok - after - 1000 -> - throw(timeout_error) - end, - - ok. diff --git a/test/etap/120-stats-collect.t b/test/etap/120-stats-collect.t deleted file mode 100755 index a30f9ac5dd5..00000000000 --- a/test/etap/120-stats-collect.t +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - etap:plan(11), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail() - end, - ok. - -test() -> - couch_stats_collector:start(), - ok = test_counters(), - ok = test_abs_values(), - ok = test_proc_counting(), - ok = test_all(), - ok. - -test_counters() -> - 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), - etap:is( - couch_stats_collector:get(foo), - 80, - "Incrememnt tracks correctly." - ), - - repeat(RemCount, 80), - etap:is( - couch_stats_collector:get(foo), - 0, - "Decremented to zaro." - ), - ok. - -test_abs_values() -> - lists:map(fun(Val) -> - couch_stats_collector:record(bar, Val) - end, lists:seq(1, 15)), - etap:is( - couch_stats_collector:get(bar), - lists:seq(1, 15), - "Absolute values are recorded correctly." - ), - - couch_stats_collector:clear(bar), - etap:is( - couch_stats_collector:get(bar), - nil, - "Absolute values are cleared correctly." - ), - ok. - -test_proc_counting() -> - Self = self(), - OnePid = spawn(fun() -> - couch_stats_collector:track_process_count(hoopla), - Self ! reporting, - receive sepuku -> ok end - end), - R1 = erlang:monitor(process, OnePid), - receive reporting -> ok end, - etap:is( - couch_stats_collector:get(hoopla), - 1, - "track_process_count increments the counter." - ), - - TwicePid = spawn(fun() -> - couch_stats_collector:track_process_count(hoopla), - couch_stats_collector:track_process_count(hoopla), - Self ! reporting, - receive sepuku -> ok end - end), - R2 = erlang:monitor(process, TwicePid), - receive reporting -> ok end, - etap:is( - couch_stats_collector:get(hoopla), - 3, - "track_process_count allows more than one incrememnt per Pid" - ), - - OnePid ! sepuku, - receive {'DOWN', R1, _, _, _} -> ok end, - timer:sleep(250), - etap:is( - couch_stats_collector:get(hoopla), - 2, - "Process count is decremented when process exits." - ), - - TwicePid ! sepuku, - receive {'DOWN', R2, _, _, _} -> ok end, - timer:sleep(250), - etap:is( - couch_stats_collector:get(hoopla), - 0, - "Process count is decremented for each call to track_process_count." - ), - ok. - -test_all() -> - couch_stats_collector:record(bar, 0.0), - couch_stats_collector:record(bar, 1.0), - etap:is( - lists:sort(couch_stats_collector:all()), - [ {bar,[1.0,0.0]}, {foo,0}, { hoopla,0} ], - "all/0 returns all counters and absolute values." - ), - - etap:is( - lists:sort(couch_stats_collector:all(incremental)), - [ {foo, 0}, {hoopla, 0} ], - "all/1 returns only the specified type." - ), - - couch_stats_collector:record(zing, 90), - etap:is( - lists:sort(couch_stats_collector:all(absolute)), - [ {bar,[1.0,0.0]}, {zing,"Z"} ], - "all/1 returns only the specified type." - ), - ok. - -repeat(_, 0) -> - ok; -repeat(Fun, Count) -> - Fun(), - repeat(Fun, Count-1). diff --git a/test/etap/121-stats-aggregates.cfg b/test/etap/121-stats-aggregates.cfg deleted file mode 100644 index 30e475da854..00000000000 --- a/test/etap/121-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"}. diff --git a/test/etap/121-stats-aggregates.ini b/test/etap/121-stats-aggregates.ini deleted file mode 100644 index cc5cd21875e..00000000000 --- a/test/etap/121-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] diff --git a/test/etap/121-stats-aggregates.t b/test/etap/121-stats-aggregates.t deleted file mode 100755 index 4436beb5c9e..00000000000 --- a/test/etap/121-stats-aggregates.t +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -ini_file() -> - test_util:source_file("test/etap/121-stats-aggregates.ini"). - -cfg_file() -> - test_util:source_file("test/etap/121-stats-aggregates.cfg"). - -main(_) -> - test_util:run(17, fun() -> test() end). - -test() -> - config_sup:start_link([ini_file()]), - couch_stats_collector:start(), - couch_stats_aggregator:start(cfg_file()), - ok = test_all_empty(), - ok = test_get_empty(), - ok = test_count_stats(), - ok = test_abs_stats(), - ok. - -test_all_empty() -> - {Aggs} = couch_stats_aggregator:all(), - - etap:is(length(Aggs), 2, "There are only two aggregate types in testing."), - etap:is( - couch_util:get_value(testing, Aggs), - {[{stuff, make_agg(<<"yay description">>, - null, null, null, null, null)}]}, - "{testing, stuff} is empty at start." - ), - etap:is( - couch_util:get_value(number, Aggs), - {[{'11', make_agg(<<"randomosity">>, - null, null, null, null, null)}]}, - "{number, '11'} is empty at start." - ), - ok. - -test_get_empty() -> - etap:is( - couch_stats_aggregator:get_json({testing, stuff}), - make_agg(<<"yay description">>, null, null, null, null, null), - "Getting {testing, stuff} returns an empty aggregate." - ), - etap:is( - couch_stats_aggregator:get_json({number, '11'}), - make_agg(<<"randomosity">>, null, null, null, null, null), - "Getting {number, '11'} returns an empty aggregate." - ), - ok. - -test_count_stats() -> - lists:foreach(fun(_) -> - couch_stats_collector:increment({testing, stuff}) - end, lists:seq(1, 100)), - couch_stats_aggregator:collect_sample(), - etap:is( - couch_stats_aggregator:get_json({testing, stuff}), - make_agg(<<"yay description">>, 100, 100, null, 100, 100), - "COUNT: Adding values changes the stats." - ), - etap:is( - couch_stats_aggregator:get_json({testing, stuff}, 1), - make_agg(<<"yay description">>, 100, 100, null, 100, 100), - "COUNT: Adding values changes stats for all times." - ), - - timer:sleep(500), - couch_stats_aggregator:collect_sample(), - etap:is( - couch_stats_aggregator:get_json({testing, stuff}), - make_agg(<<"yay description">>, 100, 50, 70.711, 0, 100), - "COUNT: Removing values changes stats." - ), - etap:is( - couch_stats_aggregator:get_json({testing, stuff}, 1), - make_agg(<<"yay description">>, 100, 50, 70.711, 0, 100), - "COUNT: Removing values changes stats for all times." - ), - - timer:sleep(600), - couch_stats_aggregator:collect_sample(), - etap:is( - couch_stats_aggregator:get_json({testing, stuff}), - make_agg(<<"yay description">>, 100, 33.333, 57.735, 0, 100), - "COUNT: Letting time passes doesn't remove data from time 0 aggregates" - ), - etap:is( - couch_stats_aggregator:get_json({testing, stuff}, 1), - make_agg(<<"yay description">>, 0, 0, 0, 0, 0), - "COUNT: Letting time pass removes data from other time aggregates." - ), - ok. - -test_abs_stats() -> - lists:foreach(fun(X) -> - couch_stats_collector:record({number, 11}, X) - end, lists:seq(0, 10)), - couch_stats_aggregator:collect_sample(), - etap:is( - couch_stats_aggregator:get_json({number, 11}), - make_agg(<<"randomosity">>, 5, 5, null, 5, 5), - "ABS: Adding values changes the stats." - ), - etap:is( - couch_stats_aggregator:get_json({number, 11}, 1), - make_agg(<<"randomosity">>, 5, 5, null, 5, 5), - "ABS: Adding values changes stats for all times." - ), - - timer:sleep(500), - couch_stats_collector:record({number, 11}, 15), - couch_stats_aggregator:collect_sample(), - etap:is( - couch_stats_aggregator:get_json({number, 11}), - make_agg(<<"randomosity">>, 20, 10, 7.071, 5, 15), - "ABS: New values changes stats" - ), - etap:is( - couch_stats_aggregator:get_json({number, 11}, 1), - make_agg(<<"randomosity">>, 20, 10, 7.071, 5, 15), - "ABS: Removing values changes stats for all times." - ), - - timer:sleep(600), - couch_stats_aggregator:collect_sample(), - etap:is( - couch_stats_aggregator:get_json({number, 11}), - make_agg(<<"randomosity">>, 20, 10, 7.071, 5, 15), - "ABS: Letting time passes doesn't remove data from time 0 aggregates" - ), - etap:is( - couch_stats_aggregator:get_json({number, 11}, 1), - make_agg(<<"randomosity">>, 15, 15, null, 15, 15), - "ABS: Letting time pass removes data from other time aggregates." - ), - ok. - -make_agg(Desc, Sum, Mean, StdDev, Min, Max) -> - {[ - {description, Desc}, - {current, Sum}, - {sum, Sum}, - {mean, Mean}, - {stddev, StdDev}, - {min, Min}, - {max, Max} - ]}. diff --git a/test/etap/130-attachments-md5.t b/test/etap/130-attachments-md5.t deleted file mode 100755 index fad057e5d4a..00000000000 --- a/test/etap/130-attachments-md5.t +++ /dev/null @@ -1,248 +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. - -test_db_name() -> - <<"etap-test-db">>. - -docid() -> - case get(docid) of - undefined -> - put(docid, 1), - "1"; - Count -> - put(docid, Count+1), - integer_to_list(Count+1) - end. - -main(_) -> - test_util:init_code_path(), - - etap:plan(16), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - ok = test_util:start_couch(), - Addr = config:get("httpd", "bind_address", any), - put(addr, Addr), - put(port, mochiweb_socket_server:get(couch_httpd, port)), - timer:sleep(1000), - - couch_server:delete(test_db_name(), []), - couch_db:create(test_db_name(), []), - - test_identity_without_md5(), - test_chunked_without_md5(), - - test_identity_with_valid_md5(), - test_chunked_with_valid_md5_header(), - test_chunked_with_valid_md5_trailer(), - - test_identity_with_invalid_md5(), - test_chunked_with_invalid_md5_header(), - test_chunked_with_invalid_md5_trailer(), - - couch_server:delete(test_db_name(), []), - ok = test_util:stop_couch(), - ok. - -test_identity_without_md5() -> - Data = [ - "PUT /", test_db_name(), "/", docid(), "/readme.txt HTTP/1.1\r\n", - "Content-Type: text/plain\r\n", - "Content-Length: 34\r\n", - "\r\n", - "We all live in a yellow submarine!"], - - {Code, Json} = do_request(Data), - etap:is(Code, 201, "Stored with identity encoding and no MD5"), - etap:is(get_json(Json, [<<"ok">>]), true, "Body indicates success."). - -test_chunked_without_md5() -> - AttData = <<"We all live in a yellow submarine!">>, - <> = AttData, - Data = [ - "PUT /", test_db_name(), "/", docid(), "/readme.txt HTTP/1.1\r\n", - "Content-Type: text/plain\r\n", - "Transfer-Encoding: chunked\r\n", - "\r\n", - to_hex(size(Part1)), "\r\n", - Part1, "\r\n", - to_hex(size(Part2)), "\r\n", - Part2, "\r\n" - "0\r\n" - "\r\n"], - - {Code, Json} = do_request(Data), - etap:is(Code, 201, "Stored with chunked encoding and no MD5"), - etap:is(get_json(Json, [<<"ok">>]), true, "Body indicates success."). - -test_identity_with_valid_md5() -> - AttData = "We all live in a yellow submarine!", - Data = [ - "PUT /", test_db_name(), "/", docid(), "/readme.txt HTTP/1.1\r\n", - "Content-Type: text/plain\r\n", - "Content-Length: 34\r\n", - "Content-MD5: ", base64:encode(couch_util:md5(AttData)), "\r\n", - "\r\n", - AttData], - - {Code, Json} = do_request(Data), - etap:is(Code, 201, "Stored with identity encoding and valid MD5"), - etap:is(get_json(Json, [<<"ok">>]), true, "Body indicates success."). - -test_chunked_with_valid_md5_header() -> - AttData = <<"We all live in a yellow submarine!">>, - <> = AttData, - Data = [ - "PUT /", test_db_name(), "/", docid(), "/readme.txt HTTP/1.1\r\n", - "Content-Type: text/plain\r\n", - "Transfer-Encoding: chunked\r\n", - "Content-MD5: ", base64:encode(couch_util:md5(AttData)), "\r\n", - "\r\n", - to_hex(size(Part1)), "\r\n", - Part1, "\r\n", - to_hex(size(Part2)), "\r\n", - Part2, "\r\n", - "0\r\n", - "\r\n"], - - {Code, Json} = do_request(Data), - etap:is(Code, 201, "Stored with chunked encoding and valid MD5 header."), - etap:is(get_json(Json, [<<"ok">>]), true, "Body indicates success."). - -test_chunked_with_valid_md5_trailer() -> - AttData = <<"We all live in a yellow submarine!">>, - <> = AttData, - Data = [ - "PUT /", test_db_name(), "/", docid(), "/readme.txt HTTP/1.1\r\n", - "Content-Type: text/plain\r\n", - "Transfer-Encoding: chunked\r\n", - "Trailer: Content-MD5\r\n", - "\r\n", - to_hex(size(Part1)), "\r\n", - Part1, "\r\n", - to_hex(size(Part2)), "\r\n", - Part2, "\r\n", - "0\r\n", - "Content-MD5: ", base64:encode(couch_util:md5(AttData)), "\r\n", - "\r\n"], - - {Code, Json} = do_request(Data), - etap:is(Code, 201, "Stored with chunked encoding and valid MD5 trailer."), - etap:is(get_json(Json, [<<"ok">>]), true, "Body indicates success."). - -test_identity_with_invalid_md5() -> - Data = [ - "PUT /", test_db_name(), "/", docid(), "/readme.txt HTTP/1.1\r\n", - "Content-Type: text/plain\r\n", - "Content-Length: 34\r\n", - "Content-MD5: ", base64:encode(<<"foobar!">>), "\r\n", - "\r\n", - "We all live in a yellow submarine!"], - - {Code, Json} = do_request(Data), - etap:is(Code, 400, "Invalid MD5 header causes an error: identity"), - etap:is( - get_json(Json, [<<"error">>]), - <<"content_md5_mismatch">>, - "Body indicates reason for failure." - ). - -test_chunked_with_invalid_md5_header() -> - AttData = <<"We all live in a yellow submarine!">>, - <> = AttData, - Data = [ - "PUT /", test_db_name(), "/", docid(), "/readme.txt HTTP/1.1\r\n", - "Content-Type: text/plain\r\n", - "Transfer-Encoding: chunked\r\n", - "Content-MD5: ", base64:encode(<<"so sneaky...">>), "\r\n", - "\r\n", - to_hex(size(Part1)), "\r\n", - Part1, "\r\n", - to_hex(size(Part2)), "\r\n", - Part2, "\r\n", - "0\r\n", - "\r\n"], - - {Code, Json} = do_request(Data), - etap:is(Code, 400, "Invalid MD5 header causes an error: chunked"), - etap:is( - get_json(Json, [<<"error">>]), - <<"content_md5_mismatch">>, - "Body indicates reason for failure." - ). - -test_chunked_with_invalid_md5_trailer() -> - AttData = <<"We all live in a yellow submarine!">>, - <> = AttData, - Data = [ - "PUT /", test_db_name(), "/", docid(), "/readme.txt HTTP/1.1\r\n", - "Content-Type: text/plain\r\n", - "Transfer-Encoding: chunked\r\n", - "Trailer: Content-MD5\r\n", - "\r\n", - to_hex(size(Part1)), "\r\n", - Part1, "\r\n", - to_hex(size(Part2)), "\r\n", - Part2, "\r\n", - "0\r\n", - "Content-MD5: ", base64:encode(<<"Kool-Aid Fountain!">>), "\r\n", - "\r\n"], - - {Code, Json} = do_request(Data), - etap:is(Code, 400, "Invalid MD5 Trailer causes an error"), - etap:is( - get_json(Json, [<<"error">>]), - <<"content_md5_mismatch">>, - "Body indicates reason for failure." - ). - - -get_socket() -> - Options = [binary, {packet, 0}, {active, false}], - {ok, Sock} = gen_tcp:connect(get(addr), get(port), Options), - Sock. - -do_request(Request) -> - Sock = get_socket(), - gen_tcp:send(Sock, list_to_binary(lists:flatten(Request))), - timer:sleep(1000), - {ok, R} = gen_tcp:recv(Sock, 0), - gen_tcp:close(Sock), - [Header, Body] = re:split(R, "\r\n\r\n", [{return, binary}]), - {ok, {http_response, _, Code, _}, _} = - erlang:decode_packet(http, Header, []), - Json = ejson:decode(Body), - {Code, Json}. - -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. - diff --git a/test/etap/140-attachment-comp.t b/test/etap/140-attachment-comp.t deleted file mode 100755 index a4de5a31f39..00000000000 --- a/test/etap/140-attachment-comp.t +++ /dev/null @@ -1,728 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -test_db_name() -> - <<"couch_test_atts_compression">>. - -main(_) -> - test_util:init_code_path(), - - etap:plan(85), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - ok = test_util:start_couch(), - put(addr, config:get("httpd", "bind_address", "127.0.0.1")), - put(port, integer_to_list(mochiweb_socket_server:get(couch_httpd, port))), - timer:sleep(1000), - couch_server:delete(test_db_name(), []), - couch_db:create(test_db_name(), []), - - config:set("attachments", "compression_level", "8", false), - config:set("attachments", "compressible_types", "text/*", false), - - create_1st_text_att(), - create_1st_png_att(), - create_2nd_text_att(), - create_2nd_png_att(), - - tests_for_1st_text_att(), - tests_for_1st_png_att(), - tests_for_2nd_text_att(), - tests_for_2nd_png_att(), - - create_already_compressed_att(db_url() ++ "/doc_comp_att", "readme.txt"), - test_already_compressed_att(db_url() ++ "/doc_comp_att", "readme.txt"), - - test_create_already_compressed_att_with_invalid_content_encoding( - db_url() ++ "/doc_att_deflate", - "readme.txt", - zlib:compress(test_text_data()), - "deflate" - ), - - % COUCHDB-1711 - avoid weird timng/scheduling/request handling issue - timer:sleep(100), - - test_create_already_compressed_att_with_invalid_content_encoding( - db_url() ++ "/doc_att_compress", - "readme.txt", - % 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 - test_text_data(), - "compress" - ), - - test_compressible_type_with_parameters(), - - timer:sleep(3000), % to avoid mochiweb socket closed exceptions - couch_server:delete(test_db_name(), []), - ok = test_util:stop_couch(), - ok. - -db_url() -> - "http://" ++ get(addr) ++ ":" ++ get(port) ++ "/" ++ - binary_to_list(test_db_name()). - -create_1st_text_att() -> - {ok, Code, _Headers, _Body} = test_util:request( - db_url() ++ "/testdoc1/readme.txt", - [{"Content-Type", "text/plain"}], - put, - test_text_data()), - etap:is(Code, 201, "Created text attachment using the standalone api"), - ok. - -create_1st_png_att() -> - {ok, Code, _Headers, _Body} = test_util:request( - db_url() ++ "/testdoc2/icon.png", - [{"Content-Type", "image/png"}], - put, - test_png_data()), - etap:is(Code, 201, "Created png attachment using the standalone api"), - ok. - -% create a text attachment using the non-standalone attachment api -create_2nd_text_att() -> - DocJson = {[ - {<<"_attachments">>, {[ - {<<"readme.txt">>, {[ - {<<"content_type">>, <<"text/plain">>}, - {<<"data">>, base64:encode(test_text_data())} - ]} - }]}} - ]}, - {ok, Code, _Headers, _Body} = test_util:request( - db_url() ++ "/testdoc3", - [{"Content-Type", "application/json"}], - put, - ejson:encode(DocJson)), - etap:is(Code, 201, "Created text attachment using the non-standalone api"), - ok. - -% create a png attachment using the non-standalone attachment api -create_2nd_png_att() -> - DocJson = {[ - {<<"_attachments">>, {[ - {<<"icon.png">>, {[ - {<<"content_type">>, <<"image/png">>}, - {<<"data">>, base64:encode(test_png_data())} - ]} - }]}} - ]}, - {ok, Code, _Headers, _Body} = test_util:request( - db_url() ++ "/testdoc4", - [{"Content-Type", "application/json"}], - put, - ejson:encode(DocJson)), - etap:is(Code, 201, "Created png attachment using the non-standalone api"), - ok. - -create_already_compressed_att(DocUri, AttName) -> - {ok, Code, _Headers, _Body} = test_util:request( - DocUri ++ "/" ++ AttName, - [{"Content-Type", "text/plain"}, {"Content-Encoding", "gzip"}], - put, - zlib:gzip(test_text_data())), - etap:is( - Code, - 201, - "Created already compressed attachment using the standalone api" - ), - ok. - -tests_for_1st_text_att() -> - test_get_1st_text_att_with_accept_encoding_gzip(), - test_get_1st_text_att_without_accept_encoding_header(), - test_get_1st_text_att_with_accept_encoding_deflate(), - test_get_1st_text_att_with_accept_encoding_deflate_only(), - test_get_doc_with_1st_text_att(), - test_1st_text_att_stub(). - -tests_for_1st_png_att() -> - test_get_1st_png_att_without_accept_encoding_header(), - test_get_1st_png_att_with_accept_encoding_gzip(), - test_get_1st_png_att_with_accept_encoding_deflate(), - test_get_doc_with_1st_png_att(), - test_1st_png_att_stub(). - -tests_for_2nd_text_att() -> - test_get_2nd_text_att_with_accept_encoding_gzip(), - test_get_2nd_text_att_without_accept_encoding_header(), - test_get_doc_with_2nd_text_att(), - test_2nd_text_att_stub(). - -tests_for_2nd_png_att() -> - test_get_2nd_png_att_without_accept_encoding_header(), - test_get_2nd_png_att_with_accept_encoding_gzip(), - test_get_doc_with_2nd_png_att(), - test_2nd_png_att_stub(). - -test_get_1st_text_att_with_accept_encoding_gzip() -> - {ok, Code, Headers, Body} = test_util:request( - db_url() ++ "/testdoc1/readme.txt", - [{"Accept-Encoding", "gzip"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Gziped = lists:member({"Content-Encoding", "gzip"}, Headers), - etap:is(Gziped, true, "received body is gziped"), - Uncompressed = zlib:gunzip(iolist_to_binary(Body)), - etap:is( - Uncompressed, - test_text_data(), - "received data for the 1st text attachment is ok" - ), - ok. - -test_get_1st_text_att_without_accept_encoding_header() -> - {ok, Code, Headers, Body} = test_util:request( - db_url() ++ "/testdoc1/readme.txt", - [], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Gziped = lists:member({"Content-Encoding", "gzip"}, Headers), - etap:is(Gziped, false, "received body is not gziped"), - etap:is( - iolist_to_binary(Body), - test_text_data(), - "received data for the 1st text attachment is ok" - ), - ok. - -test_get_1st_text_att_with_accept_encoding_deflate() -> - {ok, Code, Headers, Body} = test_util:request( - db_url() ++ "/testdoc1/readme.txt", - [{"Accept-Encoding", "deflate"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Gziped = lists:member({"Content-Encoding", "gzip"}, Headers), - etap:is(Gziped, false, "received body is not gziped"), - Deflated = lists:member({"Content-Encoding", "deflate"}, Headers), - etap:is(Deflated, false, "received body is not deflated"), - etap:is( - iolist_to_binary(Body), - test_text_data(), - "received data for the 1st text attachment is ok" - ), - ok. - -test_get_1st_text_att_with_accept_encoding_deflate_only() -> - {ok, Code, _Headers, _Body} = test_util:request( - db_url() ++ "/testdoc1/readme.txt", - [{"Accept-Encoding", "deflate, *;q=0"}], - get), - etap:is( - Code, - 406, - "HTTP response code is 406 for an unsupported content encoding request" - ), - ok. - -test_get_1st_png_att_without_accept_encoding_header() -> - {ok, Code, Headers, Body} = test_util:request( - db_url() ++ "/testdoc2/icon.png", - [], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Encoding = couch_util:get_value("Content-Encoding", Headers), - etap:is(Encoding, undefined, "received body is not gziped"), - etap:is( - iolist_to_binary(Body), - test_png_data(), - "received data for the 1st png attachment is ok" - ), - ok. - -test_get_1st_png_att_with_accept_encoding_gzip() -> - {ok, Code, Headers, Body} = test_util:request( - db_url() ++ "/testdoc2/icon.png", - [{"Accept-Encoding", "gzip"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Encoding = couch_util:get_value("Content-Encoding", Headers), - etap:is(Encoding, undefined, "received body is not gziped"), - etap:is( - iolist_to_binary(Body), - test_png_data(), - "received data for the 1st png attachment is ok" - ), - ok. - -test_get_1st_png_att_with_accept_encoding_deflate() -> - {ok, Code, Headers, Body} = test_util:request( - db_url() ++ "/testdoc2/icon.png", - [{"Accept-Encoding", "deflate"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Encoding = couch_util:get_value("Content-Encoding", Headers), - etap:is(Encoding, undefined, "received body is in identity form"), - etap:is( - iolist_to_binary(Body), - test_png_data(), - "received data for the 1st png attachment is ok" - ), - ok. - -test_get_doc_with_1st_text_att() -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/testdoc1?attachments=true", - [{"Accept", "application/json"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Json = ejson:decode(Body), - TextAttJson = couch_util:get_nested_json_value( - Json, - [<<"_attachments">>, <<"readme.txt">>] - ), - TextAttType = couch_util:get_nested_json_value( - TextAttJson, - [<<"content_type">>] - ), - TextAttData = couch_util:get_nested_json_value( - TextAttJson, - [<<"data">>] - ), - etap:is( - TextAttType, - <<"text/plain">>, - "1st text attachment has type text/plain" - ), - %% check the attachment's data is the base64 encoding of the plain text - %% and not the base64 encoding of the gziped plain text - etap:is( - TextAttData, - base64:encode(test_text_data()), - "1st text attachment data is properly base64 encoded" - ), - ok. - -test_1st_text_att_stub() -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/testdoc1?att_encoding_info=true", - [], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Json = ejson:decode(Body), - {TextAttJson} = couch_util:get_nested_json_value( - Json, - [<<"_attachments">>, <<"readme.txt">>] - ), - TextAttLength = couch_util:get_value(<<"length">>, TextAttJson), - etap:is( - TextAttLength, - byte_size(test_text_data()), - "1st text attachment stub length matches the uncompressed length" - ), - TextAttEncoding = couch_util:get_value(<<"encoding">>, TextAttJson), - etap:is( - TextAttEncoding, - <<"gzip">>, - "1st text attachment stub has the encoding field set to gzip" - ), - TextAttEncLength = couch_util:get_value(<<"encoded_length">>, TextAttJson), - etap:is( - TextAttEncLength, - iolist_size(zlib:gzip(test_text_data())), - "1st text attachment stub encoded_length matches the compressed length" - ), - ok. - -test_get_doc_with_1st_png_att() -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/testdoc2?attachments=true", - [{"Accept", "application/json"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Json = ejson:decode(Body), - PngAttJson = couch_util:get_nested_json_value( - Json, - [<<"_attachments">>, <<"icon.png">>] - ), - PngAttType = couch_util:get_nested_json_value( - PngAttJson, - [<<"content_type">>] - ), - PngAttData = couch_util:get_nested_json_value( - PngAttJson, - [<<"data">>] - ), - etap:is(PngAttType, <<"image/png">>, "attachment has type image/png"), - etap:is( - PngAttData, - base64:encode(test_png_data()), - "1st png attachment data is properly base64 encoded" - ), - ok. - -test_1st_png_att_stub() -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/testdoc2?att_encoding_info=true", - [{"Accept", "application/json"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Json = ejson:decode(Body), - {PngAttJson} = couch_util:get_nested_json_value( - Json, - [<<"_attachments">>, <<"icon.png">>] - ), - PngAttLength = couch_util:get_value(<<"length">>, PngAttJson), - etap:is( - PngAttLength, - byte_size(test_png_data()), - "1st png attachment stub length matches the uncompressed length" - ), - PngEncoding = couch_util:get_value(<<"encoding">>, PngAttJson), - etap:is( - PngEncoding, - undefined, - "1st png attachment stub doesn't have an encoding field" - ), - PngEncLength = couch_util:get_value(<<"encoded_length">>, PngAttJson), - etap:is( - PngEncLength, - undefined, - "1st png attachment stub doesn't have an encoded_length field" - ), - ok. - -test_get_2nd_text_att_with_accept_encoding_gzip() -> - {ok, Code, Headers, Body} = test_util:request( - db_url() ++ "/testdoc3/readme.txt", - [{"Accept-Encoding", "gzip"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Gziped = lists:member({"Content-Encoding", "gzip"}, Headers), - etap:is(Gziped, true, "received body is gziped"), - Uncompressed = zlib:gunzip(iolist_to_binary(Body)), - etap:is( - Uncompressed, - test_text_data(), - "received data for the 2nd text attachment is ok" - ), - ok. - -test_get_2nd_text_att_without_accept_encoding_header() -> - {ok, Code, Headers, Body} = test_util:request( - db_url() ++ "/testdoc3/readme.txt", - [], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Gziped = lists:member({"Content-Encoding", "gzip"}, Headers), - etap:is(Gziped, false, "received body is not gziped"), - etap:is( - Body, - test_text_data(), - "received data for the 2nd text attachment is ok" - ), - ok. - -test_get_2nd_png_att_without_accept_encoding_header() -> - {ok, Code, Headers, Body} = test_util:request( - db_url() ++ "/testdoc4/icon.png", - [], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Gziped = lists:member({"Content-Encoding", "gzip"}, Headers), - etap:is(Gziped, false, "received body is not gziped"), - etap:is( - Body, - test_png_data(), - "received data for the 2nd png attachment is ok" - ), - ok. - -test_get_2nd_png_att_with_accept_encoding_gzip() -> - {ok, Code, Headers, Body} = test_util:request( - db_url() ++ "/testdoc4/icon.png", - [{"Accept-Encoding", "gzip"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Gziped = lists:member({"Content-Encoding", "gzip"}, Headers), - etap:is(Gziped, false, "received body is not gziped"), - etap:is( - Body, - test_png_data(), - "received data for the 2nd png attachment is ok" - ), - ok. - -test_get_doc_with_2nd_text_att() -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/testdoc3?attachments=true", - [{"Accept", "application/json"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Json = ejson:decode(Body), - TextAttJson = couch_util:get_nested_json_value( - Json, - [<<"_attachments">>, <<"readme.txt">>] - ), - TextAttType = couch_util:get_nested_json_value( - TextAttJson, - [<<"content_type">>] - ), - TextAttData = couch_util:get_nested_json_value( - TextAttJson, - [<<"data">>] - ), - etap:is(TextAttType, <<"text/plain">>, "attachment has type text/plain"), - %% check the attachment's data is the base64 encoding of the plain text - %% and not the base64 encoding of the gziped plain text - etap:is( - TextAttData, - base64:encode(test_text_data()), - "2nd text attachment data is properly base64 encoded" - ), - ok. - -test_2nd_text_att_stub() -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/testdoc3?att_encoding_info=true", - [], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Json = ejson:decode(Body), - {TextAttJson} = couch_util:get_nested_json_value( - Json, - [<<"_attachments">>, <<"readme.txt">>] - ), - TextAttLength = couch_util:get_value(<<"length">>, TextAttJson), - etap:is( - TextAttLength, - byte_size(test_text_data()), - "2nd text attachment stub length matches the uncompressed length" - ), - TextAttEncoding = couch_util:get_value(<<"encoding">>, TextAttJson), - etap:is( - TextAttEncoding, - <<"gzip">>, - "2nd text attachment stub has the encoding field set to gzip" - ), - TextAttEncLength = couch_util:get_value(<<"encoded_length">>, TextAttJson), - etap:is( - TextAttEncLength, - iolist_size(zlib:gzip(test_text_data())), - "2nd text attachment stub encoded_length matches the compressed length" - ), - ok. - -test_get_doc_with_2nd_png_att() -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/testdoc4?attachments=true", - [{"Accept", "application/json"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Json = ejson:decode(Body), - PngAttJson = couch_util:get_nested_json_value( - Json, - [<<"_attachments">>, <<"icon.png">>] - ), - PngAttType = couch_util:get_nested_json_value( - PngAttJson, - [<<"content_type">>] - ), - PngAttData = couch_util:get_nested_json_value( - PngAttJson, - [<<"data">>] - ), - etap:is(PngAttType, <<"image/png">>, "attachment has type image/png"), - etap:is( - PngAttData, - base64:encode(test_png_data()), - "2nd png attachment data is properly base64 encoded" - ), - ok. - -test_2nd_png_att_stub() -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/testdoc4?att_encoding_info=true", - [], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Json = ejson:decode(Body), - {PngAttJson} = couch_util:get_nested_json_value( - Json, - [<<"_attachments">>, <<"icon.png">>] - ), - PngAttLength = couch_util:get_value(<<"length">>, PngAttJson), - etap:is( - PngAttLength, - byte_size(test_png_data()), - "2nd png attachment stub length matches the uncompressed length" - ), - PngEncoding = couch_util:get_value(<<"encoding">>, PngAttJson), - etap:is( - PngEncoding, - undefined, - "2nd png attachment stub doesn't have an encoding field" - ), - PngEncLength = couch_util:get_value(<<"encoded_length">>, PngAttJson), - etap:is( - PngEncLength, - undefined, - "2nd png attachment stub doesn't have an encoded_length field" - ), - ok. - -test_already_compressed_att(DocUri, AttName) -> - test_get_already_compressed_att_with_accept_gzip(DocUri, AttName), - test_get_already_compressed_att_without_accept(DocUri, AttName), - test_get_already_compressed_att_stub(DocUri, AttName). - -test_get_already_compressed_att_with_accept_gzip(DocUri, AttName) -> - {ok, Code, Headers, Body} = test_util:request( - DocUri ++ "/" ++ AttName, - [{"Accept-Encoding", "gzip"}], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Gziped = lists:member({"Content-Encoding", "gzip"}, Headers), - etap:is(Gziped, true, "received body is gziped"), - etap:is( - Body, - zlib:gzip(test_text_data()), - "received data for the already compressed attachment is ok" - ), - ok. - -test_get_already_compressed_att_without_accept(DocUri, AttName) -> - {ok, Code, Headers, Body} = test_util:request( - DocUri ++ "/" ++ AttName, - [], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Gziped = lists:member({"Content-Encoding", "gzip"}, Headers), - etap:is(Gziped, false, "received body is not gziped"), - etap:is( - Body, - test_text_data(), - "received data for the already compressed attachment is ok" - ), - ok. - -test_get_already_compressed_att_stub(DocUri, AttName) -> - {ok, Code, _Headers, Body} = test_util:request( - DocUri ++ "?att_encoding_info=true", - [], - get), - etap:is(Code, 200, "HTTP response code is 200"), - Json = ejson:decode(Body), - {AttJson} = couch_util:get_nested_json_value( - Json, - [<<"_attachments">>, iolist_to_binary(AttName)] - ), - AttLength = couch_util:get_value(<<"length">>, AttJson), - etap:is( - AttLength, - iolist_size((zlib:gzip(test_text_data()))), - "Already compressed attachment stub length matches the " - "compressed length" - ), - Encoding = couch_util:get_value(<<"encoding">>, AttJson), - etap:is( - Encoding, - <<"gzip">>, - "Already compressed attachment stub has the encoding field set to gzip" - ), - EncLength = couch_util:get_value(<<"encoded_length">>, AttJson), - etap:is( - EncLength, - AttLength, - "Already compressed attachment stub encoded_length matches the " - "length field value" - ), - ok. - -test_create_already_compressed_att_with_invalid_content_encoding( - DocUri, AttName, AttData, Encoding) -> - {ok, Code, _Headers, _Body} = test_util:request( - DocUri ++ "/" ++ AttName, - [{"Content-Encoding", Encoding}, {"Content-Type", "text/plain"}], - put, - AttData), - etap:is( - Code, - 415, - "Couldn't create an already compressed attachment using the " - "unsupported encoding '" ++ Encoding ++ "'" - ), - ok. - -test_compressible_type_with_parameters() -> - {ok, Code, _Headers, _Body} = test_util:request( - db_url() ++ "/testdoc5/readme.txt", - [{"Content-Type", "text/plain; charset=UTF-8"}], - put, - test_text_data()), - etap:is(Code, 201, "Created text attachment with MIME type " - "'text/plain; charset=UTF-8' using the standalone api"), - {ok, Code2, Headers2, Body} = test_util:request( - db_url() ++ "/testdoc5/readme.txt", - [{"Accept-Encoding", "gzip"}], - get), - etap:is(Code2, 200, "HTTP response code is 200"), - Gziped = lists:member({"Content-Encoding", "gzip"}, Headers2), - etap:is(Gziped, true, "received body is gziped"), - Uncompressed = zlib:gunzip(iolist_to_binary(Body)), - etap:is(Uncompressed, test_text_data(), "received data is gzipped"), - {ok, Code3, _Headers3, Body3} = test_util:request( - db_url() ++ "/testdoc5?att_encoding_info=true", - [], - get), - etap:is(Code3, 200, "HTTP response code is 200"), - Json = ejson:decode(Body3), - {TextAttJson} = couch_util:get_nested_json_value( - Json, - [<<"_attachments">>, <<"readme.txt">>] - ), - TextAttLength = couch_util:get_value(<<"length">>, TextAttJson), - etap:is( - TextAttLength, - byte_size(test_text_data()), - "text attachment stub length matches the uncompressed length" - ), - TextAttEncoding = couch_util:get_value(<<"encoding">>, TextAttJson), - etap:is( - TextAttEncoding, - <<"gzip">>, - "text attachment stub has the encoding field set to gzip" - ), - TextAttEncLength = couch_util:get_value(<<"encoded_length">>, TextAttJson), - etap:is( - TextAttEncLength, - iolist_size(zlib:gzip(test_text_data())), - "text attachment stub encoded_length matches the compressed length" - ), - ok. - -test_png_data() -> - {ok, Data} = file:read_file( - test_util:source_file("share/www/image/logo.png") - ), - Data. - -test_text_data() -> - {ok, Data} = file:read_file( - test_util:source_file("README.rst") - ), - Data. diff --git a/test/etap/150-invalid-view-seq.t b/test/etap/150-invalid-view-seq.t deleted file mode 100755 index a2ac22cfd87..00000000000 --- a/test/etap/150-invalid-view-seq.t +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --record(user_ctx, { - name = null, - roles = [], - handler -}). - -test_db_name() -> - <<"couch_test_invalid_view_seq">>. - -main(_) -> - test_util:run(10, fun() -> test() end). - -%% NOTE: since during the test we stop the server, -%% a huge and ugly but harmless stack trace is sent to stderr -%% -test() -> - test_util:start_couch(), - timer:sleep(1000), - delete_db(), - create_db(), - - create_docs(), - create_design_doc(), - - % make DB file backup - backup_db_file(), - - put(addr, config:get("httpd", "bind_address", "127.0.0.1")), - put(port, integer_to_list(mochiweb_socket_server:get(couch_httpd, port))), - - create_new_doc(), - query_view_before_restore_backup(), - - % restore DB file backup after querying view - restore_backup_db_file(), - - query_view_after_restore_backup(), - - delete_db(), - ok. - -admin_user_ctx() -> - {user_ctx, #user_ctx{roles=[<<"_admin">>]}}. - -create_db() -> - {ok, _} = couch_db:create(test_db_name(), [admin_user_ctx()]). - -delete_db() -> - couch_server:delete(test_db_name(), [admin_user_ctx()]). - -create_docs() -> - {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]), - 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() -> - {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]), - DDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"_design/foo">>}, - {<<"language">>, <<"javascript">>}, - {<<"views">>, {[ - {<<"bar">>, {[ - {<<"map">>, <<"function(doc) { emit(doc.value, 1); }">>} - ]}} - ]}} - ]}), - {ok, _} = couch_db:update_docs(Db, [DDoc]), - couch_db:ensure_full_commit(Db), - couch_db:close(Db). - -backup_db_file() -> - DbFile = test_util:build_file("tmp/lib/" ++ - binary_to_list(test_db_name()) ++ ".couch"), - {ok, _} = file:copy(DbFile, DbFile ++ ".backup"), - ok. - -create_new_doc() -> - {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]), - 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). - -db_url() -> - "http://" ++ get(addr) ++ ":" ++ get(port) ++ "/" ++ - binary_to_list(test_db_name()). - -query_view_before_restore_backup() -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/_design/foo/_view/bar", [], get), - etap:is(Code, 200, "Got view response before restoring backup."), - ViewJson = ejson:decode(Body), - Rows = couch_util:get_nested_json_value(ViewJson, [<<"rows">>]), - HasDoc1 = has_doc("doc1", Rows), - HasDoc2 = has_doc("doc2", Rows), - HasDoc3 = has_doc("doc3", Rows), - HasDoc666 = has_doc("doc666", Rows), - etap:is(HasDoc1, true, "Before backup restore, view has doc1"), - etap:is(HasDoc2, true, "Before backup restore, view has doc2"), - etap:is(HasDoc3, true, "Before backup restore, view has doc3"), - etap:is(HasDoc666, true, "Before backup restore, view has doc666"), - ok. - -has_doc(DocId1, Rows) -> - DocId = iolist_to_binary(DocId1), - lists:any( - fun({R}) -> lists:member({<<"id">>, DocId}, R) end, - Rows - ). - -restore_backup_db_file() -> - ok = test_util:stop_couch(), - timer:sleep(3000), - DbFile = test_util:build_file("tmp/lib/" ++ - binary_to_list(test_db_name()) ++ ".couch"), - ok = file:delete(DbFile), - ok = file:rename(DbFile ++ ".backup", DbFile), - ok = test_util:start_couch(), - timer:sleep(1000), - put(port, integer_to_list(mochiweb_socket_server:get(couch_httpd, port))), - ok. - -query_view_after_restore_backup() -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/_design/foo/_view/bar", [], get), - etap:is(Code, 200, "Got view response after restoring backup."), - ViewJson = ejson:decode(Body), - Rows = couch_util:get_nested_json_value(ViewJson, [<<"rows">>]), - HasDoc1 = has_doc("doc1", Rows), - HasDoc2 = has_doc("doc2", Rows), - HasDoc3 = has_doc("doc3", Rows), - HasDoc666 = has_doc("doc666", Rows), - etap:is(HasDoc1, true, "After backup restore, view has doc1"), - etap:is(HasDoc2, true, "After backup restore, view has doc2"), - etap:is(HasDoc3, true, "After backup restore, view has doc3"), - etap:is(HasDoc666, false, "After backup restore, view does not have doc666"), - ok. diff --git a/test/etap/160-vhosts.t b/test/etap/160-vhosts.t deleted file mode 100755 index c83d359afc4..00000000000 --- a/test/etap/160-vhosts.t +++ /dev/null @@ -1,371 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --record(user_ctx, { - name = null, - roles = [], - handler -}). - -server() -> - lists:concat([ - "http://127.0.0.1:", mochiweb_socket_server:get(couch_httpd, port), "/" - ]). - -dbname() -> "etap-test-db". -admin_user_ctx() -> {user_ctx, #user_ctx{roles=[<<"_admin">>]}}. - -main(_) -> - test_util:init_code_path(), - - etap:plan(20), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - ok = test_util:start_couch(), - ibrowse:start(), - crypto:start(), - - timer:sleep(1000), - couch_server:delete(list_to_binary(dbname()), [admin_user_ctx()]), - {ok, Db} = couch_db:create(list_to_binary(dbname()), [admin_user_ctx()]), - - 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), - - %% end boilerplate, start test - - ok = config:set("vhosts", "example.com", "/etap-test-db", false), - ok = config:set("vhosts", "*.example.com", - "/etap-test-db/_design/doc1/_rewrite", false), - ok = config:set("vhosts", "example.com/test", "/etap-test-db", false), - ok = config:set("vhosts", "example1.com", - "/etap-test-db/_design/doc1/_rewrite/", false), - ok = config:set("vhosts",":appname.:dbname.example1.com", - "/:dbname/_design/:appname/_rewrite/", false), - ok = config:set("vhosts", ":dbname.example1.com", "/:dbname", false), - - ok = config:set("vhosts", "*.example2.com", "/*", false), - ok = config:set("vhosts", "*.example2.com/test", "/*", false), - ok = config:set("vhosts", "*/test", "/etap-test-db", false), - ok = config:set("vhosts", "*/test1", - "/etap-test-db/_design/doc1/_show/test", false), - ok = config:set("vhosts", "example3.com", "/", false), - - %% reload rules - couch_httpd_vhost:reload(), - - test_regular_request(), - test_vhost_request(), - test_vhost_request_with_qs(), - test_vhost_request_with_global(), - test_vhost_requested_path(), - test_vhost_requested_path_path(), - test_vhost_request_wildcard(), - test_vhost_request_replace_var(), - test_vhost_request_replace_var1(), - test_vhost_request_replace_wildcard(), - test_vhost_request_path(), - test_vhost_request_path1(), - test_vhost_request_path2(), - test_vhost_request_path3(), - test_vhost_request_to_root(), - test_vhost_request_with_oauth(Db), - - %% restart boilerplate - couch_db:close(Db), - ok = couch_server:delete(couch_db:name(Db), [admin_user_ctx()]), - timer:sleep(3000), - ok = test_util:stop_couch(), - - ok. - -test_regular_request() -> - case ibrowse:send_req(server(), [], get, []) of - {ok, _, _, Body} -> - {Props} = ejson:decode(Body), - Couchdb = couch_util:get_value(<<"couchdb">>, Props), - Version = couch_util:get_value(<<"version">>, Props), - Vendor = couch_util:get_value(<<"vendor">>, Props), - etap:isnt(Couchdb, undefined, "Found couchdb property"), - etap:isnt(Version, undefined, "Found version property"), - etap:isnt(Vendor, undefined, "Found vendor property"); - _Else -> - etap:bail("http GET / request failed") - end. - -test_vhost_request() -> - case ibrowse:send_req(server(), [], get, [], [{host_header, "example.com"}]) of - {ok, _, _, Body} -> - {JsonBody} = ejson:decode(Body), - HasDbNameInfo = proplists:is_defined(<<"db_name">>, JsonBody), - etap:is(HasDbNameInfo, true, "should return database info"); - _Else -> - etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_with_qs() -> - Url = server() ++ "doc1?revs_info=true", - case ibrowse:send_req(Url, [], get, [], [{host_header, "example.com"}]) of - {ok, _, _, Body} -> - {JsonProps} = ejson:decode(Body), - HasRevsInfo = proplists:is_defined(<<"_revs_info">>, JsonProps), - etap:is(HasRevsInfo, true, "should return _revs_info"); - _Else -> - etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_with_global() -> - Url2 = server() ++ "_utils/index.html", - case ibrowse:send_req(Url2, [], get, [], [{host_header, "example.com"}]) of - {ok, _, _, Body2} -> - " - etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_requested_path() -> - case ibrowse:send_req(server(), [], get, [], [{host_header, "example1.com"}]) of - {ok, _, _, Body} -> - {Json} = ejson:decode(Body), - etap:is(case proplists:get_value(<<"requested_path">>, Json) of - <<"/">> -> true; - _ -> false - end, true, <<"requested path in req ok">>); - _Else -> - etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_requested_path_path() -> - case ibrowse:send_req(server(), [], get, [], [{host_header, "example1.com"}]) of - {ok, _, _, Body} -> - {Json} = ejson:decode(Body), - etap:is(case proplists:get_value(<<"path">>, Json) of - <<"/etap-test-db/_design/doc1/_show/test">> -> true; - _ -> false - end, true, <<"path in req ok">>); - _Else -> - etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_wildcard()-> - case ibrowse:send_req(server(), [], get, [], [{host_header, "test.example.com"}]) of - {ok, _, _, Body} -> - {Json} = ejson:decode(Body), - etap:is(case proplists:get_value(<<"path">>, Json) of - <<"/etap-test-db/_design/doc1/_show/test">> -> true; - _ -> false - end, true, <<"wildcard ok">>); - _Else -> etap:is(false, true, <<"ibrowse fail">>) - end. - - -test_vhost_request_replace_var() -> - case ibrowse:send_req(server(), [], get, [], [{host_header,"etap-test-db.example1.com"}]) of - {ok, _, _, Body} -> - {JsonBody} = ejson:decode(Body), - HasDbNameInfo = proplists:is_defined(<<"db_name">>, JsonBody), - etap:is(HasDbNameInfo, true, "should return database info"); - _Else -> etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_replace_var1() -> - case ibrowse:send_req(server(), [], get, [], [{host_header, "doc1.etap-test-db.example1.com"}]) of - {ok, _, _, Body} -> - {Json} = ejson:decode(Body), - etap:is(case proplists:get_value(<<"path">>, Json) of - <<"/etap-test-db/_design/doc1/_show/test">> -> true; - _ -> false - end, true, <<"wildcard ok">>); - _Else -> etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_replace_wildcard() -> - case ibrowse:send_req(server(), [], get, [], [{host_header,"etap-test-db.example2.com"}]) of - {ok, _, _, Body} -> - {JsonBody} = ejson:decode(Body), - HasDbNameInfo = proplists:is_defined(<<"db_name">>, JsonBody), - etap:is(HasDbNameInfo, true, "should return database info"); - _Else -> etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_path() -> - Uri = server() ++ "test", - case ibrowse:send_req(Uri, [], get, [], [{host_header, "example.com"}]) of - {ok, _, _, Body} -> - {JsonBody} = ejson:decode(Body), - HasDbNameInfo = proplists:is_defined(<<"db_name">>, JsonBody), - etap:is(HasDbNameInfo, true, "should return database info"); - _Else -> etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_path1() -> - Url = server() ++ "test/doc1?revs_info=true", - case ibrowse:send_req(Url, [], get, [], []) of - {ok, _, _, Body} -> - {JsonProps} = ejson:decode(Body), - HasRevsInfo = proplists:is_defined(<<"_revs_info">>, JsonProps), - etap:is(HasRevsInfo, true, "should return _revs_info"); - _Else -> etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_path2() -> - Uri = server() ++ "test", - case ibrowse:send_req(Uri, [], get, [], [{host_header,"etap-test-db.example2.com"}]) of - {ok, _, _, Body} -> - {JsonBody} = ejson:decode(Body), - HasDbNameInfo = proplists:is_defined(<<"db_name">>, JsonBody), - etap:is(HasDbNameInfo, true, "should return database info"); - _Else -> etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_path3() -> - Uri = server() ++ "test1", - case ibrowse:send_req(Uri, [], get, [], []) of - {ok, _, _, Body} -> - {Json} = ejson:decode(Body), - etap:is(case proplists:get_value(<<"path">>, Json) of - <<"/etap-test-db/_design/doc1/_show/test">> -> true; - _ -> false - end, true, <<"path in req ok">>); - _Else -> etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_to_root() -> - Uri = server(), - case ibrowse:send_req(Uri, [], get, [], []) of - {ok, _, _, Body} -> - {JsonBody} = ejson:decode(Body), - HasCouchDBWelcome = proplists:is_defined(<<"couchdb">>, JsonBody), - etap:is(HasCouchDBWelcome, true, "should allow redirect to /"); - _Else -> etap:is(false, true, <<"ibrowse fail">>) - end. - -test_vhost_request_with_oauth(Db) -> - {ok, AuthDb} = couch_db:create( - <<"tap_test_sec_db">>, [admin_user_ctx(), overwrite]), - PrevAuthDbName = config:get("couch_httpd_auth", "authentication_db"), - config:set("couch_httpd_auth", "authentication_db", "tap_test_sec_db", 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), - - DDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"_design/test">>}, - {<<"language">>, <<"javascript">>}, - {<<"rewrites">>, [ - {[ - {<<"from">>, <<"foobar">>}, - {<<"to">>, <<"_info">>} - ]} - ]} - ]}), - {ok, _} = couch_db:update_doc(Db, DDoc, []), - - RewritePath = "/etap-test-db/_design/test/_rewrite/foobar", - ok = config:set("vhosts", "oauth-example.com", RewritePath, false), - couch_httpd_vhost:reload(), - - case ibrowse:send_req(server(), [], get, [], [{host_header, "oauth-example.com"}]) of - {ok, "401", _, Body} -> - {JsonBody} = ejson:decode(Body), - etap:is( - couch_util:get_value(<<"error">>, JsonBody), - <<"unauthorized">>, - "Request without OAuth credentials failed"); - Error -> - etap:bail("Request without OAuth credentials did not fail: " ++ - couch_util:to_list(Error)) - end, - - JoeDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"org.couchdb.user:joe">>}, - {<<"type">>, <<"user">>}, - {<<"name">>, <<"joe">>}, - {<<"roles">>, []}, - {<<"password_sha">>, <<"fe95df1ca59a9b567bdca5cbaf8412abd6e06121">>}, - {<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4001a73">>} - ]}), - {ok, _} = couch_db:update_doc(AuthDb, JoeDoc, []), - - Url = "http://oauth-example.com/", - Consumer = {"consec1", "foo", hmac_sha1}, - SignedParams = oauth:sign( - "GET", Url, [], Consumer, "otoksec1", "foobar"), - OAuthUrl = oauth:uri(server(), SignedParams), - - case ibrowse:send_req(OAuthUrl, [], get, [], [{host_header, "oauth-example.com"}]) of - {ok, "200", _, Body2} -> - {JsonBody2} = ejson:decode(Body2), - etap:is(couch_util:get_value(<<"name">>, JsonBody2), <<"test">>, - "should return ddoc info with OAuth credentials"); - Error2 -> - etap:bail("Failed to access vhost with OAuth credentials: " ++ - couch_util:to_list(Error2)) - end, - - Consumer2 = {"consec1", "bad_secret", hmac_sha1}, - SignedParams2 = oauth:sign( - "GET", Url, [], Consumer2, "otoksec1", "foobar"), - OAuthUrl2 = oauth:uri(server(), SignedParams2), - - case ibrowse:send_req(OAuthUrl2, [], get, [], [{host_header, "oauth-example.com"}]) of - {ok, "401", _, Body3} -> - {JsonBody3} = ejson:decode(Body3), - etap:is( - couch_util:get_value(<<"error">>, JsonBody3), - <<"unauthorized">>, - "Request with bad OAuth credentials failed"); - Error3 -> - etap:bail("Failed to access vhost with bad OAuth credentials: " ++ - couch_util:to_list(Error3)) - end, - - config:set("couch_httpd_auth", "authentication_db", PrevAuthDbName, false), - config:set("couch_httpd_auth", "require_valid_user", "false", false), - ok = couch_server:delete(couch_db:name(AuthDb), [admin_user_ctx()]). diff --git a/test/etap/170-os-daemons.es b/test/etap/170-os-daemons.es deleted file mode 100755 index 73974e905a7..00000000000 --- a/test/etap/170-os-daemons.es +++ /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(). diff --git a/test/etap/170-os-daemons.t b/test/etap/170-os-daemons.t deleted file mode 100755 index f1961a92293..00000000000 --- a/test/etap/170-os-daemons.t +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --record(daemon, { - port, - name, - cmd, - kill, - status=running, - cfg_patterns=[], - errors=[], - buf=[] -}). - -config_files() -> - lists:map(fun test_util:build_file/1, [ - "etc/couchdb/default_dev.ini" - ]). - -daemon_cmd() -> - test_util:source_file("test/etap/170-os-daemons.es"). - -main(_) -> - test_util:init_code_path(), - - etap:plan(49), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - application:start(config), - couch_os_daemons:start_link(), - - etap:diag("Daemons boot after configuration added."), - config:set("os_daemons", "foo", daemon_cmd(), false), - timer:sleep(1000), - - {ok, [D1]} = couch_os_daemons:info([table]), - check_daemon(D1, "foo"), - - % Check table form - {ok, Tab1} = couch_os_daemons:info(), - [T1] = ets:tab2list(Tab1), - check_daemon(T1, "foo"), - - etap:diag("Daemons stop after configuration removed."), - config:delete("os_daemons", "foo", false), - timer:sleep(500), - - {ok, []} = couch_os_daemons:info([table]), - {ok, Tab2} = couch_os_daemons:info(), - etap:is(ets:tab2list(Tab2), [], "As table returns empty table."), - - etap:diag("Adding multiple daemons causes both to boot."), - config:set("os_daemons", "bar", daemon_cmd(), false), - config:set("os_daemons", "baz", daemon_cmd(), false), - timer:sleep(500), - {ok, Daemons} = couch_os_daemons:info([table]), - lists:foreach(fun(D) -> - check_daemon(D) - end, Daemons), - - {ok, Tab3} = couch_os_daemons:info(), - lists:foreach(fun(D) -> - check_daemon(D) - end, ets:tab2list(Tab3)), - - etap:diag("Removing one daemon leaves the other alive."), - config:delete("os_daemons", "bar", false), - timer:sleep(500), - - {ok, [D2]} = couch_os_daemons:info([table]), - check_daemon(D2, "baz"), - - % Check table version - {ok, Tab4} = couch_os_daemons:info(), - [T4] = ets:tab2list(Tab4), - check_daemon(T4, "baz"), - - ok. - -check_daemon(D) -> - check_daemon(D, D#daemon.name). - -check_daemon(D, Name) -> - BaseName = "170-os-daemons.es", - BaseLen = length(BaseName), - CmdLen = length(D#daemon.cmd), - CmdName = lists:sublist(D#daemon.cmd, CmdLen-BaseLen+1, BaseLen), - - etap:is(is_port(D#daemon.port), true, "Daemon port is a port."), - etap:is(D#daemon.name, Name, "Daemon name was set correctly."), - etap:is(CmdName, BaseName, "Command name was set correctly."), - etap:isnt(D#daemon.kill, undefined, "Kill command was set."), - etap:is(D#daemon.errors, [], "No errors occurred while booting."), - etap:is(D#daemon.buf, [], "No extra data left in the buffer."). diff --git a/test/etap/171-os-daemons-config.es b/test/etap/171-os-daemons-config.es deleted file mode 100755 index 15da0e9a904..00000000000 --- a/test/etap/171-os-daemons-config.es +++ /dev/null @@ -1,85 +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. - -filename() -> - list_to_binary(test_util:source_file("test/etap/171-os-daemons-config.es")). - -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() -> - FileName = filename(), - {[{<<"foo">>, FileName}]} = get_cfg(<<"os_daemons">>). - -test_get_cfg2() -> - FileName = filename(), - FileName = get_cfg(<<"os_daemons">>, <<"foo">>), - <<"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([]) -> - test_util:init_code_path(), - application:start(config), - couch_drv:start_link(), - do_tests(). diff --git a/test/etap/171-os-daemons-config.t b/test/etap/171-os-daemons-config.t deleted file mode 100755 index c8348d5aa43..00000000000 --- a/test/etap/171-os-daemons-config.t +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --record(daemon, { - port, - name, - cmd, - kill, - status=running, - cfg_patterns=[], - errors=[], - buf=[] -}). - -config_files() -> - lists:map(fun test_util:build_file/1, [ - "etc/couchdb/default_dev.ini" - ]). - -daemon_cmd() -> - test_util:source_file("test/etap/171-os-daemons-config.es"). - -main(_) -> - test_util:init_code_path(), - - etap:plan(6), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - test_util:start_couch(), - config:set("log", "level", "debug", false), - - % "foo" is a required name by this test. - config:set("os_daemons", "foo", daemon_cmd(), false), - timer:sleep(1000), - - {ok, [D1]} = couch_os_daemons:info([table]), - check_daemon(D1, "foo"), - - ok. - -check_daemon(D, Name) -> - BaseName = "171-os-daemons-config.es", - BaseLen = length(BaseName), - CmdLen = length(D#daemon.cmd), - CmdName = lists:sublist(D#daemon.cmd, CmdLen-BaseLen+1, BaseLen), - - etap:is(is_port(D#daemon.port), true, "Daemon port is a port."), - etap:is(D#daemon.name, Name, "Daemon name was set correctly."), - etap:is(CmdName, BaseName, "Command name was set correctly."), - etap:isnt(D#daemon.kill, undefined, "Kill command was set."), - etap:is(D#daemon.errors, [], "No errors occurred while booting."), - etap:is(D#daemon.buf, [], "No extra data left in the buffer."). diff --git a/test/etap/172-os-daemon-errors.1.sh b/test/etap/172-os-daemon-errors.1.sh deleted file mode 100644 index 345c8b40b26..00000000000 --- a/test/etap/172-os-daemon-errors.1.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 diff --git a/test/etap/172-os-daemon-errors.2.sh b/test/etap/172-os-daemon-errors.2.sh deleted file mode 100755 index 256ee793596..00000000000 --- a/test/etap/172-os-daemon-errors.2.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 diff --git a/test/etap/172-os-daemon-errors.3.sh b/test/etap/172-os-daemon-errors.3.sh deleted file mode 100755 index f5a13684ecd..00000000000 --- a/test/etap/172-os-daemon-errors.3.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 diff --git a/test/etap/172-os-daemon-errors.4.sh b/test/etap/172-os-daemon-errors.4.sh deleted file mode 100755 index 5bc10e83f16..00000000000 --- a/test/etap/172-os-daemon-errors.4.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 diff --git a/test/etap/172-os-daemon-errors.t b/test/etap/172-os-daemon-errors.t deleted file mode 100755 index 1b1fc3023e6..00000000000 --- a/test/etap/172-os-daemon-errors.t +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --record(daemon, { - port, - name, - cmd, - kill, - status=running, - cfg_patterns=[], - errors=[], - buf=[] -}). - -config_files() -> - lists:map(fun test_util:build_file/1, [ - "etc/couchdb/default_dev.ini" - ]). - -bad_perms() -> - test_util:source_file("test/etap/172-os-daemon-errors.1.sh"). - -die_on_boot() -> - test_util:source_file("test/etap/172-os-daemon-errors.2.sh"). - -die_quickly() -> - test_util:source_file("test/etap/172-os-daemon-errors.3.sh"). - -can_reboot() -> - test_util:source_file("test/etap/172-os-daemon-errors.4.sh"). - -main(_) -> - test_util:init_code_path(), - - etap:plan(36), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - application:start(config), - couch_os_daemons:start_link(), - - etap:diag("Daemon not executable."), - test_halts("foo", bad_perms(), 1000), - - etap:diag("Daemon dies on boot."), - test_halts("bar", die_on_boot(), 1000), - - etap:diag("Daemon dies quickly after boot."), - test_halts("baz", die_quickly(), 4000), - - etap:diag("Daemon dies, but not quickly enough to be halted."), - test_runs("bam", can_reboot()), - - ok. - -test_halts(Name, Cmd, Time) -> - config:set("os_daemons", Name, Cmd ++ " 2> /dev/null", false), - timer:sleep(Time), - {ok, [D]} = couch_os_daemons:info([table]), - check_dead(D, Name, Cmd), - config:delete("os_daemons", Name, false). - -test_runs(Name, Cmd) -> - config:set("os_daemons", Name, Cmd, false), - - timer:sleep(1000), - {ok, [D1]} = couch_os_daemons:info([table]), - check_daemon(D1, Name, Cmd, 0), - - % Should reboot every two seconds. We're at 1s, so wait - % utnil 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, Name, Cmd, 1), - - % If the kill command changed, that means we rebooted the process. - etap:isnt(D1#daemon.kill, D2#daemon.kill, "Kill command changed."). - -check_dead(D, Name, Cmd) -> - BaseName = filename:basename(Cmd) ++ " 2> /dev/null", - BaseLen = length(BaseName), - CmdLen = length(D#daemon.cmd), - CmdName = lists:sublist(D#daemon.cmd, CmdLen-BaseLen+1, BaseLen), - - etap:is(is_port(D#daemon.port), true, "Daemon port is a port."), - etap:is(D#daemon.name, Name, "Daemon name was set correctly."), - etap:is(CmdName, BaseName, "Command name was set correctly."), - etap:isnt(D#daemon.kill, undefined, "Kill command was set."), - etap:is(D#daemon.status, halted, "Daemon has been halted."), - etap:is(D#daemon.errors, nil, "Errors have been disabled."), - etap:is(D#daemon.buf, nil, "Buffer has been switched off."). - -check_daemon(D, Name, Cmd, Errs) -> - BaseName = filename:basename(Cmd), - BaseLen = length(BaseName), - CmdLen = length(D#daemon.cmd), - CmdName = lists:sublist(D#daemon.cmd, CmdLen-BaseLen+1, BaseLen), - - etap:is(is_port(D#daemon.port), true, "Daemon port is a port."), - etap:is(D#daemon.name, Name, "Daemon name was set correctly."), - etap:is(CmdName, BaseName, "Command name was set correctly."), - etap:isnt(D#daemon.kill, undefined, "Kill command was set."), - etap:is(D#daemon.status, running, "Daemon still running."), - etap:is(length(D#daemon.errors), Errs, "Found expected number of errors."), - etap:is(D#daemon.buf, [], "No extra data left in the buffer."). - diff --git a/test/etap/173-os-daemon-cfg-register.t b/test/etap/173-os-daemon-cfg-register.t deleted file mode 100755 index 065e999c518..00000000000 --- a/test/etap/173-os-daemon-cfg-register.t +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --record(daemon, { - port, - name, - cmd, - kill, - status=running, - cfg_patterns=[], - errors=[], - buf=[] -}). - -daemon_name() -> - "wheee". - -daemon_cmd() -> - test_util:build_file("test/etap/test_cfg_register"). - -main(_) -> - test_util:init_code_path(), - - etap:plan(27), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - application:start(config), - couch_os_daemons:start_link(), - - DaemonCmd = daemon_cmd() ++ " 2> /dev/null", - - etap:diag("Booting the daemon"), - config:set("os_daemons", daemon_name(), DaemonCmd, false), - wait_for_start(10), - {ok, [D1]} = couch_os_daemons:info([table]), - check_daemon(D1, running), - - etap:diag("Daemon restarts when section changes."), - config:set("s1", "k", "foo", false), - wait_for_restart(10), - {ok, [D2]} = couch_os_daemons:info([table]), - check_daemon(D2, running), - etap:isnt(D2#daemon.kill, D1#daemon.kill, "Kill command shows restart."), - - etap:diag("Daemon doesn't restart for ignored section key."), - config:set("s2", "k2", "baz", false), - timer:sleep(1000), % Message travel time. - {ok, [D3]} = couch_os_daemons:info([table]), - etap:is(D3, D2, "Same daemon info after ignored config change."), - - etap:diag("Daemon restarts for specific section/key pairs."), - config:set("s2", "k", "bingo", false), - wait_for_restart(10), - {ok, [D4]} = couch_os_daemons:info([table]), - check_daemon(D4, running), - etap:isnt(D4#daemon.kill, D3#daemon.kill, "Kill command changed again."), - - ok. - -wait_for_start(0) -> - throw({error, wait_for_start}); -wait_for_start(N) -> - case couch_os_daemons:info([table]) of - {ok, []} -> - timer:sleep(200), - wait_for_start(N-1); - _ -> - timer:sleep(1000) - end. - -wait_for_restart(0) -> - throw({error, wait_for_restart}); -wait_for_restart(N) -> - {ok, [D]} = couch_os_daemons:info([table]), - case D#daemon.status of - restarting -> - timer:sleep(200), - wait_for_restart(N-1); - _ -> - timer:sleep(1000) - end. - -check_daemon(D, Status) -> - BaseName = filename:basename(daemon_cmd()) ++ " 2> /dev/null", - BaseLen = length(BaseName), - CmdLen = length(D#daemon.cmd), - CmdName = lists:sublist(D#daemon.cmd, CmdLen-BaseLen+1, BaseLen), - - etap:is(is_port(D#daemon.port), true, "Daemon port is a port."), - etap:is(D#daemon.name, daemon_name(), "Daemon name was set correctly."), - etap:is(CmdName, BaseName, "Command name was set correctly."), - etap:isnt(D#daemon.kill, undefined, "Kill command was set."), - etap:is(D#daemon.status, Status, "Daemon status is correct."), - etap:is(D#daemon.cfg_patterns, [{"s1"}, {"s2", "k"}], "Cfg patterns set"), - etap:is(D#daemon.errors, [], "No errors have occurred."), - etap:isnt(D#daemon.buf, nil, "Buffer is active."). diff --git a/test/etap/180-http-proxy.ini b/test/etap/180-http-proxy.ini deleted file mode 100644 index 3e2ba137944..00000000000 --- a/test/etap/180-http-proxy.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. - -; 49151 is IANA Reserved, let's assume no one is listening there -[httpd_global_handlers] -_error = {couch_httpd_proxy, handle_proxy_req, <<"http://127.0.0.1:49151/">>} diff --git a/test/etap/180-http-proxy.t b/test/etap/180-http-proxy.t deleted file mode 100755 index 4897ad649e0..00000000000 --- a/test/etap/180-http-proxy.t +++ /dev/null @@ -1,366 +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. - --record(req, {method=get, path="", headers=[], body="", opts=[]}). - -server() -> - lists:concat([ - "http://127.0.0.1:", - mochiweb_socket_server:get(couch_httpd, port), - "/_test/" - ]). - -proxy() -> - "http://127.0.0.1:" ++ integer_to_list(test_web:get_port()) ++ "/". - -external() -> "https://www.google.com/". - -main(_) -> - test_util:run(61, fun() -> test() end). - -check_request(Name, 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() ++ 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 - ), - %etap:diag("ibrowse response: ~p", [Resp]), - case Local of - no_local -> ok; - _ -> etap:fun_is(Local, Resp, Name) - end, - case {Remote, Local} of - {no_remote, _} -> - ok; - {_, no_local} -> - ok; - _ -> - etap:is(test_web:check_last(), was_ok, Name ++ " - request handled") - end, - Resp. - -test() -> - ExtraConfig = [test_util:source_file("test/etap/180-http-proxy.ini")], - ok = test_util:start_couch(test_util:config_files() ++ ExtraConfig), - ibrowse:start(), - crypto:start(), - - % start the test_web server on a random port - test_web:start_link(), - Url = lists:concat([ - "{couch_httpd_proxy, handle_proxy_req, <<\"http://127.0.0.1:", - test_web:get_port(), - "/\">>}" - ]), - config:set("httpd_global_handlers", "_test", Url, false), - - % let couch_httpd restart - timer:sleep(100), - - test_basic(), - test_alternate_status(), - test_trailing_slash(), - test_passes_header(), - test_passes_host_header(), - test_passes_header_back(), - test_rewrites_location_headers(), - test_doesnt_rewrite_external_locations(), - test_rewrites_relative_location(), - test_uses_same_version(), - test_passes_body(), - test_passes_eof_body_back(), - test_passes_chunked_body(), - test_passes_chunked_body_back(), - - test_connect_error(), - - ok. - -test_basic() -> - 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, - check_request("Basic proxy test", #req{}, Remote, Local). - -test_alternate_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"}, - check_request("Alternate status", Req, Remote, Local). - -test_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/"}, - check_request("Trailing slash", Req, Remote, Local). - -test_passes_header() -> - 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"}] - }, - check_request("Passes header", Req, Remote, Local). - -test_passes_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"}] - }, - check_request("Passes host header", Req, Remote, Local). - -test_passes_header_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"}, - check_request("Passes header back", Req, Remote, Local). - -test_rewrites_location_headers() -> - etap:diag("Testing location header rewrites."), - do_rewrite_tests([ - {"Location", proxy() ++ "foo/bar", server() ++ "foo/bar"}, - {"Content-Location", proxy() ++ "bing?q=2", server() ++ "bing?q=2"}, - {"Uri", proxy() ++ "zip#frag", server() ++ "zip#frag"}, - {"Destination", proxy(), server()} - ]). - -test_doesnt_rewrite_external_locations() -> - etap:diag("Testing no rewrite of external locations."), - do_rewrite_tests([ - {"Location", external() ++ "search", external() ++ "search"}, - {"Content-Location", external() ++ "s?q=2", external() ++ "s?q=2"}, - {"Uri", external() ++ "f#f", external() ++ "f#f"}, - {"Destination", external() ++ "f?q=2#f", external() ++ "f?q=2#f"} - ]). - -test_rewrites_relative_location() -> - etap:diag("Testing relative rewrites."), - do_rewrite_tests([ - {"Location", "/foo", server() ++ "foo"}, - {"Content-Location", "bar", server() ++ "bar"}, - {"Uri", "/zing?q=3", server() ++ "zing?q=3"}, - {"Destination", "bing?q=stuff#yay", server() ++ "bing?q=stuff#yay"} - ]). - -do_rewrite_tests(Tests) -> - lists:foreach(fun({Header, Location, Url}) -> - do_rewrite_test(Header, Location, Url) - end, Tests). - -do_rewrite_test(Header, Location, Url) -> - Remote = fun(Req) -> - "/rewrite_test" = Req:get(path), - {ok, {302, [{Header, Location}], "ok"}} - end, - Local = fun - ({ok, "302", Headers, "ok"}) -> - etap:is( - couch_util:get_value(Header, Headers), - Url, - "Header rewritten correctly." - ), - true; - (_) -> - false - end, - Req = #req{path="rewrite_test"}, - Label = "Rewrite test for ", - check_request(Label ++ Header, Req, Remote, Local). - -test_uses_same_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}}] - }, - check_request("Uses same version", Req, Remote, Local). - -test_passes_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!" - }, - check_request("Passes body", Req, Remote, Local). - -test_passes_eof_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"}, - check_request("Passes eof body", Req, Remote, Local). - -test_passes_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=mk_chunked_body(BodyChunks) - }, - check_request("Passes chunked body", Req, Remote, Local). - -test_passes_chunked_body_back() -> - Name = "Passes chunked body back", - 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(Name, Req, Remote, no_local), - - etap:fun_is( - fun({ibrowse_req_id, _}) -> true; (_) -> false end, - Resp, - "Received an ibrowse request id." - ), - {_, ReqId} = Resp, - - % Grab headers from response - receive - {ibrowse_async_headers, ReqId, "200", Headers} -> - etap:is( - proplists:get_value("Transfer-Encoding", Headers), - "chunked", - "Response included the Transfer-Encoding: chunked header" - ), - ibrowse:stream_next(ReqId) - after 1000 -> - throw({error, timeout}) - end, - - % Check body received - % TODO: When we upgrade to ibrowse >= 2.0.0 this check needs to - % check that the chunks returned are what we sent from the - % Remote test. - etap:diag("TODO: UPGRADE IBROWSE"), - etap:is(recv_body(ReqId, []), <<"foobarbazinga">>, "Decoded chunked body."), - - % Check test_web server. - etap:is(test_web:check_last(), was_ok, Name ++ " - request handled"). - -test_connect_error() -> - Local = fun({ok, "500", _Headers, _Body}) -> true; (_) -> false end, - Url = lists:concat([ - "http://127.0.0.1:", - mochiweb_socket_server:get(couch_httpd, port), - "/_error" - ]), - Req = #req{opts=[{url, Url}]}, - check_request("Connect error", Req, no_remote, Local). - - -mk_chunked_body(Chunks) -> - mk_chunked_body(Chunks, []). - -mk_chunked_body([], Acc) -> - iolist_to_binary(lists:reverse(Acc, "0\r\n\r\n")); -mk_chunked_body([Chunk | Rest], Acc) -> - Size = to_hex(size(Chunk)), - mk_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 5000 -> - throw({error, timeout}) - end. diff --git a/test/etap/190-json-stream-parse.t b/test/etap/190-json-stream-parse.t deleted file mode 100755 index 49ea58f8333..00000000000 --- a/test/etap/190-json-stream-parse.t +++ /dev/null @@ -1,184 +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. - -main(_) -> - test_util:init_code_path(), - etap:plan(99), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag("Test died abnormally: ~p", [Other]), - etap:bail("Bad return value.") - end, - ok. - -test() -> - crypto:start(), - ok = test_raw_json_input(), - ok = test_1_byte_data_function(), - ok = test_multiple_bytes_data_function(). - - -test_raw_json_input() -> - etap:diag("Tests with raw JSON string as the input."), - lists:foreach( - fun({EJson, JsonString, Desc}) -> - etap:is( - equiv(EJson, json_stream_parse:to_ejson(JsonString)), - true, - Desc) - end, - cases()), - ok. - - -test_1_byte_data_function() -> - etap:diag("Tests with a 1 byte output data function as the input."), - lists:foreach( - fun({EJson, JsonString, Desc}) -> - DataFun = fun() -> single_byte_data_fun(JsonString) end, - etap:is( - equiv(EJson, json_stream_parse:to_ejson(DataFun)), - true, - Desc) - end, - cases()), - ok. - - -test_multiple_bytes_data_function() -> - etap:diag("Tests with a multiple bytes output data function as the input."), - lists:foreach( - fun({EJson, JsonString, Desc}) -> - DataFun = fun() -> multiple_bytes_data_fun(JsonString) end, - etap:is( - equiv(EJson, json_stream_parse:to_ejson(DataFun)), - true, - Desc) - end, - cases()), - ok. - - -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"} - ]. - - -%% 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]) -> - {<>, 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]). diff --git a/test/etap/200-view-group-no-db-leaks.t b/test/etap/200-view-group-no-db-leaks.t deleted file mode 100755 index 4cf23b1e90c..00000000000 --- a/test/etap/200-view-group-no-db-leaks.t +++ /dev/null @@ -1,271 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --record(user_ctx, { - name = null, - roles = [], - handler -}). - --record(db, { - main_pid = nil, - compactor_pid = nil, - instance_start_time, % number of microsecs since jan 1 1970 as a binary string - fd, - fd_monitor, - header = nil, - committed_update_seq, - id_tree, - seq_tree, - local_tree, - update_seq, - name, - filepath, - validate_doc_funs = [], - security = [], - security_ptr = nil, - user_ctx = #user_ctx{}, - waiting_delayed_commit = nil, - revs_limit = 1000, - fsync_options = [], - options = [], - compression, - before_doc_update, - after_doc_read -}). - -test_db_name() -> <<"couch_test_view_group_db_leaks">>. -ddoc_name() -> <<"foo">>. - -main(_) -> - test_util:init_code_path(), - - etap:plan(25), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - ok = test_util:start_couch(), - timer:sleep(1000), - put(addr, config:get("httpd", "bind_address", "127.0.0.1")), - put(port, integer_to_list(mochiweb_socket_server:get(couch_httpd, port))), - - delete_db(), - create_db(), - - create_docs(), - {ok, DDocRev} = create_design_doc(), - - {ok, IndexerPid} = couch_index_server:get_index( - couch_mrview_index, test_db_name(), <<"_design/", (ddoc_name())/binary>> - ), - etap:is(is_pid(IndexerPid), true, "got view group pid"), - etap:is(is_process_alive(IndexerPid), true, "view group pid is alive"), - - query_view(3, null, false), - check_db_monitor(), - etap:is(is_process_alive(IndexerPid), true, "view group pid is alive"), - - create_new_doc(<<"doc1000">>), - query_view(4, null, false), - check_db_monitor(), - etap:is(is_process_alive(IndexerPid), true, "view group pid is alive"), - - compact_db(), - check_db_monitor(), - etap:is(is_process_alive(IndexerPid), true, "view group pid is alive"), - - compact_view_group(), - check_db_monitor(), - etap:is(is_process_alive(IndexerPid), true, "view group pid is alive"), - - create_new_doc(<<"doc1001">>), - query_view(5, null, false), - check_db_monitor(), - etap:is(is_process_alive(IndexerPid), true, "view group pid is alive"), - - etap:diag("updating the design document with a new view definition"), - {ok, _NewDDocRev} = update_ddoc_view(DDocRev), - - {ok, NewIndexerPid} = couch_index_server:get_index( - couch_mrview_index, test_db_name(), <<"_design/", (ddoc_name())/binary>> - ), - etap:is(is_pid(NewIndexerPid), true, "got new view group pid"), - etap:is(is_process_alive(NewIndexerPid), true, "new view group pid is alive"), - etap:isnt(NewIndexerPid, IndexerPid, "new view group has a different pid"), - etap:diag("querying view with ?stale=ok, must return empty row set"), - query_view(0, foo, ok), - etap:diag("querying view (without stale), must return 5 rows with value 1"), - query_view(5, 1, false), - MonRef = erlang:monitor(process, IndexerPid), - receive - {'DOWN', MonRef, _, _, _} -> - etap:diag("old view group is dead after ddoc update") - after 5000 -> - etap:bail("old view group is not dead after ddoc update") - end, - - etap:diag("deleting database"), - MonRef2 = erlang:monitor(process, NewIndexerPid), - ok = couch_server:delete(test_db_name(), []), - receive - {'DOWN', MonRef2, _, _, _} -> - etap:diag("new view group is dead after DB deletion") - after 5000 -> - etap:bail("new view group did not die after DB deletion") - end, - - ok = timer:sleep(1000), - delete_db(), - ok = test_util:stop_couch(), - ok. - -admin_user_ctx() -> - {user_ctx, #user_ctx{roles=[<<"_admin">>]}}. - -create_db() -> - {ok, #db{main_pid = Pid} = Db} = couch_db:create( - test_db_name(), [admin_user_ctx()]), - put(db_main_pid, Pid), - ok = couch_db:close(Db). - -delete_db() -> - couch_server:delete(test_db_name(), [admin_user_ctx()]). - -compact_db() -> - {ok, Db} = couch_db:open_int(test_db_name(), []), - {ok, _} = couch_db:start_compact(Db), - couch_db:wait_for_compaction(Db, 5000), - ok = couch_db:close(Db). - -compact_view_group() -> - DDoc = list_to_binary("_design/" ++ binary_to_list(ddoc_name())), - {ok, Ref} = couch_mrview:compact(test_db_name(), DDoc, [monitor]), - receive {'DOWN', Ref, _, _, _} -> - ok - after 5000 -> - etap:bail("View group compaction failed to finish.") - end. - -check_db_monitor() -> - {ok, #db{fd=Fd} = Db} = couch_db:open_int(test_db_name(), []), - ok = couch_db:close(Db), - {monitored_by, Monitors} = process_info(Fd, monitored_by), - if length(Monitors) == 2 -> ok; true -> - etap:diag("Monitors: ~p ~p", [self(), Monitors]), - lists:foreach(fun(P) -> - etap:diag("~n~n======~n~p~n-----", [P]), - etap:diag("Stack:~n~s", [element(2, process_info(P, backtrace))]) - end, Monitors) - end, - etap:is(length(Monitors), 2, - "DB fd is only monitored by couch_db_updater and couch_stats_collector"), - ok. - -create_docs() -> - {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]), - 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() -> - {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]), - DDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"_design/", (ddoc_name())/binary>>}, - {<<"language">>, <<"javascript">>}, - {<<"views">>, {[ - {<<"bar">>, {[ - {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>} - ]}} - ]}} - ]}), - {ok, Rev} = couch_db:update_doc(Db, DDoc, []), - couch_db:ensure_full_commit(Db), - couch_db:close(Db), - {ok, Rev}. - -update_ddoc_view(DDocRev) -> - {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]), - DDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"_design/", (ddoc_name())/binary>>}, - {<<"_rev">>, couch_doc:rev_to_str(DDocRev)}, - {<<"language">>, <<"javascript">>}, - {<<"views">>, {[ - {<<"bar">>, {[ - {<<"map">>, <<"function(doc) { emit(doc._id, 1); }">>} - ]}} - ]}} - ]}), - {ok, NewRev} = couch_db:update_doc(Db, DDoc, []), - couch_db:ensure_full_commit(Db), - couch_db:close(Db), - {ok, NewRev}. - -create_new_doc(Id) -> - {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]), - Doc666 = couch_doc:from_json_obj({[ - {<<"_id">>, Id}, - {<<"value">>, 999} - ]}), - {ok, _} = couch_db:update_docs(Db, [Doc666]), - couch_db:ensure_full_commit(Db), - couch_db:close(Db). - -db_url() -> - "http://" ++ get(addr) ++ ":" ++ get(port) ++ "/" ++ - binary_to_list(test_db_name()). - -query_view(ExpectedRowCount, ExpectedRowValue, Stale) -> - {ok, Code, _Headers, Body} = test_util:request( - db_url() ++ "/_design/" ++ binary_to_list(ddoc_name()) ++ "/_view/bar" - ++ case Stale of - false -> []; - _ -> "?stale=" ++ atom_to_list(Stale) - end, - [{"Connection", "close"}], - get), - etap:is(Code, 200, "got view response"), - {Props} = ejson:decode(Body), - Rows = couch_util:get_value(<<"rows">>, Props, []), - etap:is(length(Rows), ExpectedRowCount, "result set has correct # of rows"), - lists:foreach( - fun({Row}) -> - case couch_util:get_value(<<"value">>, Row) of - ExpectedRowValue -> - ok; - _ -> - etap:bail("row has incorrect value") - end - end, - Rows). diff --git a/test/etap/201-view-group-shutdown.t b/test/etap/201-view-group-shutdown.t deleted file mode 100755 index f374888c8e8..00000000000 --- a/test/etap/201-view-group-shutdown.t +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --record(user_ctx, { - name = null, - roles = [], - handler -}). - --record(db, { - main_pid = nil, - compactor_pid = nil, - instance_start_time, % number of microsecs since jan 1 1970 as a binary string - fd, - fd_monitor, - header = nil, - committed_update_seq, - id_tree, - seq_tree, - local_tree, - update_seq, - name, - filepath, - validate_doc_funs = [], - security = [], - security_ptr = nil, - user_ctx = #user_ctx{}, - waiting_delayed_commit = nil, - revs_limit = 1000, - fsync_options = [], - options = [], - compression, - before_doc_update, - after_doc_read -}). - -main_db_name() -> <<"couch_test_view_group_shutdown">>. - - -main(_) -> - test_util:init_code_path(), - - etap:plan(17), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - - -test() -> - ok = test_util:start_couch(), - ok = config:set("couchdb", "max_dbs_open", "3", false), - ok = config:set("couchdb", "delayed_commits", "false", false), - crypto:start(), - - % Test that while a view group is being compacted its database can not - % be closed by the database LRU system. - test_view_group_compaction(), - - ok = test_util:stop_couch(), - ok. - - -test_view_group_compaction() -> - {ok, DbWriter3} = create_db(<<"couch_test_view_group_shutdown_w3">>), - ok = couch_db:close(DbWriter3), - - {ok, MainDb} = create_main_db(), - ok = couch_db:close(MainDb), - - {ok, DbWriter1} = create_db(<<"couch_test_view_group_shutdown_w1">>), - ok = couch_db:close(DbWriter1), - - {ok, DbWriter2} = create_db(<<"couch_test_view_group_shutdown_w2">>), - ok = couch_db:close(DbWriter2), - - Writer1 = spawn_writer(DbWriter1#db.name), - Writer2 = spawn_writer(DbWriter2#db.name), - etap:is(is_process_alive(Writer1), true, "Spawned writer 1"), - etap:is(is_process_alive(Writer2), true, "Spawned writer 2"), - - etap:is(get_writer_status(Writer1), ok, "Writer 1 opened his database"), - etap:is(get_writer_status(Writer2), ok, "Writer 2 opened his database"), - - {ok, MonRef} = couch_mrview:compact(MainDb#db.name, <<"_design/foo">>, [monitor]), - - % Add some more docs to database and trigger view update - {ok, MainDb2} = couch_db:open_int(MainDb#db.name, []), - ok = populate_main_db(MainDb2, 3, 3), - update_view(MainDb2#db.name, <<"_design/foo">>, <<"foo">>), - ok = couch_db:close(MainDb2), - - % Assuming the view compaction takes more than 50ms to complete - ok = timer:sleep(50), - Writer3 = spawn_writer(DbWriter3#db.name), - etap:is(is_process_alive(Writer3), true, "Spawned writer 3"), - - etap:is(get_writer_status(Writer3), {error, all_dbs_active}, - "Writer 3 got {error, all_dbs_active} when opening his database"), - - etap:is(is_process_alive(Writer1), true, "Writer 1 still alive"), - etap:is(is_process_alive(Writer2), true, "Writer 2 still alive"), - etap:is(is_process_alive(Writer3), true, "Writer 3 still alive"), - - receive - {'DOWN', MonRef, process, _, normal} -> - etap:diag("View group compaction successful"), - ok; - {'DOWN', MonRef, process, _, _Reason} -> - etap:bail("Failure compacting view group") - end, - - ok = timer:sleep(2000), - - etap:is(writer_try_again(Writer3), ok, - "Told writer 3 to try open his database again"), - etap:is(get_writer_status(Writer3), ok, - "Writer 3 was able to open his database"), - - etap:is(is_process_alive(Writer1), true, "Writer 1 still alive"), - etap:is(is_process_alive(Writer2), true, "Writer 2 still alive"), - etap:is(is_process_alive(Writer3), true, "Writer 3 still alive"), - - etap:is(stop_writer(Writer1), ok, "Stopped writer 1"), - etap:is(stop_writer(Writer2), ok, "Stopped writer 2"), - etap:is(stop_writer(Writer3), ok, "Stopped writer 3"), - - delete_db(MainDb), - delete_db(DbWriter1), - delete_db(DbWriter2), - delete_db(DbWriter3). - - -create_main_db() -> - {ok, Db} = create_db(main_db_name()), - 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); }">>} - ]}}, - {<<"foo4">>, {[ - {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>} - ]}}, - {<<"foo5">>, {[ - {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>} - ]}} - ]}} - ]}), - {ok, _} = couch_db:update_doc(Db, DDoc, []), - ok = populate_main_db(Db, 1000, 20000), - update_view(Db#db.name, <<"_design/foo">>, <<"foo">>), - {ok, Db}. - - -populate_main_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_main_db(Db, BatchSize, N - length(Docs)); -populate_main_db(_Db, _, _) -> - ok. - - -update_view(DbName, DDocName, ViewName) -> - {ok, Db} = couch_db:open_int(DbName, []), - {ok, DDoc} = couch_db:open_doc(Db, DDocName, [ejson_body]), - couch_mrview:query_view(Db, DDoc, ViewName, [{stale, false}]), - ok = couch_db:close(Db), - etap:diag("View group updated"). - - -create_db(DbName) -> - {ok, Db} = couch_db:create( - DbName, - [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}, overwrite]), - {ok, Db}. - - -delete_db(#db{name = DbName, main_pid = Pid}) -> - ok = couch_server:delete( - DbName, [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}]), - MonRef = erlang:monitor(process, Pid), - receive - {'DOWN', MonRef, process, Pid, _Reason} -> - ok - after 30000 -> - etap:bail("Timeout deleting database") - 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 5000 -> - timeout - end. - - -writer_try_again(Writer) -> - Ref = make_ref(), - Writer ! {try_again, Ref}, - receive - {ok, Ref} -> - ok - after 5000 -> - timeout - end. - - -stop_writer(Writer) -> - Ref = make_ref(), - Writer ! {stop, Ref}, - receive - {ok, Ref} -> - ok - after 5000 -> - etap:bail("Timeout stopping writer process") - end. - - -% Just keep the database open, no need to actually do something on it. -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. diff --git a/test/etap/210-os-proc-pool.t b/test/etap/210-os-proc-pool.t deleted file mode 100755 index 20c45e4c6d5..00000000000 --- a/test/etap/210-os-proc-pool.t +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -% 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. - -main(_) -> - test_util:init_code_path(), - - etap:plan(21), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - - -test() -> - ok = test_util:start_couch(), - config:set("query_server_config", "os_process_limit", "3", false), - - test_pool_full(), - test_client_unexpected_exit(), - - ok = test_util:stop_couch(), - ok. - - -test_pool_full() -> - Client1 = spawn_client(), - Client2 = spawn_client(), - Client3 = spawn_client(), - - etap:diag("Check that we can spawn the max number of processes."), - etap:is(ping_client(Client1), ok, "Client 1 started ok."), - etap:is(ping_client(Client2), ok, "Client 2 started ok."), - etap:is(ping_client(Client3), ok, "Client 3 started ok."), - - Proc1 = get_client_proc(Client1, "1"), - Proc2 = get_client_proc(Client2, "2"), - Proc3 = get_client_proc(Client3, "3"), - etap:isnt(Proc1, Proc2, "Clients 1 and 2 got different procs."), - etap:isnt(Proc2, Proc3, "Clients 2 and 3 got different procs."), - etap:isnt(Proc1, Proc3, "Clients 1 and 3 got different procs."), - - etap:diag("Check that client 4 blocks waiting for a process."), - Client4 = spawn_client(), - etap:is(ping_client(Client4), timeout, "Client 4 blocked while waiting."), - - etap:diag("Check that stopping a client gives up its process."), - etap:is(stop_client(Client1), ok, "First client stopped."), - - etap:diag("And check that our blocked process has been unblocked."), - etap:is(ping_client(Client4), ok, "Client was unblocked."), - - Proc4 = get_client_proc(Client4, "4"), - etap:is(Proc4, Proc1, "Client 4 got proc that client 1 got before."), - - lists:map(fun(C) -> ok = stop_client(C) end, [Client2, Client3, Client4]). - - -test_client_unexpected_exit() -> - Client1 = spawn_client(), - Client2 = spawn_client(), - Client3 = spawn_client(), - - etap:diag("Check that up to os_process_limit clients started."), - etap:is(ping_client(Client1), ok, "Client 1 started ok."), - etap:is(ping_client(Client2), ok, "Client 2 started ok."), - etap:is(ping_client(Client3), ok, "Client 3 started ok."), - - Proc1 = get_client_proc(Client1, "1"), - Proc2 = get_client_proc(Client2, "2"), - Proc3 = get_client_proc(Client3, "3"), - etap:isnt(Proc1, Proc2, "Clients 1 and 2 got different procs."), - etap:isnt(Proc2, Proc3, "Clients 2 and 3 got different procs."), - etap:isnt(Proc1, Proc3, "Clients 1 and 3 got different procs."), - - etap:diag("Check that killing a client frees an os_process."), - etap:is(kill_client(Client1), ok, "Client 1 died all right."), - - etap:diag("Check that a new client is not blocked on boot."), - Client4 = spawn_client(), - etap:is(ping_client(Client4), ok, "New client booted without blocking."), - - Proc4 = get_client_proc(Client4, "4"), - etap:isnt(Proc4, Proc1, - "Client 4 got a proc different from the one client 1 got before."), - etap:isnt(Proc4, Proc2, "Client 4's proc different from client 2's proc."), - etap:isnt(Proc4, Proc3, "Client 4's proc different from client 3's proc."), - - lists:map(fun(C) -> ok = stop_client(C) end, [Client2, Client3, Client4]). - - -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 3000 -> timeout - end. - - -get_client_proc({Pid, Ref}, ClientName) -> - Pid ! get_proc, - receive - {proc, Ref, Proc} -> Proc - after 3000 -> - etap:bail("Timeout getting client " ++ ClientName ++ " proc.") - end. - - -stop_client({Pid, Ref}) -> - Pid ! stop, - receive - {stop, Ref} -> ok - after 3000 -> timeout - end. - - -kill_client({Pid, Ref}) -> - Pid ! die, - receive - {die, Ref} -> ok - after 3000 -> 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. diff --git a/test/etap/220-compaction-daemon.t b/test/etap/220-compaction-daemon.t deleted file mode 100755 index 262113dd8dd..00000000000 --- a/test/etap/220-compaction-daemon.t +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --record(user_ctx, { - name = null, - roles = [], - handler -}). - -test_db_name() -> - <<"couch_test_compaction_daemon">>. - -main(_) -> - test_util:init_code_path(), - - etap:plan(10), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - ok = test_util:start_couch(), - timer:sleep(1000), - put(addr, config:get("httpd", "bind_address", "127.0.0.1")), - put(port, integer_to_list(mochiweb_socket_server:get(couch_httpd, port))), - - disable_compact_daemon(), - - delete_db(), - {ok, Db} = create_db(), - - add_design_doc(Db), - couch_db:close(Db), - populate(70, 70, 200 * 1024), - - {_, DbFileSize} = get_db_frag(), - {_, ViewFileSize} = get_view_frag(), - - % enable automatic compaction - ok = config:set("compaction_daemon", "check_interval", "3", false), - ok = config:set("compaction_daemon", "min_file_size", "100000", false), - ok = config:set( - "compactions", - binary_to_list(test_db_name()), - "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"70%\"}]", - false), - - ok = timer:sleep(4000), % something >= check_interval - wait_compaction_finished(), - - {DbFrag2, DbFileSize2} = get_db_frag(), - {ViewFrag2, ViewFileSize2} = get_view_frag(), - - etap:is(true, (DbFrag2 < 70), "Database fragmentation is < 70% after compaction"), - etap:is(true, (ViewFrag2 < 70), "View fragmentation is < 70% after compaction"), - etap:is(true, (DbFileSize2 < DbFileSize), "Database file size decreased"), - etap:is(true, (ViewFileSize2 < ViewFileSize), "View file size decreased"), - - disable_compact_daemon(), - ok = timer:sleep(6000), % 2 times check_interval - etap:is(couch_db:is_idle(Db), true, "Database is idle"), - populate(70, 70, 200 * 1024), - {_, DbFileSize3} = get_db_frag(), - {_, ViewFileSize3} = get_view_frag(), - - % enable automatic compaction - ok = config:set( - "compactions", - "_default", - "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"70%\"}]", - false), - - ok = timer:sleep(4000), % something >= check_interval - wait_compaction_finished(), - - {DbFrag4, DbFileSize4} = get_db_frag(), - {ViewFrag4, ViewFileSize4} = get_view_frag(), - - etap:is(true, (DbFrag4 < 70), "Database fragmentation is < 70% after compaction"), - etap:is(true, (ViewFrag4 < 70), "View fragmentation is < 70% after compaction"), - etap:is(true, (DbFileSize4 < DbFileSize3), "Database file size decreased again"), - etap:is(true, (ViewFileSize4 < ViewFileSize3), "View file size decreased again"), - - ok = timer:sleep(6000), % 2 times check_interval - etap:is(couch_db:is_idle(Db), true, "Database is idle"), - - delete_db(), - ok = test_util:stop_couch(), - ok. - -disable_compact_daemon() -> - Configs = config:get("compactions"), - lists:foreach( - fun({DbName, _}) -> - ok = config:delete("compactions", DbName, false) - end, - Configs). - -admin_user_ctx() -> - {user_ctx, #user_ctx{roles = [<<"_admin">>]}}. - -create_db() -> - {ok, _} = couch_db:create(test_db_name(), [admin_user_ctx()]). - -delete_db() -> - couch_server:delete(test_db_name(), [admin_user_ctx()]). - -add_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(DbFrag, ViewFrag, MinFileSize) -> - {CurDbFrag, DbFileSize} = get_db_frag(), - {CurViewFrag, ViewFileSize} = get_view_frag(), - populate( - DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag, - lists:min([DbFileSize, ViewFileSize])). - -populate(DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag, FileSize) - when CurDbFrag >= DbFrag, CurViewFrag >= ViewFrag, FileSize >= MinFileSize -> - ok; -populate(DbFrag, ViewFrag, MinFileSize, _, _, _) -> - update(), - {CurDbFrag, DbFileSize} = get_db_frag(), - {CurViewFrag, ViewFileSize} = get_view_frag(), - populate( - DbFrag, ViewFrag, MinFileSize, CurDbFrag, CurViewFrag, - lists:min([DbFileSize, ViewFileSize])). - -update() -> - {ok, Db} = couch_db:open_int(test_db_name(), []), - lists:foreach(fun(_) -> - Doc = couch_doc:from_json_obj({[{<<"_id">>, couch_uuids:new()}]}), - {ok, _} = couch_db:update_docs(Db, [Doc]), - query_view() - end, lists:seq(1, 100)), - couch_db:close(Db). - -db_url() -> - "http://" ++ get(addr) ++ ":" ++ get(port) ++ "/" ++ - binary_to_list(test_db_name()). - -query_view() -> - {ok, Code, _Headers, _Body} = test_util:request( - db_url() ++ "/_design/foo/_view/foo", [], get), - case Code of - 200 -> - ok; - _ -> - etap:bail("error querying view") - end. - -get_db_frag() -> - {ok, Db} = couch_db:open_int(test_db_name(), []), - {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() -> - {ok, Db} = couch_db:open_int(test_db_name(), []), - {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() -> - Parent = self(), - Loop = spawn_link(fun() -> wait_loop(Parent) end), - receive - {done, Loop} -> - etap:diag("Database and view compaction have finished") - after 60000 -> - etap:bail("Compaction not triggered") - end. - -wait_loop(Parent) -> - {ok, Db} = couch_db:open_int(test_db_name(), []), - {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(500), - wait_loop(Parent) - end. diff --git a/test/etap/230-pbkfd2.t b/test/etap/230-pbkfd2.t deleted file mode 100644 index d980ef6981d..00000000000 --- a/test/etap/230-pbkfd2.t +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -main(_) -> - test_util:init_code_path(), - etap:plan(6), - etap:is(couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 1, 20), - {ok, <<"0c60c80f961f0e71f3a9b524af6012062fe037a6">>}, - "test vector #1"), - etap:is(couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 2, 20), - {ok, <<"ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957">>}, - "test vector #2"), - etap:is(couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 4096, 20), - {ok, <<"4b007901b765489abead49d926f721d065a429c1">>}, - "test vector #3"), - etap:is(couch_passwords:pbkdf2(<<"passwordPASSWORDpassword">>, - <<"saltSALTsaltSALTsaltSALTsaltSALTsalt">>, 4096, 25), - {ok, <<"3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038">>}, - "test vector #4"), - etap:is(couch_passwords:pbkdf2(<<"pass\0word">>, <<"sa\0lt">>, 4096, 16), - {ok, <<"56fa6aa75548099dcc37d7f03425e0c3">>}, - "test vector #5"), - etap:is(couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 16777216, 20), - {ok, <<"eefe3d61cd4da4e4e9945b3d6ba2158c2634e984">>}, - "test vector #6"), - etap:end_tests(). diff --git a/test/etap/231-cors.t b/test/etap/231-cors.t deleted file mode 100644 index 0eb0252aad8..00000000000 --- a/test/etap/231-cors.t +++ /dev/null @@ -1,430 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - --record(user_ctx, { - name = null, - roles = [], - handler -}). - - --define(SUPPORTED_METHODS, "GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, COPY, OPTIONS"). -server() -> - lists:concat([ - "http://127.0.0.1:", - mochiweb_socket_server:get(couch_httpd, port), - "/" - ]). - - -main(_) -> - test_util:init_code_path(), - - etap:plan(29), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -dbname() -> "etap-test-db". -dbname1() -> "etap-test-db1". -dbname2() -> "etap-test-db2". - -admin_user_ctx() -> {user_ctx, #user_ctx{roles=[<<"_admin">>]}}. - -set_admin_password(UserName, Password) -> - Hashed = couch_passwords:hash_admin_password(Password), - config:set("admins", UserName, Hashed, false). - -cycle_db(DbName) -> - couch_server:delete(list_to_binary(DbName), [admin_user_ctx()]), - {ok, Db} = couch_db:create(list_to_binary(DbName), [admin_user_ctx()]), - Db. - -test() -> - %% launch couchdb - ok = test_util:start_couch(), - - %% initialize db - timer:sleep(1000), - Db = cycle_db(dbname()), - Db1 = cycle_db(dbname1()), - Db2 = cycle_db(dbname2()), - - % CORS is disabled by default - test_no_headers_server(), - test_no_headers_db(), - - % Now enable CORS - ok = config:set("httpd", "enable_cors", "true", false), - ok = config:set("cors", "origins", "http://example.com", false), - - %% do tests - test_incorrect_origin_simple_request(), - test_incorrect_origin_preflight_request(), - - test_preflight_request(), - test_db_request(), - test_doc_with_attachment_request(), - test_doc_with_attachment_range_request(), - test_db_preflight_request(), - test_db1_origin_request(), - test_preflight_with_port1(), - test_preflight_with_scheme1(), - test_if_none_match_header(), - - ok = config:set("cors", "origins", "http://example.com:5984", false), - test_preflight_with_port2(), - - ok = config:set("cors", "origins", "https://example.com:5984", false), - test_preflight_with_scheme2(), - - ok = config:set("cors", "origins", "*", false), - test_preflight_with_wildcard(), - - ok = config:set("cors", "origins", "http://example.com", false), - test_case_sensitive_mismatch_of_allowed_origins(), - - % http://www.w3.org/TR/cors/#supports-credentials - % 6.1.3 - % If the resource supports credentials add a single - % Access-Control-Allow-Origin header, with the value - % of the Origin header as value, and add a single - % Access-Control-Allow-Credentials header with the - % case-sensitive string "true" as value. - % Otherwise, add a single Access-Control-Allow-Origin - % header, with either the value of the Origin header - % or the string "*" as value. - % Note: The string "*" cannot be used for a resource - % that supports credentials. - test_db_request_credentials_header_off(), - ok = config:set("cors", "credentials", "true", false), - test_db_request_credentials_header_on(), - % We don’t test wildcards & credentials as that would - % fall into the realm of validating config values - % which we don’t do at all yet - - % test with vhosts - ok = config:set("vhosts", "example.com", "/", false), - test_preflight_request(true), - test_db_request(true), - test_db_preflight_request(true), - test_db1_origin_request(true), - test_preflight_with_port1(true), - test_preflight_with_scheme1(true), - - % TBD - % test multiple per-host configuration - - %% do tests with auth - ok = set_admin_password("test", <<"test">>), - - test_db_preflight_auth_request(), - test_db_origin_auth_request(), - - - %% restart boilerplate - catch couch_db:close(Db), - catch couch_db:close(Db1), - catch couch_db:close(Db2), - - couch_server:delete(list_to_binary(dbname()), [admin_user_ctx()]), - couch_server:delete(list_to_binary(dbname1()), [admin_user_ctx()]), - couch_server:delete(list_to_binary(dbname2()), [admin_user_ctx()]), - - timer:sleep(3000), - ok = test_util:stop_couch(), - ok. - -test_preflight_request() -> test_preflight_request(false). -test_db_request() -> test_db_request(false). -test_db_preflight_request() -> test_db_preflight_request(false). -test_db1_origin_request() -> test_db1_origin_request(false). -test_preflight_with_port1() -> test_preflight_with_port1(false). -test_preflight_with_scheme1() -> test_preflight_with_scheme1(false). - -%% Cors is disabled, should not return Access-Control-Allow-Origin -test_no_headers_server() -> - Headers = [{"Origin", "http://127.0.0.1"}], - {ok, _, Resp, _} = ibrowse:send_req(server(), Headers, get, []), - etap:is(proplists:get_value("Access-Control-Allow-Origin", Resp), - undefined, "No CORS Headers when disabled"). - -%% Cors is disabled, should not return Access-Control-Allow-Origin -test_no_headers_db() -> - Headers = [{"Origin", "http://127.0.0.1"}], - Url = server() ++ "etap-test-db", - {ok, _, Resp, _} = ibrowse:send_req(Url, Headers, get, []), - etap:is(proplists:get_value("Access-Control-Allow-Origin", Resp), - undefined, "No CORS Headers when disabled"). - -test_incorrect_origin_simple_request() -> - Headers = [{"Origin", "http://127.0.0.1"}], - {ok, _, RespHeaders, _} = ibrowse:send_req(server(), Headers, get, []), - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - undefined, - "Specified invalid origin, no Access"). - -test_incorrect_origin_preflight_request() -> - Headers = [{"Origin", "http://127.0.0.1"}, - {"Access-Control-Request-Method", "GET"}], - {ok, _, RespHeaders, _} = ibrowse:send_req(server(), Headers, options, []), - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - undefined, - "invalid origin"). - -test_preflight_request(VHost) -> - Headers = [{"Origin", "http://example.com"}, - {"Access-Control-Request-Method", "GET"}] - ++ maybe_append_vhost(VHost), - - case ibrowse:send_req(server(), Headers, options, []) of - {ok, _, RespHeaders, _} -> - etap:is(proplists:get_value("Access-Control-Allow-Methods", RespHeaders), - ?SUPPORTED_METHODS, - "test_preflight_request Access-Control-Allow-Methods ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -test_db_request(VHost) -> - Headers = [{"Origin", "http://example.com"}] - ++ maybe_append_vhost(VHost), - Url = server() ++ "etap-test-db", - case ibrowse:send_req(Url, Headers, get, []) of - {ok, _, RespHeaders, _Body} -> - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - "http://example.com", - "db Access-Control-Allow-Origin ok"), - etap:is(proplists:get_value("Access-Control-Expose-Headers", RespHeaders), - "Cache-Control, Content-Type, Server", - "db Access-Control-Expose-Headers ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -% COUCHDB-1689 -test_doc_with_attachment_request() -> - DocUrl = server() ++ "etap-test-db/doc1", - ibrowse:send_req(DocUrl ++ "/attachment.txt", - [{"Content-Type", "text/plain"}], put, "this is a text attachment"), - - Headers = [{"Origin", "http://example.com"}], - Url = DocUrl ++ "?attachments=true", - case ibrowse:send_req(Url, Headers, get, []) of - {ok, Code, _RespHeaders, _Body} -> - etap:is(Code, "200", "Response without errors"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -% COUCHDB-1689 -test_doc_with_attachment_range_request() -> - AttachmentUrl = server() ++ "etap-test-db/doc2/attachment.bin", - % Use a Content-Type that doesn't get compressed - ibrowse:send_req(AttachmentUrl, - [{"Content-Type", "application/octet-stream"}], put, - "this is an attachment"), - - Headers = [{"Origin", "http://example.com"}, {"Range", "bytes=0-6"}], - case ibrowse:send_req(AttachmentUrl, Headers, get, []) of - {ok, Code, _RespHeaders, _Body} -> - etap:is(Code, "206", "Response without errors"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -% COUCHDB-1697 -test_if_none_match_header() -> - Url = server() ++ "etap-test-db/doc2", - Headers = [{"Origin", "http://example.com"}], - {ok, _, _RespHeaders, _} = ibrowse:send_req(Url, Headers, get, []), - ETag = proplists:get_value("ETag", _RespHeaders), - Headers2 = [{"Origin", "http://example.com"}, {"If-None-Match", ETag}], - case ibrowse:send_req(Url, Headers2, get, []) of - {ok, Code, _RespHeaders2, _} -> - etap:is(Code, "304", "Responded with Not Modified"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -test_db_request_credentials_header_off() -> - Headers = [{"Origin", "http://example.com"}], - Url = server() ++ "etap-test-db", - case ibrowse:send_req(Url, Headers, get, []) of - {ok, _, RespHeaders, _Body} -> - etap:is(proplists:get_value("Access-Control-Allow-Credentials", RespHeaders), - undefined, - "db Access-Control-Allow-Credentials off"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -test_db_request_credentials_header_on() -> - Headers = [{"Origin", "http://example.com"}], - Url = server() ++ "etap-test-db", - case ibrowse:send_req(Url, Headers, get, []) of - {ok, _, RespHeaders, _Body} -> - etap:is(proplists:get_value("Access-Control-Allow-Credentials", RespHeaders), - "true", - "db Access-Control-Allow-Credentials ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -test_db_preflight_request(VHost) -> - Url = server() ++ "etap-test-db", - Headers = [{"Origin", "http://example.com"}, - {"Access-Control-Request-Method", "GET"}] - ++ maybe_append_vhost(VHost), - case ibrowse:send_req(Url, Headers, options, []) of - {ok, _, RespHeaders, _} -> - etap:is(proplists:get_value("Access-Control-Allow-Methods", RespHeaders), - ?SUPPORTED_METHODS, - "db Access-Control-Allow-Methods ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - - -test_db1_origin_request(VHost) -> - Headers = [{"Origin", "http://example.com"}] - ++ maybe_append_vhost(VHost), - Url = server() ++ "etap-test-db1", - case ibrowse:send_req(Url, Headers, get, [], [{host_header, "example.com"}]) of - {ok, _, RespHeaders, _Body} -> - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - "http://example.com", - "db origin ok"); - _Else -> - io:format("else ~p~n", [_Else]), - etap:is(false, true, "ibrowse failed") - end. - -test_db_preflight_auth_request() -> - Url = server() ++ "etap-test-db2", - Headers = [{"Origin", "http://example.com"}, - {"Access-Control-Request-Method", "GET"}], - case ibrowse:send_req(Url, Headers, options, []) of - {ok, _Status, RespHeaders, _} -> - etap:is(proplists:get_value("Access-Control-Allow-Methods", RespHeaders), - ?SUPPORTED_METHODS, - "db Access-Control-Allow-Methods ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - - -test_db_origin_auth_request() -> - Headers = [{"Origin", "http://example.com"}], - Url = server() ++ "etap-test-db2", - - case ibrowse:send_req(Url, Headers, get, [], - [{basic_auth, {"test", "test"}}]) of - {ok, _, RespHeaders, _Body} -> - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - "http://example.com", - "db origin ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -test_preflight_with_wildcard() -> - Headers = [{"Origin", "http://example.com"}, - {"Access-Control-Request-Method", "GET"}], - case ibrowse:send_req(server(), Headers, options, []) of - {ok, _, RespHeaders, _} -> - % I would either expect the current origin or a wildcard to be returned - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - "http://example.com", - "db origin ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -test_preflight_with_port1(VHost) -> - Headers = [{"Origin", "http://example.com:5984"}, - {"Access-Control-Request-Method", "GET"}] - ++ maybe_append_vhost(VHost), - case ibrowse:send_req(server(), Headers, options, []) of - {ok, _, RespHeaders, _} -> - % I would either expect the current origin or a wildcard to be returned - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - undefined, - "check non defined host:port in origin ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -test_preflight_with_port2() -> - Headers = [{"Origin", "http://example.com:5984"}, - {"Access-Control-Request-Method", "GET"}], - case ibrowse:send_req(server(), Headers, options, []) of - {ok, _, RespHeaders, _} -> - % I would either expect the current origin or a wildcard to be returned - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - "http://example.com:5984", - "check host:port in origin ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -test_preflight_with_scheme1(VHost) -> - Headers = [{"Origin", "https://example.com:5984"}, - {"Access-Control-Request-Method", "GET"}] - ++ maybe_append_vhost(VHost), - case ibrowse:send_req(server(), Headers, options, []) of - {ok, _, RespHeaders, _} -> - % I would either expect the current origin or a wildcard to be returned - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - undefined, - "check non defined scheme in origin ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -test_preflight_with_scheme2() -> - Headers = [{"Origin", "https://example.com:5984"}, - {"Access-Control-Request-Method", "GET"}], - case ibrowse:send_req(server(), Headers, options, []) of - {ok, _, RespHeaders, _} -> - % I would either expect the current origin or a wildcard to be returned - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - "https://example.com:5984", - "check scheme in origin ok"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -test_case_sensitive_mismatch_of_allowed_origins() -> - Headers = [{"Origin", "http://EXAMPLE.COM"}], - Url = server() ++ "etap-test-db", - case ibrowse:send_req(Url, Headers, get, []) of - {ok, _, RespHeaders, _Body} -> - etap:is(proplists:get_value("Access-Control-Allow-Origin", RespHeaders), - undefined, - "db access config case mismatch"); - _ -> - etap:is(false, true, "ibrowse failed") - end. - -maybe_append_vhost(true) -> - [{"Host", "http://example.com"}]; -maybe_append_vhost(Else) -> - []. diff --git a/test/etap/232-csp.t b/test/etap/232-csp.t deleted file mode 100644 index 6dbce6aa0c8..00000000000 --- a/test/etap/232-csp.t +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- - -% 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. - -server() -> - lists:concat([ - "http://127.0.0.1:", - mochiweb_socket_server:get(couch_httpd, port), - "/_utils/" - ]). - - -main(_) -> - test_util:init_code_path(), - - etap:plan(3), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - -test() -> - %% launch couchdb - couch_server_sup:start_link(test_util:config_files()), - - % CSP is disabled by default - test_no_csp_headers_server(), - - % Now enable CSP - ok = couch_config:set("csp", "enable", "true", false), - - test_default_header_value(), - - ok = couch_config:set("csp", "header_value", "default-src 'http://example.com';", false), - test_custom_header_value(), - - % Disabled on all other values than true - ok = couch_config:set("csp", "enable", "blerg", false), - test_all_other_values_for_enable(), - - timer:sleep(3000), - couch_server_sup:stop(), - ok. - -test_no_csp_headers_server() -> - Headers = [{"Origin", "http://127.0.0.1"}], - {ok, _, Resp, _} = ibrowse:send_req(server(), Headers, get, []), - etap:is(proplists:get_value("Content-Security-Policy", Resp), - undefined, "No CSP Headers when disabled"). - -test_default_header_value() -> - Headers = [{"Origin", "http://127.0.0.1"}], - {ok, _, Resp, _} = ibrowse:send_req(server(), Headers, get, []), - etap:is(proplists:get_value("Content-Security-Policy", Resp), - "default-src 'self'; img-src 'self'; font-src 'self'; " - "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';", - "Default CSP Headers when enabled"). - -test_custom_header_value() -> - Headers = [{"Origin", "http://127.0.0.1"}], - {ok, _, Resp, _} = ibrowse:send_req(server(), Headers, get, []), - etap:is(proplists:get_value("Content-Security-Policy", Resp), - "default-src 'http://example.com';", - "Custom CSP Headers possible"). - -test_all_other_values_for_enable() -> - Headers = [{"Origin", "http://127.0.0.1"}], - {ok, _, Resp, _} = ibrowse:send_req(server(), Headers, get, []), - etap:is(proplists:get_value("Content-Security-Policy", Resp), - undefined, "No CSP Headers when wrong value given"). diff --git a/test/etap/250-upgrade-legacy-view-files.t b/test/etap/250-upgrade-legacy-view-files.t deleted file mode 100644 index e720b1c446c..00000000000 --- a/test/etap/250-upgrade-legacy-view-files.t +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -% 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. - -main(_) -> - test_util:init_code_path(), - - etap:plan(8), - case (catch test()) of - ok -> - etap:end_tests(); - Other -> - etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), - etap:bail(Other) - end, - ok. - - -test() -> - couch_server_sup:start_link(test_util:config_files()), - - % commit sofort - ok = couch_config:set("query_server_config", "commit_freq", "0"), - - test_upgrade(), - - couch_server_sup:stop(), - ok. - -fixture_path() -> - test_util:source_file("test/etap/fixtures"). - -old_db() -> - fixture_path() ++ "/" ++ old_db_name(). - -old_db_name() -> - "test.couch". - -old_view() -> - fixture_path() ++ "/" ++ old_view_name(). - -old_view_name() -> - "3b835456c235b1827e012e25666152f3.view". - -new_view_name() -> - "a1c5929f912aca32f13446122cc6ce50.view". - -couch_url() -> - "http://" ++ addr() ++ ":" ++ port(). - -addr() -> - couch_config:get("httpd", "bind_address", "127.0.0.1"). - -port() -> - integer_to_list(mochiweb_socket_server:get(couch_httpd, port)). - - -% <= 1.2.x --record(index_header, - {seq=0, - purge_seq=0, - id_btree_state=nil, - view_states=nil - }). - -% >= 1.3.x --record(mrheader, { - seq=0, - purge_seq=0, - id_btree_state=nil, - view_states=nil -}). - -ensure_header(File, MatchFun, Msg) -> - {ok, Fd} = couch_file:open(File), - {ok, {_Sig, Header}} = couch_file:read_header(Fd), - couch_file:close(Fd), - etap:fun_is(MatchFun, Header, "ensure " ++ Msg ++ " header for file: " ++ File). - -file_exists(File) -> - % open without creating - case file:open(File, [read, raw]) of - {ok, Fd_Read} -> - file:close(Fd_Read), - true; - _Error -> - false - end. - -cleanup() -> - DbDir = couch_config:get("couchdb", "database_dir"), - Files = [ - DbDir ++ "/test.couch", - DbDir ++ "/.test_design/" ++ old_view_name(), - DbDir ++ "/.test_design/mrview/" ++ new_view_name() - ], - lists:foreach(fun(File) -> file:delete(File) end, Files), - etap:ok(true, "cleanup"). - -test_upgrade() -> - - cleanup(), - - % copy old db file into db dir - DbDir = couch_config:get("couchdb", "database_dir"), - DbTarget = DbDir ++ "/" ++ old_db_name(), - filelib:ensure_dir(DbDir), - OldDbName = old_db(), - {ok, _} = file:copy(OldDbName, DbTarget), - - % copy old view file into view dir - ViewDir = couch_config:get("couchdb", "view_index_dir"), - ViewTarget = ViewDir ++ "/.test_design/" ++ old_view_name(), - filelib:ensure_dir(ViewTarget), - OldViewName = old_view(), - {ok, _} = file:copy(OldViewName, ViewTarget), - - % ensure old header - ensure_header(ViewTarget, fun(#index_header{}) -> true; (_) -> false end, "old"), - - % query view - ViewUrl = couch_url() ++ "/test/_design/test/_view/test", - {ok, Code, _Headers, Body} = test_util:request(ViewUrl, [], get), - - % expect results - etap:is(Code, 200, "valid view result http status code"), - ExpectBody = <<"{\"total_rows\":2,\"offset\":0,\"rows\":[\r\n{\"id\":\"193f2f9c596ddc7ad326f7da470009ec\",\"key\":1,\"value\":null},\r\n{\"id\":\"193f2f9c596ddc7ad326f7da470012b6\",\"key\":2,\"value\":null}\r\n]}\n">>, - etap:is(Body, ExpectBody, "valid view result"), - - % ensure old file gone. - etap:is(file_exists(ViewTarget), false, "ensure old file is gone"), - - % ensure new header - NewViewFile = ViewDir ++ "/.test_design/mrview/" ++ new_view_name(), - - % add doc(s) - test_util:request( - couch_url() ++ "/test/boo", - [{"Content-Type", "application/json"}], - put, - <<"{\"a\":3}">>), - - % query again - {ok, Code2, _Headers2, Body2} = test_util:request(ViewUrl, [], get), - - % expect results - etap:is(Code2, 200, "valid view result http status code"), - ExpectBody2 = <<"{\"total_rows\":3,\"offset\":0,\"rows\":[\r\n{\"id\":\"193f2f9c596ddc7ad326f7da470009ec\",\"key\":1,\"value\":null},\r\n{\"id\":\"193f2f9c596ddc7ad326f7da470012b6\",\"key\":2,\"value\":null},\r\n{\"id\":\"boo\",\"key\":3,\"value\":null}\r\n]}\n">>, - etap:is(Body2, ExpectBody2, "valid view result after doc add"), - - % ensure no rebuild - % TBD no idea how to actually test this. - - % ensure new header. - timer:sleep(2000), - ensure_header(NewViewFile, fun(#mrheader{}) -> true; (_) -> false end, "new"), - - ok. diff --git a/test/etap/etap.erl b/test/etap/etap.erl deleted file mode 100644 index ae3896c01dd..00000000000 --- a/test/etap/etap.erl +++ /dev/null @@ -1,614 +0,0 @@ -%% Copyright (c) 2008-2009 Nick Gerakines -%% -%% Permission is hereby granted, free of charge, to any person -%% obtaining a copy of this software and associated documentation -%% files (the "Software"), to deal in the Software without -%% restriction, including without limitation the rights to use, -%% copy, modify, merge, publish, distribute, sublicense, and/or sell -%% copies of the Software, and to permit persons to whom the -%% Software is furnished to do so, subject to the following -%% conditions: -%% -%% The above copyright notice and this permission notice shall be -%% included in all copies or substantial portions of the Software. -%% -%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -%% OTHER DEALINGS IN THE SOFTWARE. -%% -%% @author Nick Gerakines [http://socklabs.com/] -%% @author Jeremy Wall -%% @version 0.3.4 -%% @copyright 2007-2008 Jeremy Wall, 2008-2009 Nick Gerakines -%% @reference http://testanything.org/wiki/index.php/Main_Page -%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol -%% @todo Finish implementing the skip directive. -%% @todo Document the messages handled by this receive loop. -%% @todo Explain in documentation why we use a process to handle test input. -%% @doc etap is a TAP testing module for Erlang components and applications. -%% This module allows developers to test their software using the TAP method. -%% -%%

-%% TAP, the Test Anything Protocol, is a simple text-based interface between -%% testing modules in a test harness. TAP started life as part of the test -%% harness for Perl but now has implementations in C/C++, Python, PHP, Perl -%% and probably others by the time you read this. -%%

-%% -%% The testing process begins by defining a plan using etap:plan/1, running -%% a number of etap tests and then calling eta:end_tests/0. Please refer to -%% the Erlang modules in the t directory of this project for example tests. --module(etap). --vsn("0.3.4"). - --export([ - ensure_test_server/0, - start_etap_server/0, - test_server/1, - msg/1, msg/2, - diag/1, diag/2, - expectation_mismatch_message/3, - plan/1, - end_tests/0, - not_ok/2, ok/2, is_ok/2, is/3, isnt/3, any/3, none/3, - fun_is/3, expect_fun/3, expect_fun/4, - is_greater/3, - skip/1, skip/2, - datetime/1, - skip/3, - bail/0, bail/1, - test_state/0, failure_count/0 -]). - --export([ - contains_ok/3, - is_before/4 -]). - --export([ - is_pid/2, - is_alive/2, - is_mfa/3 -]). - --export([ - loaded_ok/2, - can_ok/2, can_ok/3, - has_attrib/2, is_attrib/3, - is_behaviour/2 -]). - --export([ - dies_ok/2, - lives_ok/2, - throws_ok/3 -]). - - --record(test_state, { - planned = 0, - count = 0, - pass = 0, - fail = 0, - skip = 0, - skip_reason = "" -}). - -%% @spec plan(N) -> Result -%% N = unknown | skip | {skip, string()} | integer() -%% Result = ok -%% @doc Create a test plan and boot strap the test server. -plan(unknown) -> - ensure_test_server(), - etap_server ! {self(), plan, unknown}, - ok; -plan(skip) -> - io:format("1..0 # skip~n"); -plan({skip, Reason}) -> - io:format("1..0 # skip ~s~n", [Reason]); -plan(N) when is_integer(N), N > 0 -> - ensure_test_server(), - etap_server ! {self(), plan, N}, - ok. - -%% @spec end_tests() -> ok -%% @doc End the current test plan and output test results. -%% @todo This should probably be done in the test_server process. -end_tests() -> - Ref = make_ref(), - case whereis(etap_server) of - undefined -> self() ! {Ref, true}; - _ -> etap_server ! {self(), state, Ref} - end, - State = receive {Ref, X} -> X end, - if - is_record(State, test_state) andalso State#test_state.planned == -1 -> - io:format("1..~p~n", [State#test_state.count]); - true -> - ok - end, - case whereis(etap_server) of - undefined -> ok; - _ -> etap_server ! done, ok - end. - -bail() -> - bail(""). - -bail(Reason) -> - etap_server ! {self(), diag, "Bail out! " ++ Reason}, - etap_server ! done, ok, - ok. - -%% @spec test_state() -> Return -%% Return = test_state_record() | {error, string()} -%% @doc Return the current test state -test_state() -> - etap_server ! {self(), state}, - receive - X when is_record(X, test_state) -> X - after - 1000 -> {error, "Timed out waiting for etap server reply.~n"} - end. - -%% @spec failure_count() -> Return -%% Return = integer() | {error, string()} -%% @doc Return the current failure count -failure_count() -> - case test_state() of - #test_state{fail=FailureCount} -> FailureCount; - X -> X - end. - -%% @spec msg(S) -> ok -%% S = string() -%% @doc Print a message in the test output. -msg(S) -> etap_server ! {self(), diag, S}, ok. - -%% @spec msg(Format, Data) -> ok -%% Format = atom() | string() | binary() -%% Data = [term()] -%% UnicodeList = [Unicode] -%% Unicode = int() -%% @doc Print a message in the test output. -%% Function arguments are passed through io_lib:format/2. -msg(Format, Data) -> msg(io_lib:format(Format, Data)). - -%% @spec diag(S) -> ok -%% S = string() -%% @doc Print a debug/status message related to the test suite. -diag(S) -> msg("# " ++ S). - -%% @spec diag(Format, Data) -> ok -%% Format = atom() | string() | binary() -%% Data = [term()] -%% UnicodeList = [Unicode] -%% Unicode = int() -%% @doc Print a debug/status message related to the test suite. -%% Function arguments are passed through io_lib:format/2. -diag(Format, Data) -> diag(io_lib:format(Format, Data)). - -%% @spec expectation_mismatch_message(Got, Expected, Desc) -> ok -%% Got = any() -%% Expected = any() -%% Desc = string() -%% @doc Print an expectation mismatch message in the test output. -expectation_mismatch_message(Got, Expected, Desc) -> - msg(" ---"), - msg(" description: ~p", [Desc]), - msg(" found: ~p", [Got]), - msg(" wanted: ~p", [Expected]), - msg(" ..."), - ok. - -% @spec evaluate(Pass, Got, Expected, Desc) -> Result -%% Pass = true | false -%% Got = any() -%% Expected = any() -%% Desc = string() -%% Result = true | false -%% @doc Evaluate a test statement, printing an expectation mismatch message -%% if the test failed. -evaluate(Pass, Got, Expected, Desc) -> - case mk_tap(Pass, Desc) of - false -> - expectation_mismatch_message(Got, Expected, Desc), - false; - true -> - true - end. - -%% @spec ok(Expr, Desc) -> Result -%% Expr = true | false -%% Desc = string() -%% Result = true | false -%% @doc Assert that a statement is true. -ok(Expr, Desc) -> evaluate(Expr == true, Expr, true, Desc). - -%% @spec not_ok(Expr, Desc) -> Result -%% Expr = true | false -%% Desc = string() -%% Result = true | false -%% @doc Assert that a statement is false. -not_ok(Expr, Desc) -> evaluate(Expr == false, Expr, false, Desc). - -%% @spec is_ok(Expr, Desc) -> Result -%% Expr = any() -%% Desc = string() -%% Result = true | false -%% @doc Assert that two values are the same. -is_ok(Expr, Desc) -> evaluate(Expr == ok, Expr, ok, Desc). - -%% @spec is(Got, Expected, Desc) -> Result -%% Got = any() -%% Expected = any() -%% Desc = string() -%% Result = true | false -%% @doc Assert that two values are the same. -is(Got, Expected, Desc) -> evaluate(Got == Expected, Got, Expected, Desc). - -%% @spec isnt(Got, Expected, Desc) -> Result -%% Got = any() -%% Expected = any() -%% Desc = string() -%% Result = true | false -%% @doc Assert that two values are not the same. -isnt(Got, Expected, Desc) -> evaluate(Got /= Expected, Got, Expected, Desc). - -%% @spec is_greater(ValueA, ValueB, Desc) -> Result -%% ValueA = number() -%% ValueB = number() -%% Desc = string() -%% Result = true | false -%% @doc Assert that an integer is greater than another. -is_greater(ValueA, ValueB, Desc) when is_integer(ValueA), is_integer(ValueB) -> - mk_tap(ValueA > ValueB, Desc). - -%% @spec any(Got, Items, Desc) -> Result -%% Got = any() -%% Items = [any()] -%% Desc = string() -%% Result = true | false -%% @doc Assert that an item is in a list. -any(Got, Items, Desc) when is_function(Got) -> - is(lists:any(Got, Items), true, Desc); -any(Got, Items, Desc) -> - is(lists:member(Got, Items), true, Desc). - -%% @spec none(Got, Items, Desc) -> Result -%% Got = any() -%% Items = [any()] -%% Desc = string() -%% Result = true | false -%% @doc Assert that an item is not in a list. -none(Got, Items, Desc) when is_function(Got) -> - is(lists:any(Got, Items), false, Desc); -none(Got, Items, Desc) -> - is(lists:member(Got, Items), false, Desc). - -%% @spec fun_is(Fun, Expected, Desc) -> Result -%% Fun = function() -%% Expected = any() -%% Desc = string() -%% Result = true | false -%% @doc Use an anonymous function to assert a pattern match. -fun_is(Fun, Expected, Desc) when is_function(Fun) -> - is(Fun(Expected), true, Desc). - -%% @spec expect_fun(ExpectFun, Got, Desc) -> Result -%% ExpectFun = function() -%% Got = any() -%% Desc = string() -%% Result = true | false -%% @doc Use an anonymous function to assert a pattern match, using actual -%% value as the argument to the function. -expect_fun(ExpectFun, Got, Desc) -> - evaluate(ExpectFun(Got), Got, ExpectFun, Desc). - -%% @spec expect_fun(ExpectFun, Got, Desc, ExpectStr) -> Result -%% ExpectFun = function() -%% Got = any() -%% Desc = string() -%% ExpectStr = string() -%% Result = true | false -%% @doc Use an anonymous function to assert a pattern match, using actual -%% value as the argument to the function. -expect_fun(ExpectFun, Got, Desc, ExpectStr) -> - evaluate(ExpectFun(Got), Got, ExpectStr, Desc). - -%% @equiv skip(TestFun, "") -skip(TestFun) when is_function(TestFun) -> - skip(TestFun, ""). - -%% @spec skip(TestFun, Reason) -> ok -%% TestFun = function() -%% Reason = string() -%% @doc Skip a test. -skip(TestFun, Reason) when is_function(TestFun), is_list(Reason) -> - begin_skip(Reason), - catch TestFun(), - end_skip(), - ok. - -%% @spec skip(Q, TestFun, Reason) -> ok -%% Q = true | false | function() -%% TestFun = function() -%% Reason = string() -%% @doc Skips a test conditionally. The first argument to this function can -%% either be the 'true' or 'false' atoms or a function that returns 'true' or -%% 'false'. -skip(QFun, TestFun, Reason) when is_function(QFun), is_function(TestFun), is_list(Reason) -> - case QFun() of - true -> begin_skip(Reason), TestFun(), end_skip(); - _ -> TestFun() - end, - ok; - -skip(Q, TestFun, Reason) when is_function(TestFun), is_list(Reason), Q == true -> - begin_skip(Reason), - TestFun(), - end_skip(), - ok; - -skip(_, TestFun, Reason) when is_function(TestFun), is_list(Reason) -> - TestFun(), - ok. - -%% @private -begin_skip(Reason) -> - etap_server ! {self(), begin_skip, Reason}. - -%% @private -end_skip() -> - etap_server ! {self(), end_skip}. - -%% @spec contains_ok(string(), string(), string()) -> true | false -%% @doc Assert that a string is contained in another string. -contains_ok(Source, String, Desc) -> - etap:isnt( - string:str(Source, String), - 0, - Desc - ). - -%% @spec is_before(string(), string(), string(), string()) -> true | false -%% @doc Assert that a string comes before another string within a larger body. -is_before(Source, StringA, StringB, Desc) -> - etap:is_greater( - string:str(Source, StringB), - string:str(Source, StringA), - Desc - ). - -%% @doc Assert that a given variable is a pid. -is_pid(Pid, Desc) when is_pid(Pid) -> etap:ok(true, Desc); -is_pid(_, Desc) -> etap:ok(false, Desc). - -%% @doc Assert that a given process/pid is alive. -is_alive(Pid, Desc) -> - etap:ok(erlang:is_process_alive(Pid), Desc). - -%% @doc Assert that the current function of a pid is a given {M, F, A} tuple. -is_mfa(Pid, MFA, Desc) -> - etap:is({current_function, MFA}, erlang:process_info(Pid, current_function), Desc). - -%% @spec loaded_ok(atom(), string()) -> true | false -%% @doc Assert that a module has been loaded successfully. -loaded_ok(M, Desc) when is_atom(M) -> - etap:fun_is(fun({module, _}) -> true; (_) -> false end, code:load_file(M), Desc). - -%% @spec can_ok(atom(), atom()) -> true | false -%% @doc Assert that a module exports a given function. -can_ok(M, F) when is_atom(M), is_atom(F) -> - Matches = [X || {X, _} <- M:module_info(exports), X == F], - etap:ok(Matches > 0, lists:concat([M, " can ", F])). - -%% @spec can_ok(atom(), atom(), integer()) -> true | false -%% @doc Assert that a module exports a given function with a given arity. -can_ok(M, F, A) when is_atom(M); is_atom(F), is_number(A) -> - Matches = [X || X <- M:module_info(exports), X == {F, A}], - etap:ok(Matches > 0, lists:concat([M, " can ", F, "/", A])). - -%% @spec has_attrib(M, A) -> true | false -%% M = atom() -%% A = atom() -%% @doc Asserts that a module has a given attribute. -has_attrib(M, A) when is_atom(M), is_atom(A) -> - etap:isnt( - proplists:get_value(A, M:module_info(attributes), 'asdlkjasdlkads'), - 'asdlkjasdlkads', - lists:concat([M, " has attribute ", A]) - ). - -%% @spec has_attrib(M, A. V) -> true | false -%% M = atom() -%% A = atom() -%% V = any() -%% @doc Asserts that a module has a given attribute with a given value. -is_attrib(M, A, V) when is_atom(M) andalso is_atom(A) -> - etap:is( - proplists:get_value(A, M:module_info(attributes)), - [V], - lists:concat([M, "'s ", A, " is ", V]) - ). - -%% @spec is_behavior(M, B) -> true | false -%% M = atom() -%% B = atom() -%% @doc Asserts that a given module has a specific behavior. -is_behaviour(M, B) when is_atom(M) andalso is_atom(B) -> - is_attrib(M, behaviour, B). - -%% @doc Assert that an exception is raised when running a given function. -dies_ok(F, Desc) -> - case (catch F()) of - {'EXIT', _} -> etap:ok(true, Desc); - _ -> etap:ok(false, Desc) - end. - -%% @doc Assert that an exception is not raised when running a given function. -lives_ok(F, Desc) -> - etap:is(try_this(F), success, Desc). - -%% @doc Assert that the exception thrown by a function matches the given exception. -throws_ok(F, Exception, Desc) -> - try F() of - _ -> etap:ok(nok, Desc) - catch - _:E -> - etap:is(E, Exception, Desc) - end. - -%% @private -%% @doc Run a function and catch any exceptions. -try_this(F) when is_function(F, 0) -> - try F() of - _ -> success - catch - throw:E -> {throw, E}; - error:E -> {error, E}; - exit:E -> {exit, E} - end. - -%% @private -%% @doc Start the etap_server process if it is not running already. -ensure_test_server() -> - case whereis(etap_server) of - undefined -> - proc_lib:start(?MODULE, start_etap_server,[]); - _ -> - diag("The test server is already running.") - end. - -%% @private -%% @doc Start the etap_server loop and register itself as the etap_server -%% process. -start_etap_server() -> - catch register(etap_server, self()), - proc_lib:init_ack(ok), - etap:test_server(#test_state{ - planned = 0, - count = 0, - pass = 0, - fail = 0, - skip = 0, - skip_reason = "" - }). - - -%% @private -%% @doc The main etap_server receive/run loop. The etap_server receive loop -%% responds to seven messages apperatining to failure or passing of tests. -%% It is also used to initiate the testing process with the {_, plan, _} -%% message that clears the current test state. -test_server(State) -> - NewState = receive - {_From, plan, unknown} -> - io:format("# Current time local ~s~n", [datetime(erlang:localtime())]), - io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]), - State#test_state{ - planned = -1, - count = 0, - pass = 0, - fail = 0, - skip = 0, - skip_reason = "" - }; - {_From, plan, N} -> - io:format("# Current time local ~s~n", [datetime(erlang:localtime())]), - io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]), - io:format("1..~p~n", [N]), - State#test_state{ - planned = N, - count = 0, - pass = 0, - fail = 0, - skip = 0, - skip_reason = "" - }; - {_From, begin_skip, Reason} -> - State#test_state{ - skip = 1, - skip_reason = Reason - }; - {_From, end_skip} -> - State#test_state{ - skip = 0, - skip_reason = "" - }; - {_From, pass, Desc} -> - FullMessage = skip_diag( - " - " ++ Desc, - State#test_state.skip, - State#test_state.skip_reason - ), - io:format("ok ~p ~s~n", [State#test_state.count + 1, FullMessage]), - State#test_state{ - count = State#test_state.count + 1, - pass = State#test_state.pass + 1 - }; - - {_From, fail, Desc} -> - FullMessage = skip_diag( - " - " ++ Desc, - State#test_state.skip, - State#test_state.skip_reason - ), - io:format("not ok ~p ~s~n", [State#test_state.count + 1, FullMessage]), - State#test_state{ - count = State#test_state.count + 1, - fail = State#test_state.fail + 1 - }; - {From, state, Ref} -> - From ! {Ref, State}, - State; - {_From, diag, Message} -> - io:format("~s~n", [Message]), - State; - {From, count} -> - From ! State#test_state.count, - State; - {From, is_skip, Ref} -> - From ! {Ref, State#test_state.skip}, - State; - done -> - exit(normal) - end, - test_server(NewState). - -%% @private -%% @doc Process the result of a test and send it to the etap_server process. -mk_tap(Result, Desc) -> - etap_server ! {self(), is_skip, Ref = make_ref()} , - receive {Ref, IsSkip} -> ok end, - case [IsSkip, Result] of - [_, true] -> - etap_server ! {self(), pass, Desc}, - true; - [1, _] -> - etap_server ! {self(), pass, Desc}, - true; - _ -> - etap_server ! {self(), fail, Desc}, - false - end. - -%% @private -%% @doc Format a date/time string. -datetime(DateTime) -> - {{Year, Month, Day}, {Hour, Min, Sec}} = DateTime, - io_lib:format("~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B", [Year, Month, Day, Hour, Min, Sec]). - -%% @private -%% @doc Craft an output message taking skip/todo into consideration. -skip_diag(Message, 0, _) -> - Message; -skip_diag(_Message, 1, "") -> - " # SKIP"; -skip_diag(_Message, 1, Reason) -> - " # SKIP : " ++ Reason. diff --git a/test/etap/fixtures/3b835456c235b1827e012e25666152f3.view b/test/etap/fixtures/3b835456c235b1827e012e25666152f3.view deleted file mode 100644 index 9c67648be34ff950d1e47e8d5e362d47fb81e65a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4192 zcmZQ%U|`TIb$u;xJvCNj+tQF#ad#uvHD@s80>uTan?u45nQm;VW7JbkOAPv)!oZxD znFExGVXS-rRFuNNo?RB7m!Faf5@dqtQZTeMPBTigOg6PNOG!yKPfRg3GD|a0Ni;Dx zFfg!8O=i@QVa#AkWJqMp>M~|Xs18IkdF=b@|*|Ch}A6&3m0My3_avDKPQW#kB zN^^2D7>yX1nv589K(;`|Ko*RG(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R z7*Qd>2=r~ViJR5|vq)qP9cCMZ_V4fg(e6uA?G2K@d>{U&vVB4C-igcK!oJeJ%+sYm&L>n=+of zIFetEd!FCtxEJ3~o&aO)e&^7^xxY`1_C%j+czd<`iCE6KUSqNr4jDSv^eNuJ8D|<3 zQ)Z(4c==kGF>z%-W3Bu4v<#~LOn%_^zutW3!~9n{Lr7v3DQ7_3ek0`KcpQhsg+BaB zW7mymL#YW(Pv9yP&s9ABol$SpAGy=-)3iu0*LHe+(O!-B^s1`r*CU3=fwLyBi-RG8 z&vGAnik3SN~d4R+$K1P)93j zVU@vR6}%3;hyW2F0z`la5CI}U1c(3;AOb{y2y8+EfIS88OHMa`5q{ROzr{TNy#Mf2 zW&X#ojgdQBjNQD0PvEq|q~j`JlG(26FYEbIg4 z<3t(Q+0({f8_l*x4empuqIU;RY ze)p}K7C?Dy#xyRf9*5&?O4N; z$L9}zdg7L3{`<<`|HV@NR~9h;VzTB5{&6AyZym;uuU;wt&ptL26TeG%e%;YJ^YDpn zv&*IYKLt4$DNO>DV!boLQ?U$>>Cn{K80T4@j!c=8mVDcdj;yIqr)3WVxz#OMdfM`~ zFqfF*lh8NP9?hg8mN}8?)FvX^m8TmOMNgU*H1FEMyOmTXk=TA$;|%8COaDGtgO7wN zLk8j|)(b9MFTj{Q+V2W!dfJSo?yx!F{g4KBv%td2fg`sLw>f+A;|HAkX46f(6*a^q zzEI5lpTHm0d<1T|4j&>Y5g-B^2-x|5EcxE)>Bs+E@tn+k{o2xA%c{)(_;30`+*g0LfB)tT TGZ_H?sj%ibhbyORDu90gj@U@p diff --git a/test/etap/run.tpl b/test/etap/run.tpl deleted file mode 100644 index b3171605048..00000000000 --- a/test/etap/run.tpl +++ /dev/null @@ -1,32 +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. - -SRCDIR="%abs_top_srcdir%" -BUILDDIR="%abs_top_builddir%" -export ERL_LIBS="$BUILDDIR/src/:$ERL_LIBS" -export ERL_FLAGS="$ERL_FLAGS -pa $BUILDDIR/test/etap/" - -if test $# -eq 1; then - OPTS="" - TGT=$1 -else - OPTS=$1 - TGT=$2 -fi - -if test -f $TGT; then - prove $OPTS -v $TGT -else - prove $OPTS $TGT/*.t -fi diff --git a/test/etap/test_cfg_register.c b/test/etap/test_cfg_register.c deleted file mode 100644 index c910bac4862..00000000000 --- a/test/etap/test_cfg_register.c +++ /dev/null @@ -1,31 +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. - -#include -#include - -int -main(int argc, const char * argv[]) -{ - char c = '\0'; - size_t num = 1; - - fprintf(stdout, "[\"register\", \"s1\"]\n"); - fprintf(stdout, "[\"register\", \"s2\", \"k\"]\n"); - fflush(stdout); - - while(c != '\n' && num > 0) { - num = fread(&c, 1, 1, stdin); - } - - exit(0); -} diff --git a/test/etap/test_util.erl.in b/test/etap/test_util.erl.in deleted file mode 100644 index bdb7a176ba6..00000000000 --- a/test/etap/test_util.erl.in +++ /dev/null @@ -1,115 +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_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]). - -srcdir() -> - "@abs_top_srcdir@". - -builddir() -> - "@abs_top_builddir@". - -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"), - source_file("test/random_port.ini"), - build_file("etc/couchdb/local_dev.ini") - ]. - - -run(Plan, Fun) -> - test_util:init_code_path(), - etap:plan(Plan), - try Fun() of - ok -> - etap:end_tests(); - Else -> - etap:diag("Bad return:~n~p", [Else]), - timer:sleep(100), - etap:bail(Else) - catch Type:Reason -> - etap:diag("Test died: ~p~n ~p~n ~p~n", - [Type, Reason, erlang:get_stacktrace()]), - timer:sleep(1000), - etap:bail({Type, Reason}) - end, - ok. - - -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 = application:start(config), - ok = application:start(twig), - ok = application:start(couch), - ok. - -stop_couch() -> - ok = application:stop(couch), - ok = application:stop(twig), - ok = application:stop(config). diff --git a/test/etap/test_web.erl b/test/etap/test_web.erl deleted file mode 100644 index ed78651f1a9..00000000000 --- a/test/etap/test_web.erl +++ /dev/null @@ -1,99 +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). - --export([start_link/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). - -start_link() -> - gen_server:start({local, ?HANDLER}, ?MODULE, [], []), - mochiweb_http:start([ - {name, ?SERVER}, - {loop, {?MODULE, loop}}, - {port, 0} - ]). - -loop(Req) -> - %etap:diag("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(500), - lists:foreach(fun(C) -> Resp:write_chunk(C) end, BodyChunks), - Resp:write_chunk([]), - {ok, Resp}; - {error, Reason} -> - etap:diag("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) -> - ok = gen_server:call(?HANDLER, {set_assert, Fun}). - -check_last() -> - gen_server:call(?HANDLER, last_status). - -init(_) -> - {ok, nil}. - -terminate(_Reason, _State) -> - ok. - -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(Msg, State) -> - etap:diag("Ignoring cast message: ~p", [Msg]), - {noreply, State}. - -handle_info(Msg, State) -> - etap:diag("Ignoring info message: ~p", [Msg]), - {noreply, State}. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}.