Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add loop/4 expect function
  • Loading branch information
eproxus committed May 25, 2011
1 parent cb697b8 commit 8d86012
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 9 deletions.
46 changes: 38 additions & 8 deletions src/meck.erl
Expand Up @@ -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]).
Expand Down Expand Up @@ -142,7 +143,7 @@ expect(Mod, Func, Arity, Result) when is_list(Mod) ->
%% @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
Expand All @@ -158,6 +159,24 @@ sequence(Mod, Func, Arity, Sequence) when is_list(Mod) ->
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.
Expand Down Expand Up @@ -276,6 +295,10 @@ 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({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}};
Expand Down Expand Up @@ -374,11 +397,17 @@ init_expects(Mod, Options) ->

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.
Expand Down Expand Up @@ -439,7 +468,11 @@ var(Name) -> {var, ?LINE, Name}.

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),
Expand Down Expand Up @@ -484,10 +517,7 @@ mock_exception_fun(Class, Reason) -> fun() -> {exception, Class, Reason} end.

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) ->
Expand Down
19 changes: 18 additions & 1 deletion test/meck_tests.erl
Expand Up @@ -66,7 +66,9 @@ meck_test_() ->
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() ->
Expand Down Expand Up @@ -398,6 +400,21 @@ sequence_multi_(Mod) ->
[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() ->
Expand Down

0 comments on commit 8d86012

Please sign in to comment.