diff --git a/src/meck.erl b/src/meck.erl index dcfe6fbd..f99fc78d 100644 --- a/src/meck.erl +++ b/src/meck.erl @@ -26,6 +26,7 @@ -export([new/2]). -export([expect/3]). -export([expect/4]). +-export([sequence/4]). -export([delete/3]). -export([exception/2]). -export([passthrough/1]). @@ -138,6 +139,25 @@ expect(Mod, Func, Arity, Result) when is_list(Mod) -> lists:foreach(fun(M) -> expect(M, Func, Arity, Result) end, Mod), ok. +%% @spec sequence(Mod:: atom() | list(atom()), Func::atom(), +%% Arity::pos_integer(), Sequence::[term()]) -> ok +%% @doc Adds an expectation with the supplied arity which returns a +%% value from `Sequence' one at a time. +%% +%% This creates an expectation which takes `Arity' number of arguments +%% and returns one element from `Sequence' at a time. Thus, calls to +%% this expect will exhaust the list of return values in order until +%% the last value is reached. That value is then returned for all +%% subsequent calls. +-spec sequence(Mod:: atom() | [atom()], Func::atom(), + Arity::pos_integer(), Result::[term()]) -> ok. +sequence(Mod, Func, Arity, Sequence) + when is_atom(Mod), is_atom(Func), is_integer(Arity), Arity >= 0 -> + call(Mod, {sequence, Func, Arity, Sequence}); +sequence(Mod, Func, Arity, Sequence) when is_list(Mod) -> + lists:foreach(fun(M) -> sequence(M, Func, Arity, Sequence) end, Mod), + ok. + %% @spec delete(Mod:: atom() | list(atom()), Func::atom(), %% Arity::pos_integer()) -> ok %% @doc Deletes an expectation. @@ -243,8 +263,8 @@ init([Mod, Options]) -> %% @hidden handle_call({get_expect, Func, Arity}, _From, S) -> - Expect = get_expect(S#state.expects, Func, Arity), - {reply, Expect, S}; + {Expect, NewExpects} = get_expect(S#state.expects, Func, Arity), + {reply, Expect, S#state{expects = NewExpects}}; handle_call({expect, Func, Expect}, _From, S) -> NewExpects = store_expect(S#state.mod, Func, Expect, S#state.expects), {reply, ok, S#state{expects = NewExpects}}; @@ -252,6 +272,10 @@ handle_call({expect, Func, Arity, Result}, _From, S) -> NewExpects = store_expect(S#state.mod, Func, {anon, Arity, Result}, S#state.expects), {reply, ok, S#state{expects = NewExpects}}; +handle_call({sequence, Func, Arity, Sequence}, _From, S) -> + NewExpects = store_expect(S#state.mod, Func, {sequence, Arity, Sequence}, + S#state.expects), + {reply, ok, S#state{expects = NewExpects}}; handle_call({delete, Func, Arity}, _From, S) -> NewExpects = delete_expect(S#state.mod, Func, Arity, S#state.expects), {reply, ok, S#state{expects = NewExpects}}; @@ -349,7 +373,15 @@ init_expects(Mod, Options) -> get_expect(Expects, Func, Arity) -> - e_fetch(Expects, Func, Arity). + case e_fetch(Expects, Func, Arity) of + {sequence, Arity, [Last]} -> + {{sequence, Arity, Last}, Expects}; + {sequence, Arity, [Result|Rest]} -> + {{sequence, Arity, Result}, + e_store(Expects, Func, {sequence, Arity, Rest})}; + Other -> + {Other, Expects} + end. store_expect(Mod, Func, Expect, Expects) -> change_expects(fun e_store/3, Mod, Func, Expect, Expects). @@ -407,7 +439,7 @@ var(Name) -> {var, ?LINE, Name}. var_name(A) -> list_to_atom("A"++integer_to_list(A)). -arity({anon, Arity, _Result}) -> +arity({Type, Arity, _Result}) when Type == anon; Type == sequence -> Arity; arity(Fun) when is_function(Fun) -> {arity, Arity} = erlang:fun_info(Fun, arity), @@ -455,6 +487,9 @@ passthrough_fun(Args) -> fun() -> {passthrough, Args} end. call_expect(_Mod, _Func, {anon, Arity, Return}, VarList) when Arity == length(VarList) -> Return; +call_expect(_Mod, _Func, {sequence, Arity, Return}, VarList) + when Arity == length(VarList) -> + Return; call_expect(Mod, Func, passthrough, VarList) -> apply(original_name(Mod), Func, VarList); call_expect(_Mod, _Func, Fun, VarList) when is_function(Fun) -> diff --git a/test/meck_tests.erl b/test/meck_tests.erl index 48b594a7..15d517b5 100644 --- a/test/meck_tests.erl +++ b/test/meck_tests.erl @@ -64,7 +64,9 @@ meck_test_() -> fun called_false_few_args_/1, fun called_true_few_args_/1, fun called_false_error_/1, - fun called_true_error_/1 + fun called_true_error_/1, + fun sequence_/1, + fun sequence_multi_/1 ]]}. setup() -> @@ -372,6 +374,30 @@ called_true_error_(Mod) -> ?assertEqual(true, meck:called(Mod, test, Args)), ?assert(meck:validate(Mod)). +sequence_(Mod) -> + Sequence = [a, b, c, d, e], + ?assertEqual(ok, meck:sequence(Mod, s, 2, Sequence)), + ?assertEqual(Sequence, + [Mod:s(a, b) || _ <- lists:seq(1, length(Sequence))]), + ?assertEqual([e, e, e, e, e], + [Mod:s(a, b) || _ <- lists:seq(1, 5)]), + ?assert(meck:validate(Mod)). + +sequence_multi_(Mod) -> + meck:new(mymod2), + Mods = [Mod, mymod2], + Sequence = [a, b, c, d, e], + ?assertEqual(ok, meck:sequence(Mods, s, 2, Sequence)), + ?assertEqual(Sequence, + [Mod:s(a, b) || _ <- lists:seq(1, length(Sequence))]), + ?assertEqual([e, e, e, e, e], + [Mod:s(a, b) || _ <- lists:seq(1, 5)]), + ?assertEqual(Sequence, + [mymod2:s(a, b) || _ <- lists:seq(1, length(Sequence))]), + ?assertEqual([e, e, e, e, e], + [mymod2:s(a, b) || _ <- lists:seq(1, 5)]), + ?assert(meck:validate(Mods)). + %% --- Tests with own setup ---------------------------------------------------- call_original_test() ->