This is an automated email from the ASF dual-hosted git repository. jiangphcn pushed a commit to branch COUCHDB-3326-clustered-purge-pr2-simplify-mem3-rep in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 3dfeeae5d89005b277edc58cef44600bdbf858ae Author: Paul J. Davis <paul.joseph.da...@gmail.com> AuthorDate: Wed May 23 12:32:48 2018 -0500 Update fabric_doc_open eunit tests Modernize and fix the eunit tests in fabric_doc_open.erl --- src/fabric/src/fabric_doc_open.erl | 631 ++++++++++++++++++------------------- 1 file changed, 310 insertions(+), 321 deletions(-) diff --git a/src/fabric/src/fabric_doc_open.erl b/src/fabric/src/fabric_doc_open.erl index 9c45bd9..93f73a8 100644 --- a/src/fabric/src/fabric_doc_open.erl +++ b/src/fabric/src/fabric_doc_open.erl @@ -174,85 +174,84 @@ format_reply(not_found, _) -> format_reply(Else, _) -> Else. -is_r_met_test() -> - Workers0 = [], - Workers1 = [nil], - Workers2 = [nil,nil], - - % Successful cases - - ?assertEqual( - {true, foo}, - is_r_met([], [fabric_util:kv(foo,2)], 2) - ), - - ?assertEqual( - {true, foo}, - is_r_met([], [fabric_util:kv(foo,3)], 2) - ), - - ?assertEqual( - {true, foo}, - is_r_met([], [fabric_util:kv(foo,1)], 1) - ), - - ?assertEqual( - {true, foo}, - is_r_met([], [fabric_util:kv(foo,2), fabric_util:kv(bar,1)], 2) - ), - - ?assertEqual( - {true, bar}, - is_r_met([], [fabric_util:kv(bar,1), fabric_util:kv(bar,2)], 2) - ), - - ?assertEqual( - {true, bar}, - is_r_met([], [fabric_util:kv(bar,2), fabric_util:kv(foo,1)], 2) - ), - - % Not met, but wait for more messages - - ?assertEqual( - wait_for_more, - is_r_met(Workers2, [fabric_util:kv(foo,1)], 2) - ), - - ?assertEqual( - wait_for_more, - is_r_met(Workers2, [fabric_util:kv(foo,2)], 3) - ), - - ?assertEqual( - wait_for_more, - is_r_met(Workers2, [fabric_util:kv(foo,1), fabric_util:kv(bar,1)], 2) - ), - - % Not met, bail out - - ?assertEqual( - no_more_workers, - is_r_met(Workers0, [fabric_util:kv(foo,1)], 2) - ), - - ?assertEqual( - no_more_workers, - is_r_met(Workers1, [fabric_util:kv(foo,1)], 2) - ), - - ?assertEqual( - no_more_workers, - is_r_met(Workers1, [fabric_util:kv(foo,1), fabric_util:kv(bar,1)], 2) - ), - - ?assertEqual( - no_more_workers, - is_r_met(Workers1, [fabric_util:kv(foo,2)], 3) - ), - - ok. - -handle_message_down_test() -> + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + + +setup() -> + meck:new([ + couch_log, + couch_stats, + fabric, + fabric_util, + mem3, + rexi, + rexi_monitor + ], [passthrough]). + + +teardown(_) -> + meck:unload(). + + +open_doc_test_() -> + { + foreach, + fun setup/0, + fun teardown/1, + [ + t_is_r_met(), + t_handle_message_down(), + t_handle_message_exit(), + t_handle_message_reply(), + t_read_repair(), + t_handle_response_quorum_met(), + t_get_doc_info() + ] + }. + + +t_is_r_met() -> + ?_test(begin + Workers0 = [], + Workers1 = [nil], + Workers2 = [nil, nil], + + SuccessCases = [ + {{true, foo}, [fabric_util:kv(foo, 2)], 2}, + {{true, foo}, [fabric_util:kv(foo, 3)], 2}, + {{true, foo}, [fabric_util:kv(foo, 1)], 1}, + {{true, foo}, [fabric_util:kv(foo, 2), fabric_util:kv(bar, 1)], 2}, + {{true, bar}, [fabric_util:kv(bar, 1), fabric_util:kv(bar, 2)], 2}, + {{true, bar}, [fabric_util:kv(bar, 2), fabric_util:kv(foo, 1)], 2} + ], + lists:foreach(fun({Expect, Replies, Q}) -> + ?assertEqual(Expect, is_r_met(Workers0, Replies, Q)) + end, SuccessCases), + + WaitForMoreCases = [ + {[fabric_util:kv(foo, 1)], 2}, + {[fabric_util:kv(foo, 2)], 3}, + {[fabric_util:kv(foo, 1), fabric_util:kv(bar, 1)], 2} + ], + lists:foreach(fun({Replies, Q}) -> + ?assertEqual(wait_for_more, is_r_met(Workers2, Replies, Q)) + end, WaitForMoreCases), + + FailureCases = [ + {Workers0, [fabric_util:kv(foo, 1)], 2}, + {Workers1, [fabric_util:kv(foo, 1)], 2}, + {Workers1, [fabric_util:kv(foo, 1), fabric_util:kv(bar, 1)], 2}, + {Workers1, [fabric_util:kv(foo, 2)], 3} + ], + lists:foreach(fun({Workers, Replies, Q}) -> + ?assertEqual(no_more_workers, is_r_met(Workers, Replies, Q)) + end, FailureCases) + end). + + +t_handle_message_down() -> Node0 = 'foo@localhost', Node1 = 'bar@localhost', Down0 = {rexi_DOWN, nil, {nil, Node0}, nil}, @@ -261,279 +260,269 @@ handle_message_down_test() -> Worker1 = #shard{node=Node1}, Workers1 = Workers0 ++ [Worker1], - % Stop when no more workers are left - ?assertEqual( - {stop, #acc{workers=[]}}, - handle_message(Down0, nil, #acc{workers=Workers0}) - ), + ?_test(begin + % Stop when no more workers are left + ?assertEqual( + {stop, #acc{workers=[]}}, + handle_message(Down0, nil, #acc{workers=Workers0}) + ), - % Continue when we have more workers - ?assertEqual( - {ok, #acc{workers=[Worker1]}}, - handle_message(Down0, nil, #acc{workers=Workers1}) - ), + % Continue when we have more workers + ?assertEqual( + {ok, #acc{workers=[Worker1]}}, + handle_message(Down0, nil, #acc{workers=Workers1}) + ), - % A second DOWN removes the remaining workers - ?assertEqual( - {stop, #acc{workers=[]}}, - handle_message(Down1, nil, #acc{workers=[Worker1]}) - ), + % A second DOWN removes the remaining workers + ?assertEqual( + {stop, #acc{workers=[]}}, + handle_message(Down1, nil, #acc{workers=[Worker1]}) + ) + end). - ok. -handle_message_exit_test() -> +t_handle_message_exit() -> Exit = {rexi_EXIT, nil}, Worker0 = #shard{ref=erlang:make_ref()}, Worker1 = #shard{ref=erlang:make_ref()}, - % Only removes the specified worker - ?assertEqual( - {ok, #acc{workers=[Worker1]}}, - handle_message(Exit, Worker0, #acc{workers=[Worker0, Worker1]}) - ), - - ?assertEqual( - {ok, #acc{workers=[Worker0]}}, - handle_message(Exit, Worker1, #acc{workers=[Worker0, Worker1]}) - ), + ?_test(begin + % Only removes the specified worker + ?assertEqual( + {ok, #acc{workers=[Worker1]}}, + handle_message(Exit, Worker0, #acc{workers=[Worker0, Worker1]}) + ), - % We bail if it was the last worker - ?assertEqual( - {stop, #acc{workers=[]}}, - handle_message(Exit, Worker0, #acc{workers=[Worker0]}) - ), + ?assertEqual( + {ok, #acc{workers=[Worker0]}}, + handle_message(Exit, Worker1, #acc{workers=[Worker0, Worker1]}) + ), - ok. + % We bail if it was the last worker + ?assertEqual( + {stop, #acc{workers=[]}}, + handle_message(Exit, Worker0, #acc{workers=[Worker0]}) + ) + end). -handle_message_reply_test() -> - start_meck_(), - meck:expect(rexi, kill, fun(_, _) -> ok end), +t_handle_message_reply() -> Worker0 = #shard{ref=erlang:make_ref()}, Worker1 = #shard{ref=erlang:make_ref()}, Worker2 = #shard{ref=erlang:make_ref()}, Workers = [Worker0, Worker1, Worker2], Acc0 = #acc{workers=Workers, r=2, replies=[]}, - % Test that we continue when we haven't met R yet - ?assertEqual( - {ok, Acc0#acc{ - workers=[Worker0, Worker1], - replies=[fabric_util:kv(foo,1)] - }}, - handle_message(foo, Worker2, Acc0) - ), - - ?assertEqual( - {ok, Acc0#acc{ - workers=[Worker0, Worker1], - replies=[fabric_util:kv(bar,1), fabric_util:kv(foo,1)] - }}, - handle_message(bar, Worker2, Acc0#acc{ - replies=[fabric_util:kv(foo,1)] - }) - ), - - % Test that we don't get a quorum when R isn't met. q_reply - % isn't set and state remains unchanged and {stop, NewAcc} - % is returned. Bit subtle on the assertions here. - - ?assertEqual( - {stop, Acc0#acc{workers=[],replies=[fabric_util:kv(foo,1)]}}, - handle_message(foo, Worker0, Acc0#acc{workers=[Worker0]}) - ), - - ?assertEqual( - {stop, Acc0#acc{ - workers=[], - replies=[fabric_util:kv(bar,1), fabric_util:kv(foo,1)] - }}, - handle_message(bar, Worker0, Acc0#acc{ - workers=[Worker0], - replies=[fabric_util:kv(foo,1)] - }) - ), - - % Check that when R is met we stop with a new state and - % a q_reply. - - ?assertEqual( - {stop, Acc0#acc{ - workers=[], - replies=[fabric_util:kv(foo,2)], - state=r_met, - q_reply=foo - }}, - handle_message(foo, Worker1, Acc0#acc{ - workers=[Worker0, Worker1], - replies=[fabric_util:kv(foo,1)] - }) - ), - - ?assertEqual( - {stop, Acc0#acc{ - workers=[], - r=1, - replies=[fabric_util:kv(foo,1)], - state=r_met, - q_reply=foo - }}, - handle_message(foo, Worker0, Acc0#acc{r=1}) - ), - - ?assertEqual( - {stop, Acc0#acc{ - workers=[], - replies=[fabric_util:kv(bar,1), fabric_util:kv(foo,2)], - state=r_met, - q_reply=foo - }}, - handle_message(foo, Worker0, Acc0#acc{ - workers=[Worker0], - replies=[fabric_util:kv(bar,1), fabric_util:kv(foo,1)] - }) - ), - - stop_meck_(), - ok. - -read_repair_test() -> - start_meck_(), - meck:expect(couch_log, notice, fun(_, _) -> ok end), - meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - + ?_test(begin + meck:expect(rexi, kill, fun(_, _) -> ok end), + + % Test that we continue when we haven't met R yet + ?assertMatch( + {ok, #acc{ + workers=[Worker0, Worker1], + replies=[{foo, {foo, 1}}] + }}, + handle_message(foo, Worker2, Acc0) + ), + + ?assertMatch( + {ok, #acc{ + workers=[Worker0, Worker1], + replies=[{bar, {bar, 1}}, {foo, {foo, 1}}] + }}, + handle_message(bar, Worker2, Acc0#acc{ + replies=[{foo, {foo, 1}}] + }) + ), + + % Test that we don't get a quorum when R isn't met. q_reply + % isn't set and state remains unchanged and {stop, NewAcc} + % is returned. Bit subtle on the assertions here. + + ?assertMatch( + {stop, #acc{workers=[], replies=[{foo, {foo, 1}}]}}, + handle_message(foo, Worker0, Acc0#acc{workers=[Worker0]}) + ), + + ?assertMatch( + {stop, #acc{ + workers=[], + replies=[{bar, {bar, 1}}, {foo, {foo, 1}}] + }}, + handle_message(bar, Worker0, Acc0#acc{ + workers=[Worker0], + replies=[{foo, {foo, 1}}] + }) + ), + + % Check that when R is met we stop with a new state and + % a q_reply. + + ?assertMatch( + {stop, #acc{ + workers=[], + replies=[{foo, {foo, 2}}], + state=r_met, + q_reply=foo + }}, + handle_message(foo, Worker1, Acc0#acc{ + workers=[Worker0, Worker1], + replies=[{foo, {foo, 1}}] + }) + ), + + ?assertEqual( + {stop, #acc{ + workers=[], + r=1, + replies=[{foo, {foo, 1}}], + state=r_met, + q_reply=foo + }}, + handle_message(foo, Worker0, Acc0#acc{r=1}) + ), + + ?assertMatch( + {stop, #acc{ + workers=[], + replies=[{bar, {bar, 1}}, {foo, {foo, 2}}], + state=r_met, + q_reply=foo + }}, + handle_message(foo, Worker0, Acc0#acc{ + workers=[Worker0], + replies=[{bar, {bar, 1}}, {foo, {foo, 1}}] + }) + ) + end). + + +t_read_repair() -> Foo1 = {ok, #doc{revs = {1,[<<"foo">>]}}}, Foo2 = {ok, #doc{revs = {2,[<<"foo2">>,<<"foo">>]}}}, NFM = {not_found, missing}, - % Test when we have actual doc data to repair - - meck:expect(fabric, update_docs, fun(_, [_], _) -> {ok, []} end), - Acc0 = #acc{ - dbname = <<"name">>, - replies = [fabric_util:kv(Foo1,1)] - }, - ?assertEqual(Foo1, read_repair(Acc0)), - - meck:expect(fabric, update_docs, fun(_, [_, _], _) -> {ok, []} end), - Acc1 = #acc{ - dbname = <<"name">>, - replies = [fabric_util:kv(Foo1,1), fabric_util:kv(Foo2,1)] - }, - ?assertEqual(Foo2, read_repair(Acc1)), + ?_test(begin + meck:expect(couch_log, notice, fun(_, _) -> ok end), + meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - % Test when we have nothing but errors + % Test when we have actual doc data to repair + meck:expect(fabric, update_docs, fun(_, [_], _) -> {ok, []} end), + Acc0 = #acc{ + dbname = <<"name">>, + replies = [fabric_util:kv(Foo1,1)] + }, + ?assertEqual(Foo1, read_repair(Acc0)), - Acc2 = #acc{replies=[fabric_util:kv(NFM, 1)]}, - ?assertEqual(NFM, read_repair(Acc2)), + meck:expect(fabric, update_docs, fun(_, [_, _], _) -> {ok, []} end), + Acc1 = #acc{ + dbname = <<"name">>, + replies = [fabric_util:kv(Foo1,1), fabric_util:kv(Foo2,1)] + }, + ?assertEqual(Foo2, read_repair(Acc1)), - Acc3 = #acc{replies=[fabric_util:kv(NFM,1), fabric_util:kv(foo,2)]}, - ?assertEqual(NFM, read_repair(Acc3)), + % Test when we have nothing but errors + Acc2 = #acc{replies=[fabric_util:kv(NFM, 1)]}, + ?assertEqual(NFM, read_repair(Acc2)), - Acc4 = #acc{replies=[fabric_util:kv(foo,1), fabric_util:kv(bar,1)]}, - ?assertEqual(bar, read_repair(Acc4)), + Acc3 = #acc{replies=[fabric_util:kv(NFM,1), fabric_util:kv(foo,2)]}, + ?assertEqual(NFM, read_repair(Acc3)), - stop_meck_(), - ok. + Acc4 = #acc{replies=[fabric_util:kv(foo,1), fabric_util:kv(bar,1)]}, + ?assertEqual(bar, read_repair(Acc4)) + end). -handle_response_quorum_met_test() -> - start_meck_(), - meck:expect(couch_log, notice, fun(_, _) -> ok end), - meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, []} end), - meck:expect(couch_stats, increment_counter, fun(_) -> ok end), +t_handle_response_quorum_met() -> Foo1 = {ok, #doc{revs = {1,[<<"foo">>]}}}, Foo2 = {ok, #doc{revs = {2,[<<"foo2">>,<<"foo">>]}}}, Bar1 = {ok, #doc{revs = {1,[<<"bar">>]}}}, - BasicOkAcc = #acc{ - state=r_met, - replies=[fabric_util:kv(Foo1,2)], - q_reply=Foo1 - }, - ?assertEqual(Foo1, handle_response(BasicOkAcc)), + ?_test(begin + meck:expect(couch_log, notice, fun(_, _) -> ok end), + meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, []} end), + meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - WithAncestorsAcc = #acc{ - state=r_met, - replies=[fabric_util:kv(Foo1,1), fabric_util:kv(Foo2,2)], - q_reply=Foo2 - }, - ?assertEqual(Foo2, handle_response(WithAncestorsAcc)), - - % This also checks when the quorum isn't the most recent - % revision. - DeeperWinsAcc = #acc{ - state=r_met, - replies=[fabric_util:kv(Foo1,2), fabric_util:kv(Foo2,1)], - q_reply=Foo1 - }, - ?assertEqual(Foo2, handle_response(DeeperWinsAcc)), - - % Check that we return the proper doc based on rev - % (ie, pos is equal) - BiggerRevWinsAcc = #acc{ - state=r_met, - replies=[fabric_util:kv(Foo1,1), fabric_util:kv(Bar1,2)], - q_reply=Bar1 - }, - ?assertEqual(Foo1, handle_response(BiggerRevWinsAcc)), - - % r_not_met is a proxy to read_repair so we rely on - % read_repair_test for those conditions. - - stop_meck_(), - ok. - -get_doc_info_test() -> - start_meck_(), - meck:new([mem3, rexi_monitor, fabric_util]), - meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, []} end), - meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - meck:expect(fabric_util, submit_jobs, fun(_, _, _) -> ok end), - meck:expect(fabric_util, create_monitors, fun(_) -> ok end), - meck:expect(rexi_monitor, stop, fun(_) -> ok end), - meck:expect(mem3, shards, fun(_, _) -> ok end), - meck:expect(mem3, n, fun(_) -> 3 end), - meck:expect(mem3, quorum, fun(_) -> 2 end), - - meck:expect(fabric_util, recv, fun(_, _, _, _) -> - {ok, #acc{state = r_not_met}} - end), - Rsp1 = fabric_doc_open:go("test", "one", [doc_info]), - ?assertEqual({error, quorum_not_met}, Rsp1), - - Rsp2 = fabric_doc_open:go("test", "one", [{doc_info, full}]), - ?assertEqual({error, quorum_not_met}, Rsp2), - - meck:expect(fabric_util, recv, fun(_, _, _, _) -> - {ok, #acc{state = r_met, q_reply = not_found}} - end), - MissingRsp1 = fabric_doc_open:go("test", "one", [doc_info]), - ?assertEqual({not_found, missing}, MissingRsp1), - MissingRsp2 = fabric_doc_open:go("test", "one", [{doc_info, full}]), - ?assertEqual({not_found, missing}, MissingRsp2), - - meck:expect(fabric_util, recv, fun(_, _, _, _) -> - A = #doc_info{}, - {ok, #acc{state = r_met, q_reply = {ok, A}}} - end), - {ok, Rec1} = fabric_doc_open:go("test", "one", [doc_info]), - ?assert(is_record(Rec1, doc_info)), - - meck:expect(fabric_util, recv, fun(_, _, _, _) -> - A = #full_doc_info{deleted = true}, - {ok, #acc{state = r_met, q_reply = {ok, A}}} - end), - Rsp3 = fabric_doc_open:go("test", "one", [{doc_info, full}]), - ?assertEqual({not_found, deleted}, Rsp3), - {ok, Rec2} = fabric_doc_open:go("test", "one", [{doc_info, full},deleted]), - ?assert(is_record(Rec2, full_doc_info)), - - meck:unload([mem3, rexi_monitor, fabric_util]), - stop_meck_(). - -start_meck_() -> - meck:new([couch_log, rexi, fabric, couch_stats]). - -stop_meck_() -> - meck:unload([couch_log, rexi, fabric, couch_stats]). + BasicOkAcc = #acc{ + state=r_met, + replies=[fabric_util:kv(Foo1,2)], + q_reply=Foo1 + }, + ?assertEqual(Foo1, handle_response(BasicOkAcc)), + + WithAncestorsAcc = #acc{ + state=r_met, + replies=[fabric_util:kv(Foo1,1), fabric_util:kv(Foo2,2)], + q_reply=Foo2 + }, + ?assertEqual(Foo2, handle_response(WithAncestorsAcc)), + + % This also checks when the quorum isn't the most recent + % revision. + DeeperWinsAcc = #acc{ + state=r_met, + replies=[fabric_util:kv(Foo1,2), fabric_util:kv(Foo2,1)], + q_reply=Foo1 + }, + ?assertEqual(Foo2, handle_response(DeeperWinsAcc)), + + % Check that we return the proper doc based on rev + % (ie, pos is equal) + BiggerRevWinsAcc = #acc{ + state=r_met, + replies=[fabric_util:kv(Foo1,1), fabric_util:kv(Bar1,2)], + q_reply=Bar1 + }, + ?assertEqual(Foo1, handle_response(BiggerRevWinsAcc)) + + % r_not_met is a proxy to read_repair so we rely on + % read_repair_test for those conditions. + end). + + +t_get_doc_info() -> + ?_test(begin + meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, []} end), + meck:expect(couch_stats, increment_counter, fun(_) -> ok end), + meck:expect(fabric_util, submit_jobs, fun(_, _, _) -> ok end), + meck:expect(fabric_util, create_monitors, fun(_) -> ok end), + meck:expect(rexi_monitor, stop, fun(_) -> ok end), + meck:expect(mem3, shards, fun(_, _) -> ok end), + meck:expect(mem3, n, fun(_) -> 3 end), + meck:expect(mem3, quorum, fun(_) -> 2 end), + + meck:expect(fabric_util, recv, fun(_, _, _, _) -> + {ok, #acc{state = r_not_met}} + end), + Rsp1 = fabric_doc_open:go("test", "one", [doc_info]), + ?assertEqual({error, quorum_not_met}, Rsp1), + + Rsp2 = fabric_doc_open:go("test", "one", [{doc_info, full}]), + ?assertEqual({error, quorum_not_met}, Rsp2), + + meck:expect(fabric_util, recv, fun(_, _, _, _) -> + {ok, #acc{state = r_met, q_reply = not_found}} + end), + MissingRsp1 = fabric_doc_open:go("test", "one", [doc_info]), + ?assertEqual({not_found, missing}, MissingRsp1), + MissingRsp2 = fabric_doc_open:go("test", "one", [{doc_info, full}]), + ?assertEqual({not_found, missing}, MissingRsp2), + + meck:expect(fabric_util, recv, fun(_, _, _, _) -> + A = #doc_info{}, + {ok, #acc{state = r_met, q_reply = {ok, A}}} + end), + {ok, Rec1} = fabric_doc_open:go("test", "one", [doc_info]), + ?assert(is_record(Rec1, doc_info)), + + meck:expect(fabric_util, recv, fun(_, _, _, _) -> + A = #full_doc_info{deleted = true}, + {ok, #acc{state = r_met, q_reply = {ok, A}}} + end), + Rsp3 = fabric_doc_open:go("test", "one", [{doc_info, full}]), + ?assertEqual({not_found, deleted}, Rsp3), + {ok, Rec2} = fabric_doc_open:go("test", "one", [{doc_info, full},deleted]), + ?assert(is_record(Rec2, full_doc_info)) + end). + +-endif. \ No newline at end of file