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 2014/07/31 23:31:01 UTC
[07/50] meck commit: updated refs/heads/master to dde7590
Address code inspection comments:
- Move guards to `meck:wait`;
- Allow `0` call number in `meck:wait`
- Allow waiting from different processes on different or the
same call patterns;
- Instead of starting a timer do garbage collection of stale
wait trackers.
Project: http://git-wip-us.apache.org/repos/asf/couchdb-meck/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-meck/commit/3bd9ed36
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-meck/tree/3bd9ed36
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-meck/diff/3bd9ed36
Branch: refs/heads/master
Commit: 3bd9ed36aaca6bee2efb382873727fcb3e9d70a9
Parents: ed44176
Author: Maxim Vladimirsky <ma...@maigunhq.com>
Authored: Sun Jun 9 14:29:12 2013 +0400
Committer: Maxim Vladimirsky <ma...@maigunhq.com>
Committed: Sun Jun 9 14:29:12 2013 +0400
----------------------------------------------------------------------
src/meck.erl | 12 +++--
src/meck_proc.erl | 118 +++++++++++++++++++++++++++++++----------------
test/meck_tests.erl | 94 ++++++++++++++++++++++++++++++++++++-
3 files changed, 177 insertions(+), 47 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-meck/blob/3bd9ed36/src/meck.erl
----------------------------------------------------------------------
diff --git a/src/meck.erl b/src/meck.erl
index 33e830b..d0ab62a 100644
--- a/src/meck.erl
+++ b/src/meck.erl
@@ -452,7 +452,7 @@ num_calls(Mod, OptFun, OptArgsSpec, OptPid) ->
Mod :: atom(),
OptFunc :: '_' | atom(),
OptArgsSpec :: '_' | args_spec(),
- Timeout :: timeout().
+ Timeout :: non_neg_integer().
wait(Mod, OptFunc, OptArgsSpec, Timeout) ->
wait(1, Mod, OptFunc, OptArgsSpec, '_', Timeout).
@@ -471,7 +471,7 @@ wait(Mod, OptFunc, OptArgsSpec, Timeout) ->
Mod :: atom(),
OptFunc :: '_' | atom(),
OptArgsSpec :: '_' | args_spec(),
- Timeout :: timeout().
+ Timeout :: non_neg_integer().
wait(Times, Mod, OptFunc, OptArgsSpec, Timeout) ->
wait(Times, Mod, OptFunc, OptArgsSpec, '_', Timeout).
@@ -489,8 +489,12 @@ wait(Times, Mod, OptFunc, OptArgsSpec, Timeout) ->
OptFunc :: '_' | atom(),
OptArgsSpec :: '_' | args_spec(),
OptCallerPid :: '_' | pid(),
- Timeout :: timeout().
-wait(Times, Mod, OptFunc, OptArgsSpec, OptCallerPid, Timeout) ->
+ Timeout :: non_neg_integer().
+wait(0, _Mod, _OptFunc, _OptArgsSpec, _OptCallerPid, _Timeout) ->
+ ok;
+wait(Times, Mod, OptFunc, OptArgsSpec, OptCallerPid, Timeout)
+ when is_integer(Times) andalso Times > 0 andalso
+ is_integer(Timeout) andalso Timeout >= 0 ->
ArgsMatcher = meck_args_matcher:new(OptArgsSpec),
meck_proc:wait(Mod, Times, OptFunc, ArgsMatcher, OptCallerPid, Timeout).
http://git-wip-us.apache.org/repos/asf/couchdb-meck/blob/3bd9ed36/src/meck_proc.erl
----------------------------------------------------------------------
diff --git a/src/meck_proc.erl b/src/meck_proc.erl
index ddbaa0b..060d8fe 100644
--- a/src/meck_proc.erl
+++ b/src/meck_proc.erl
@@ -55,14 +55,14 @@
was_sticky = false :: boolean(),
reload :: {Compiler::pid(), {From::pid(), Tag::any()}} |
undefined,
- tracker :: tracker()}).
+ trackers = [] :: [tracker()]}).
-record(tracker, {opt_func :: '_' | atom(),
args_matcher :: meck_args_matcher:args_matcher(),
opt_caller_pid :: '_' | pid(),
countdown :: non_neg_integer(),
- timer_ref :: reference(),
- reply_to :: {Caller::pid(), Tag::any()}}).
+ reply_to :: {Caller::pid(), Tag::any()},
+ expire_at :: erlang:timestamp()}).
%%%============================================================================
%%% Types
@@ -134,21 +134,27 @@ get_history(Mod) ->
OptFunc::'_' | atom(),
meck_args_matcher:args_matcher(),
OptCallerPid::'_' | pid(),
- timeout()) ->
+ Timeout::non_neg_integer()) ->
ok.
-wait(Mod, Times, OptFunc, ArgsMatcher, OptCallerPid, Timeout)
- when erlang:is_integer(Times) andalso Times > 0 andalso
- erlang:is_integer(Timeout) andalso Timeout >= 0 ->
+wait(Mod, Times, OptFunc, ArgsMatcher, OptCallerPid, Timeout) ->
+ EffectiveTimeout = case Timeout of
+ 0 ->
+ infinity;
+ _Else ->
+ Timeout
+ end,
Name = meck_util:proc_name(Mod),
try gen_server:call(Name, {wait, Times, OptFunc, ArgsMatcher, OptCallerPid,
Timeout},
- infinity)
+ EffectiveTimeout)
of
ok ->
ok;
{error, timeout} ->
erlang:error(timeout)
catch
+ exit:{timeout, _Details} ->
+ erlang:error(timeout);
exit:_Reason ->
erlang:error({not_mocked, Mod})
end.
@@ -230,25 +236,21 @@ handle_call(get_history, _From, S = #state{history = undefined}) ->
handle_call(get_history, _From, S) ->
{reply, lists:reverse(S#state.history), S};
handle_call({wait, Times, OptFunc, ArgsMatcher, OptCallerPid, Timeout}, From,
- S = #state{history = History, tracker = undefined}) ->
+ S = #state{history = History, trackers = Trackers}) ->
case times_called(OptFunc, ArgsMatcher, OptCallerPid, History) of
CalledSoFar when CalledSoFar >= Times ->
{reply, ok, S};
_CalledSoFar when Timeout =:= 0 ->
{reply, {error, timeout}, S};
CalledSoFar ->
- TimerRef = erlang:start_timer(Timeout, erlang:self(), tracker),
Tracker = #tracker{opt_func = OptFunc,
args_matcher = ArgsMatcher,
opt_caller_pid = OptCallerPid,
countdown = Times - CalledSoFar,
- timer_ref = TimerRef,
- reply_to = From},
- {noreply, S#state{tracker = Tracker}}
+ reply_to = From,
+ expire_at = timeout_to_timestamp(Timeout)},
+ {noreply, S#state{trackers = [Tracker | Trackers]}}
end;
-handle_call({wait, _Times, _OptFunc, _ArgsMatcher, _OptCallerPid, _Timeout},
- _From, S) ->
- {reply, {error, concurrent_wait}, S};
handle_call(reset, _From, S) ->
{reply, ok, S#state{history = []}};
handle_call(invalidate, _From, S) ->
@@ -260,17 +262,17 @@ handle_call(stop, _From, S) ->
%% @hidden
handle_cast({add_history, HistoryRecord}, S = #state{history = undefined,
- tracker = Tracker}) ->
- UpdTracker = update_tracker(HistoryRecord, Tracker),
- {noreply, S#state{tracker = UpdTracker}};
+ trackers = Trackers}) ->
+ UpdTracker = update_trackers(HistoryRecord, Trackers),
+ {noreply, S#state{trackers = UpdTracker}};
handle_cast({add_history, HistoryRecord}, S = #state{history = History,
- tracker = Tracker,
+ trackers = Trackers,
reload = Reload}) ->
case Reload of
undefined ->
- UpdTracker = update_tracker(HistoryRecord, Tracker),
+ UpdTrackers = update_trackers(HistoryRecord, Trackers),
{noreply, S#state{history = [HistoryRecord | History],
- tracker = UpdTracker}};
+ trackers = UpdTrackers}};
_ ->
% Skip Item if the mocked module compiler is running.
{noreply, S}
@@ -287,11 +289,6 @@ handle_info({'EXIT', Pid, _Reason}, S = #state{reload = Reload}) ->
_ ->
{noreply, S}
end;
-handle_info({timeout, TimerRef, tracker},
- #state{tracker = #tracker{timer_ref = TimerRef,
- reply_to = ReplyTo}} = S) ->
- gen_server:reply(ReplyTo, {error, timeout}),
- {noreply, S#state{tracker = undefined}};
handle_info(_Info, S) ->
{noreply, S}.
@@ -571,35 +568,74 @@ times_called(OptFunc, ArgsMatcher, OptCallerPid, History) ->
end
end, 0, History).
--spec update_tracker(meck_history:history_record(), tracker() | undefined) ->
- UpdTracker::tracker() | undefined.
-update_tracker(_HistoryRecord, undefined) ->
- undefined;
-update_tracker(HistoryRecord, Tracker) ->
+-spec update_trackers(meck_history:history_record(), [tracker()]) ->
+ UpdTracker::[tracker()].
+update_trackers(HistoryRecord, Trackers) ->
+ update_trackers(HistoryRecord, Trackers, []).
+
+-spec update_trackers(meck_history:history_record(),
+ Trackers::[tracker()],
+ CheckedSoFar::[tracker()]) ->
+ UpdTrackers::[tracker()].
+update_trackers(_HistoryRecord, [], UpdatedSoFar) ->
+ UpdatedSoFar;
+update_trackers(HistoryRecord, [Tracker | Rest], UpdatedSoFar) ->
CallerPid = erlang:element(1, HistoryRecord),
{_Mod, Func, Args} = erlang:element(2, HistoryRecord),
- update_tracker(Func, Args, CallerPid, Tracker).
+ case update_tracker(Func, Args, CallerPid, Tracker) of
+ expired ->
+ update_trackers(HistoryRecord, Rest, UpdatedSoFar);
+ UpdTracker ->
+ update_trackers(HistoryRecord, Rest, [UpdTracker | UpdatedSoFar])
+ end.
+
-spec update_tracker(Func::atom(), Args::[any()], Caller::pid(), tracker()) ->
- UpdTracker::tracker() | undefined.
+ expired |
+ (UpdTracker::tracker()).
update_tracker(Func, Args, CallerPid,
#tracker{opt_func = OptFunc,
args_matcher = ArgsMatcher,
opt_caller_pid = OptCallerPid,
countdown = Countdown,
- timer_ref = TimerRef,
- reply_to = ReplyTo} = Tracker)
+ reply_to = ReplyTo,
+ expire_at = ExpireAt} = Tracker)
when (OptFunc =:= '_' orelse Func =:= OptFunc) andalso
(OptCallerPid =:= '_' orelse CallerPid =:= OptCallerPid) ->
case meck_args_matcher:match(Args, ArgsMatcher) of
false ->
Tracker;
- true when Countdown == 1 ->
- erlang:cancel_timer(TimerRef),
- gen_server:reply(ReplyTo, ok),
- undefined;
true ->
- Tracker#tracker{countdown = Countdown - 1}
+ case is_expired(ExpireAt) of
+ true ->
+ expired;
+ false when Countdown == 1 ->
+ gen_server:reply(ReplyTo, ok),
+ expired;
+ false ->
+ Tracker#tracker{countdown = Countdown - 1}
+ end
end;
update_tracker(_Func, _Args, _CallerPid, Tracker) ->
Tracker.
+
+-spec timeout_to_timestamp(Timeout::non_neg_integer()) -> erlang:timestamp().
+timeout_to_timestamp(Timeout) ->
+ {MacroSecs, Secs, MicroSecs} = os:timestamp(),
+ MicroSecs2 = MicroSecs + Timeout * 1000,
+ UpdMicroSecs = MicroSecs2 rem 1000000,
+ Secs2 = Secs + MicroSecs2 div 1000000,
+ UpdSecs = Secs2 rem 1000000,
+ UpdMacroSecs = MacroSecs + Secs2 div 1000000,
+ {UpdMacroSecs, UpdSecs, UpdMicroSecs}.
+
+-spec is_expired(erlang:timestamp()) -> boolean().
+is_expired({MacroSecs, Secs, MicroSecs}) ->
+ {NowMacroSecs, NowSecs, NowMicroSecs} = os:timestamp(),
+ ((NowMacroSecs > MacroSecs) orelse
+ (NowMacroSecs == MacroSecs andalso NowSecs > Secs) orelse
+ (NowMacroSecs == MacroSecs andalso NowSecs == Secs andalso
+ NowMicroSecs > MicroSecs)).
+
+
+
http://git-wip-us.apache.org/repos/asf/couchdb-meck/blob/3bd9ed36/test/meck_tests.erl
----------------------------------------------------------------------
diff --git a/test/meck_tests.erl b/test/meck_tests.erl
index 08ea955..0c1e37c 100644
--- a/test/meck_tests.erl
+++ b/test/meck_tests.erl
@@ -21,6 +21,20 @@
-include_lib("eunit/include/eunit.hrl").
-include_lib("hamcrest/include/hamcrest.hrl").
+
+-define(assertTerminated(MonitorRef, Reason, Timeout),
+ (fun() ->
+ receive
+ {'DOWN', MonitorRef, process, _Pid, Reason} ->
+ ok;
+ {'DOWN', MonitorRef, process, _Pid, AnotherReason} ->
+ erlang:error({dead_for_another_reason, AnotherReason})
+ after
+ Timeout ->
+ erlang:error(still_alive)
+ end
+ end)()).
+
meck_test_() ->
{foreach, fun setup/0, fun teardown/1,
[{with, [T]} || T <- [fun ?MODULE:new_/1,
@@ -1241,6 +1255,14 @@ meck_implicit_new_test()->
?assertMatch(foo, meck_test_module:c(1, 1)),
meck:unload().
+wait_for_zero_calls_test() ->
+ %% Given
+ meck:new(test, [non_strict]),
+ %% When/Then
+ ?assertMatch(ok, meck:wait(0, test, foo, [1, '_'], 100)),
+ %% Clean
+ meck:unload().
+
wait_already_called_test() ->
%% Given
meck:new(test, [non_strict]),
@@ -1272,11 +1294,10 @@ wait_not_called_another_proc_test() ->
%% When
test:foo(1, 2), % Called, but not by the expected proc.
Pid = erlang:spawn(fun() ->
- timer:sleep(50),
test:foo(2, 2) % Unexpected first argument
end),
%% Then
- ?assertError(timeout, meck:wait(1, test, foo, [1, '_'], Pid, 0)),
+ ?assertError(timeout, meck:wait(1, test, foo, [1, '_'], Pid, 100)),
%% Clean
meck:unload().
@@ -1307,6 +1328,75 @@ wait_timeout_test() ->
%% Clean
meck:unload().
+wait_for_the_same_pattern_on_different_processes_test() ->
+ %% Given
+ meck:new(test, [non_strict]),
+ meck:expect(test, foo, 2, ok),
+ Pid1 = erlang:spawn(fun() ->
+ ?assertMatch(ok,
+ meck:wait(2, test, foo,
+ [1, 2], 100))
+ end),
+ MonitorRef1 = erlang:monitor(process, Pid1),
+ Pid2 = erlang:spawn(fun() ->
+ ?assertMatch(ok,
+ meck:wait(3, test, foo,
+ [1, 2], 100))
+ end),
+ MonitorRef2 = erlang:monitor(process, Pid2),
+ %% When
+ timer:sleep(50),
+ test:foo(1, 2),
+ test:foo(1, 2),
+ %% Then
+ ?assertTerminated(MonitorRef1, normal, 300),
+ ?assertTerminated(MonitorRef2, {timeout, _}, 300),
+ %% Clean
+ meck:unload().
+
+wait_for_different_patterns_on_different_processes_test() ->
+ %% Given
+ meck:new(test, [non_strict]),
+ meck:expect(test, foo, 1, ok),
+ meck:expect(test, bar, 2, ok),
+ Pid1 = erlang:spawn(fun() ->
+ ?assertMatch(ok,
+ meck:wait(2, test, foo,
+ [1], 100))
+ end),
+ MonitorRef1 = erlang:monitor(process, Pid1),
+ Pid2 = erlang:spawn(fun() ->
+ ?assertMatch(ok,
+ meck:wait(3, test, bar,
+ [1, 2], 100))
+ end),
+ MonitorRef2 = erlang:monitor(process, Pid2),
+ %% When
+ timer:sleep(50),
+ test:bar(1, 2),
+ test:foo(1),
+ test:bar(1, 2),
+ test:bar(1, 2),
+ %% Then
+ ?assertTerminated(MonitorRef1, {timeout, _}, 300),
+ ?assertTerminated(MonitorRef2, normal, 300),
+ %% Clean
+ meck:unload().
+
+wait_purge_expired_tracker_test() ->
+ %% Given
+ meck:new(test, [non_strict]),
+ meck:expect(test, foo, 2, ok),
+ ?assertError(timeout, meck:wait(1, test, foo, [1, '_'], 1)),
+ %% When
+ timer:sleep(50),
+ % Makes expired tracker be purged. There is no way to check that from the
+ % code only in the coverage report. But at least we exercise this code path
+ % here.
+ test:foo(1, 2),
+ %% Clean
+ meck:unload().
+
%%=============================================================================
%% Internal Functions
%%=============================================================================