summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul J. Davis <paul.joseph.davis@gmail.com>2020-03-03 10:30:25 -0600
committerPaul J. Davis <paul.joseph.davis@gmail.com>2020-03-03 10:30:25 -0600
commit685481f5381a621479598f2e3288258fdf25996e (patch)
tree8ec008af9920029027424b5e1b9696925dffc2ad
parentb8742a7332511ad26aa33a305a798bed6ce71e82 (diff)
downloadcouchdb-685481f5381a621479598f2e3288258fdf25996e.tar.gz
WIP: generating test cases
-rw-r--r--src/couch_views/test/couch_views_size_test.erl243
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.