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/03/03 16:30:49 UTC

[couchdb] 03/03: WIP: generating test cases

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

davisp pushed a commit to branch prototype/fdb-layer-couch-views-size-tests
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 685481f5381a621479598f2e3288258fdf25996e
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Tue Mar 3 10:30:25 2020 -0600

    WIP: generating test cases
---
 src/couch_views/test/couch_views_size_test.erl | 243 ++++++++++++++++++++-----
 1 file 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 888a7ea..16a6708 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.