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
 %%=============================================================================