Add loop/4 expect function
diff --git a/src/meck.erl b/src/meck.erl
index c955a6a..9ea1957 100644
--- a/src/meck.erl
+++ b/src/meck.erl
@@ -27,6 +27,7 @@
-export([expect/3]).
-export([expect/4]).
-export([sequence/4]).
+-export([loop/4]).
-export([delete/3]).
-export([exception/2]).
-export([passthrough/1]).
@@ -142,7 +143,7 @@
%% @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.
+%% value from `Sequence' until exhausted.
%%
%% This creates an expectation which takes `Arity' number of arguments
%% and returns one element from `Sequence' at a time. Thus, calls to
@@ -158,6 +159,24 @@
lists:foreach(fun(M) -> sequence(M, Func, Arity, Sequence) end, Mod),
ok.
+%% @spec loop(Mod:: atom() | list(atom()), Func::atom(),
+%% Arity::pos_integer(), Loop::[term()]) -> ok
+%% @doc Adds an expectation with the supplied arity which returns a
+%% value from `Loop' infinitely.
+%%
+%% This creates an expectation which takes `Arity' number of arguments
+%% and returns one element from `Loop' at a time. Thus, calls to this
+%% expect will return one element at a time from the list and will
+%% restart at the first element when the end is reached.
+-spec loop(Mod:: atom() | [atom()], Func::atom(),
+ Arity::pos_integer(), Loop::[term()]) -> ok.
+loop(Mod, Func, Arity, Loop)
+ when is_atom(Mod), is_atom(Func), is_integer(Arity), Arity >= 0 ->
+ call(Mod, {loop, Func, Arity, Loop});
+loop(Mod, Func, Arity, Loop) when is_list(Mod) ->
+ lists:foreach(fun(M) -> loop(M, Func, Arity, Loop) end, Mod),
+ ok.
+
%% @spec delete(Mod:: atom() | list(atom()), Func::atom(),
%% Arity::pos_integer()) -> ok
%% @doc Deletes an expectation.
@@ -276,6 +295,10 @@
NewExpects = store_expect(S#state.mod, Func, {sequence, Arity, Sequence},
S#state.expects),
{reply, ok, S#state{expects = NewExpects}};
+handle_call({loop, Func, Arity, Loop}, _From, S) ->
+ NewExpects = store_expect(S#state.mod, Func, {loop, Arity, Loop, Loop},
+ 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}};
@@ -374,11 +397,17 @@
get_expect(Expects, Func, Arity) ->
case e_fetch(Expects, Func, Arity) of
- {sequence, Arity, [Last]} ->
- {{sequence, Arity, Last}, Expects};
+ {sequence, Arity, [Result]} ->
+ {{sequence, Arity, Result}, Expects};
{sequence, Arity, [Result|Rest]} ->
{{sequence, Arity, Result},
e_store(Expects, Func, {sequence, Arity, Rest})};
+ {loop, Arity, [Result], Loop} ->
+ {{loop, Arity, Result},
+ e_store(Expects, Func, {loop, Arity, Loop, Loop})};
+ {loop, Arity, [Result|Rest], Loop} ->
+ {{loop, Arity, Result},
+ e_store(Expects, Func, {loop, Arity, Rest, Loop})};
Other ->
{Other, Expects}
end.
@@ -439,7 +468,11 @@
var_name(A) -> list_to_atom("A"++integer_to_list(A)).
-arity({Type, Arity, _Result}) when Type == anon; Type == sequence ->
+arity({anon, Arity, _Result}) ->
+ Arity;
+arity({sequence, Arity, _Sequence}) ->
+ Arity;
+arity({loop, Arity, _Current, _Loop}) ->
Arity;
arity(Fun) when is_function(Fun) ->
{arity, Arity} = erlang:fun_info(Fun, arity),
@@ -484,10 +517,7 @@
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)
+call_expect(_Mod, _Func, {_Type, Arity, Return}, VarList)
when Arity == length(VarList) ->
Return;
call_expect(Mod, Func, passthrough, VarList) ->
diff --git a/test/meck_tests.erl b/test/meck_tests.erl
index 15d517b..4416300 100644
--- a/test/meck_tests.erl
+++ b/test/meck_tests.erl
@@ -66,7 +66,9 @@
fun called_false_error_/1,
fun called_true_error_/1,
fun sequence_/1,
- fun sequence_multi_/1
+ fun sequence_multi_/1,
+ fun loop_/1,
+ fun loop_multi_/1
]]}.
setup() ->
@@ -398,6 +400,21 @@
[mymod2:s(a, b) || _ <- lists:seq(1, 5)]),
?assert(meck:validate(Mods)).
+loop_(Mod) ->
+ Loop = [a, b, c, d, e],
+ ?assertEqual(ok, meck:loop(Mod, l, 2, Loop)),
+ [?assertEqual(V, Mod:l(a, b)) || _ <- lists:seq(1, length(Loop)), V <- Loop],
+ ?assert(meck:validate(Mod)).
+
+loop_multi_(Mod) ->
+ meck:new(mymod2),
+ Mods = [Mod, mymod2],
+ Loop = [a, b, c, d, e],
+ ?assertEqual(ok, meck:loop(Mods, l, 2, Loop)),
+ [[?assertEqual(V, M:l(a, b)) || _ <- lists:seq(1, length(Loop)), V <- Loop]
+ || M <- Mods],
+ ?assert(meck:validate(Mods)).
+
%% --- Tests with own setup ----------------------------------------------------
call_original_test() ->