You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2020/02/12 23:17:27 UTC

[couchdb] 11/13: fixup: add size tests

This is an automated email from the ASF dual-hosted git repository.

davisp pushed a commit to branch prototype/fdb-layer-get-dbs-info
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 9d5062bf8f9b9b92db54e7b28d12d072ad560a99
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Mon Feb 10 16:41:42 2020 -0600

    fixup: add size tests
---
 src/fabric/test/fabric2_doc_size_tests.erl | 274 +++++++++++++++++++++++++++++
 1 file changed, 274 insertions(+)

diff --git a/src/fabric/test/fabric2_doc_size_tests.erl b/src/fabric/test/fabric2_doc_size_tests.erl
new file mode 100644
index 0000000..37f1740
--- /dev/null
+++ b/src/fabric/test/fabric2_doc_size_tests.erl
@@ -0,0 +1,274 @@
+% 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(fabric2_doc_size_tests).
+
+
+-include_lib("couch/include/couch_db.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+% Doc body size calculations
+% ID: size(Doc#doc.id)
+% Rev: size(erlfdb_tuple:encode(Start)) + size(Rev) % where Rev is usually 16
+% Deleted: 1 % (binary value is one byte)
+% Body: couch_ejson_size:external_size(Body) % Where empty is {} which is 2)
+
+
+-define(NUM_RANDOM_TESTS, 1000).
+
+
+-define(DOC_IDS, [
+    {0, <<>>},
+    {1, <<"a">>},
+    {3, <<"foo">>},
+    {6, <<"foobar">>},
+    {32, <<"af196ae095631b020eedf8f69303e336">>}
+]).
+
+-define(REV_STARTS, [
+    {1, 0},
+    {2, 1},
+    {2, 255},
+    {3, 256},
+    {3, 65535},
+    {4, 65536},
+    {4, 16777215},
+    {5, 16777216},
+    {5, 4294967295},
+    {6, 4294967296},
+    {6, 1099511627775},
+    {7, 1099511627776},
+    {7, 281474976710655},
+    {8, 281474976710656},
+    {8, 72057594037927935},
+    {9, 72057594037927936},
+    {9, 18446744073709551615},
+
+    % The jump from 9 to 11 bytes is because when we
+    % spill over into the bigint range of 9-255
+    % bytes we have an extra byte that encodes the
+    % length of the bigint.
+    {11, 18446744073709551616}
+]).
+
+-define(REVS, [
+    {0, <<>>},
+    {8, <<"foobarba">>},
+    {16, <<"foobarbazbambang">>}
+]).
+
+-define(DELETED, [
+    {1, true},
+    {1, false}
+]).
+
+-define(BODIES, [
+    {2, {[]}},
+    {13, {[{<<"foo">>, <<"bar">>}]}},
+    {28, {[{<<"b">>, <<"a">>}, {<<"c">>, [true, null, []]}]}}
+]).
+
+-define(ATT_NAMES, [
+    {5, <<"a.txt">>},
+    {7, <<"foo.csv">>},
+    {29, <<"a-longer-name-for-example.bat">>}
+]).
+
+-define(ATT_TYPES, [
+    {24, <<"application/octet-stream">>},
+    {10, <<"text/plain">>},
+    {9, <<"image/png">>}
+]).
+
+-define(ATT_BODIES, [
+    {0, <<>>},
+    {1, <<"g">>},
+    {6, <<"foobar">>},
+    {384, <<
+        "xlasdjfsapoiewrposdlfadfuaducvwerwlkdsfljdfusfsd"
+        "xlasdjfsapoiewrposdlfadfuaducvwerwlkdsfljdfusfsd"
+        "xlasdjfsapoiewrposdlfadfuaducvwerwlkdsfljdfusfsd"
+        "xlasdjfsapoiewrposdlfadfuaducvwerwlkdsfljdfusfsd"
+        "xlasdjfsapoiewrposdlfadfuaducvwerwlkdsfljdfusfsd"
+        "xlasdjfsapoiewrposdlfadfuaducvwerwlkdsfljdfusfsd"
+        "xlasdjfsapoiewrposdlfadfuaducvwerwlkdsfljdfusfsd"
+        "xlasdjfsapoiewrposdlfadfuaducvwerwlkdsfljdfusfsd"
+    >>}
+]).
+
+
+-define(ATT_HEADERS, [
+    {0, undefined},
+    {2, {[]}},
+    {13, {[{<<"foo">>, <<"bar">>}]}},
+    {32, {[{<<"a">>, <<"header">>}, {<<"b">>, <<"such header">>}]}}
+]).
+
+
+empty_doc_test() ->
+    ?assertEqual(4, fabric2_util:rev_size(#doc{})).
+
+
+docid_size_test() ->
+    lists:foreach(fun({Size, DocId}) ->
+        ?assertEqual(4 + Size, fabric2_util:rev_size(#doc{id = DocId}))
+    end, ?DOC_IDS).
+
+
+rev_size_test() ->
+    lists:foreach(fun({StartSize, Start}) ->
+        lists:foreach(fun({RevSize, Rev}) ->
+            Doc = #doc{
+                revs = {Start, [Rev]}
+            },
+            ?assertEqual(3 + StartSize + RevSize, fabric2_util:rev_size(Doc))
+        end, ?REVS)
+    end, ?REV_STARTS).
+
+
+deleted_size_test() ->
+    lists:foreach(fun({Size, Deleted}) ->
+        ?assertEqual(3 + Size, fabric2_util:rev_size(#doc{deleted = Deleted}))
+    end, ?DELETED).
+
+
+body_size_test() ->
+    lists:foreach(fun({Size, Body}) ->
+        ?assertEqual(2 + Size, fabric2_util:rev_size(#doc{body = Body}))
+    end, ?BODIES).
+
+
+att_names_test() ->
+    lists:foreach(fun({Size, AttName}) ->
+        Att = mk_att(AttName, <<>>, <<>>, false),
+        Doc = #doc{atts = [Att]},
+        ?assertEqual(4 + Size, fabric2_util:rev_size(Doc))
+    end, ?ATT_NAMES).
+
+
+att_types_test() ->
+    lists:foreach(fun({Size, AttType}) ->
+        Att = mk_att(<<"foo">>, AttType, <<>>, false),
+        Doc = #doc{atts = [Att]},
+        ?assertEqual(7 + Size, fabric2_util:rev_size(Doc))
+    end, ?ATT_TYPES).
+
+
+att_bodies_test() ->
+    lists:foreach(fun({Size, AttBody}) ->
+        Att1 = mk_att(<<"foo">>, <<>>, AttBody, false),
+        Doc1 = #doc{atts = [Att1]},
+        ?assertEqual(7 + Size, fabric2_util:rev_size(Doc1)),
+
+        Att2 = mk_att(<<"foo">>, <<>>, AttBody, true),
+        Doc2 = #doc{atts = [Att2]},
+        ?assertEqual(7 + 16 + Size, fabric2_util:rev_size(Doc2))
+    end, ?ATT_BODIES).
+
+
+att_headers_test() ->
+    lists:foreach(fun({Size, AttHeaders}) ->
+        Att = mk_att(<<"foo">>, <<>>, <<>>, false, AttHeaders),
+        Doc = #doc{atts = [Att]},
+        ?assertEqual(7 + Size, fabric2_util:rev_size(Doc))
+    end, ?ATT_HEADERS).
+
+
+combinatorics_test() ->
+    Elements = [
+        {?DOC_IDS, fun(Doc, DocId) -> Doc#doc{id = DocId} end},
+        {?REV_STARTS, fun(Doc, RevStart) ->
+            #doc{revs = {_, RevIds}} = Doc,
+            Doc#doc{revs = {RevStart, RevIds}}
+        end},
+        {?REVS, fun(Doc, Rev) ->
+           #doc{revs = {Start, _}} = Doc,
+           Doc#doc{revs = {Start, [Rev]}}
+        end},
+        {?DELETED, fun(Doc, Deleted) -> Doc#doc{deleted = Deleted} end},
+        {?BODIES, fun(Doc, Body) -> Doc#doc{body = Body} end}
+    ],
+    combine(Elements, 0, #doc{}).
+
+
+combine([], TotalSize, Doc) ->
+    ?assertEqual(TotalSize, fabric2_util:rev_size(Doc));
+
+combine([{Elems, UpdateFun} | Rest], TotalSize, Doc) ->
+    lists:foreach(fun({Size, Elem}) ->
+        combine(Rest, TotalSize + Size, UpdateFun(Doc, Elem))
+    end, Elems).
+
+
+random_docs_test() ->
+    lists:foreach(fun(_) ->
+        {DocIdSize, DocId} = choose(?DOC_IDS),
+        {RevStartSize, RevStart} = choose(?REV_STARTS),
+        {RevSize, Rev} = choose(?REVS),
+        {DeletedSize, Deleted} = choose(?DELETED),
+        {BodySize, Body} = choose(?BODIES),
+        NumAtts = choose([0, 1, 2, 5]),
+        {Atts, AttSize} = lists:mapfoldl(fun(_, Acc) ->
+            {S, A} = random_att(),
+            {A, Acc + S}
+        end, 0, lists:seq(1, NumAtts)),
+        Doc = #doc{
+            id = DocId,
+            revs = {RevStart, [Rev]},
+            deleted = Deleted,
+            body = Body,
+            atts = Atts
+        },
+        Expect = lists:sum([
+            DocIdSize,
+            RevStartSize,
+            RevSize,
+            DeletedSize,
+            BodySize,
+            AttSize
+        ]),
+        ?assertEqual(Expect, fabric2_util:rev_size(Doc))
+    end, lists:seq(1, ?NUM_RANDOM_TESTS)).
+
+
+random_att() ->
+    {NameSize, Name} = choose(?ATT_NAMES),
+    {TypeSize, Type} = choose(?ATT_TYPES),
+    {BodySize, Body} = choose(?ATT_BODIES),
+    {Md5Size, AddMd5} = choose([{0, false}, {16, true}]),
+    {HdrSize, Headers} = choose(?ATT_HEADERS),
+    AttSize = lists:sum([NameSize, TypeSize, BodySize, Md5Size, HdrSize]),
+    {AttSize, mk_att(Name, Type, Body, AddMd5, Headers)}.
+
+
+mk_att(Name, Type, Data, AddMd5) ->
+    mk_att(Name, Type, Data, AddMd5, undefined).
+
+mk_att(Name, Type, Data, AddMd5, Headers) ->
+    Md5 = if not AddMd5 -> <<>>; true ->
+        erlang:md5(Data)
+    end,
+    couch_att:new([
+        {name, Name},
+        {type, Type},
+        {att_len, size(Data)},
+        {data, Data},
+        {encoding, identity},
+        {md5, Md5},
+        {headers, Headers}
+    ]).
+
+
+choose(Options) ->
+    Pos = rand:uniform(length(Options)),
+    lists:nth(Pos, Options).