| %%%============================================================================ |
| %%% Copyright 2011 Adam Lindberg & Erlang Solutions Ltd. |
| %%% |
| %%% Licensed under the Apache License, Version 2.0 (the "License"); |
| %%% you may not use this file except in compliance with the License. |
| %%% You may obtain a copy of the License at |
| %%% |
| %%% http://www.apache.org/licenses/LICENSE-2.0 |
| %%% |
| %%% Unless required by applicable law or agreed to in writing, software |
| %%% distributed under the License is distributed on an "AS IS" BASIS, |
| %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| %%% See the License for the specific language governing permissions and |
| %%% limitations under the License. |
| %%%============================================================================ |
| |
| %%% @author Adam Lindberg <eproxus@gmail.com> |
| %%% @copyright 2011, Adam Lindberg & Erlang Solutions Ltd |
| %%% @doc Module mocking library for Erlang. |
| |
| -module(meck). |
| |
| %% API |
| -export_type([matcher/0]). |
| -export_type([args_spec/0]). |
| -export_type([ret_spec/0]). |
| -export_type([func_clause_spec/0]). |
| |
| %% Interface exports |
| -export([new/1]). |
| -export([new/2]). |
| -export([expect/3]). |
| -export([expect/4]). |
| -export([sequence/4]). |
| -export([loop/4]). |
| -export([delete/3]). |
| -export([exception/2]). |
| -export([passthrough/1]). |
| -export([history/1]). |
| -export([history/2]). |
| -export([validate/1]). |
| -export([unload/0]). |
| -export([unload/1]). |
| -export([called/3]). |
| -export([called/4]). |
| -export([num_calls/3]). |
| -export([num_calls/4]). |
| -export([reset/1]). |
| -export([capture/5]). |
| -export([capture/6]). |
| -export([wait/4]). |
| -export([wait/5]). |
| -export([wait/6]). |
| |
| %% Syntactic sugar |
| -export([loop/1]). |
| -export([seq/1]). |
| -export([val/1]). |
| -export([raise/2]). |
| -export([passthrough/0]). |
| -export([exec/1]). |
| -export([is/1]). |
| |
| %%%============================================================================ |
| %%% Types |
| %%%============================================================================ |
| |
| -type meck_mfa() :: {Mod::atom(), Func::atom(), Args::[any()]}. |
| %% Module, function and arguments that the mock module got called with. |
| |
| -type stack_trace() :: [{Mod::atom(), Func::atom(), AriOrArgs::byte()|[any()]} | |
| {Mod::atom(), Func::atom(), AriOrArgs::byte()|[any()], |
| Location::[{atom(), any()}]}]. |
| %% Erlang stack trace. |
| |
| -type history() :: [{CallerPid::pid(), meck_mfa(), Result::any()} | |
| {CallerPid::pid(), meck_mfa(), Class::throw|error|exit, |
| Reason::any(), stack_trace()}]. |
| %% Represents a list of either successful function calls with a returned |
| %% result or function calls that resulted in an exception with a type, |
| %% reason and a stack trace. Each tuple begins with the pid of the process |
| %% that made the call to the function. |
| |
| -opaque matcher() :: meck_matcher:matcher(). |
| %% Matcher is an entity that is used to check that a particular value meets |
| %% some criteria. They are used in defining expectation where Erlang patterns |
| %% are not enough. E.g. to check that a numeric value is within bounds. |
| %% Instances of `matcher' can be created by {@link is/1} function from either a |
| %% predicate function or a hamcrest matcher. (see {@link is/1} for details). |
| %% An instance of this type may be specified in any or even all positions of an |
| %% {@link arg_spec()}. |
| |
| -type args_spec() :: [any() | '_' | matcher()] | non_neg_integer(). |
| %% Argument specification is used to specify argument patterns throughout Meck. |
| %% In particular it is used in definition of expectation clauses by |
| %% {@link expect/3}, {@link expect/4}, and by history digging functions |
| %% {@link num_called/3}, {@link called/3} to specify what arguments of a |
| %% function call of interest should look like. |
| %% |
| %% An argument specification can be given as a argument pattern list or |
| %% as a non-negative integer that represents function clause/call arity. |
| %% |
| %% If an argument specification is given as an argument pattern, then every |
| %% pattern element corresponds to a function argument at the respective |
| %% position. '_' is a wildcard that matches any value. In fact you can specify |
| %% atom wildcard '_' at any level in the value structure. |
| %% (E.g.: {1, [blah, {'_', "bar", 2} | '_'], 3}). It is also possible to use a |
| %% {@link matcher()} created by {@link is/1} in-place of a value pattern. |
| %% |
| %% If an argument specification is given by an arity, then it is equivalent to |
| %% a pattern based argument specification that consists solely of wildcards, |
| %% and has the length of arity (e.g.: 3 is equivalent to ['_', '_', '_']). |
| |
| -opaque ret_spec() :: meck_ret_spec:ret_spec(). |
| %% Opaque data structure that specifies a value or a set of values to be returned |
| %% by a mock stub function defined by either {@link expect/3} and {@link expect/4}. |
| %% Values of `ret_spec()' are constructed by {@link seq/1}, {@link loop/1}, |
| %% {@link val/1}, and {@link raise/2} functions. They are used to specify |
| %% return values in {@link expect/3} and {@link expect/4} functions, and also |
| %% as a parameter of the `stub_all' option of {@link new/2} function. |
| %% |
| %% Note that any Erlang term `X' is a valid `ret_spec()' equivalent to |
| %% `meck:val(X)'. |
| |
| -type func_clause_spec() :: {args_spec(), ret_spec()}. |
| %% It is used in {@link expect/3} and {@link expect/4} to define a function |
| %% clause of complex multi-clause expectations. |
| |
| %%%============================================================================ |
| %%% Interface exports |
| %%%============================================================================ |
| |
| %% @equiv new(Mod, []) |
| -spec new(Mods) -> ok when |
| Mods :: Mod | [Mod], |
| Mod :: atom(). |
| new(Mod) when is_atom(Mod) -> new(Mod, []); |
| new(Mod) when is_list(Mod) -> lists:foreach(fun new/1, Mod), ok. |
| |
| %% @doc Creates new mocked module(s). |
| %% |
| %% This replaces the current version (if any) of the modules in `Mod' |
| %% with an empty module. |
| %% |
| %% Since this library is intended to use from test code, this |
| %% function links a process for each mock to the calling process. |
| %% |
| %% The valid options are: |
| %% <dl> |
| %% <dt>`passthrough'</dt> |
| %% <dd>Retains the original functions, if not mocked by meck. If used along |
| %% with `stub_all' then `stub_all' is ignored.</dd> |
| %% |
| %% <dt>`no_link'</dt> |
| %% <dd>Does not link the meck process to the caller process (needed for using |
| %% meck in rpc calls).</dd> |
| %% |
| %% <dt>`unstick'</dt> |
| %% <dd>Unstick the module to be mocked (e.g. needed for using meck with |
| %% kernel and stdlib modules).</dd> |
| %% |
| %% <dt>`no_passthrough_cover'</dt> |
| %% <dd>If cover is enabled on the module to be mocked then meck will continue |
| %% to capture coverage on passthrough calls. This option allows you to |
| %% disable that feature if it causes problems.</dd> |
| %% |
| %% <dt>`{spawn_opt, list()}'</dt> |
| %% <dd>Specify Erlang process spawn options. Typically used to specify |
| %% non-default, garbage collection options.</dd> |
| %% |
| %% <dt>`no_history'</dt> |
| %% <dd>Do not store history of meck calls.</dd> |
| %% |
| %% <dt>`non_strict'</dt> |
| %% <dd>A mock created with this option will allow setting expectations on |
| %% functions that does not exist in the mocked module. With this |
| %% option on it is even possible to mock non existing modules.</dd> |
| %% |
| %% <dt>`{stub_all, '{@link ret_spec()}`}'</dt> |
| %% <dd>Stubs all functions exported from the mocked module. The stubs will |
| %% return whatever defined by {@link ret_spec()} regardless of arguments |
| %% passed in. It is possible to specify this option as just `stub_all' |
| %% then stubs will return atom `ok'. If used along with `passthrough' |
| %% then `stub_all' is ignored. </dd> |
| %% </dl> |
| -spec new(Mods, Options) -> ok when |
| Mods :: Mod | [Mod], |
| Mod :: atom(), |
| Options :: [proplists:property()]. |
| new(Mod, Options) when is_atom(Mod), is_list(Options) -> |
| meck_proc:start(Mod, Options); |
| new(Mod, Options) when is_list(Mod) -> |
| lists:foreach(fun(M) -> new(M, Options) end, Mod), |
| ok. |
| |
| %% @doc Add expectation for a function `Func' to the mocked modules `Mod'. |
| %% |
| %% An expectation is either of the following: |
| %% <dl> |
| %% <dt>`function()'</dt><dd>a stub function that is executed whenever the |
| %% function `Func' is called. The arity of `function()' identifies for which |
| %% particular `Func' variant an expectation is created for (that is, a function |
| %% with arity 2 will generate an expectation for `Func/2').</dd> |
| %% <dt>`['{@link func_clause_spec()}`]'</dt><dd>a list of {@link |
| %% arg_spec()}/{@link ret_spec()} pairs. Whenever the function `Func' is called |
| %% the arguments are matched against the {@link arg_spec()} in the list. As |
| %% soon as the first match is found then a value defined by the corresponding |
| %% {@link ret_spec()} is returned.</dd> |
| %% </dl> |
| %% |
| %% It affects the validation status of the mocked module(s). If an |
| %% expectation is called with the wrong number of arguments or invalid |
| %% arguments the mock module(s) is invalidated. It is also invalidated if |
| %% an unexpected exception occurs. |
| -spec expect(Mods, Func, Expectation) -> ok when |
| Mods :: Mod | [Mod], |
| Mod :: atom(), |
| Func :: atom(), |
| Expectation :: function() | [func_clause_spec()]. |
| expect(Mod, Func, Expectation) when is_list(Mod) -> |
| lists:foreach(fun(M) -> expect(M, Func, Expectation) end, Mod), |
| ok; |
| expect(_Mod, _Func, []) -> |
| erlang:error(empty_clause_list); |
| expect(Mod, Func, Expectation) when is_atom(Mod), is_atom(Func) -> |
| Expect = meck_expect:new(Func, Expectation), |
| check_expect_result(meck_proc:set_expect(Mod, Expect)). |
| |
| %% @doc Adds an expectation with the supplied arity and return value. |
| %% |
| %% This creates an expectation that has the only clause {`ArgsSpec', `RetSpec'}. |
| %% |
| %% @equiv expect(Mod, Func, [{ArgsSpec, RetSpec}]) |
| -spec expect(Mods, Func, ArgsSpec, RetSpec) -> ok when |
| Mods :: Mod | [Mod], |
| Mod :: atom(), |
| Func :: atom(), |
| ArgsSpec :: args_spec(), |
| RetSpec :: ret_spec(). |
| expect(Mod, Func, ArgsSpec, RetSpec) when is_list(Mod) -> |
| lists:foreach(fun(M) -> expect(M, Func, ArgsSpec, RetSpec) end, Mod), |
| ok; |
| expect(Mod, Func, ArgsSpec, RetSpec) when is_atom(Mod), is_atom(Func) -> |
| Expect = meck_expect:new(Func, ArgsSpec, RetSpec), |
| check_expect_result(meck_proc:set_expect(Mod, Expect)). |
| |
| %% @equiv expect(Mod, Func, Ari, seq(Sequence)) |
| %% @deprecated Please use {@link expect/3} or {@link expect/4} along with |
| %% {@link ret_spec()} generated by {@link seq/1}. |
| -spec sequence(Mods, Func, Ari, Sequence) -> ok when |
| Mods :: Mod | [Mod], |
| Mod :: atom(), |
| Func :: atom(), |
| Ari :: byte(), |
| Sequence :: [any()]. |
| sequence(Mod, Func, Ari, Sequence) |
| when is_atom(Mod), is_atom(Func), is_integer(Ari), Ari >= 0 -> |
| Expect = meck_expect:new(Func, Ari, meck_ret_spec:seq(Sequence)), |
| check_expect_result(meck_proc:set_expect(Mod, Expect)); |
| sequence(Mod, Func, Ari, Sequence) when is_list(Mod) -> |
| lists:foreach(fun(M) -> sequence(M, Func, Ari, Sequence) end, Mod), |
| ok. |
| |
| %% @equiv expect(Mod, Func, Ari, loop(Loop)) |
| %% @deprecated Please use {@link expect/3} or {@link expect/4} along with |
| %% {@link ret_spec()} generated by {@link loop/1}. |
| -spec loop(Mods, Func, Ari, Loop) -> ok when |
| Mods :: Mod | [Mod], |
| Mod :: atom(), |
| Func :: atom(), |
| Ari :: byte(), |
| Loop :: [any()]. |
| loop(Mod, Func, Ari, Loop) |
| when is_atom(Mod), is_atom(Func), is_integer(Ari), Ari >= 0 -> |
| Expect = meck_expect:new(Func, Ari, meck_ret_spec:loop(Loop)), |
| check_expect_result(meck_proc:set_expect(Mod, Expect)); |
| loop(Mod, Func, Ari, Loop) when is_list(Mod) -> |
| lists:foreach(fun(M) -> loop(M, Func, Ari, Loop) end, Mod), |
| ok. |
| |
| %% @doc Deletes an expectation. |
| %% |
| %% Deletes the expectation for the function `Func' with the matching |
| %% arity `Arity'. |
| -spec delete(Mods, Func, Ari) -> ok when |
| Mods :: Mod | [Mod], |
| Mod :: atom(), |
| Func :: atom(), |
| Ari :: byte(). |
| delete(Mod, Func, Ari) |
| when is_atom(Mod), is_atom(Func), Ari >= 0 -> |
| meck_proc:delete_expect(Mod, Func, Ari); |
| delete(Mod, Func, Ari) when is_list(Mod) -> |
| lists:foreach(fun(M) -> delete(M, Func, Ari) end, Mod), |
| ok. |
| |
| %% @doc Throws an expected exception inside an expect fun. |
| %% |
| %% This exception will get thrown without invalidating the mocked |
| %% module. That is, the code using the mocked module is expected to |
| %% handle this exception. |
| %% |
| %% <em>Note: this code should only be used inside an expect fun.</em> |
| -spec exception(Class, Reason) -> no_return() when |
| Class :: throw | error | exit, |
| Reason :: any(). |
| exception(Class, Reason) when Class == throw; Class == error; Class == exit -> |
| erlang:throw(meck_ret_spec:raise(Class, Reason)). |
| |
| %% @doc Calls the original function (if existing) inside an expectation fun. |
| %% |
| %% <em>Note: this code should only be used inside an expect fun.</em> |
| -spec passthrough(Args) -> Result when |
| Args :: [any()], |
| Result :: any(). |
| passthrough(Args) when is_list(Args) -> |
| {Mod, Func} = meck_code_gen:get_current_call(), |
| erlang:apply(meck_util:original_name(Mod), Func, Args). |
| |
| %% @doc Validate the state of the mock module(s). |
| %% |
| %% The function returns `true' if the mocked module(s) has been used |
| %% according to its expectations. It returns `false' if a call has |
| %% failed in some way. Reasons for failure are wrong number of |
| %% arguments or non-existing function (undef), wrong arguments |
| %% (function clause) or unexpected exceptions. |
| %% |
| %% Use the {@link history/1} or {@link history/2} function to analyze errors. |
| -spec validate(Mods) -> boolean() when |
| Mods :: Mod | [Mod], |
| Mod :: atom(). |
| validate(Mod) when is_atom(Mod) -> |
| meck_proc:validate(Mod); |
| validate(Mod) when is_list(Mod) -> |
| not lists:member(false, [validate(M) || M <- Mod]). |
| |
| %% @doc Return the call history of the mocked module for all processes. |
| %% |
| %% @equiv history(Mod, '_') |
| -spec history(Mod) -> history() when |
| Mod :: atom(). |
| history(Mod) when is_atom(Mod) -> meck_history:get_history('_', Mod). |
| |
| %% @doc Return the call history of the mocked module for the specified process. |
| %% |
| %% Returns a list of calls to the mocked module and their results for |
| %% the specified `Pid'. Results can be either normal Erlang terms or |
| %% exceptions that occurred. |
| %% |
| %% @see history/1 |
| %% @see called/3 |
| %% @see called/4 |
| %% @see num_calls/3 |
| %% @see num_calls/4 |
| -spec history(Mod, OptCallerPid) -> history() when |
| Mod :: atom(), |
| OptCallerPid :: '_' | pid(). |
| history(Mod, OptCallerPid) |
| when is_atom(Mod), is_pid(OptCallerPid) orelse OptCallerPid == '_' -> |
| meck_history:get_history(OptCallerPid, Mod). |
| |
| %% @doc Unloads all mocked modules from memory. |
| %% |
| %% The function returns the list of mocked modules that were unloaded |
| %% in the process. |
| -spec unload() -> Unloaded when |
| Unloaded :: [Mod], |
| Mod :: atom(). |
| unload() -> lists:foldl(fun unload_if_mocked/2, [], registered()). |
| |
| %% @doc Unload a mocked module or a list of mocked modules. |
| %% |
| %% This will purge and delete the module(s) from the Erlang virtual |
| %% machine. If the mocked module(s) replaced an existing module, this |
| %% module will still be in the Erlang load path and can be loaded |
| %% manually or when called. |
| -spec unload(Mods) -> ok when |
| Mods :: Mod | [Mod], |
| Mod :: atom(). |
| unload(Mod) when is_atom(Mod) -> |
| meck_proc:stop(Mod), |
| wait_for_exit(Mod); |
| unload(Mods) when is_list(Mods) -> |
| lists:foreach(fun unload/1, Mods), ok. |
| |
| %% @doc Returns whether `Mod:Func' has been called with `Args'. |
| %% |
| %% @equiv called(Mod, Fun, Args, '_') |
| -spec called(Mod, OptFun, OptArgsSpec) -> boolean() when |
| Mod :: atom(), |
| OptFun :: '_' | atom(), |
| OptArgsSpec :: '_' | args_spec(). |
| called(Mod, OptFun, OptArgsSpec) -> |
| meck_history:num_calls('_', Mod, OptFun, OptArgsSpec) > 0. |
| |
| %% @doc Returns whether `Pid' has called `Mod:Func' with `Args'. |
| %% |
| %% This will check the history for the module, `Mod', to determine |
| %% whether process `Pid' call the function, `Fun', with arguments, `Args'. If |
| %% so, this function returns true, otherwise false. |
| %% |
| %% Wildcards can be used, at any level in any term, by using the underscore |
| %% atom: ``'_' '' |
| %% |
| %% @see called/3 |
| -spec called(Mod, OptFun, OptArgsSpec, OptCallerPid) -> boolean() when |
| Mod :: atom(), |
| OptFun :: '_' | atom(), |
| OptArgsSpec :: '_' | args_spec(), |
| OptCallerPid :: '_' | pid(). |
| called(Mod, OptFun, OptArgsSpec, OptPid) -> |
| meck_history:num_calls(OptPid, Mod, OptFun, OptArgsSpec) > 0. |
| |
| %% @doc Returns the number of times `Mod:Func' has been called with `Args'. |
| %% |
| %% @equiv num_calls(Mod, Fun, Args, '_') |
| -spec num_calls(Mod, OptFun, OptArgsSpec) -> non_neg_integer() when |
| Mod :: atom(), |
| OptFun :: '_' | atom(), |
| OptArgsSpec :: '_' | args_spec(). |
| num_calls(Mod, OptFun, OptArgsSpec) -> |
| meck_history:num_calls('_', Mod, OptFun, OptArgsSpec). |
| |
| %% @doc Returns the number of times process `Pid' has called `Mod:Func' |
| %% with `Args'. |
| %% |
| %% This will check the history for the module, `Mod', to determine how |
| %% many times process `Pid' has called the function, `Fun', with |
| %% arguments, `Args' and returns the result. |
| %% |
| %% @see num_calls/3 |
| -spec num_calls(Mod, OptFun, OptArgsSpec, OptCallerPid) -> |
| non_neg_integer() when |
| Mod :: atom(), |
| OptFun :: '_' | atom(), |
| OptArgsSpec :: '_' | args_spec(), |
| OptCallerPid :: '_' | pid(). |
| num_calls(Mod, OptFun, OptArgsSpec, OptPid) -> |
| meck_history:num_calls(OptPid, Mod, OptFun, OptArgsSpec). |
| |
| %% @doc Blocks until either function `Mod:Func' is called at least once with |
| %% arguments matching `OptArgsSpec', or `Timeout' has elapsed. In the latter |
| %% case the call fails with `error:timeout'. |
| %% |
| %% The number of calls is counted starting from the most resent call to |
| %% {@link reset/1} on the mock or from the mock creation, whichever occurred |
| %% latter. If a matching call has already occurred, then the function returns |
| %% `ok' immediately. |
| %% |
| %% @equiv wait(1, Mod, OptFunc, OptArgsSpec, '_', Timeout) |
| -spec wait(Mod, OptFunc, OptArgsSpec, Timeout) -> ok when |
| Mod :: atom(), |
| OptFunc :: '_' | atom(), |
| OptArgsSpec :: '_' | args_spec(), |
| Timeout :: non_neg_integer(). |
| wait(Mod, OptFunc, OptArgsSpec, Timeout) -> |
| wait(1, Mod, OptFunc, OptArgsSpec, '_', Timeout). |
| |
| %% @doc Blocks until either function `Mod:Func' is called at least `Times' with |
| %% arguments matching `OptArgsSpec', or `Timeout' has elapsed. In the latter |
| %% case the call fails with `error:timeout'. |
| %% |
| %% The number of calls is counted starting from the most resent call to |
| %% {@link reset/1} on the mock or from the mock creation, whichever occurred |
| %% latter. If `Times' number of matching calls has already occurred, then the |
| %% function returns `ok' immediately. |
| %% |
| %% @equiv wait(Times, Mod, OptFunc, OptArgsSpec, '_', Timeout) |
| -spec wait(Times, Mod, OptFunc, OptArgsSpec, Timeout) -> ok when |
| Times :: pos_integer(), |
| Mod :: atom(), |
| OptFunc :: '_' | atom(), |
| OptArgsSpec :: '_' | args_spec(), |
| Timeout :: non_neg_integer(). |
| wait(Times, Mod, OptFunc, OptArgsSpec, Timeout) -> |
| wait(Times, Mod, OptFunc, OptArgsSpec, '_', Timeout). |
| |
| %% @doc Blocks until either function `Mod:Func' is called at least `Times' with |
| %% arguments matching `OptArgsSpec' by process `OptCallerPid', or `Timeout' has |
| %% elapsed. In the latter case the call fails with `error:timeout'. |
| %% |
| %% The number of calls is counted starting from the most resent call to |
| %% {@link reset/1} on the mock or from the mock creation, whichever occurred |
| %% latter. If `Times' number of matching call has already occurred, then the |
| %% function returns `ok' immediately. |
| -spec wait(Times, Mod, OptFunc, OptArgsSpec, OptCallerPid, Timeout) -> ok when |
| Times :: pos_integer(), |
| Mod :: atom(), |
| OptFunc :: '_' | atom(), |
| OptArgsSpec :: '_' | args_spec(), |
| OptCallerPid :: '_' | pid(), |
| 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). |
| |
| %% @doc Erases the call history for a mocked module or a list of mocked modules. |
| %% |
| %% This function will erase all calls made heretofore from the history of the |
| %% specified modules. It is intended to prevent cluttering of test results with |
| %% calls to mocked modules made during the test setup phase. |
| -spec reset(Mods) -> ok when |
| Mods :: Mod | [Mod], |
| Mod :: atom(). |
| reset(Mod) when is_atom(Mod) -> |
| meck_proc:reset(Mod); |
| reset(Mods) when is_list(Mods) -> |
| lists:foreach(fun(Mod) -> reset(Mod) end, Mods). |
| |
| %% @doc Converts a list of terms into {@link ret_spec()} defining a loop of |
| %% values. It is intended to be in construction of clause specs for the |
| %% {@link expect/3} function. |
| %% |
| %% Calls to an expect, created with {@link ret_spec()} returned by this function, |
| %% will return one element at a time from the `Loop' list and will restart at |
| %% the first element when the end is reached. |
| -spec loop(Loop) -> ret_spec() when |
| Loop :: [ret_spec()]. |
| loop(Loop) -> meck_ret_spec:loop(Loop). |
| |
| %% @doc Converts a list of terms into {@link ret_spec()} defining a sequence of |
| %% values. It is intended to be in construction of clause specs for the |
| %% {@link expect/3} function. |
| %% |
| %% Calls to an expect, created with {@link ret_spec} returned by this function, |
| %% will exhaust the `Sequence' list of return values in order until the last |
| %% value is reached. That value is then returned for all subsequent calls. |
| -spec seq(Sequence) -> ret_spec() when |
| Sequence :: [ret_spec()]. |
| seq(Sequence) -> meck_ret_spec:seq(Sequence). |
| |
| %% @doc Converts a term into {@link ret_spec()} defining an individual value. |
| %% It is intended to be in construction of clause specs for the |
| %% {@link expect/3} function. |
| -spec val(Value) -> ret_spec() when |
| Value :: any(). |
| val(Value) -> meck_ret_spec:val(Value). |
| |
| %% @doc Creates a {@link ret_spec()} that defines an exception. |
| %% |
| %% Calls to an expect, created with {@link ret_spec()} returned by this function, |
| %% will raise the specified exception. |
| -spec raise(Class, Reason) -> ret_spec() when |
| Class :: throw | error | exit, |
| Reason :: term. |
| raise(Class, Reason) -> meck_ret_spec:raise(Class, Reason). |
| |
| %% @doc Creates a {@link ret_spec()} that makes the original module function be |
| %% called. |
| %% |
| %% Calls to an expect, created with {@link ret_spec()} returned by this function, |
| %% will be forwarded to the original function. |
| -spec passthrough() -> ret_spec(). |
| passthrough() -> meck_ret_spec:passthrough(). |
| |
| %% @doc Creates a {@link ret_spec()} from a function. Calls to an expect, |
| %% created with {@link ret_spec()} returned by this function, will be forwarded |
| %% to the specified function. |
| -spec exec(fun()) -> ret_spec(). |
| exec(Fun) -> meck_ret_spec:exec(Fun). |
| |
| %% @doc creates a {@link matcher/0} instance from either `Predicate' or |
| %% `HamcrestMatcher'. |
| %% <ul> |
| %% <li>`Predicate' - is a single parameter function. If it returns `true' then |
| %% the argument passed to it is considered as meeting the matcher criteria, |
| %% otherwise as not.</li> |
| %% <li>`HamcrestMatcher' - is a matcher created by |
| %% <a href="https://github.com/hyperthunk/hamcrest-erlang">Hamcrest-Erlang</a> |
| %% library</li> |
| %% </ul> |
| -spec is(MatcherImpl) -> matcher() when |
| MatcherImpl :: Predicate | HamcrestMatcher, |
| Predicate :: fun((any()) -> any()), |
| HamcrestMatcher :: hamcrest:matchspec(). |
| is(MatcherImpl) -> |
| meck_matcher:new(MatcherImpl). |
| |
| %% @doc Returns the value of an argument as it was passed to a particular |
| %% function call made by a particular process. It fails with `not_found' error |
| %% if a function call of interest has never been made. |
| %% |
| %% It retrieves the value of argument at `ArgNum' position as it was passed |
| %% to function call `Mod:Func' with arguments that match `OptArgsSpec' made by |
| %% process `CallerPid' that occurred `Occur''th according to the call history. |
| %% |
| %% Atoms `first' and `last' can be used in place of the occurrence number to |
| %% retrieve the argument value passed when the function was called the first |
| %% or the last time respectively. |
| %% |
| %% If an occurrence of a function call irrespective of the calling process needs |
| %% to be captured then `_' might be passed as `OptCallerPid', but it is better |
| %% to use {@link capture/3} instead. |
| -spec capture(Occur, Mod, Func, OptArgsSpec, ArgNum, OptCallerPid) -> ArgValue when |
| Occur :: first | last | pos_integer(), |
| Mod :: atom(), |
| Func :: atom(), |
| OptArgsSpec :: '_' | args_spec(), |
| ArgNum :: pos_integer(), |
| OptCallerPid :: '_' | pid(), |
| ArgValue :: any(). |
| capture(Occur, Mod, Func, OptArgsSpec, ArgNum, OptCallerPid) -> |
| meck_history:capture(Occur, OptCallerPid, Mod, Func, OptArgsSpec, ArgNum). |
| |
| %% @doc Returns the value of an argument as it was passed to a particular |
| %% function call, It fails with `not_found' error if a function call of |
| %% interest has never been made. |
| %% |
| %% It retrieves the value of argument at `ArgNum' position as it was passed |
| %% to function call `Mod:Func' with arguments that match `OptArgsSpec' that |
| %% occurred `Occur''th according to the call history. |
| %% |
| %% Atoms `first' and `last' can be used in place of the occurrence number to |
| %% retrieve the argument value passed when the function was called the first |
| %% or the last time respectively. |
| %% |
| %% @equiv capture(Occur, '_', Mod, Func, OptArgsSpec, ArgNum) |
| -spec capture(Occur, Mod, Func, OptArgsSpec, ArgNum) -> ArgValue when |
| Occur :: first | last | pos_integer(), |
| Mod::atom(), |
| Func::atom(), |
| OptArgsSpec :: args_spec(), |
| ArgNum :: pos_integer(), |
| ArgValue :: any(). |
| capture(Occur, Mod, Func, OptArgsSpec, ArgNum) -> |
| meck_history:capture(Occur, '_', Mod, Func, OptArgsSpec, ArgNum). |
| |
| %%%============================================================================ |
| %%% Internal functions |
| %%%============================================================================ |
| |
| -spec wait_for_exit(Mod::atom()) -> ok. |
| wait_for_exit(Mod) -> |
| MonitorRef = erlang:monitor(process, meck_util:proc_name(Mod)), |
| receive {'DOWN', MonitorRef, _Type, _Object, _Info} -> ok end. |
| |
| -spec unload_if_mocked(Mod::atom() | string(), Unloaded::[atom()]) -> |
| NewUnloaded::[atom()]. |
| unload_if_mocked(Mod, Unloaded) when is_atom(Mod) -> |
| unload_if_mocked(atom_to_list(Mod), Unloaded); |
| unload_if_mocked(ModName, Unloaded) when length(ModName) > 5 -> |
| case lists:split(length(ModName) - 5, ModName) of |
| {Name, "_meck"} -> |
| Mocked = erlang:list_to_existing_atom(Name), |
| try |
| unload(Mocked) |
| catch error:{not_mocked, Mocked} -> |
| ok |
| end, |
| [Mocked | Unloaded]; |
| _Else -> |
| Unloaded |
| end; |
| unload_if_mocked(_P, Unloaded) -> |
| Unloaded. |
| |
| -spec check_expect_result(ok | {error, Reason::any()}) -> ok. |
| check_expect_result(ok) -> ok; |
| check_expect_result({error, Reason}) -> erlang:error(Reason). |