diff options
author | Paul J. Davis <paul.joseph.davis@gmail.com> | 2020-03-03 10:30:25 -0600 |
---|---|---|
committer | Paul J. Davis <paul.joseph.davis@gmail.com> | 2020-03-03 10:30:25 -0600 |
commit | 685481f5381a621479598f2e3288258fdf25996e (patch) | |
tree | 8ec008af9920029027424b5e1b9696925dffc2ad | |
parent | b8742a7332511ad26aa33a305a798bed6ce71e82 (diff) | |
download | couchdb-685481f5381a621479598f2e3288258fdf25996e.tar.gz |
WIP: generating test cases
-rw-r--r-- | src/couch_views/test/couch_views_size_test.erl | 243 |
1 files changed, 195 insertions, 48 deletions
diff --git a/src/couch_views/test/couch_views_size_test.erl b/src/couch_views/test/couch_views_size_test.erl index 888a7ea51..16a67080a 100644 --- a/src/couch_views/test/couch_views_size_test.erl +++ b/src/couch_views/test/couch_views_size_test.erl @@ -83,24 +83,48 @@ generate_sets() -> end, [], ?N_DOMAIN). --define(I_HEART_EUNIT(Tests), [{with, [T]} || T <- Tests]). +generate_transitions() -> + Sets = generate_sets(), + Pairs = [{Set1, Set2} || Set1 <- Sets, Set2 <- Sets], + lists:flatmap(fun({{N1, D1, R1} = S1, {N2, D2, R2} = S2}) -> + lists:foldl(fun(DeltaN, DNAcc) -> + case DeltaN > min(N1, N2) of + true -> + DNAcc; + false when DeltaN == 0 -> + [{S1, S2, {0, 0, 0}}]; + false -> + lists:foldl(fun(DeltaD, DDAcc) -> + case DeltaD > min(D1, D2) orelse DeltaD > DeltaN of + true -> + DDAcc; + false when DeltaD == 0 -> + [{S1, S2, {DeltaN, 0, 0}} | DDAcc]; + false -> + lists:foldl(fun(DeltaR, DRAcc) -> + case DeltaR > min(R1, R2) of + true -> + DRAcc; + false -> + T = {DeltaN, DeltaD, DeltaR}, + [{S1, S2, T} | DRAcc] + end + end, DDAcc, ?DELTA_R_DOMAIN) + end + end, DNAcc, ?DELTA_D_DOMAIN) + end + end, [], ?DELTA_N_DOMAIN) + end, Pairs). -size_test_() -> +row_transition_test_() -> { "Test view size tracking", { setup, fun setup/0, fun cleanup/1, - { - foreach, - fun foreach_setup/0, - fun foreach_teardown/1, - ?I_HEART_EUNIT([ - fun single_view/1 - ]) - } + fun create_transition_tests/1 } }. @@ -112,52 +136,175 @@ setup() -> couch_js, couch_views ]), - Ctx. + {ok, Db} = fabric2_db:create(?tempdb(), [{user_ctx, ?ADMIN_USER}]), + {Ctx, Db}. -cleanup(Ctx) -> +cleanup({Ctx, Db}) -> + ok = fabric2_db:delete(fabric2_db:name(Db), []), test_util:stop_couch(Ctx). -foreach_setup() -> - {ok, Db} = fabric2_db:create(?tempdb(), [{user_ctx, ?ADMIN_USER}]), - Db. +create_transition_tests({_Ctx, Db}) -> + lists:map(fun(T) -> + Name = lists:flatten(io_lib:format("~w", [T])), + {Name, fun() -> check_transition(Db, T) end} + end, lists:sort(generate_transitions())). -foreach_teardown(Db) -> - ok = fabric2_db:delete(fabric2_db:name(Db), []). +check_transition(Db, {Set1, Set2, Transition}) -> + InitKVs = init_set(Set1, [a, b, c, d, e]), + CommonKVs = reduce_set(Transition, InitKVs), + FinalKVs = fill_set(Set2, CommonKVs, [v, w, x, y, z]), + {InitJSONKVs, Bindings} = unlabel(InitKVs, #{}), + {FinalJSONKVs, _} = unlabel(FinalKVs, Bindings), + Sig = couch_uuids:random(), + DocId = couch_uuids:random(), -single_view(_Db) -> - Sets = generate_sets(), - Pairs = [{Set1, Set2} || Set1 <- Sets, Set2 <- Sets], - Transitions = lists:flatmap(fun({{N1, D1, R1} = S1, {N2, D2, R2} = S2}) -> - lists:foldl(fun(DeltaN, DNAcc) -> - case DeltaN > min(N1, N2) of + fabric2_fdb:transactional(Db, fun(TxDb) -> + Doc = make_doc(DocId, InitJSONKVs), + couch_views_indexer:write_doc(TxDb, Sig, Doc, [1]) + end), + + fabric2_fdb:transactional(Db, fun(TxDb) -> + Doc = make_doc(DocId, FinalJSONKVs), + couch_views_indexer:write_doc(TxDb, Sig, Doc, [1]) + end), + + validate_index(Db, Sig). + + +make_doc(DocId, []) -> + case rand:uniform() < 0.5 of + true -> + #{ + id => DocId, + deleted => true, + results => [] + }; + false -> + #{ + id => DocId, + deleted => false, + results => [] + } + end; +make_doc(DocId, Results) -> + #{ + id => DocId, + deleted => false, + results => Results + }. + + +validate_index(_Db, _Sig) -> + ok. + + +init_set({N, D, R}, Labels) -> + {Dupes, RestLabels} = fill_keys(D, Labels, []), + {Unique, _} = fill_keys(N - D, RestLabels, []), + % Sanity assertions + N = length(Unique) + length(Dupes), + D = length(Dupes), + {Unique, [{Key, R} || Key <- Dupes]}. + + +reduce_set({DeltaN, DeltaD, DeltaR}, {Unique, Dupes}) -> + NewDupes = lists:sublist(Dupes, DeltaD), + NewUnique = lists:sublist(Unique, DeltaN), + {NewUnique, [{Key, DeltaR} || {Key, _} <- NewDupes]}. + + +fill_set({N, D, R}, {Unique, Dupes}, Labels) -> + AddDupes = D - length(Dupes), + {NewDupes, RestLabels} = fill_keys(AddDupes, Labels, Dupes), + + AddUnique = N - length(Unique) - length(NewDupes), + {NewUnique, _} = fill_keys(AddUnique, RestLabels, Unique), + % Sanity assertions + N = length(NewUnique) + length(NewDupes), + D = length(NewDupes), + {NewUnique, lists:map(fun(Dupe) -> + case Dupe of + {_, _} -> Dupe; + A when is_atom(A) -> {A, R} + end + end, NewDupes)}. + + +fill_keys(0, Labels, Acc) -> + {Acc, Labels}; +fill_keys(Count, [Label | RestLabels], Acc) when Count > 0 -> + fill_keys(Count - 1, RestLabels, [Label | Acc]). + + +unlabel({Unique, Dupes}, Bindings) -> + lists:foldl(fun(Item, {KVAcc, BindingsAcc}) -> + {KVs, NewBindingsAcc} = unlabel_item(Item, BindingsAcc), + {KVs ++ KVAcc, NewBindingsAcc} + end, {[], Bindings}, Unique ++ Dupes). + + +unlabel_item(Label, Bindings) when is_atom(Label) -> + NewBindings = maybe_bind(Label, Bindings), + KV = maps:get(Label, Bindings), + {[KV], NewBindings}; +unlabel_item({Label, Count}, Bindings) when is_atom(Label), is_integer(Count) -> + NewBindings = maybe_bind(Label, Bindings), + {K, _} = KV = maps:get(Label, Bindings), + ToAdd = lists:map(fun(_) -> + {K, gen_value()} + end, lists:seq(1, Count - 1)), + {[KV | ToAdd], NewBindings}. + + +maybe_bind(Label, Bindings) -> + case maps:is_key(Label, Bindings) of + true -> + case rand:uniform() < 0.5 of true -> - DNAcc; - false when DeltaN == 0 -> - [{S1, S2, {0, 0, 0}}]; + rebind(Label, Bindings); false -> - lists:foldl(fun(DeltaD, DDAcc) -> - case DeltaD > min(D1, D2) orelse DeltaD > DeltaN of - true -> - DDAcc; - false when DeltaD == 0 -> - [{S1, S2, {DeltaN, 0, 0}} | DDAcc]; - false -> - lists:foldl(fun(DeltaR, DRAcc) -> - case DeltaR > min(R1, R2) of - true -> - DRAcc; - false -> - T = {DeltaN, DeltaD, DeltaR}, - [{S1, S2, T} | DRAcc] - end - end, DDAcc, ?DELTA_R_DOMAIN) - end - end, DNAcc, ?DELTA_D_DOMAIN) - end - end, [], ?DELTA_N_DOMAIN) - end, Pairs), - io:format(standard_error, "~n~n~p~n~n", [lists:sort(Transitions)]). + Bindings + end; + false -> + bind(Label, Bindings) + end. + + +bind(Label, Bindings) -> + maps:put(Label, {gen_key(), gen_value()}, Bindings). + + +rebind(Label, Bindings) -> + {Key, _} = maps:get(Label, Bindings), + maps:put(Label, {Key, gen_value()}, Bindings). + + +gen_key() -> + Unique = couch_uuids:random(), + case rand:uniform() of + N when N < 0.2 -> + [Unique, true, rand:uniform()]; + N when N < 0.4 -> + {[{Unique, true}, {<<"foo">>, [<<"bar">>, null, 1, {[]}]}]}; + _ -> + Unique + end. + + +gen_value() -> + case rand:uniform() of + N when N < 0.2 -> + [false, rand:uniform(), {[]}]; + N when N < 0.4 -> + {[{<<"a">>, 1}, {<<"b">>, 2}]}; + N when N < 0.6 -> + rand:uniform(100); + N when N < 0.8 -> + rand:uniform(); + _ -> + 1 + end. |