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/13 20:13:37 UTC
[couchdb] branch prototype/fdb-layer-get-dbs-info updated: more db
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
The following commit(s) were added to refs/heads/prototype/fdb-layer-get-dbs-info by this push:
new ec59327 more db size tests
ec59327 is described below
commit ec59327c39cb340420ba997e2d7575959356bf5a
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Thu Feb 13 14:16:09 2020 -0600
more db size tests
---
src/fabric/test/fabric2_db_size_tests.erl | 545 +++++++++++++++++++++++++-----
1 file changed, 452 insertions(+), 93 deletions(-)
diff --git a/src/fabric/test/fabric2_db_size_tests.erl b/src/fabric/test/fabric2_db_size_tests.erl
index e963670..3ffca9f 100644
--- a/src/fabric/test/fabric2_db_size_tests.erl
+++ b/src/fabric/test/fabric2_db_size_tests.erl
@@ -22,56 +22,49 @@
-include("fabric2_test.hrl").
--define(DIFF(Db, Change, Fun), begin
- ((fun() ->
- __Before = db_size(Db),
- __Result = Fun(),
- __After = db_size(Db),
- ?debugFmt("~p - ~p == ~p ?= ~p", [
- __After,
- __Before,
- __After - __Before,
- Change
- ]),
- ?assertEqual(Change, __After - __Before),
- __Result
- end)())
- end
-).
-
-
db_size_test_() ->
{
- "Test document CRUD operations",
+ "Test database size calculations",
{
setup,
fun setup/0,
fun cleanup/1,
with([
?TDEF(new_doc),
+ ?TDEF(replicate_new_doc),
?TDEF(edit_doc),
?TDEF(delete_doc),
?TDEF(create_conflict),
?TDEF(replicate_new_winner),
?TDEF(replicate_deep_deleted),
?TDEF(delete_winning_revision),
- ?TDEF(delete_conflict_revision)
+ ?TDEF(delete_conflict_revision),
+ ?TDEF(replicate_existing_revision),
+ ?TDEF(create_doc_with_attachment),
+ ?TDEF(add_attachment_in_update),
+ ?TDEF(add_second_attachment),
+ ?TDEF(delete_attachment),
+ ?TDEF(delete_one_attachment),
+ ?TDEF(delete_all_attachments),
+ ?TDEF(re_add_attachment),
+ ?TDEF(update_and_remove_attachment),
+ ?TDEF(replicate_new_doc_with_attachment),
+ ?TDEF(replicate_remove_attachment),
+ ?TDEF(replicate_stub_attachment),
+ ?TDEF(replicate_stub_and_new_attachment),
+ ?TDEF(replicate_new_att_to_winner),
+ ?TDEF(replicate_change_att_to_winner),
+ ?TDEF(replicate_rem_att_from_winner),
+ ?TDEF(replicate_stub_to_winner),
+ ?TDEF(replicate_new_att_to_conflict),
+ ?TDEF(replicate_change_att_to_conflict),
+ ?TDEF(replicate_rem_att_from_conflict),
+ ?TDEF(replicate_stub_to_conflict)
])
}
}.
-% TODO:
-% replicate existing revision
-% replicate with attachment
-% replicate removing attachment
-% replicate reusing attachment
-% replicate adding attachment with stub
-% for each, replicate to winner vs non-winner
-% for each, replicate extending winner, vs extending conflict vs new branch
-
-
-
setup() ->
Ctx = test_util:start_couch([fabric]),
{ok, Db} = fabric2_db:create(?tempdb(), [{user_ctx, ?ADMIN_USER}]),
@@ -84,68 +77,276 @@ cleanup({Db, Ctx}) ->
new_doc({Db, _}) ->
- Actions = [
+ check(Db, [
{create, #{tgt => rev1}}
- ],
- check(Db, Actions).
+ ]).
+
+
+replicate_new_doc({Db, _}) ->
+ check(Db, [
+ {replicate, #{tgt => rev1}}
+ ]).
edit_doc({Db, _}) ->
- Actions = [
+ check(Db, [
{create, #{tgt => rev1}},
{update, #{src => rev1, tgt => rev2}}
- ],
- check(Db, Actions).
+ ]).
delete_doc({Db, _}) ->
- Actions = [
+ check(Db, [
{create, #{tgt => rev1}},
{delete, #{src => rev1, tgt => rev2}}
- ],
- check(Db, Actions).
+ ]).
create_conflict({Db, _}) ->
- Actions = [
+ check(Db, [
{create, #{tgt => rev1}},
{replicate, #{tgt => rev2}}
- ],
- check(Db, Actions).
+ ]).
replicate_new_winner({Db, _}) ->
- Actions = [
+ check(Db, [
{create, #{tgt => rev1}},
{replicate, #{tgt => rev2, depth => 3}}
- ],
- check(Db, Actions).
+ ]).
replicate_deep_deleted({Db, _}) ->
- Actions = [
+ check(Db, [
{create, #{tgt => rev1, depth => 2}},
{replicate, #{tgt => rev2, depth => 5, deleted => true}}
- ],
- check(Db, Actions).
+ ]).
delete_winning_revision({Db, _}) ->
- Actions = [
+ check(Db, [
{create, #{tgt => rev1}},
{replicate, #{tgt => rev2}},
- {delete, #{src => {min, [rev1, rev2]}, tgt => rev3}}
- ],
- check(Db, Actions).
+ {delete, #{src => {winner, [rev1, rev2]}, tgt => rev3}}
+ ]).
delete_conflict_revision({Db, _}) ->
- Actions = [
+ check(Db, [
{create, #{tgt => rev1}},
{replicate, #{tgt => rev2}},
- {delete, #{src => {max, [rev1, rev2]}, tgt => rev3}}
- ],
- check(Db, Actions).
+ {delete, #{src => {conflict, [rev1, rev2]}, tgt => rev3}}
+ ]).
+
+
+replicate_existing_revision({Db, _}) ->
+ check(Db, [
+ {create, #{tgt => rev1}},
+ {replicate, #{src => rev1, tgt => rev2, depth => 0}}
+ ]).
+
+
+create_doc_with_attachment({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {create, #{tgt => rev1, atts => [att1]}}
+ ]).
+
+
+add_attachment_in_update({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {create, #{tgt => rev1}},
+ {update, #{src => rev1, tgt => rev2, atts => [att1]}}
+ ]).
+
+
+add_second_attachment({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {mk_att, #{tgt => att2}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {update, #{src => rev1, tgt => rev2, atts => [att1, att2]}}
+ ]).
+
+
+delete_attachment({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {update, #{src => rev1, tgt => rev2}}
+ ]).
+
+
+delete_one_attachment({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {mk_att, #{tgt => att2}},
+ {mk_att, #{tgt => att3, stub => att1, revpos => 1}},
+ {create, #{tgt => rev1, atts => [att1, att2]}},
+ {update, #{src => rev1, tgt => rev2, atts => [att3]}}
+ ]).
+
+
+delete_all_attachments({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {mk_att, #{tgt => att2}},
+ {create, #{tgt => rev1, atts => [att1, att2]}},
+ {update, #{src => rev1, tgt => rev2, atts => []}}
+ ]).
+
+
+re_add_attachment({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {update, #{src => rev1, tgt => rev2}},
+ {update, #{src => rev2, tgt => rev3, atts => [att1]}}
+ ]).
+
+
+update_and_remove_attachment({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {mk_att, #{tgt => att2}},
+ {mk_att, #{tgt => att3, stub => att1, revpos => 1}},
+ {mk_att, #{tgt => att4}},
+ {create, #{tgt => rev1, atts => [att1, att2]}},
+ {update, #{src => rev1, tgt => rev2, atts => [att3, att4]}}
+ ]).
+
+
+replicate_new_doc_with_attachment({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {replicate, #{tgt => rev1, atts => [att1]}}
+ ]).
+
+
+replicate_remove_attachment({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {replicate, #{src => rev1, tgt => rev2}}
+ ]).
+
+
+replicate_stub_attachment({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {mk_att, #{tgt => att2, stub => att1, revpos => 1}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {replicate, #{src => rev1, tgt => rev2, atts => [att2]}}
+ ]).
+
+
+replicate_stub_and_new_attachment({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {mk_att, #{tgt => att2, stub => att1, revpos => 1}},
+ {mk_att, #{tgt => att3}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {replicate, #{src => rev1, tgt => rev2, atts => [att2, att3]}}
+ ]).
+
+
+replicate_new_att_to_winner({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {create, #{tgt => rev1}},
+ {replicate, #{tgt => rev2}},
+ {replicate, #{
+ src => {winner, [rev1, rev2]},
+ tgt => rev3,
+ atts => [att1]}
+ }
+ ]).
+
+
+replicate_change_att_to_winner({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {mk_att, #{tgt => att2}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {replicate, #{tgt => rev2, atts => [att1]}},
+ {replicate, #{
+ src => {winner, [rev1, rev2]},
+ tgt => rev3,
+ atts => [att2]}
+ }
+ ]).
+
+
+replicate_rem_att_from_winner({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {replicate, #{tgt => rev2, atts => [att1]}},
+ {replicate, #{src => {winner, [rev1, rev2]}, tgt => rev3}}
+ ]).
+
+
+replicate_stub_to_winner({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {mk_att, #{tgt => att2, stub => att1, revpos => 1}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {replicate, #{tgt => rev2, atts => [att1]}},
+ {replicate, #{
+ src => {winner, [rev1, rev2]},
+ tgt => rev3,
+ atts => [att2]}}
+ ]).
+
+
+replicate_new_att_to_conflict({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {create, #{tgt => rev1}},
+ {replicate, #{tgt => rev2}},
+ {replicate, #{
+ src => {conflict, [rev1, rev2]},
+ tgt => rev3,
+ atts => [att1]}
+ }
+ ]).
+
+
+replicate_change_att_to_conflict({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {mk_att, #{tgt => att2}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {replicate, #{tgt => rev2, atts => [att1]}},
+ {replicate, #{
+ src => {conflict, [rev1, rev2]},
+ tgt => rev3,
+ atts => [att2]}
+ }
+ ]).
+
+
+replicate_rem_att_from_conflict({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {replicate, #{tgt => rev2, atts => [att1]}},
+ {replicate, #{src => {conflict, [rev1, rev2]}, tgt => rev3}}
+ ]).
+
+
+replicate_stub_to_conflict({Db, _}) ->
+ check(Db, [
+ {mk_att, #{tgt => att1}},
+ {mk_att, #{tgt => att2, stub => att1, revpos => 1}},
+ {create, #{tgt => rev1, atts => [att1]}},
+ {replicate, #{tgt => rev2, atts => [att1]}},
+ {replicate, #{
+ src => {conflict, [rev1, rev2]},
+ tgt => rev3,
+ atts => [att2]}}
+ ]).
check(Db, Actions) ->
@@ -160,7 +361,9 @@ check(Db, Actions) ->
create -> create_doc(Db, Opts, StAcc);
update -> update_doc(Db, Opts, StAcc);
delete -> delete_doc(Db, Opts, StAcc);
- replicate -> replicate_doc(Db, Opts, StAcc)
+ replicate -> replicate_doc(Db, Opts, StAcc);
+ mk_att -> make_attachment(Opts, StAcc);
+ log_state -> log_state(Opts, StAcc)
end
end, InitSt, Actions).
@@ -169,24 +372,36 @@ create_doc(Db, Opts, St) ->
#{
doc_id := DocId,
revs := Revs,
+ atts := Atts,
size := InitDbSize
} = St,
?assert(maps:is_key(tgt, Opts)),
Tgt = maps:get(tgt, Opts),
+ AttKeys = maps:get(atts, Opts, []),
Depth = maps:get(depth, Opts, 1),
?assert(not maps:is_key(Tgt, Revs)),
+ lists:foreach(fun(AttKey) ->
+ ?assert(maps:is_key(AttKey, Atts))
+ end, AttKeys),
?assert(Depth >= 1),
+ AttRecords = lists:map(fun(AttKey) ->
+ maps:get(AttKey, Atts)
+ end, AttKeys),
+
InitDoc = #doc{id = DocId},
- FinalDoc = lists:foldl(fun(_, Doc0) ->
+ FinalDoc = lists:foldl(fun(Iter, Doc0) ->
#doc{
revs = {_OldStart, OldRevs}
} = Doc1 = randomize_doc(Doc0),
- {ok, {Pos, Rev}} = fabric2_db:update_doc(Db, Doc1),
- Doc1#doc{revs = {Pos, [Rev | OldRevs]}}
+ Doc2 = if Iter < Depth -> Doc1; true ->
+ Doc1#doc{atts = AttRecords}
+ end,
+ {ok, {Pos, Rev}} = fabric2_db:update_doc(Db, Doc2),
+ Doc2#doc{revs = {Pos, [Rev | OldRevs]}}
end, InitDoc, lists:seq(1, Depth)),
FinalDocSize = fabric2_util:rev_size(FinalDoc),
@@ -194,15 +409,13 @@ create_doc(Db, Opts, St) ->
?assertEqual(FinalDbSize - InitDbSize, FinalDocSize),
- St#{
- revs := maps:put(Tgt, FinalDoc, Revs),
- size := FinalDbSize
- }.
+ store_rev(Db, St, FinalDbSize, Tgt, FinalDoc).
update_doc(Db, Opts, St) ->
#{
revs := Revs,
+ atts := Atts,
size := InitDbSize
} = St,
@@ -211,19 +424,30 @@ update_doc(Db, Opts, St) ->
Src = pick_rev(Revs, maps:get(src, Opts)),
Tgt = maps:get(tgt, Opts),
+ AttKeys = maps:get(atts, Opts, []),
Depth = maps:get(depth, Opts, 1),
?assert(maps:is_key(Src, Revs)),
?assert(not maps:is_key(Tgt, Revs)),
+ lists:foreach(fun(AttKey) ->
+ ?assert(maps:is_key(AttKey, Atts))
+ end, AttKeys),
?assert(Depth >= 1),
+ AttRecords = lists:map(fun(AttKey) ->
+ maps:get(AttKey, Atts)
+ end, AttKeys),
+
InitDoc = maps:get(Src, Revs),
- FinalDoc = lists:foldl(fun(_, Doc0) ->
+ FinalDoc = lists:foldl(fun(Iter, Doc0) ->
#doc{
revs = {_OldStart, OldRevs}
} = Doc1 = randomize_doc(Doc0),
- {ok, {Pos, Rev}} = fabric2_db:update_doc(Db, Doc1),
- Doc1#doc{revs = {Pos, [Rev | OldRevs]}}
+ Doc2 = if Iter < Depth -> Doc1; true ->
+ Doc1#doc{atts = AttRecords}
+ end,
+ {ok, {Pos, Rev}} = fabric2_db:update_doc(Db, Doc2),
+ Doc2#doc{revs = {Pos, [Rev | OldRevs]}}
end, InitDoc, lists:seq(1, Depth)),
InitDocSize = fabric2_util:rev_size(InitDoc),
@@ -232,10 +456,7 @@ update_doc(Db, Opts, St) ->
?assertEqual(FinalDbSize - InitDbSize, FinalDocSize - InitDocSize),
- St#{
- revs := maps:put(Tgt, FinalDoc, Revs),
- size := FinalDbSize
- }.
+ store_rev(Db, St, FinalDbSize, Tgt, FinalDoc).
delete_doc(Db, Opts, St) ->
@@ -270,16 +491,14 @@ delete_doc(Db, Opts, St) ->
?assertEqual(FinalDbSize - InitDbSize, FinalDocSize - InitDocSize),
- St#{
- revs := maps:put(Tgt, FinalDoc, Revs),
- size := FinalDbSize
- }.
+ store_rev(Db, St, FinalDbSize, Tgt, FinalDoc).
replicate_doc(Db, Opts, St) ->
#{
doc_id := DocId,
revs := Revs,
+ atts := Atts,
size := InitDbSize
} = St,
@@ -288,6 +507,7 @@ replicate_doc(Db, Opts, St) ->
Src = pick_rev(Revs, maps:get(src, Opts, undefined)),
Tgt = maps:get(tgt, Opts),
Deleted = maps:get(deleted, Opts, false),
+ AttKeys = maps:get(atts, Opts, []),
Depth = maps:get(depth, Opts, 1),
if Src == undefined -> ok; true ->
@@ -295,14 +515,17 @@ replicate_doc(Db, Opts, St) ->
end,
?assert(not maps:is_key(Tgt, Revs)),
?assert(is_boolean(Deleted)),
- ?assert(Depth >= 1),
+ lists:foreach(fun(AttKey) ->
+ ?assert(maps:is_key(AttKey, Atts))
+ end, AttKeys),
+ ?assert(Depth >= 0),
- InitDoc1 = maps:get(Src, Revs, #doc{id = DocId}),
- InitDoc2 = case Deleted of
- true -> randomize_deleted_doc(InitDoc1);
- false -> randomize_doc(InitDoc1)
+ if Depth > 0 -> ok; true ->
+ ?assert(length(AttKeys) == 0)
end,
- FinalDoc = lists:foldl(fun(_, Doc0) ->
+
+ InitDoc = maps:get(Src, Revs, #doc{id = DocId}),
+ NewRevsDoc = lists:foldl(fun(_, Doc0) ->
#doc{
revs = {RevStart, RevIds}
} = Doc0,
@@ -310,11 +533,22 @@ replicate_doc(Db, Opts, St) ->
Doc0#doc{
revs = {RevStart + 1, [NewRev | RevIds]}
}
- end, InitDoc2, lists:seq(1, Depth)),
+ end, InitDoc, lists:seq(1, Depth)),
+
+ FinalDoc = if NewRevsDoc == InitDoc -> NewRevsDoc; true ->
+ UpdateDoc = case Deleted of
+ true -> randomize_deleted_doc(NewRevsDoc);
+ false -> randomize_doc(NewRevsDoc)
+ end,
+ AttRecords = lists:map(fun(AttKey) ->
+ maps:get(AttKey, Atts)
+ end, AttKeys),
+ UpdateDoc#doc{atts = AttRecords}
+ end,
{ok, _} = fabric2_db:update_doc(Db, FinalDoc, [replicated_changes]),
- InitDocSize = fabric2_util:rev_size(InitDoc1),
+ InitDocSize = fabric2_util:rev_size(InitDoc),
FinalDocSize = fabric2_util:rev_size(FinalDoc),
FinalDbSize = db_size(Db),
@@ -324,15 +558,99 @@ replicate_doc(Db, Opts, St) ->
end,
?assertEqual(FinalDbSize - InitDbSize, SizeChange),
- St#{
- revs := maps:put(Tgt, FinalDoc, Revs),
- size := FinalDbSize
- }.
+ store_rev(Db, St, FinalDbSize, Tgt, FinalDoc).
+
+
+make_attachment(Opts, St) ->
+ #{
+ atts := Atts
+ } = St,
+
+ ?assert(maps:is_key(tgt, Opts)),
+
+ Tgt = maps:get(tgt, Opts),
+ Stub = maps:get(stub, Opts, undefined),
+ RevPos = maps:get(revpos, Opts, undefined),
+ NameRaw = maps:get(name, Opts, undefined),
+
+ ?assert(not maps:is_key(Tgt, Atts)),
+ if Stub == undefined -> ok; true ->
+ ?assert(maps:is_key(Stub, Atts))
+ end,
+ ?assert(RevPos == undefined orelse RevPos >= 0),
+
+ Name = if
+ NameRaw == undefined -> undefined;
+ is_atom(NameRaw) -> atom_to_binary(NameRaw, utf8);
+ is_binary(NameRaw) -> NameRaw;
+ is_list(NameRaw) -> list_to_binary(NameRaw)
+ end,
+
+ Att0 = case Stub of
+ undefined ->
+ random_attachment(Name);
+ _ ->
+ SrcAtt = maps:get(Stub, Atts),
+ couch_att:store(data, stub, SrcAtt)
+ end,
+ Att1 = if RevPos == undefined -> Att0; true ->
+ couch_att:store(revpos, RevPos, Att0)
+ end,
+
+ St#{atts := maps:put(Tgt, Att1, Atts)}.
+
+
+log_state(_Opts, St) ->
+ #{
+ doc_id := DocId,
+ revs := Revs,
+ atts := Atts,
+ size := DbSize
+ } = St,
+
+ ?debugFmt("~nDocId: ~p~n", [DocId]),
+ ?debugFmt("Db Size: ~p~n~n", [DbSize]),
+
+ RevKeys = maps:keys(Revs),
+ lists:foreach(fun(RevKey) ->
+ #doc{
+ id = RevDocId,
+ revs = {Pos, [Rev | RestRevs]},
+ body = Body,
+ deleted = Deleted,
+ atts = DocAtts,
+ meta = Meta
+ } = Doc = maps:get(RevKey, Revs),
+ ?debugFmt("Doc: ~p (~p)~n", [RevKey, fabric2_util:rev_size(Doc)]),
+ ?debugFmt("Id: ~p~n", [RevDocId]),
+ ?debugFmt("Rev: ~p ~w~n", [Pos, Rev]),
+ lists:foreach(fun(R) ->
+ ?debugFmt(" ~p~n", [R])
+ end, RestRevs),
+ ?debugFmt("Deleted: ~p~n", [Deleted]),
+ ?debugFmt("Atts:~n", []),
+ lists:foreach(fun(Att) ->
+ ?debugFmt(" ~p~n", [Att])
+ end, DocAtts),
+ ?debugFmt("Body: ~p~n", [Body]),
+ ?debugFmt("Meta: ~p~n", [Meta]),
+ ?debugFmt("~n", [])
+ end, lists:sort(RevKeys)),
+
+ AttKeys = maps:keys(Atts),
+ ?debugFmt("~n~nAtts:~n", []),
+ lists:foreach(fun(AttKey) ->
+ Att = maps:get(AttKey, Atts),
+ ?debugFmt("Att: ~p (~p)~n", [AttKey, couch_att:external_size(Att)]),
+ ?debugFmt(" ~p~n", [Att])
+ end, lists:sort(AttKeys)),
+
+ St.
pick_rev(_Revs, Rev) when is_atom(Rev) ->
Rev;
-pick_rev(Revs, {Op, RevList}) when Op == min; Op == max ->
+pick_rev(Revs, {Op, RevList}) when Op == winner; Op == conflict ->
ChooseFrom = lists:map(fun(Rev) ->
#doc{
revs = {S, [R | _]},
@@ -346,12 +664,31 @@ pick_rev(Revs, {Op, RevList}) when Op == min; Op == max ->
end, RevList),
Sorted = fabric2_util:sort_revinfos(ChooseFrom),
RetRev = case Op of
- min -> lists:last(Sorted);
- max -> hd(Sorted)
+ winner -> hd(Sorted);
+ conflict -> choose(tl(Sorted))
end,
maps:get(name, RetRev).
+store_rev(Db, St, DbSize, Tgt, #doc{} = Doc) ->
+ #{
+ revs := Revs
+ } = St,
+ #doc{
+ id = DocId,
+ revs = {Pos, [Rev | _]}
+ } = Doc,
+ RevId = {Pos, Rev},
+ {ok, [{ok, DbDoc}]} = fabric2_db:open_doc_revs(Db, DocId, [RevId], []),
+
+ ?assert(not maps:is_key(Tgt, Revs)),
+
+ St#{
+ revs := maps:put(Tgt, DbDoc, Revs),
+ size := DbSize
+ }.
+
+
randomize_doc(#doc{} = Doc) ->
Doc#doc{
deleted = false,
@@ -376,7 +713,6 @@ db_size(Db) when is_map(Db) ->
db_size(Info).
-
-define(MAX_JSON_ELEMENTS, 5).
-define(MAX_STRING_LEN, 10).
-define(MAX_INT, 4294967296).
@@ -434,6 +770,28 @@ random_json_number() ->
random_json_string() ->
+ random_string(0, ?MAX_STRING_LEN).
+
+
+random_attachment(undefined) ->
+ random_attachment(random_string(1, 32));
+
+random_attachment(Name) when is_binary(Name) ->
+ Type = random_string(1, 32),
+ Data = random_string(1, 512),
+ Md5 = erlang:md5(Data),
+ couch_att:new([
+ {name, Name},
+ {type, Type},
+ {att_len, size(Data)},
+ {data, Data},
+ {encoding, identity},
+ {md5, Md5},
+ {headers, undefined}
+ ]).
+
+
+random_string(MinLen, MaxLen) ->
Alphabet = [
$a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m,
$n, $o, $p, $q, $r, $s, $t, $u, $v, $w, $x, $y, $z,
@@ -444,13 +802,14 @@ random_json_string() ->
$ , ${, $}, $[, $], $", $', $-, $_, $+, $=, $,, $.,
$\x{1}, $\x{a2}, $\x{20ac}, $\x{10348}
],
- Len = rand:uniform(?MAX_STRING_LEN) - 1,
+ Len = MinLen + rand:uniform(MaxLen - MinLen) - 1,
Str = lists:map(fun(_) ->
choose(Alphabet)
end, lists:seq(1, Len)),
unicode:characters_to_binary(Str).
+
choose(Options) ->
Pos = rand:uniform(length(Options)),
lists:nth(Pos, Options).
\ No newline at end of file