summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul J. Davis <paul.joseph.davis@gmail.com>2020-02-10 16:41:42 -0600
committerPaul J. Davis <paul.joseph.davis@gmail.com>2020-02-12 17:19:39 -0600
commit9d5062bf8f9b9b92db54e7b28d12d072ad560a99 (patch)
tree2c9b7689978f0afa22d688b2755c2e260a674e95
parent0161223c6acb3204c520044efdd2c509b2596809 (diff)
downloadcouchdb-9d5062bf8f9b9b92db54e7b28d12d072ad560a99.tar.gz
fixup: add size tests
-rw-r--r--src/fabric/test/fabric2_doc_size_tests.erl274
1 files changed, 274 insertions, 0 deletions
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 000000000..37f174044
--- /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).