pax_global_header00006660000000000000000000000064122256350110014507gustar00rootroot0000000000000052 comment=f95c28928a67ef181332bbf0d54bafb4e10a4f21 erlang-meck-0.8.1+dfsg/000077500000000000000000000000001222563501100146215ustar00rootroot00000000000000erlang-meck-0.8.1+dfsg/.gitignore000066400000000000000000000002571222563501100166150ustar00rootroot00000000000000*.beam *.log *.dump *.coverdata doc/*.html doc/*.css doc/*.png doc/edoc-info ebin .eunit .DS_Store tags .*.swp .settings .project *.sublime-workspace *.sublime-project deps/* erlang-meck-0.8.1+dfsg/.scripts/000077500000000000000000000000001222563501100163665ustar00rootroot00000000000000erlang-meck-0.8.1+dfsg/.scripts/tag_with_changelog.sh000077500000000000000000000016231222563501100225440ustar00rootroot00000000000000#!/bin/bash # Install for alias 'tag-cl' with: # git config alias.tag-cl '!.scripts/tag_with_changelog.sh' set -e # Abort on first failure, so we don't mess something up if [ -z "$1" ]; then # Missing tag name echo "usage: git tag-cl " >&2 exit 129 fi if [ ! -f CHANGELOG ]; then # No changelog to be used echo "fatal: CHANGELOG missing" >&2 exit 128 fi if [ ! -z "$(git status --short)" ]; then # Sanity check echo "fatal: dirty repository" >&2 exit 128 fi CHANGELOG=$(cat CHANGELOG) # Clean up changelog echo "" > CHANGELOG git add CHANGELOG # Update version in .app file sed -i "" -e "s/{vsn, .*}/{vsn, \"$1\"}/g" src/meck.app.src sed -i "" -e "s/@version .*/@version $1/g" doc/overview.edoc git add src/meck.app.src git add doc/overview.edoc # Commit, tag and push git commit -m "Version $1" git tag -s $1 -m "Version $ $CHANGELOG" git push && git push --tags erlang-meck-0.8.1+dfsg/.travis.yml000066400000000000000000000002341222563501100167310ustar00rootroot00000000000000language: erlang notifications: email: - hello@alind.io otp_release: - R16B - R15B03 - R14B04 before_script: "make get-deps" script: "make all" erlang-meck-0.8.1+dfsg/CHANGELOG000066400000000000000000000001351222563501100160320ustar00rootroot00000000000000- The return value from passthrough/1 can now be used in expect functions before returning erlang-meck-0.8.1+dfsg/LICENSE000066400000000000000000000236771222563501100156450ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS erlang-meck-0.8.1+dfsg/Makefile000066400000000000000000000027411222563501100162650ustar00rootroot00000000000000PLTFILE=$(CURDIR)/.deps.plt APP_DEPS=kernel stdlib eunit tools compiler ERLFLAGS= -pa $(CURDIR)/.eunit -pa $(CURDIR)/ebin -pa $(CURDIR)/deps/*/ebin REBAR="./rebar" ifeq ($(REBAR),) $(error "Rebar not available on this system") endif ERL = $(shell which erl) ifeq ($(ERL),) $(error "Erlang must be available on this system") endif BUILD_PLT_INC=$(shell test -d deps && echo '-r deps') DIALYZER_INC=$(shell test -d include && echo '-I include') $(shell test -d deps && echo '-I deps') .PHONY: all rebuild compile doc clean test dialyzer typer get-deps clean-deps \ shell clean-plt clean-doc distclean all: get-deps compile test doc rebuild: distclean get-deps all get-deps: @$(REBAR) -C test.config get-deps @$(REBAR) -C test.config compile compile: @$(REBAR) -C test.config skip_deps=true compile doc: @$(REBAR) -C test.config skip_deps=true doc clean: @$(REBAR) -C test.config skip_deps=true clean test: get-deps @$(REBAR) -C test.config skip_deps=true eunit $(PLTFILE): - dialyzer --build_plt --apps $(APP_DEPS) $(BUILD_PLT_INC) --output_plt $(PLTFILE) dialyzer: compile $(PLTFILE) @dialyzer --fullpath --plt $(PLTFILE) $(DIALYZER_INC) -pa $(CURDIR)/ebin -c src --src | \ fgrep -v -f ./dialyzer.ignore-warnings typer: typer --plt $(PLTFILE) $(DIALYZER_INC) -r src shell: @$(ERL) $(ERLFLAGS) clean-plt: @rm -rf $(PLTFILE) clean-doc: @cd doc; ls * | grep -v overview.edoc | xargs rm -f clean-deps: @rm -rvf $(CURDIR)/deps/* distclean: clean clean-plt clean-doc clean-deps erlang-meck-0.8.1+dfsg/NOTICE000066400000000000000000000002331222563501100155230ustar00rootroot00000000000000Copyright 2011 Adam Lindberg Copyright 2011 Erlang Solutions This product contains code developed at Erlang Solutions. (http://www.erlang-solutions.com/) erlang-meck-0.8.1+dfsg/README.md000066400000000000000000000142671222563501100161120ustar00rootroot00000000000000[![Build Status](https://secure.travis-ci.org/eproxus/meck.png)](http://travis-ci.org/eproxus/meck) Meck ==== A mocking library for Erlang. Features -------- See what's new in [0.8 Release Notes][1]. * Dynamic return values using sequences and loops of static values * Compact definition of mock arguments, clauses and return values * Pass through: call functions in the original module * Complete call history showing calls, return values and exceptions * Mock validation, will invalidate mocks that were not called correctly * Throwing of expected exceptions that keeps the module valid * Throws an error when mocking a module that doesn't exist or has been renamed (disable with option `non_strict`) * Support for [Hamcrest][2] matchers * Automatic backup and restore of cover data * Mock is linked to the creating process and will unload automatically when a crash occurs (disable with option `no_link`) * Mocking of sticky modules (using the option `unstick`) Examples -------- Here's an example of using Meck in the Erlang shell: ```erl Eshell V5.8.4 (abort with ^G) 1> meck:new(dog). ok 2> meck:expect(dog, bark, fun() -> "Woof!" end). ok 3> dog:bark(). "Woof!" 4> meck:validate(dog). true 5> meck:unload(dog). ok 6> dog:bark(). ** exception error: undefined function dog:bark/0 ``` Exceptions can be anticipated by Meck (resulting in validation still passing). This is intended to be used to test code that can and should handle certain exceptions indeed does take care of them: ```erl 5> meck:expect(dog, meow, fun() -> meck:exception(error, not_a_cat) end). ok 6> catch dog:meow(). {'EXIT',{not_a_cat,[{meck,exception,2}, {meck,exec,4}, {dog,meow,[]}, {erl_eval,do_apply,5}, {erl_eval,expr,5}, {shell,exprs,6}, {shell,eval_exprs,6}, {shell,eval_loop,3}]}} 7> meck:validate(dog). true ``` Normal Erlang exceptions result in a failed validation. The following example is just to demonstrate the behavior, in real test code the exception would normally come from the code under test (which should, if not expected, invalidate the mocked module): ```erl 8> meck:expect(dog, jump, fun(Height) when Height > 3 -> erlang:error(too_high); (Height) -> ok end). ok 9> dog:jump(2). ok 10> catch dog:jump(5). {'EXIT',{too_high,[{meck,exec,4}, {dog,jump,[5]}, {erl_eval,do_apply,5}, {erl_eval,expr,5}, {shell,exprs,6}, {shell,eval_exprs,6}, {shell,eval_loop,3}]}} 11> meck:validate(dog). false ``` Here's an example of using Meck inside an EUnit test case: ```erlang my_test() -> meck:new(my_library_module), meck:expect(my_library_module, fib, fun(8) -> 21 end), ?assertEqual(21, code_under_test:run(fib, 8)), % Uses my_library_module ?assert(meck:validate(my_library_module)), meck:unload(my_library_module). ``` Pass-through is used when the original functionality of a module should be kept. When the option `passthrough` is used when calling `new/2` all functions in the original module will be kept in the mock. These can later be overridden by calling `expect/3` or `expect/4`. ```erl Eshell V5.8.4 (abort with ^G) 1> meck:new(string, [unstick, passthrough]). ok 2> string:strip(" test "). "test" ``` It's also possible to pass calls to the original function allowing us to override only a certain behavior of a function (this usage is compatible with the `passthrough` option). `passthrough/1` will always call the original function with the same name as the expect is defined in): ```erl Eshell V5.8.4 (abort with ^G) 1> meck:new(string, [unstick]). ok 2> meck:expect(string, strip, fun(String) -> meck:passthrough([String]) end). ok 3> string:strip(" test "). "test" 4> meck:unload(string). ok 5> string:strip(" test "). "test" ``` Build ----- Meck requires `make` and [rebar][1] to build. To build Meck and run tests, go to the Meck directory and simply type: ```sh make ``` Two things might seem alarming when running the tests: 1. Warnings emitted by cover 2. En exception printed by SASL Both are expected due to the way Erlang currently prints errors. The important line you should look for is `All XX tests passed`, if that appears all is correct. Install ------- Meck is best used via [rebar][3]. Add the following dependency t your `rebar.config` in your project root: ```erlang {deps, [ {meck, ".*", {git, "https://github.com/eproxus/meck.git", {tag, "0.8"}}} ]}. ``` If you want to install your own built version of Meck add the ebin directory to your Erlang code path or move the Meck folder into your release folder and make sure that folder is in your `ERL_LIBS` environment variable. Contribute ---------- Patches are greatly appreciated! For a much nicer history, please [write good commit messages][5]. Use a branch name prefixed by `feature/` (e.g. `feature/my_example_branch`) for easier integration when developing new features or fixes for meck. Should you find yourself using Meck and have issues, comments or feedback please [create an issue here on GitHub][4]. Contributors: - Maxim Vladimirsky (@horkhe) - Ryan Zezeski (@rzezeski) - David Haglund (@daha) - Magnus Henoch (@legoscia) - Susan Potter (@mbbx6spp) - Andreas Amsenius (@adbl) - Anthony Molinaro (@djnym) - Matt Campbell (@xenolinguist) - Martynas Pumputis (@brb) - Shunichi Shinohara (@shino) - Miƫtek Bak - Henry Nystrom - Ward Bekker (@wardbekker) - Damon Richardson - Christopher Meiklejohn - Joseph Wayne Norton (@norton) - Erkan Yilmaz (@Erkan-Yilmaz) - Joe Williams (@joewilliams) - Russell Brown - Michael Klishin (@michaelklishin) - Magnus Klaar [1]: https://github.com/eproxus/meck/wiki/0.8-Release-Notes "0.8 Release Notes" [2]: https://github.com/hyperthunk/hamcrest-erlang "Hamcrest for Erlang" [3]: https://github.com/basho/rebar "Rebar - A build tool for Erlang" [4]: http://github.com/eproxus/meck/issues "Meck issues" erlang-meck-0.8.1+dfsg/dialyzer.ignore-warnings000066400000000000000000000004161222563501100215000ustar00rootroot00000000000000### meck:code_cover.erl has to call this unexported functions to do its ### cover passthrough magic: Call to missing or unexported function cover:compile_beam/2 Call to missing or unexported function cover:get_term/1 Call to missing or unexported function cover:write/2 erlang-meck-0.8.1+dfsg/doc/000077500000000000000000000000001222563501100153665ustar00rootroot00000000000000erlang-meck-0.8.1+dfsg/doc/overview.edoc000066400000000000000000000017111222563501100200700ustar00rootroot00000000000000%%============================================================================== %% Copyright 2010 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 @copyright 2011, Adam Lindberg & Erlang Solutions Ltd @version "0.7.2" @title meck, a Mocking Library for Erlang @doc == About == meck is a mocking library for Erlang. erlang-meck-0.8.1+dfsg/rebar.config000066400000000000000000000004101222563501100170760ustar00rootroot00000000000000%% Compiler Options =========================================================== {erl_opts, [warnings_as_errors, debug_info]}. %% Misc ======================================================================= {clean_files, [".eunit", "ebin/*.beam", "test/*.beam"]}. erlang-meck-0.8.1+dfsg/src/000077500000000000000000000000001222563501100154105ustar00rootroot00000000000000erlang-meck-0.8.1+dfsg/src/meck.app.src000066400000000000000000000003121222563501100176130ustar00rootroot00000000000000%% -*- mode: erlang; -*- {application, meck, [{description, "A mocking framework for Erlang"}, {vsn, "0.8.1"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}, {env, []}]}. erlang-meck-0.8.1+dfsg/src/meck.erl000066400000000000000000000650011222563501100170350ustar00rootroot00000000000000%%%============================================================================ %%% 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 %%% @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: %%
%%
`passthrough'
%%
Retains the original functions, if not mocked by meck. If used along %% with `stub_all' then `stub_all' is ignored.
%% %%
`no_link'
%%
Does not link the meck process to the caller process (needed for using %% meck in rpc calls).
%% %%
`unstick'
%%
Unstick the module to be mocked (e.g. needed for using meck with %% kernel and stdlib modules).
%% %%
`no_passthrough_cover'
%%
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.
%% %%
`{spawn_opt, list()}'
%%
Specify Erlang process spawn options. Typically used to specify %% non-default, garbage collection options.
%% %%
`no_history'
%%
Do not store history of meck calls.
%% %%
`non_strict'
%%
A mock created with this option will allow setting expectations on %% functions that are not exported from the mocked module. With this %% option on it is even possible to mock non existing modules.
%% %%
`{stub_all, '{@link ret_spec()}`}'
%%
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.
%%
-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: %%
%%
`function()'
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').
%%
`['{@link func_clause_spec()}`]'
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.
%%
%% %% 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. %% %% Note: this code should only be used inside an expect fun. -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. %% %% Note: this code should only be used inside an expect fun. -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'. %%
-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). erlang-meck-0.8.1+dfsg/src/meck_args_matcher.erl000066400000000000000000000117601222563501100215570ustar00rootroot00000000000000%%%============================================================================ %%% 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. %%%============================================================================ %%% @private -module(meck_args_matcher). -export_type([args_spec/0]). -export_type([opt_args_spec/0]). -export_type([args_matcher/0]). %% API -export([new/1]). -export([arity/1]). -export([match/2]). %%%============================================================================ %%% Definitions %%%============================================================================ -record(args_matcher, {opt_args_pattern :: opt_args_pattern(), comp_match_spec :: ets:comp_match_spec(), has_matchers :: boolean()}). %%%============================================================================ %%% Types %%%============================================================================ -type opt_args_spec() :: args_spec() | '_'. -type args_spec() :: args_pattern() | non_neg_integer(). -type opt_args_pattern() :: args_pattern() | '_'. -type args_pattern() :: [any() | '_' | meck_matcher:matcher()]. -opaque args_matcher() :: #args_matcher{}. %%%============================================================================ %%% API %%%============================================================================ -spec new(opt_args_spec()) -> args_matcher(). new('_') -> MatchSpecItem = meck_util:match_spec_item({'_'}), CompMatchSpec = ets:match_spec_compile([MatchSpecItem]), #args_matcher{opt_args_pattern = '_', comp_match_spec = CompMatchSpec, has_matchers = false}; new(Arity) when is_number(Arity) -> ArgsPattern = lists:duplicate(Arity, '_'), MatchSpecItem = meck_util:match_spec_item({ArgsPattern}), CompMatchSpec = ets:match_spec_compile([MatchSpecItem]), #args_matcher{opt_args_pattern = ArgsPattern, comp_match_spec = CompMatchSpec, has_matchers = false}; new(ArgsPattern) when is_list(ArgsPattern) -> {HasMatchers, Pattern} = case strip_off_matchers(ArgsPattern) of unchanged -> {false, ArgsPattern}; StrippedArgsSpec -> {true, StrippedArgsSpec} end, MatchSpecItem = meck_util:match_spec_item({Pattern}), CompMatchSpec = ets:match_spec_compile([MatchSpecItem]), #args_matcher{opt_args_pattern = ArgsPattern, comp_match_spec = CompMatchSpec, has_matchers = HasMatchers}. -spec arity(args_matcher()) -> Arity::non_neg_integer(). arity(#args_matcher{opt_args_pattern = ArgsPattern}) -> erlang:length(ArgsPattern). -spec match(Args::any(), args_matcher()) -> boolean(). match(Args, #args_matcher{opt_args_pattern = OptArgsPattern, comp_match_spec = CompMatchSpec, has_matchers = HasMatchers}) -> case ets:match_spec_run([{Args}], CompMatchSpec) of [] -> false; _Matches when HasMatchers andalso erlang:is_list(OptArgsPattern) -> check_by_matchers(Args, OptArgsPattern); _Matches -> true end. %%%============================================================================ %%% Internal functions %%%============================================================================ -spec strip_off_matchers(args_pattern()) -> NewArgsPattern::args_pattern() | unchanged. strip_off_matchers(ArgsPattern) -> strip_off_matchers(ArgsPattern, [], false). -spec strip_off_matchers(args_pattern(), Stripped::[any() | '_'], boolean()) -> NewArgsPattern::args_pattern() | unchanged. strip_off_matchers([ArgPattern | Rest], Stripped, HasMatchers) -> case meck_matcher:is_matcher(ArgPattern) of true -> strip_off_matchers(Rest, ['_' | Stripped], true); _ -> strip_off_matchers(Rest, [ArgPattern | Stripped], HasMatchers) end; strip_off_matchers([], Stripped, true) -> lists:reverse(Stripped); strip_off_matchers([], _Stripped, false) -> unchanged. -spec check_by_matchers(Args ::[any()], MaybeMatchers::[any()]) -> boolean(). check_by_matchers([Arg | RestArgs], [MaybeMatcher | RestMaybeMatchers]) -> case meck_matcher:match_ignore(Arg, MaybeMatcher) of true -> check_by_matchers(RestArgs, RestMaybeMatchers); _Else -> false end; check_by_matchers([], []) -> true.erlang-meck-0.8.1+dfsg/src/meck_code.erl000066400000000000000000000105151222563501100200270ustar00rootroot00000000000000%%============================================================================= %% 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. %%============================================================================= %% @hidden %% @author Adam Lindberg %% @copyright 2011, Adam Lindberg & Erlang Solutions Ltd %% @doc Module wrangling helper functions. -module(meck_code). %% Interface exports -export([abstract_code/1]). -export([add_exports/2]). -export([beam_file/1]). -export([compile_and_load_forms/1]). -export([compile_and_load_forms/2]). -export([compile_options/1]). -export([rename_module/2]). %% Types -type erlang_form() :: term(). -type compile_options() :: [term()]. -type export() :: {atom(), byte()}. %%============================================================================= %% Interface exports %%============================================================================= -spec abstract_code(binary()) -> erlang_form(). abstract_code(BeamFile) -> case beam_lib:chunks(BeamFile, [abstract_code]) of {ok, {_, [{abstract_code, {raw_abstract_v1, Forms}}]}} -> Forms; {ok, {_, [{abstract_code, no_abstract_code}]}} -> throw(no_abstract_code) end. -spec add_exports([export()], erlang_form()) -> erlang_form(). add_exports(Exports, AbsCode) -> {attribute, Line, export, OrigExports} = lists:keyfind(export, 3, AbsCode), Attr = {attribute, Line, export, OrigExports ++ Exports}, lists:keyreplace(export, 3, AbsCode, Attr). -spec beam_file(module()) -> binary(). beam_file(Module) -> % code:which/1 cannot be used for cover_compiled modules case code:get_object_code(Module) of {_, Binary, _Filename} -> Binary; error -> throw({object_code_not_found, Module}) end. -spec compile_and_load_forms(erlang_form()) -> binary(). compile_and_load_forms(AbsCode) -> compile_and_load_forms(AbsCode, []). -spec compile_and_load_forms(erlang_form(), compile_options()) -> binary(). compile_and_load_forms(AbsCode, Opts) -> case compile:forms(AbsCode, [return_errors|Opts]) of {ok, ModName, Binary} -> load_binary(ModName, Binary), Binary; {ok, ModName, Binary, _Warnings} -> load_binary(ModName, Binary), Binary; Error -> exit({compile_forms, Error}) end. -spec compile_options(binary() | module()) -> compile_options(). compile_options(BeamFile) when is_binary(BeamFile) -> case beam_lib:chunks(BeamFile, [compile_info]) of {ok, {_, [{compile_info, Info}]}} -> filter_options(proplists:get_value(options, Info)); _ -> [] end; compile_options(Module) -> filter_options(proplists:get_value(options, Module:module_info(compile))). -spec rename_module(erlang_form(), module()) -> erlang_form(). rename_module([{attribute, Line, module, OldAttribute}|T], NewName) -> case OldAttribute of {_OldName, Variables} -> [{attribute, Line, module, {NewName, Variables}}|T]; _OldName -> [{attribute, Line, module, NewName}|T] end; rename_module([H|T], NewName) -> [H|rename_module(T, NewName)]. %%============================================================================= %% Internal functions %%============================================================================= load_binary(Name, Binary) -> case code:load_binary(Name, "", Binary) of {module, Name} -> ok; {error, Reason} -> exit({error_loading_module, Name, Reason}) end. % parse transforms have already been applied to the abstract code in the % module, and often are not always available when compiling the forms, so % filter them out of the options filter_options (Options) -> lists:filter(fun({parse_transform,_}) -> false; (_) -> true end, Options). erlang-meck-0.8.1+dfsg/src/meck_code_gen.erl000066400000000000000000000162601222563501100206630ustar00rootroot00000000000000%%%============================================================================ %%% 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. %%%============================================================================ %%% @private %%% @doc Implements code generation for mocked module and also contains code %%% pieces that are called in the generated module context. -module(meck_code_gen). %% API -export([to_forms/2]). -export([get_current_call/0]). %% Exported to be accessible from generated modules. -export([exec/4]). %%%============================================================================ %%% Definitions %%%============================================================================ -define(CURRENT_CALL, '$meck_call'). -define(call(Module, Function, Arguments), {call, ?LINE, {remote, ?LINE, ?atom(Module), ?atom(Function)}, Arguments}). -define(atom(Atom), {atom, ?LINE, Atom}). -define(integer(Integer), {integer, ?LINE, Integer}). -define(var(Name), {var, ?LINE, Name}). -define(attribute(Attribute, Args), {attribute, ?LINE, Attribute, Args}). -define(function(Name, Arity, Clauses), {function, ?LINE, Name, Arity, Clauses}). -define(clause(Arguments, Body), {clause, ?LINE, Arguments, [], Body}). -define(tuple(Elements), {tuple, ?LINE, Elements}). %%%============================================================================ %%% API %%%============================================================================ to_forms(Mod, Expects) -> {Exports, Functions} = functions(Mod, Expects), [?attribute(module, Mod)] ++ attributes(Mod) ++ Exports ++ Functions. -spec get_current_call() -> {Mod::atom(), Func::atom()}. get_current_call() -> get(?CURRENT_CALL). %%%============================================================================ %%% Internal functions %%%============================================================================ attributes(Mod) -> try [?attribute(Key, Val) || {Key, Val} <- proplists:get_value(attributes, Mod:module_info(), []), Key =/= vsn, Key =/= deprecated] catch error:undef -> [] end. functions(Mod, Expects) -> dict:fold(fun(Export, Expect, {Exports, Functions}) -> {[?attribute(export, [Export]) | Exports], [func(Mod, Export, Expect) | Functions]} end, {[], []}, Expects). func(Mod, {Func, Arity}, {anon, Arity, Result}) -> case contains_opaque(Result) of true -> func_exec(Mod, Func, Arity); false -> func_native(Mod, Func, Arity, Result) end; func(Mod, {Func, Arity}, _Expect) -> func_exec(Mod, Func, Arity). func_exec(Mod, Func, Arity) -> Args = args(Arity), ?function(Func, Arity, [?clause(Args, [?call(?MODULE, exec, [?call(erlang, self, []), ?atom(Mod), ?atom(Func), list(Args)])])]). func_native(Mod, Func, Arity, Result) -> Args = args(Arity), AbsResult = erl_parse:abstract(Result), ?function( Func, Arity, [?clause( Args, [?call(gen_server, cast, [?atom(meck_util:proc_name(Mod)), ?tuple([?atom(add_history), ?tuple([?call(erlang, self, []), ?tuple([?atom(Mod), ?atom(Func), list(Args)]), AbsResult])])]), AbsResult])]). contains_opaque(Term) when is_pid(Term); is_port(Term); is_function(Term); is_reference(Term) -> true; contains_opaque(Term) when is_list(Term) -> lists_any(fun contains_opaque/1, Term); contains_opaque(Term) when is_tuple(Term) -> lists_any(fun contains_opaque/1, tuple_to_list(Term)); contains_opaque(_Term) -> false. %% based on lists.erl but accepts improper lists. lists_any(Pred, []) when is_function(Pred, 1) -> false; lists_any(Pred, [Hd|Tail]) -> case Pred(Hd) of true -> true; false -> lists_any(Pred, Tail) end; lists_any(Pred, Improper) -> Pred(Improper). args(0) -> []; args(Arity) -> [?var(var_name(N)) || N <- lists:seq(1, Arity)]. list([]) -> {nil, ?LINE}; list([H|T]) -> {cons, ?LINE, H, list(T)}. var_name(A) -> list_to_atom("A"++integer_to_list(A)). %% @hidden -spec exec(CallerPid::pid(), Mod::atom(), Func::atom(), Args::[any()]) -> Result::any(). exec(Pid, Mod, Func, Args) -> case meck_proc:get_result_spec(Mod, Func, Args) of undefined -> meck_proc:invalidate(Mod), raise(Pid, Mod, Func, Args, error, function_clause); ResultSpec -> put(?CURRENT_CALL, {Mod, Func}), try Result = meck_ret_spec:eval_result(Mod, Func, Args, ResultSpec), meck_proc:add_history(Mod, Pid, Func, Args, Result), Result catch Class:Reason -> handle_exception(Pid, Mod, Func, Args, Class, Reason) after erase(?CURRENT_CALL) end end. -spec handle_exception(CallerPid::pid(), Mod::atom(), Func::atom(), Args::[any()], Class:: exit | error | throw, Reason::any()) -> no_return(). handle_exception(Pid, Mod, Func, Args, Class, Reason) -> case meck_ret_spec:is_meck_exception(Reason) of {true, MockedClass, MockedReason} -> raise(Pid, Mod, Func, Args, MockedClass, MockedReason); _ -> meck_proc:invalidate(Mod), raise(Pid, Mod, Func, Args, Class, Reason) end. -spec raise(CallerPid::pid(), Mod::atom(), Func::atom(), Args::[any()], Class:: exit | error | throw, Reason::any()) -> no_return(). raise(Pid, Mod, Func, Args, Class, Reason) -> StackTrace = inject(Mod, Func, Args, erlang:get_stacktrace()), meck_proc:add_history(Mod, Pid, Func, Args, {Class, Reason, StackTrace}), erlang:raise(Class, Reason, StackTrace). -spec inject(Mod::atom(), Func::atom(), Args::[any()], meck_history:stack_trace()) -> NewStackTrace::meck_history:stack_trace(). inject(_Mod, _Func, _Args, []) -> []; inject(Mod, Func, Args, [{?MODULE, exec, _AriOrArgs, _Loc}|Stack]) -> [{Mod, Func, Args} | Stack]; inject(Mod, Func, Args, [{?MODULE, exec, _AriOrArgs}|Stack]) -> [{Mod, Func, Args} | Stack]; inject(Mod, Func, Args, [Call|Stack]) when element(1, Call) == ?MODULE -> inject(Mod, Func, Args, Stack); inject(Mod, Func, Args, [H | Stack]) -> [H | inject(Mod, Func, Args, Stack)]. erlang-meck-0.8.1+dfsg/src/meck_cover.erl000066400000000000000000000075111222563501100202350ustar00rootroot00000000000000%%============================================================================= %% 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. %%============================================================================= %% @private %% @doc Module containing functions needed by meck to integrate with cover. -module(meck_cover). %% Interface exports -export([compile_beam/2]). -export([rename_module/2]). %%============================================================================= %% Interface exports %%============================================================================= %% @doc Enabled cover on `_meck_original'. compile_beam(OriginalMod, Bin) -> alter_cover(), {ok, _} = cover:compile_beam(OriginalMod, Bin). %% @doc Given a cover file `File' exported by `cover:export' overwrite %% the module name with `Name'. rename_module(File, Name) -> NewTerms = change_cover_mod_name(read_cover_file(File), Name), write_terms(File, NewTerms), ok. %%============================================================================= %% Internal functions %%============================================================================= %% @private %% %% @doc Alter the cover BEAM module to export some of it's private %% functions. This is done for two reasons: %% %% 1. Meck needs to alter the export analysis data on disk and %% therefore needs to understand this format. This is why `get_term' %% and `write' are exposed. %% %% 2. In order to avoid creating temporary files meck needs direct %% access to `compile_beam/2' which allows passing a binary. alter_cover() -> case lists:member({compile_beam,2}, cover:module_info(exports)) of true -> ok; false -> Beam = meck_code:beam_file(cover), AbsCode = meck_code:abstract_code(Beam), Exports = [{compile_beam, 2}, {get_term, 1}, {write, 2}], AbsCode2 = meck_code:add_exports(Exports, AbsCode), _Bin = meck_code:compile_and_load_forms(AbsCode2), ok end. change_cover_mod_name(CoverTerms, Name) -> {_, Terms} = lists:foldl(fun change_name_in_term/2, {Name,[]}, CoverTerms), Terms. change_name_in_term({file, Mod, File}, {Name, Terms}) -> Term2 = {file, Name, replace_string(File, Mod, Name)}, {Name, [Term2|Terms]}; change_name_in_term({Bump={bump,_,_,_,_,_},_}=Term, {Name, Terms}) -> Bump2 = setelement(2, Bump, Name), Term2 = setelement(1, Term, Bump2), {Name, [Term2|Terms]}; change_name_in_term({_Mod,Clauses}, {Name, Terms}) -> Clauses2 = lists:foldl(fun change_name_in_clause/2, {Name, []}, Clauses), Term2 = {Name, Clauses2}, {Name, [Term2|Terms]}. change_name_in_clause(Clause, {Name, NewClauses}) -> {Name, [setelement(1, Clause, Name)|NewClauses]}. replace_string(File, Old, New) -> Old2 = atom_to_list(Old), New2 = atom_to_list(New), re:replace(File, Old2, New2, [{return, list}]). read_cover_file(File) -> {ok, Fd} = file:open(File, [read, binary, raw]), Terms = get_terms(Fd, []), ok = file:close(Fd), Terms. get_terms(Fd, Terms) -> case cover:get_term(Fd) of eof -> Terms; Term -> get_terms(Fd, [Term|Terms]) end. write_terms(File, Terms) -> {ok, Fd} = file:open(File, [write, binary, raw]), lists:foreach(write_term(Fd), Terms), ok. write_term(Fd) -> fun(Term) -> cover:write(Term, Fd) end. erlang-meck-0.8.1+dfsg/src/meck_expect.erl000066400000000000000000000124031222563501100204030ustar00rootroot00000000000000%%%============================================================================ %%% 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. %%%============================================================================ %%% @private %%% @doc Provides expectation processing functions. -module(meck_expect). %% API -export_type([func_ari/0]). -export_type([expect/0]). -export([new/2]). -export([new/3]). -export([new_passthrough/1]). -export([new_dummy/2]). -export([func_ari/1]). -export([fetch_result/2]). %%%============================================================================ %%% Types %%%============================================================================ -type func_clause_spec() :: {meck_args_matcher:args_spec(), meck_ret_spec:ret_spec()}. -type func_clause() :: {meck_args_matcher:args_matcher(), meck_ret_spec:ret_spec()}. -type func_ari() :: {Func::atom(), Ari::byte()}. -opaque expect() :: {func_ari(), [func_clause()]}. %%%============================================================================ %%% API %%%============================================================================ -spec new(Func::atom(), fun() | func_clause_spec()) -> expect(). new(Func, StubFun) when is_function(StubFun) -> {arity, Arity} = erlang:fun_info(StubFun, arity), Clause = {meck_args_matcher:new(Arity), meck_ret_spec:exec(StubFun)}, {{Func, Arity}, [Clause]}; new(Func, ClauseSpecs) when is_list(ClauseSpecs) -> {Arity, Clauses} = parse_clause_specs(ClauseSpecs), {{Func, Arity}, Clauses}. -spec new(Func::atom(), meck_args_matcher:args_spec(), meck_ret_spec:ret_spec()) -> expect(). new(Func, ArgsSpec, RetSpec) -> {Ari, Clause} = parse_clause_spec({ArgsSpec, RetSpec}), {{Func, Ari}, [Clause]}. -spec new_passthrough(func_ari()) -> expect(). new_passthrough({Func, Ari}) -> {{Func, Ari}, [{meck_args_matcher:new(Ari), meck_ret_spec:passthrough()}]}. -spec new_dummy(func_ari(), meck_ret_spec:ret_spec()) -> expect(). new_dummy({Func, Ari}, RetSpec) -> {{Func, Ari}, [{meck_args_matcher:new(Ari), RetSpec}]}. -spec func_ari(expect()) -> func_ari(). func_ari({FuncAri, _Clauses}) -> FuncAri. -spec fetch_result(Args::[any()], expect()) -> {undefined, unchanged} | {meck_ret_spec:result_spec(), unchanged} | {meck_ret_spec:result_spec(), NewExpect::expect()}. fetch_result(Args, {FuncAri, Clauses}) -> case find_matching_clause(Args, Clauses) of not_found -> {undefined, unchanged}; {ArgsMatcher, RetSpec} -> case meck_ret_spec:retrieve_result(RetSpec) of {ResultSpec, unchanged} -> {ResultSpec, unchanged}; {ResultSpec, NewRetSpec} -> NewClauses = lists:keyreplace(ArgsMatcher, 1, Clauses, {ArgsMatcher, NewRetSpec}), {ResultSpec, {FuncAri, NewClauses}} end end. %%%============================================================================ %%% Internal functions %%%============================================================================ -spec parse_clause_specs([func_clause_spec()]) -> {Ari::byte(), [func_clause()]}. parse_clause_specs([ClauseSpec | Rest]) -> {Ari, Clause} = parse_clause_spec(ClauseSpec), parse_clause_specs(Rest, Ari, [Clause]). -spec parse_clause_specs([func_clause_spec()], FirstClauseAri::byte(), Clauses::[func_clause()]) -> {Ari::byte(), [func_clause()]}. parse_clause_specs([ClauseSpec | Rest], FirstClauseAri, Clauses) -> {Ari, Clause} = parse_clause_spec(ClauseSpec), case Ari of FirstClauseAri -> parse_clause_specs(Rest, FirstClauseAri, [Clause | Clauses]); _ -> erlang:error({invalid_arity, {{expected, FirstClauseAri}, {actual, Ari}, {clause, ClauseSpec}}}) end; parse_clause_specs([], FirstClauseAri, Clauses) -> {FirstClauseAri, lists:reverse(Clauses)}. -spec parse_clause_spec(func_clause_spec()) -> {Ari::byte(), func_clause()}. parse_clause_spec({ArgsSpec, RetSpec}) -> ArgsMatcher = meck_args_matcher:new(ArgsSpec), Ari = meck_args_matcher:arity(ArgsMatcher), Clause = {ArgsMatcher, RetSpec}, {Ari, Clause}. -spec find_matching_clause(Args::[any()], Defined::[func_clause()]) -> Matching::func_clause() | not_found. find_matching_clause(Args, [{ArgsMatcher, RetSpec} | Rest]) -> case meck_args_matcher:match(Args, ArgsMatcher) of true -> {ArgsMatcher, RetSpec}; _Else -> find_matching_clause(Args, Rest) end; find_matching_clause(_Args, []) -> not_found. erlang-meck-0.8.1+dfsg/src/meck_history.erl000066400000000000000000000120001222563501100206050ustar00rootroot00000000000000%%%============================================================================ %%% 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. %%%============================================================================ %%% @private %%% @doc Provides functions for digging information from the recorded call %%% history. -module(meck_history). %% API -export_type([stack_trace_rec_r14b/0]). -export_type([stack_trace_rec_r15b/0]). -export_type([stack_trace/0]). -export_type([meck_mfa/0]). -export_type([successfull_call/0]). -export_type([faulty_call/0]). -export_type([history/0]). -export([get_history/2]). -export([num_calls/4]). -export([capture/6]). -export([new_filter/3]). %%%============================================================================ %%% Types %%%============================================================================ -type stack_trace_rec_r14b() :: {Mod::atom(), Func::atom(), AriOrArgs::byte() | [any()]}. -type stack_trace_rec_r15b() :: {Mod::atom(), Func::atom(), AriOrArgs::byte() | [any()], Location::[{atom(), any()}]}. -type stack_trace() :: [stack_trace_rec_r14b() | stack_trace_rec_r15b()]. -type meck_mfa() :: {Mod::atom(), Func::atom(), Args::[term()]}. -type successfull_call() :: {CallerPid::pid(), meck_mfa(), Result::any()}. -type faulty_call() :: {CallerPid::pid(), meck_mfa(), Class::exit|error|throw, Reason::term(), stack_trace()}. -type history_record() :: successfull_call() | faulty_call(). -type history() :: [history_record()]. -type opt_pid() :: pid() | '_'. -type opt_func() :: atom() | '_'. %%%============================================================================ %%% API %%%============================================================================ -spec get_history(opt_pid(), Mod::atom()) -> history(). get_history('_', Mod) -> meck_proc:get_history(Mod); get_history(CallerPid, Mod) -> ArgsMatcher = meck_args_matcher:new('_'), lists:filter(new_filter(CallerPid, '_', ArgsMatcher), meck_proc:get_history(Mod)). -spec num_calls(opt_pid(), Mod::atom(), opt_func(), meck_args_matcher:opt_args_spec()) -> non_neg_integer(). num_calls(CallerPid, Mod, OptFunc, OptArgsSpec) -> ArgsMatcher = meck_args_matcher:new(OptArgsSpec), Filter = new_filter(CallerPid, OptFunc, ArgsMatcher), Filtered = lists:filter(Filter, meck_proc:get_history(Mod)), length(Filtered). -spec capture(Occur::pos_integer(), opt_pid(), Mod::atom(), Func::atom(), meck_args_matcher:opt_args_spec(), ArgNum::pos_integer()) -> ArgValue::any(). capture(Occur, OptCallerPid, Mod, Func, OptArgsSpec, ArgNum) -> ArgsMatcher = meck_args_matcher:new(OptArgsSpec), Filter = new_filter(OptCallerPid, Func, ArgsMatcher), Filtered = lists:filter(Filter, meck_proc:get_history(Mod)), case nth_record(Occur, Filtered) of not_found -> erlang:error(not_found); {_CallerPid, {_Mod, _Func, Args}, _Result} -> lists:nth(ArgNum, Args); {_CallerPid, {_Mod, Func, Args}, _Class, _Reason, _Trace} -> lists:nth(ArgNum, Args) end. -spec new_filter(opt_pid(), opt_func(), meck_args_matcher:args_matcher()) -> fun((history_record()) -> boolean()). new_filter(TheCallerPid, TheFunc, ArgsMatcher) -> fun({CallerPid, {_Mod, Func, Args}, _Result}) when (TheFunc == '_' orelse Func == TheFunc) andalso (TheCallerPid == '_' orelse CallerPid == TheCallerPid) -> meck_args_matcher:match(Args, ArgsMatcher); ({CallerPid, {_Mod, Func, Args}, _Class, _Reason, _StackTrace}) when (TheFunc == '_' orelse Func == TheFunc) andalso (TheCallerPid == '_' orelse CallerPid == TheCallerPid) -> meck_args_matcher:match(Args, ArgsMatcher); (_Other) -> false end. %%%============================================================================ %%% Internal functions %%%============================================================================ -spec nth_record(Occur::pos_integer(), history()) -> history_record() | not_found. nth_record(Occur, History) -> try case Occur of first -> lists:nth(1, History); last -> lists:last(History); _Else -> lists:nth(Occur, History) end catch error:_Reason -> not_found end. erlang-meck-0.8.1+dfsg/src/meck_matcher.erl000066400000000000000000000064411222563501100205430ustar00rootroot00000000000000%%%============================================================================ %%% 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. %%%============================================================================ %%% @private %%% @doc This module introduces Matcher abstraction. It is an entity that can %%% be created from a predicate function or a matcher provided by a supported %%% framework (at the moment only Hamcrest is supported). Then it can be used %%% to check that an arbitrary value meets the matcher's criteria, matches in %%% other words. -module(meck_matcher). -export_type([matcher/0]). %% API -export([new/1]). -export([is_matcher/1]). -export([match_ignore/2]). %%%============================================================================ %%% Definitions %%%============================================================================ -record('$meck.matcher', {type :: predicate | hamcrest, impl :: predicate() | hamcrest:matchspec()}). %%%============================================================================ %%% Types %%%============================================================================ -type predicate() :: fun((X::any()) -> any()). -type matcher() :: #'$meck.matcher'{}. %%%============================================================================ %%% API %%%============================================================================ -spec new(predicate() | hamcrest:matchspec()) -> matcher(). new(Predicate) when is_function(Predicate) -> {arity, 1} = erlang:fun_info(Predicate, arity), #'$meck.matcher'{type = predicate, impl = Predicate}; new(Something) -> case is_hamcrest_matcher(Something) of true -> #'$meck.matcher'{type = hamcrest, impl = Something}; _Else -> erlang:error({invalid_matcher, Something}) end. -spec is_matcher(any()) -> boolean(). is_matcher(#'$meck.matcher'{}) -> true; is_matcher(_Other) -> false. %% @doc If `Something' is a {@link meck_matcher()} instance then `Value' is %% matched with it, otherwise `true' is returned effectively ignoring %% `Something''s value. -spec match_ignore(Value::any(), Something::any()) -> boolean(). match_ignore(Value, #'$meck.matcher'{type = predicate, impl = Predicate}) -> Predicate(Value) == true; match_ignore(Value, #'$meck.matcher'{type = hamcrest, impl = HamcrestMatcher}) -> (catch hamcrest:assert_that(Value, HamcrestMatcher)) == true; match_ignore(_Value, _NotMatcher) -> true. %%%============================================================================ %%% Internal functions %%%============================================================================ -spec is_hamcrest_matcher(any()) -> boolean(). is_hamcrest_matcher(Something) -> try hamcrest:is_matcher(Something) catch _Class:_Reason -> false end. erlang-meck-0.8.1+dfsg/src/meck_proc.erl000066400000000000000000000603151222563501100200630ustar00rootroot00000000000000%%%============================================================================ %%% 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. %%%============================================================================ %%% @hidden %%% @doc Implements a gen_server that maintains the state of a mocked module. %%% The state includes function stubs, call history, etc. Meck starts one such %%% process per mocked module. -module(meck_proc). -behaviour(gen_server). %% API -export([start/2]). -export([set_expect/2]). -export([delete_expect/3]). -export([get_history/1]). -export([wait/6]). -export([reset/1]). -export([validate/1]). -export([stop/1]). %% To be accessible from generated modules -export([get_result_spec/3]). -export([add_history/5]). -export([invalidate/1]). %% gen_server callbacks -export([init/1]). -export([handle_call/3]). -export([handle_cast/2]). -export([handle_info/2]). -export([terminate/2]). -export([code_change/3]). %%%============================================================================ %%% Definitions %%%============================================================================ -record(state, {mod :: atom(), can_expect :: any | [{Mod::atom(), Ari::byte()}], expects :: dict(), valid = true :: boolean(), history = [] :: meck_history:history() | undefined, original :: term(), was_sticky = false :: boolean(), reload :: {Compiler::pid(), {From::pid(), Tag::any()}} | undefined, trackers = [] :: [tracker()]}). -record(tracker, {opt_func :: '_' | atom(), args_matcher :: meck_args_matcher:args_matcher(), opt_caller_pid :: '_' | pid(), countdown :: non_neg_integer(), reply_to :: {Caller::pid(), Tag::any()}, expire_at :: erlang:timestamp()}). %%%============================================================================ %%% Types %%%============================================================================ -type tracker() :: #tracker{}. %%%============================================================================ %%% API %%%============================================================================ -spec start(Mod::atom(), Options::[proplists:property()]) -> {ok, MockProcPid::pid()} | {error, Reason::any()}. start(Mod, Options) -> StartFunc = case proplists:is_defined(no_link, Options) of true -> start; false -> start_link end, SpawnOpt = proplists:get_value(spawn_opt, Options, []), case gen_server:StartFunc({local, meck_util:proc_name(Mod)}, ?MODULE, [Mod, Options], [{spawn_opt, SpawnOpt}]) of {ok, _Pid} -> ok; {error, Reason} -> erlang:error(Reason, [Mod, Options]) end. -spec get_result_spec(Mod::atom(), Func::atom(), Args::[any()]) -> meck_ret_spec:result_spec() | undefined. get_result_spec(Mod, Func, Args) -> gen_server(call, Mod, {get_result_spec, Func, Args}). -spec set_expect(Mod::atom(), meck_expect:expect()) -> ok | {error, Reason::any()}. set_expect(Mod, Expect) -> Proc = meck_util:proc_name(Mod), try gen_server:call(Proc, {set_expect, Expect}) catch exit:{noproc, _Details} -> Options = [Mod, [passthrough]], case gen_server:start({local, Proc}, ?MODULE, Options, []) of {ok, Pid} -> Result = gen_server:call(Proc, {set_expect, Expect}), true = erlang:link(Pid), Result; {error, {{undefined_module, Mod}, _StackTrace}} -> erlang:error({not_mocked, Mod}) end end. -spec delete_expect(Mod::atom(), Func::atom(), Ari::byte()) -> ok. delete_expect(Mod, Func, Ari) -> gen_server(call, Mod, {delete_expect, Func, Ari}). -spec add_history(Mod::atom(), CallerPid::pid(), Func::atom(), Args::[any()], Result::any()) -> ok. add_history(Mod, CallerPid, Func, Args, {Class, Reason, StackTrace}) -> gen_server(cast, Mod, {add_history, {CallerPid, {Mod, Func, Args}, Class, Reason, StackTrace}}); add_history(Mod, CallerPid, Func, Args, Result) -> gen_server(cast, Mod, {add_history, {CallerPid, {Mod, Func, Args}, Result}}). -spec get_history(Mod::atom()) -> meck_history:history(). get_history(Mod) -> gen_server(call, Mod, get_history). -spec wait(Mod::atom(), Times::non_neg_integer(), OptFunc::'_' | atom(), meck_args_matcher:args_matcher(), OptCallerPid::'_' | pid(), Timeout::non_neg_integer()) -> ok. 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}, EffectiveTimeout) of ok -> ok; {error, timeout} -> erlang:error(timeout) catch exit:{timeout, _Details} -> erlang:error(timeout); exit:_Reason -> erlang:error({not_mocked, Mod}) end. -spec reset(Mod::atom()) -> ok. reset(Mod) -> gen_server(call, Mod, reset). -spec validate(Mod::atom()) -> boolean(). validate(Mod) -> gen_server(call, Mod, validate). -spec invalidate(Mod::atom()) -> ok. invalidate(Mod) -> gen_server(call, Mod, invalidate). -spec stop(Mod::atom()) -> ok. stop(Mod) -> gen_server(call, Mod, stop). %%%============================================================================ %%% gen_server callbacks %%%============================================================================ %% @hidden init([Mod, Options]) -> Exports = normal_exports(Mod), WasSticky = case proplists:get_bool(unstick, Options) of true -> {module, Mod} = code:ensure_loaded(Mod), unstick_original(Mod); _ -> false end, NoPassCover = proplists:get_bool(no_passthrough_cover, Options), Original = backup_original(Mod, NoPassCover), NoHistory = proplists:get_bool(no_history, Options), History = if NoHistory -> undefined; true -> [] end, CanExpect = resolve_can_expect(Mod, Exports, Options), Expects = init_expects(Exports, Options), process_flag(trap_exit, true), try Forms = meck_code_gen:to_forms(Mod, Expects), _Bin = meck_code:compile_and_load_forms(Forms), {ok, #state{mod = Mod, can_expect = CanExpect, expects = Expects, original = Original, was_sticky = WasSticky, history = History}} catch exit:{error_loading_module, Mod, sticky_directory} -> {stop, module_is_sticky} end. %% @hidden handle_call({get_result_spec, Func, Args}, _From, S) -> {ResultSpec, NewExpects} = do_get_result_spec(S#state.expects, Func, Args), {reply, ResultSpec, S#state{expects = NewExpects}}; handle_call({set_expect, Expect}, From, S = #state{mod = Mod, expects = Expects}) -> check_if_being_reloaded(S), FuncAri = {Func, Ari} = meck_expect:func_ari(Expect), case validate_expect(Mod, Func, Ari, S#state.can_expect) of ok -> {NewExpects, CompilerPid} = store_expect(Mod, FuncAri, Expect, Expects), {noreply, S#state{expects = NewExpects, reload = {CompilerPid, From}}}; {error, Reason} -> {reply, {error, Reason}, S} end; handle_call({delete_expect, Func, Ari}, From, S = #state{mod = Mod, expects = Expects}) -> check_if_being_reloaded(S), {NewExpects, CompilerPid} = do_delete_expect(Mod, {Func, Ari}, Expects), {noreply, S#state{expects = NewExpects, reload = {CompilerPid, From}}}; handle_call(get_history, _From, S = #state{history = undefined}) -> {reply, [], S}; 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, 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 -> Tracker = #tracker{opt_func = OptFunc, args_matcher = ArgsMatcher, opt_caller_pid = OptCallerPid, countdown = Times - CalledSoFar, reply_to = From, expire_at = timeout_to_timestamp(Timeout)}, {noreply, S#state{trackers = [Tracker | Trackers]}} end; handle_call(reset, _From, S) -> {reply, ok, S#state{history = []}}; handle_call(invalidate, _From, S) -> {reply, ok, S#state{valid = false}}; handle_call(validate, _From, S) -> {reply, S#state.valid, S}; handle_call(stop, _From, S) -> {stop, normal, ok, S}. %% @hidden handle_cast({add_history, HistoryRecord}, S = #state{history = undefined, trackers = Trackers}) -> UpdTracker = update_trackers(HistoryRecord, Trackers), {noreply, S#state{trackers = UpdTracker}}; handle_cast({add_history, HistoryRecord}, S = #state{history = History, trackers = Trackers, reload = Reload}) -> case Reload of undefined -> UpdTrackers = update_trackers(HistoryRecord, Trackers), {noreply, S#state{history = [HistoryRecord | History], trackers = UpdTrackers}}; _ -> % Skip Item if the mocked module compiler is running. {noreply, S} end; handle_cast(_Msg, S) -> {noreply, S}. %% @hidden handle_info({'EXIT', Pid, _Reason}, S = #state{reload = Reload}) -> case Reload of {Pid, From} -> gen_server:reply(From, ok), {noreply, S#state{reload = undefined}}; _ -> {noreply, S} end; handle_info(_Info, S) -> {noreply, S}. %% @hidden terminate(_Reason, #state{mod = Mod, original = OriginalState, was_sticky = WasSticky}) -> export_original_cover(Mod, OriginalState), cleanup(Mod), restore_original(Mod, OriginalState, WasSticky), ok. %% @hidden code_change(_OldVsn, S, _Extra) -> {ok, S}. %%%============================================================================ %%% Internal functions %%%============================================================================ -spec normal_exports(Mod::atom()) -> [meck_expect:func_ari()] | undefined. normal_exports(Mod) -> try [FuncAri || FuncAri = {Func, Ari} <- Mod:module_info(exports), normal == expect_type(Mod, Func, Ari)] catch error:undef -> undefined end. -spec expect_type(Mod::atom(), Func::atom(), Ari::byte()) -> autogenerated | builtin | normal. expect_type(_, module_info, 0) -> autogenerated; expect_type(_, module_info, 1) -> autogenerated; expect_type(Mod, Func, Ari) -> case erlang:is_builtin(Mod, Func, Ari) of true -> builtin; false -> normal end. -spec backup_original(Mod::atom(), NoPassCover::boolean()) -> {Cover:: false | {File::string(), Data::string(), CompiledOptions::[any()]}, Binary:: no_binary | no_passthrough_cover | binary()}. backup_original(Mod, NoPassCover) -> Cover = get_cover_state(Mod), try Forms = meck_code:abstract_code(meck_code:beam_file(Mod)), NewName = meck_util:original_name(Mod), CompileOpts = meck_code:compile_options(meck_code:beam_file(Mod)), Renamed = meck_code:rename_module(Forms, NewName), Binary = meck_code:compile_and_load_forms(Renamed, CompileOpts), %% At this point we care about `Binary' if and only if we want %% to recompile it to enable cover on the original module code %% so that we can still collect cover stats on functions that %% have not been mocked. Below are the different values %% passed back along with `Cover'. %% %% `no_passthrough_cover' - there is no coverage on the %% original module OR passthrough coverage has been disabled %% via the `no_passthrough_cover' option %% %% `no_binary' - something went wrong while trying to compile %% the original module in `backup_original' %% %% Binary - a `binary()' of the compiled code for the original %% module that is being mocked, this needs to be passed around %% so that it can be passed to Cover later. There is no way %% to use the code server to access this binary without first %% saving it to disk. Instead, it's passed around as state. if (Cover == false) orelse NoPassCover -> Binary2 = no_passthrough_cover; true -> Binary2 = Binary, meck_cover:compile_beam(NewName, Binary2) end, {Cover, Binary2} catch throw:{object_code_not_found, _Module} -> {Cover, no_binary}; % TODO: What to do here? throw:no_abstract_code -> {Cover, no_binary} % TODO: What to do here? end. -spec get_cover_state(Mod::atom()) -> {File::string(), Data::string(), CompileOptions::[any()]} | false. get_cover_state(Mod) -> case cover:is_compiled(Mod) of {file, File} -> Data = atom_to_list(Mod) ++ ".coverdata", ok = cover:export(Data, Mod), CompileOptions = try meck_code:compile_options(meck_code:beam_file(Mod)) catch throw:{object_code_not_found, _Module} -> [] end, {File, Data, CompileOptions}; _ -> false end. -spec resolve_can_expect(Mod::atom(), Exports::[meck_expect:func_ari()] | undefined, Options::[proplists:property()]) -> any | [meck_expect:func_ari()]. resolve_can_expect(Mod, Exports, Options) -> NonStrict = proplists:get_bool(non_strict, Options), case {Exports, NonStrict} of {_, true} -> any; {undefined, _} -> erlang:error({undefined_module, Mod}); _ -> Exports end. -spec init_expects(Exports::[meck_expect:func_ari()] | undefined, Options::[proplists:property()]) -> dict(). init_expects(Exports, Options) -> Passthrough = proplists:get_bool(passthrough, Options), StubAll = proplists:is_defined(stub_all, Options), Expects = case Exports of undefined -> []; Exports when Passthrough -> [meck_expect:new_passthrough(FuncArity) || FuncArity <- Exports]; Exports when StubAll -> StubRet = case lists:keyfind(stub_all, 1, Options) of {stub_all, RetSpec} -> RetSpec; _ -> meck:val(ok) end, [meck_expect:new_dummy(FuncArity, StubRet) || FuncArity <- Exports]; Exports -> [] end, lists:foldl(fun(Expect, D) -> dict:store(meck_expect:func_ari(Expect), Expect, D) end, dict:new(), Expects). -spec gen_server(Method:: call | cast, Mod::atom(), Msg::tuple() | atom()) -> any(). gen_server(Func, Mod, Msg) -> Name = meck_util:proc_name(Mod), try gen_server:Func(Name, Msg) catch exit:_Reason -> erlang:error({not_mocked, Mod}) end. -spec check_if_being_reloaded(#state{}) -> ok. check_if_being_reloaded(#state{reload = undefined}) -> ok; check_if_being_reloaded(_S) -> erlang:error(concurrent_reload). -spec do_get_result_spec(Expects::dict(), Func::atom(), Args::[any()]) -> {meck_ret_spec:result_spec() | undefined, NewExpects::dict()}. do_get_result_spec(Expects, Func, Args) -> FuncAri = {Func, erlang:length(Args)}, Expect = dict:fetch(FuncAri, Expects), {ResultSpec, NewExpect} = meck_expect:fetch_result(Args, Expect), NewExpects = case NewExpect of unchanged -> Expects; _ -> dict:store(FuncAri, NewExpect, Expects) end, {ResultSpec, NewExpects}. -spec validate_expect(Mod::atom(), Func::atom(), Ari::byte(), CanExpect::any | [meck_expect:func_ari()]) -> ok | {error, Reason::any()}. validate_expect(Mod, Func, Ari, CanExpect) -> case expect_type(Mod, Func, Ari) of autogenerated -> {error, {cannot_mock_autogenerated, {Mod, Func, Ari}}}; builtin -> {error, {cannot_mock_builtin, {Mod, Func, Ari}}}; normal -> case CanExpect =:= any orelse lists:member({Func, Ari}, CanExpect) of true -> ok; _ -> {error, {undefined_function, {Mod, Func, Ari}}} end end. -spec store_expect(Mod::atom(), meck_expect:func_ari(), meck_expect:expect(), Expects::dict()) -> {NewExpects::dict(), CompilerPid::pid()}. store_expect(Mod, FuncAri, Expect, Expects) -> NewExpects = dict:store(FuncAri, Expect, Expects), compile_expects(Mod, NewExpects). -spec do_delete_expect(Mod::atom(), meck_expect:func_ari(), Expects::dict()) -> {NewExpects::dict(), CompilerPid::pid()}. do_delete_expect(Mod, FuncAri, Expects) -> NewExpects = dict:erase(FuncAri, Expects), compile_expects(Mod, NewExpects). -spec compile_expects(Mod::atom(), Expects::dict()) -> {NewExpects::dict(), CompilerPid::pid()}. compile_expects(Mod, Expects) -> %% If the recompilation is made by the server that executes a module %% no module that is called from meck_code:compile_and_load_forms/2 %% can be mocked by meck. CompilerPid = erlang:spawn_link(fun() -> Forms = meck_code_gen:to_forms(Mod, Expects), meck_code:compile_and_load_forms(Forms) end), {Expects, CompilerPid}. restore_original(Mod, {false, _}, WasSticky) -> restick_original(Mod, WasSticky), ok; restore_original(Mod, OriginalState={{File, Data, Options},_}, WasSticky) -> case filename:extension(File) of ".erl" -> {ok, Mod} = cover:compile_module(File, Options); ".beam" -> cover:compile_beam(File) end, restick_original(Mod, WasSticky), import_original_cover(Mod, OriginalState), ok = cover:import(Data), ok = file:delete(Data), ok. %% @doc Import the cover data for `_meck_original' but since it %% was modified by `export_original_cover' it will count towards %% `'. import_original_cover(Mod, {_,Bin}) when is_binary(Bin) -> OriginalData = atom_to_list(meck_util:original_name(Mod)) ++ ".coverdata", ok = cover:import(OriginalData), ok = file:delete(OriginalData); import_original_cover(_, _) -> ok. %% @doc Export the cover data for `_meck_original' and modify %% the data so it can be imported under `'. export_original_cover(Mod, {_, Bin}) when is_binary(Bin) -> OriginalMod = meck_util:original_name(Mod), File = atom_to_list(OriginalMod) ++ ".coverdata", ok = cover:export(File, OriginalMod), ok = meck_cover:rename_module(File, Mod); export_original_cover(_, _) -> ok. unstick_original(Module) -> unstick_original(Module, code:is_sticky(Module)). unstick_original(Module, true) -> code:unstick_mod(Module); unstick_original(_,_) -> false. restick_original(Module, true) -> code:stick_mod(Module), {module, Module} = code:ensure_loaded(Module), ok; restick_original(_,_) -> ok. -spec cleanup(Mod::atom()) -> boolean(). cleanup(Mod) -> code:purge(Mod), code:delete(Mod), code:purge(meck_util:original_name(Mod)), code:delete(meck_util:original_name(Mod)). -spec times_called(OptFunc::'_' | atom(), meck_args_matcher:args_matcher(), OptCallerPid::'_' | pid(), meck_history:history()) -> non_neg_integer(). times_called(OptFunc, ArgsMatcher, OptCallerPid, History) -> Filter = meck_history:new_filter(OptCallerPid, OptFunc, ArgsMatcher), lists:foldl(fun(HistoryRec, Acc) -> case Filter(HistoryRec) of true -> Acc + 1; _Else -> Acc end end, 0, History). -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), 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()) -> expired | (UpdTracker::tracker()). update_tracker(Func, Args, CallerPid, #tracker{opt_func = OptFunc, args_matcher = ArgsMatcher, opt_caller_pid = OptCallerPid, countdown = Countdown, 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 -> 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)). erlang-meck-0.8.1+dfsg/src/meck_ret_spec.erl000066400000000000000000000130201222563501100207130ustar00rootroot00000000000000%%%============================================================================ %%% 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. %%%============================================================================ %%% @private %%% @doc Provides expectation processing functions. -module(meck_ret_spec). -export_type([result_spec/0]). -export_type([ret_spec/0]). %% API -export([passthrough/0]). -export([val/1]). -export([exec/1]). -export([seq/1]). -export([loop/1]). -export([raise/2]). -export([is_meck_exception/1]). -export([retrieve_result/1]). -export([eval_result/4]). %%%============================================================================ %%% Types %%%============================================================================ -opaque result_spec() :: {meck_value, any()} | {meck_exec, fun()} | {meck_raise, Class::throw | error | exit, Reason::any()} | meck_passthrough. -type ret_spec() :: {meck_seq, [ret_spec()]} | {meck_loop, [ret_spec()], [ret_spec()]} | result_spec() | any(). %%%============================================================================ %%% API %%%============================================================================ -spec passthrough() -> ret_spec(). passthrough() -> meck_passthrough. -spec val(any()) -> ret_spec(). val(Value) -> {meck_value, Value}. -spec exec(fun()) -> ret_spec(). exec(Fun) when is_function(Fun)-> {meck_exec, Fun}. -spec seq([ret_spec()]) -> ret_spec(). seq(Sequence) when is_list(Sequence) -> {meck_seq, Sequence}. -spec loop([ret_spec()]) -> ret_spec(). loop(Loop) when is_list(Loop) -> {meck_loop, Loop, Loop}. -spec raise(Class:: throw | error | exit, Reason::any()) -> ret_spec(). raise(throw, Reason) -> {meck_raise, throw, Reason}; raise(error, Reason) -> {meck_raise, error, Reason}; raise(exit, Reason) -> {meck_raise, exit, Reason}. -spec is_meck_exception(Reason::any()) -> boolean(). is_meck_exception({meck_raise, MockedClass, MockedReason}) -> {true, MockedClass, MockedReason}; is_meck_exception(_Reason) -> false. -spec retrieve_result(RetSpec::ret_spec()) -> {result_spec(), NewRetSpec::ret_spec() | unchanged}. retrieve_result(RetSpec) -> retrieve_result(RetSpec, []). -spec eval_result(Mod::atom(), Func::atom(), Args::[any()], result_spec()) -> Result::any(). eval_result(_Mod, _Func, _Args, {meck_value, Value}) -> Value; eval_result(_Mod, _Func, Args, {meck_exec, Fun}) when is_function(Fun) -> erlang:apply(Fun, Args); eval_result(_Mod, _Func, _Args, MockedEx = {meck_raise, _Class, _Reason}) -> erlang:throw(MockedEx); eval_result(Mod, Func, Args, meck_passthrough) -> erlang:apply(meck_util:original_name(Mod), Func, Args). %%%============================================================================ %%% Internal functions %%%============================================================================ -spec retrieve_result(RetSpec::ret_spec(), ExplodedRs::[ret_spec()]) -> {result_spec(), NewRetSpec::ret_spec() | unchanged}. retrieve_result(RetSpec = {meck_seq, [InnerRs | _Rest]}, ExplodedRs) -> retrieve_result(InnerRs, [RetSpec | ExplodedRs]); retrieve_result(RetSpec = {meck_loop, [InnerRs | _Rest], _Loop}, ExplodedRs) -> retrieve_result(InnerRs, [RetSpec | ExplodedRs]); retrieve_result(RetSpec, ExplodedRs) -> ResultSpec = case is_result_spec(RetSpec) of true -> RetSpec; _ when erlang:is_function(RetSpec) -> exec(RetSpec); _ -> val(RetSpec) end, {ResultSpec, update_rs(RetSpec, ExplodedRs, false)}. -spec is_result_spec(any()) -> boolean(). is_result_spec({meck_value, _Value}) -> true; is_result_spec({meck_exec, _Fun}) -> true; is_result_spec({meck_raise, _Class, _Reason}) -> true; is_result_spec(meck_passthrough) -> true; is_result_spec(_Other) -> false. -spec update_rs(InnerRs::ret_spec(), ExplodedRs::[ret_spec()], Done::boolean()) -> NewRetSpec::ret_spec() | unchanged. update_rs(InnerRs, [], true) -> InnerRs; update_rs(_InnerRs, [], false) -> unchanged; update_rs(InnerRs, [CurrRs = {meck_seq, [InnerRs]} | ExplodedRs], Updated) -> update_rs(CurrRs, ExplodedRs, Updated); update_rs(InnerRs, [{meck_seq, [InnerRs | Rest]} | ExplodedRs], _Updated) -> update_rs({meck_seq, Rest}, ExplodedRs, true); update_rs(NewInnerRs, [{meck_seq, [_InnerRs | Rest]} | ExplodedRs], _Updated) -> update_rs({meck_seq, [NewInnerRs | Rest]}, ExplodedRs, true); update_rs(InnerRs, [{meck_loop, [InnerRs], Loop} | ExplodedRs], _Updated) -> update_rs({meck_loop, Loop, Loop}, ExplodedRs, true); update_rs(InnerRs, [{meck_loop, [InnerRs | Rest], Loop} | ExplodedRs], _Updated) -> update_rs({meck_loop, Rest, Loop}, ExplodedRs, true); update_rs(NewInnerRs, [{meck_loop, [_InnerRs | Rest], Loop} | ExplodedRs], _Updated) -> update_rs({meck_loop, [NewInnerRs | Rest], Loop}, ExplodedRs, true). erlang-meck-0.8.1+dfsg/src/meck_util.erl000066400000000000000000000033141222563501100200710ustar00rootroot00000000000000%%%============================================================================ %%% 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. %%%============================================================================ %%% @private %%% @doc Contains utility functions that used around other meck modules. -module(meck_util). %% API -export_type([match_spec_item/0]). -export([proc_name/1]). -export([original_name/1]). -export([match_spec_item/1]). %%%============================================================================ %%% Types %%%============================================================================ -type match_spec_item() :: {Pattern::tuple(), Guards::[any()], Result::[any()]}. %%%============================================================================ %%% API %%%============================================================================ -spec proc_name(Mod::atom()) -> MockMod::atom(). proc_name(Name) -> list_to_atom(atom_to_list(Name) ++ "_meck"). -spec original_name(Mod::atom()) -> OrigMod::atom(). original_name(Name) -> list_to_atom(atom_to_list(Name) ++ "_meck_original"). -spec match_spec_item(Pattern::tuple()) -> match_spec_item(). match_spec_item(Pattern) -> {Pattern, [], ['$_']}. erlang-meck-0.8.1+dfsg/test.config000066400000000000000000000012021222563501100167620ustar00rootroot00000000000000%% Dependencies =============================================================== {deps, [{hamcrest, ".*", {git, "https://github.com/hyperthunk/hamcrest-erlang.git", {branch, "master"}}}]}. %% Compiler Options =========================================================== % FIXME: Add warnings_as_errors once Hamcrest is fixed {erl_opts, [debug_info]}. %% Eunit Options ============================================================== {cover_enabled, true}. {cover_print_enabled, true}. %% Misc ======================================================================= {clean_files, [".eunit", "ebin/*.beam", "test/*.beam"]}. erlang-meck-0.8.1+dfsg/test/000077500000000000000000000000001222563501100156005ustar00rootroot00000000000000erlang-meck-0.8.1+dfsg/test/cover_test_module.dontcompile000066400000000000000000000010531222563501100235600ustar00rootroot00000000000000%% -*- mode: erlang -*- %% This file needs not to have the extension .erl, since otherwise %% rebar will try to compile it, which won't work since it requires %% special compilation options. See meck_tests:cover_test_. -module(cover_test_module). -export([a/0, b/0, c/2]). %% a/0 is defined in include/cover_test.hrl. We don't put the full %% path here, since it should be provided as a compiler option. -include("cover_test.hrl"). %% Also, make that this module was compiled with -DTEST. -ifdef(TEST). b() -> b. -endif. c(A, B) -> {A, B}. erlang-meck-0.8.1+dfsg/test/include/000077500000000000000000000000001222563501100172235ustar00rootroot00000000000000erlang-meck-0.8.1+dfsg/test/include/cover_test.hrl000066400000000000000000000000121222563501100221000ustar00rootroot00000000000000a() -> a. erlang-meck-0.8.1+dfsg/test/meck_args_matcher_tests.erl000066400000000000000000000061001222563501100231610ustar00rootroot00000000000000%%%============================================================================ %%% 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. %%%============================================================================ -module(meck_args_matcher_tests). -include_lib("eunit/include/eunit.hrl"). from_wildcard_test() -> ArgsMatcher = meck_args_matcher:new('_'), ?assertMatch(true, meck_args_matcher:match([], ArgsMatcher)), ?assertMatch(true, meck_args_matcher:match([1], ArgsMatcher)), ?assertMatch(true, meck_args_matcher:match([1, 2, 3], ArgsMatcher)). from_arity_test() -> ArgsMatcher = meck_args_matcher:new(3), ?assertMatch(true, meck_args_matcher:match([1, 2, 3], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, 2], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, 2, 3, 4], ArgsMatcher)). from_zero_arity_test() -> ArgsMatcher = meck_args_matcher:new(0), ?assertMatch(true, meck_args_matcher:match([], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, 2, 3], ArgsMatcher)). from_args_test() -> ArgsMatcher = meck_args_matcher:new([1, {2, [<<"3">>]}]), ?assertMatch(true, meck_args_matcher:match([1, {2, [<<"3">>]}], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, {2, [<<"3">>]}, 1], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, {0, [<<"3">>]}], ArgsMatcher)). from_empty_args_test() -> ArgsMatcher = meck_args_matcher:new([]), ?assertMatch(true, meck_args_matcher:match([], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, 2, 3], ArgsMatcher)). matcher_featured_test() -> ArgsSpec = [meck:is(hamcrest_matchers:equal_to(1)), 2, meck:is(fun(X) -> X == 3 end), {4, [5, '_'], <<"7">>}], ArgsMatcher = meck_args_matcher:new(ArgsSpec), ?assertMatch(true, meck_args_matcher:match([1, 2, 3, {4, [5, 6], <<"7">>}], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([0, 2, 3, {4, [5, 6], <<"7">>}], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, 0, 3, {4, [5, 6], <<"7">>}], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, 2, 0, {4, [5, 6], <<"7">>}], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, 2, 3, {0, [5, 6], <<"7">>}], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, 2, 3, {4, [0, 6], <<"7">>}], ArgsMatcher)), ?assertMatch(true, meck_args_matcher:match([1, 2, 3, {4, [5, 0], <<"7">>}], ArgsMatcher)), ?assertMatch(false, meck_args_matcher:match([1, 2, 3, {4, [5, 6], <<"0">>}], ArgsMatcher)). erlang-meck-0.8.1+dfsg/test/meck_expect_tests.erl000066400000000000000000000112741222563501100220220ustar00rootroot00000000000000%%%============================================================================ %%% Copyright 2010 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. %%%============================================================================ -module(meck_expect_tests). -include_lib("eunit/include/eunit.hrl"). expect_explicit_values_test() -> %% When E = meck_expect:new(blah, [1000, a, {1002, [{<<"b">>, 1003}]}], 2001), %% Then V2001 = meck_ret_spec:val(2001), ?assertMatch({V2001, _}, meck_expect:fetch_result([1000, a, {1002, [{<<"b">>, 1003}]}], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1001, a, {1002, [{<<"b">>, 1003}]}], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1000, b, {1002, [{<<"b">>, 1003}]}], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1000, a, {1003, [{<<"b">>, 1003}]}], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1000, a, {1002, [{<<"c">>, 1003}]}], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1000, a, {1002, [{<<"b">>, 1004}]}], E)). expect_wildcard_test() -> %% When E = meck_expect:new(blah, [1000, '_', {'_', [{'_', 1003}]}], 2001), %% Then V2001 = meck_ret_spec:val(2001), ?assertMatch({V2001, _}, meck_expect:fetch_result([1000, a, {1002, [{<<"b">>, 1003}]}], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1001, a, {1002, [{<<"b">>, 1003}]}], E)), ?assertMatch({V2001, _}, meck_expect:fetch_result([1000, b, {1002, [{<<"b">>, 1003}]}], E)), ?assertMatch({V2001, _}, meck_expect:fetch_result([1000, a, {1003, [{<<"b">>, 1003}]}], E)), ?assertMatch({V2001, _}, meck_expect:fetch_result([1000, a, {1002, [{[1, {2}, 3], 1003}]}], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1000, a, {1002, [{<<"b">>, 1004}]}], E)). expect_matchers_test() -> %% Given Is1003 = meck_matcher:new(fun(X) -> X == 1003 end), LessThen1004 = meck_matcher:new(hamcrest_matchers:less_than(1004)), %% When E = meck_expect:new(blah, [Is1003, LessThen1004], 2001), %% Then V2001 = meck_ret_spec:val(2001), ?assertMatch({V2001, _}, meck_expect:fetch_result([1003, 1002], E)), ?assertMatch({V2001, _}, meck_expect:fetch_result([1003, 1003], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1003, 1004], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1002, 1002], E)). expect_with_matchers_multiclause_test() -> %% Given Is1003 = meck_matcher:new(fun(X) -> X == 1003 end), LessThen1004 = meck_matcher:new(hamcrest_matchers:less_than(1004)), %% When E = meck_expect:new(blah, [{['_', Is1003, 1004], 2001}, {['_', Is1003, LessThen1004], 2002}, {['_', '_', LessThen1004], 2003}]), %% Then V2001 = meck_ret_spec:val(2001), ?assertMatch({V2001, _}, meck_expect:fetch_result([1002, 1003, 1004], E)), V2002 = meck_ret_spec:val(2002), ?assertMatch({V2002, _}, meck_expect:fetch_result([1002, 1003, 1003], E)), V2003 = meck_ret_spec:val(2003), ?assertMatch({V2003, _}, meck_expect:fetch_result([1002, 1004, 1003], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1002, 1003, 1005], E)). expect_with_matchers_masked_clause_test() -> %% Given Is1003 = meck_matcher:new(fun(X) -> X == 1003 end), LessThen1004 = meck_matcher:new(hamcrest_matchers:less_than(1004)), %% When E = meck_expect:new(blah, [{[Is1003, LessThen1004], 2001}, {[Is1003, Is1003], 2002}]), %% Then V2001 = meck_ret_spec:val(2001), ?assertMatch({V2001, _}, meck_expect:fetch_result([1003, 1003], E)). expect_with_arity_test() -> %% When E = meck_expect:new(foo, [{2, 2001}]), %% Then V2001 = meck_ret_spec:val(2001), ?assertMatch({V2001, _}, meck_expect:fetch_result([1, 2], E)), ?assertMatch({undefined, _}, meck_expect:fetch_result([1, 2, 3], E)). erlang-meck-0.8.1+dfsg/test/meck_history_tests.erl000066400000000000000000000057241222563501100222360ustar00rootroot00000000000000%%%============================================================================ %%% Copyright 2013 Maxim Vladimirsky %%% %%% 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. %%%============================================================================ -module(meck_history_tests). -include_lib("eunit/include/eunit.hrl"). num_calls_with_arity_test() -> %% Given meck:new(test, [non_strict]), meck:expect(test, foo, 2, ok), meck:expect(test, foo, 3, ok), %% When test:foo(1, 2, 3), test:foo(1, 2), test:foo(1, 2, 3), test:foo(1, 2, 3), test:foo(1, 2), %% Then ?assertMatch(2, meck:num_calls(test, foo, 2)), ?assertMatch(3, meck:num_calls(test, foo, 3)), ?assertMatch(0, meck:num_calls(test, foo, 4)), %% Clean meck:unload(). capture_different_positions_test() -> %% Given meck:new(test, [non_strict]), meck:expect(test, foo, 3, ok), meck:expect(test, foo, 4, ok), meck:expect(test, bar, 3, ok), test:foo(1001, 2001, 3001, 4001), test:bar(1002, 2002, 3002), test:foo(1003, 2003, 3003), test:bar(1004, 2004, 3004), test:foo(1005, 2005, 3005), test:foo(1006, 2006, 3006), test:bar(1007, 2007, 3007), test:foo(1008, 2008, 3008), %% When/Then ?assertMatch(2003, meck:capture(first, test, foo, ['_', '_', '_'], 2)), ?assertMatch(2008, meck:capture(last, test, foo, ['_', '_', '_'], 2)), ?assertMatch(2006, meck:capture(3, test, foo, ['_', '_', '_'], 2)), ?assertError(not_found, meck:capture(5, test, foo, ['_', '_', '_'], 2)), %% Clean meck:unload(). capture_different_args_specs_test() -> %% Given meck:new(test, [non_strict]), meck:expect(test, foo, 2, ok), meck:expect(test, foo, 3, ok), meck:expect(test, foo, 4, ok), meck:expect(test, bar, 3, ok), test:foo(1001, 2001, 3001, 4001), test:bar(1002, 2002, 3002), test:foo(1003, 2003, 3003), test:bar(1004, 2004, 3004), test:foo(1005, 2005), test:foo(1006, 2006, 3006), test:bar(1007, 2007, 3007), test:foo(1008, 2008, 3008), %% When/Then ?assertMatch(2001, meck:capture(first, test, foo, '_', 2)), ?assertMatch(2003, meck:capture(first, test, foo, 3, 2)), ?assertMatch(2005, meck:capture(first, test, foo, ['_', '_'], 2)), ?assertMatch(2006, meck:capture(first, test, foo, [1006, '_', '_'], 2)), ?assertMatch(2008, meck:capture(first, test, foo, ['_', '_', meck:is(hamcrest_matchers:greater_than(3006))], 2)), %% Clean meck:unload(). erlang-meck-0.8.1+dfsg/test/meck_matcher_tests.erl000066400000000000000000000043311222563501100221510ustar00rootroot00000000000000%%%============================================================================ %%% 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. %%%============================================================================ -module(meck_matcher_tests). -include_lib("eunit/include/eunit.hrl"). match_predicate_test() -> Matcher = meck_matcher:new(fun(X) -> X == 1000 end), ?assertMatch(true, meck_matcher:match_ignore(1000, Matcher)), ?assertMatch(false, meck_matcher:match_ignore(1001, Matcher)). match_predicate_not_bool_test() -> Matcher = meck_matcher:new(fun(1000) -> true; (Other) -> Other end), ?assertMatch(true, meck_matcher:match_ignore(1000, Matcher)), ?assertMatch(false, meck_matcher:match_ignore(1001, Matcher)). match_hamcrest_test() -> Matcher = meck_matcher:new(hamcrest_matchers:equal_to(1000)), ?assertMatch(true, meck_matcher:match_ignore(1000, Matcher)), ?assertMatch(false, meck_matcher:match_ignore(1001, Matcher)). match_not_matcher_test() -> ?assertMatch(true, meck_matcher:match_ignore(something, '_')), ?assertMatch(true, meck_matcher:match_ignore({1, [2, 3], undefined}, {1, [2, 3], undefined})). predicate_wrong_arity_test() -> Predicate = fun(X, Y) -> X == Y end, ?assertError(_, meck_matcher:new(Predicate)). is_matcher_test() -> ?assertMatch(true, meck_matcher:is_matcher(meck_matcher:new(fun(X) -> X == 1000 end))), ?assertMatch(false, meck_matcher:is_matcher(fun(X) -> X == 1000 end)), ?assertMatch(true, meck_matcher:is_matcher(meck_matcher:new(hamcrest_matchers:equal_to(1000)))), ?assertMatch(false, meck_matcher:is_matcher(hamcrest_matchers:equal_to(1000))), ?assertMatch(false, meck_matcher:is_matcher(blah)). erlang-meck-0.8.1+dfsg/test/meck_performance_test.erl000066400000000000000000000045671222563501100226570ustar00rootroot00000000000000%% @doc -module(meck_performance_test). %% Interface exports -export([run/1]). %%============================================================================= %% Interface exports %%============================================================================= run(N) -> meck:new(test, [non_strict]), io:format("\t\tMin\tMax\tMed\tAvg~n"), io:format("expect/3\t~p\t~p\t~p\t~p~n", test_avg(meck, expect, [test, normal, fun() -> ok end], N)), io:format("expect/3+args\t~p\t~p\t~p\t~p~n", test_avg(meck, expect, [test, normal_args, fun(_, _) -> ok end], N)), io:format("expect/4\t~p\t~p\t~p\t~p~n", test_avg(meck, expect, [test, shortcut, 0, ok], N)), io:format("expect/4+args\t~p\t~p\t~p\t~p~n", test_avg(meck, expect, [test, shortcut_args, 2, ok], N)), meck:expect(test, shortcut_opaque, 0, self()), io:format("~n\t\tMin\tMax\tMed\tAvg~n"), io:format("normal\t\t~p\t~p\t~p\t~p~n", test_avg(test, normal, [], N)), io:format("normal_args\t~p\t~p\t~p\t~p~n", test_avg(test, normal_args, [a, b], N)), io:format("shortcut\t~p\t~p\t~p\t~p~n", test_avg(test, shortcut, [], N)), io:format("shortcut_args\t~p\t~p\t~p\t~p~n", test_avg(test, shortcut_args, [a, b], N)), io:format("shortcut_opaque\t~p\t~p\t~p\t~p~n", test_avg(test, shortcut_opaque, [], N)), meck:unload(test), meck:new(test, [non_strict]), meck:expect(test, func, 1, ok), [test:func(I) || I <- lists:seq(1, 100)], io:format("~n\t\tMin\tMax\tMed\tAvg~n"), io:format("called\t\t~p\t~p\t~p\t~p~n", test_avg(meck, called, [test, func, 50], N)), meck:unload(test), ok. %%============================================================================= %% Internal functions %%============================================================================= test_avg(M, F, A, N) when N > 0 -> L = test_loop(M, F, A, N, []), Length = length(L), Min = lists:min(L), Max = lists:max(L), Med = lists:nth(round((Length / 2)), lists:sort(L)), Avg = round(lists:foldl(fun(X, Sum) -> X + Sum end, 0, L) / Length), [Min, Max, Med, Avg]. test_loop(_M, _F, _A, 0, List) -> List; test_loop(M, F, A, N, List) -> {T, _Result} = timer:tc(M, F, A), test_loop(M, F, A, N - 1, [T|List]). erlang-meck-0.8.1+dfsg/test/meck_ret_spec_tests.erl000066400000000000000000000040751222563501100223370ustar00rootroot00000000000000%%%============================================================================ %%% Copyright 2010 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. %%%============================================================================ -module(meck_ret_spec_tests). -include_lib("eunit/include/eunit.hrl"). passthrough_test() -> %% Given meck:new(meck_test_module), %% When meck:expect(meck_test_module, c, 2, meck:passthrough()), %% Then ?assertEqual({1, 3}, meck_test_module:c(1, 3)), %% Cleanup meck:unload(). explicit_exec_test() -> %% Given meck:new(meck_test_module), %% When meck:expect(meck_test_module, c, 2, meck:exec(fun(A, B) -> {B, A} end)), %% Then ?assertEqual({3, 1}, meck_test_module:c(1, 3)), %% Cleanup meck:unload(). exec_test() -> %% Given meck:new(meck_test_module), %% When meck:expect(meck_test_module, c, 2, fun(A, B) -> {B, A} end), %% Then ?assertEqual({3, 1}, meck_test_module:c(1, 3)), %% Cleanup meck:unload(). deep_exec_test() -> %% Given meck:new(meck_test_module), %% When meck:expect(meck_test_module, c, 2, meck_ret_spec:seq([fun(A, B) -> {B, A} end])), %% Then ?assertEqual({3, 1}, meck_test_module:c(1, 3)), %% Cleanup meck:unload(). invalid_arity_exec_test() -> %% Given meck:new(meck_test_module), %% When meck:expect(meck_test_module, c, 2, meck_ret_spec:seq([fun(A, B) -> {B, A} end])), %% Then ?assertError(undef, meck_test_module:c(1, 2, 3)), %% Cleanup meck:unload(). erlang-meck-0.8.1+dfsg/test/meck_test_module.erl000066400000000000000000000002041222563501100216230ustar00rootroot00000000000000-module(meck_test_module). -tag(foobar). -deprecated([a/0]). -export([a/0, b/0, c/2]). a() -> a. b() -> b. c(A, B) -> {A, B}. erlang-meck-0.8.1+dfsg/test/meck_tests.erl000066400000000000000000001446641222563501100204640ustar00rootroot00000000000000%%============================================================================= %% Copyright 2010 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. %%============================================================================= -module(meck_tests). -compile(export_all). -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, fun ?MODULE:unload_/1, fun ?MODULE:double_new_/1, fun ?MODULE:validate_/1, fun ?MODULE:expect_/1, fun ?MODULE:exports_/1, fun ?MODULE:call_return_value_/1, fun ?MODULE:call_return_value_improper_list_/1, fun ?MODULE:call_argument_/1, fun ?MODULE:call_undef_/1, fun ?MODULE:call_function_clause_/1, fun ?MODULE:validate_unexpected_error_/1, fun ?MODULE:validate_expected_error_/1, fun ?MODULE:validate_chained_/1, fun ?MODULE:stacktrace_/1, fun ?MODULE:stacktrace_function_clause_/1, fun ?MODULE:change_func_/1, fun ?MODULE:caller_does_not_crash_on_reload_/1, fun ?MODULE:call_original_undef_/1, fun ?MODULE:history_empty_/1, fun ?MODULE:history_call_/1, fun ?MODULE:history_throw_/1, fun ?MODULE:history_throw_fun_/1, fun ?MODULE:history_exit_/1, fun ?MODULE:history_error_/1, fun ?MODULE:history_error_args_/1, fun ?MODULE:history_meck_throw_/1, fun ?MODULE:history_meck_throw_fun_/1, fun ?MODULE:history_meck_exit_/1, fun ?MODULE:history_meck_error_/1, fun ?MODULE:history_by_pid_/1, fun ?MODULE:reset_/1, fun ?MODULE:shortcut_expect_/1, fun ?MODULE:shortcut_expect_negative_arity_/1, fun ?MODULE:shortcut_call_return_value_/1, fun ?MODULE:shortcut_call_argument_/1, fun ?MODULE:shortcut_re_add_/1, fun ?MODULE:shortcut_opaque_/1, fun ?MODULE:delete_/1, fun ?MODULE:called_false_no_args_/1, fun ?MODULE:called_true_no_args_/1, fun ?MODULE:called_true_two_functions_/1, fun ?MODULE:called_false_one_arg_/1, fun ?MODULE:called_true_one_arg_/1, fun ?MODULE:called_false_few_args_/1, fun ?MODULE:called_true_few_args_/1, fun ?MODULE:called_few_args_matchers_/1, fun ?MODULE:called_false_error_/1, fun ?MODULE:called_true_error_/1, fun ?MODULE:called_with_pid_no_args_/1, fun ?MODULE:num_calls_/1, fun ?MODULE:num_calls_error_/1, fun ?MODULE:num_calls_with_pid_no_args_/1, fun ?MODULE:called_wildcard_/1, fun ?MODULE:sequence_/1, fun ?MODULE:expect_args_sequence_/1, fun ?MODULE:expect_arity_sequence_/1, fun ?MODULE:expect_complex_sequence_/1, fun ?MODULE:sequence_multi_/1, fun ?MODULE:loop_/1, fun ?MODULE:expect_empty_clause_list_/1, fun ?MODULE:expect_args_value_/1, fun ?MODULE:expect_args_invalid_call_/1, fun ?MODULE:expect_arity_value_/1, fun ?MODULE:expect_args_loop_/1, fun ?MODULE:expect_arity_loop_/1, fun ?MODULE:expect_complex_loop_/1, fun ?MODULE:expect_loop_in_seq_/1, fun ?MODULE:expect_args_exception_/1, fun ?MODULE:expect_arity_exception_/1, fun ?MODULE:expect_arity_clause_/1, fun ?MODULE:loop_multi_/1, fun ?MODULE:expect_args_pattern_override_/1, fun ?MODULE:expect_args_pattern_shadow_/1, fun ?MODULE:expect_args_pattern_missing_/1, fun ?MODULE:expect_args_pattern_invalid_/1, fun ?MODULE:expect_args_matchers_/1, fun ?MODULE:expect_ret_specs_/1 ]]}. setup() -> % Uncomment to run tests with dbg: % dbg:tracer(), % dbg:p(all, call), % dbg:tpl(meck, []), ok = meck:new(mymod, [non_strict]), mymod. teardown(Module) -> catch meck:unload(Module). %% --- Tests using setup and teardown ----------------------------------------- new_(Mod) -> Info = Mod:module_info(), ?assert(is_list(Info)). unload_(Mod) -> ok = meck:unload(Mod), ?assertEqual(false, code:is_loaded(Mod)). double_new_(Mod) -> ?assertError({already_started, _}, meck:new(Mod)). validate_(Mod) -> ?assertEqual(true, meck:validate(Mod)). expect_(Mod) -> ok = meck:expect(Mod, test, fun() -> ok end), ?assertEqual(true, meck:validate(Mod)). exports_(Mod) -> ok = meck:expect(Mod, test1, fun() -> ok end), ok = meck:expect(Mod, test2, fun(_) -> ok end), ok = meck:expect(Mod, test3, fun(_, _) -> ok end), ?assertEqual(0, proplists:get_value(test1, Mod:module_info(exports))), ?assertEqual(1, proplists:get_value(test2, Mod:module_info(exports))), ?assertEqual(2, proplists:get_value(test3, Mod:module_info(exports))), ?assertEqual(true, meck:validate(Mod)). call_return_value_(Mod) -> ok = meck:expect(Mod, test, fun() -> apa end), ?assertEqual(apa, Mod:test()), ?assertEqual(true, meck:validate(Mod)). call_return_value_improper_list_(Mod) -> Dict = dict:store(hest, apa, dict:new()), ok = meck:expect(Mod, test, 0, Dict), ?assertEqual(Dict, Mod:test()), ?assertEqual(true, meck:validate(Mod)). call_argument_(Mod) -> ok = meck:expect(Mod, test, fun(hest, 1) -> apa end), ?assertEqual(apa, Mod:test(hest, 1)), ?assertEqual(true, meck:validate(Mod)). call_function_clause_(Mod) -> ok = meck:expect(Mod, test, fun(hest, 1) -> apa end), ?assertError(function_clause, Mod:test(hest, 2)), ?assertEqual(false, meck:validate(Mod)). validate_unexpected_error_(Mod) -> ok = meck:expect(Mod, test, fun(hest, 1) -> erlang:error(timeout) end), ?assertError(timeout, Mod:test(hest, 1)), ?assertEqual(false, meck:validate(Mod)). validate_expected_error_(Mod) -> ok = meck:expect(Mod, test, fun(hest, 1) -> meck:exception(error, timeout) end), ?assertError(timeout, Mod:test(hest, 1)), ?assertEqual(true, meck:validate(Mod)). validate_chained_(Mod) -> ok = meck:new(mymod2, [non_strict]), ok = meck:expect(mymod2, test, fun() -> meck:exception(error, test_error) end), ok = meck:expect(Mod, test, fun() -> mymod2:test() end), ?assertError(test_error, Mod:test()), ?assertEqual(false, meck:validate(Mod)), ?assertEqual(true, meck:validate(mymod2)), ok = meck:unload(mymod2). stacktrace_(Mod) -> ok = meck:expect(Mod, test, fun() -> erlang:error(test_error) end), try Mod:test(), throw(failed) catch error:test_error -> ?assert(lists:any(fun({M, test, []}) when M == Mod -> true; ({M, test, [],[]}) when M == Mod -> true; (_) -> false end, erlang:get_stacktrace())) end. stacktrace_function_clause_(Mod) -> ok = meck:expect(Mod, test, fun(1) -> ok end), try Mod:test(error), throw(failed) catch error:function_clause -> Stacktrace = erlang:get_stacktrace(), ?assert(lists:any( fun ({M, test, [error]}) when M == Mod -> true; ({M, test, [error], []}) when M == Mod -> true; (_) -> false end, Stacktrace)) end. call_undef_(Mod) -> ok = meck:expect(Mod, test, fun(hest, 1) -> apa end), ?assertError(undef, Mod:test(hest)). caller_does_not_crash_on_reload_(Mod) -> ok = meck:expect(Mod, test, fun() -> timer:sleep(infinity) end), Pid = spawn(fun() -> Mod:test() end), ok = meck:expect(Mod, new1, fun() -> ok end), ok = meck:expect(Mod, new2, fun() -> ok end), ok = meck:expect(Mod, new3, fun() -> ok end), ?assertEqual(true, is_process_alive(Pid)). change_func_(Mod) -> ok = meck:expect(Mod, test, fun() -> 1 end), ?assertEqual(1, Mod:test()), ok = meck:expect(Mod, test, fun() -> 2 end), ?assertEqual(2, Mod:test()). call_original_undef_(Mod) -> ok = meck:expect(Mod, test, fun() -> meck:passthrough([]) end), ?assertError(undef, Mod:test()), ?assert(not meck:validate(Mod)), ?assertEqual(undefined, get('$meck_call')). history_empty_(Mod) -> ?assertEqual([], meck:history(Mod)). history_call_(Mod) -> ok = meck:expect(Mod, test, fun() -> ok end), ok = meck:expect(Mod, test2, fun(_, _) -> result end), ok = meck:expect(Mod, test3, 0, 3), Mod:test(), Mod:test2(a, b), Mod:test3(), ?assertEqual([{self(), {Mod, test, []}, ok}, {self(), {Mod, test2, [a, b]}, result}, {self(), {Mod, test3, []}, 3}], meck:history(Mod)). history_throw_(Mod) -> ok = meck:expect(Mod, test, fun() -> throw(test_exception) end), catch Mod:test(), ?assertMatch([{_Pid, {Mod, test, []}, throw, test_exception, _Stacktrace}], meck:history(Mod)). history_throw_fun_(Mod) -> Fun = fun() -> exception_fun end, ok = meck:expect(Mod, test, fun() -> throw(Fun) end), catch Mod:test(), ?assertMatch([{_Pid, {Mod, test, []}, throw, Fun, _Stacktrace}], meck:history(Mod)). history_exit_(Mod) -> ok = meck:expect(Mod, test, fun() -> exit(test_exit) end), catch Mod:test(), ?assertMatch([{_Pid, {Mod, test, []}, exit, test_exit, _Stacktrace}], meck:history(Mod)). history_error_(Mod) -> ok = meck:expect(Mod, test, fun() -> erlang:error(test_error) end), catch Mod:test(), ?assertMatch([{_Pid, {Mod, test, []}, error, test_error, _Stacktrace}], meck:history(Mod)). history_error_args_(Mod) -> ok = meck:expect(Mod, test, fun() -> erlang:error(test_error, [fake_args]) end), catch Mod:test(), History = meck:history(Mod), ?assertMatch([{_Pid, {Mod, test, []}, error, test_error, _Stacktrace}], meck:history(Mod)), [{_Pid, _MFA, error, test_error, Stacktrace}] = History, ?assert(lists:any(fun({_M, _F, [fake_args]}) -> true; ({_M, _F, [fake_args], [{file,_},{line,_}]}) -> true; (_) -> false end, Stacktrace)). history_meck_throw_(Mod) -> ok = meck:expect(Mod, test, fun() -> meck:exception(throw, test_exception) end), catch Mod:test(), ?assertMatch([{_Pid, {Mod, test, []}, throw, test_exception, _Stacktrace}], meck:history(Mod)). history_meck_throw_fun_(Mod) -> Fun = fun() -> exception_fun end, ok = meck:expect(Mod, test, fun() -> meck:exception(throw, Fun) end), catch Mod:test(), ?assertMatch([{_Pid, {Mod, test, []}, throw, Fun, _Stacktrace}], meck:history(Mod)). history_meck_exit_(Mod) -> ok = meck:expect(Mod, test, fun() -> meck:exception(exit, test_exit) end), catch Mod:test(), ?assertMatch([{_Pid, {Mod, test, []}, exit, test_exit, _Stacktrace}], meck:history(Mod)). history_meck_error_(Mod) -> ok = meck:expect(Mod, test, fun() -> meck:exception(error, test_error) end), catch Mod:test(), ?assertMatch([{_Pid, {Mod, test, []}, error, test_error, _Stacktrace}], meck:history(Mod)). history_by_pid_(Mod) -> ok = meck:expect(Mod, test1, fun() -> ok end), ok = meck:expect(Mod, test2, fun() -> ok end), TestPid = self(), Fun = fun() -> Mod:test1(), TestPid ! {self(), done} end, Pid = spawn(Fun), Mod:test1(), Mod:test2(), receive {Pid, done} -> ok end, ?assertEqual([{Pid, {Mod, test1, []}, ok}], meck:history(Mod, Pid)), ?assertEqual([{TestPid, {Mod, test1, []}, ok}, {TestPid, {Mod, test2, []}, ok}], meck:history(Mod, TestPid)), ?assertEqual(meck:history(Mod), meck:history(Mod, '_')). reset_(Mod) -> % Given meck:expect(Mod, test1, fun() -> ok end), meck:expect(Mod, test2, fun() -> ok end), Mod:test1(), Mod:test2(), % When meck:reset(Mod), Mod:test1(), % Then ?assertMatch([{_Pid, {Mod, test1, []}, ok}], meck:history(Mod)). shortcut_expect_(Mod) -> ok = meck:expect(Mod, test, 0, ok), ?assertEqual(true, meck:validate(Mod)). shortcut_expect_negative_arity_(Mod) -> ?assertError(function_clause, meck:expect(Mod, test, -1, ok)). shortcut_call_return_value_(Mod) -> ok = meck:expect(Mod, test, 0, apa), ?assertEqual(apa, Mod:test()), ?assertEqual(true, meck:validate(Mod)). shortcut_call_argument_(Mod) -> ok = meck:expect(Mod, test, 2, apa), ?assertEqual(apa, Mod:test(hest, 1)), ?assertEqual(true, meck:validate(Mod)). shortcut_re_add_(Mod) -> ok = meck:expect(Mod, test, 2, apa), ?assertEqual(apa, Mod:test(hest, 1)), ok = meck:expect(Mod, test, 2, new), ?assertEqual(new, Mod:test(hest, 1)), ?assertEqual(true, meck:validate(Mod)). shortcut_opaque_(Mod) -> Ref = make_ref(), ok = meck:expect(Mod, test, 0, {test, [a, self()], Ref}), ?assertMatch({test, [a, P], Ref} when P == self(), Mod:test()). delete_(Mod) -> ok = meck:expect(Mod, test, 2, ok), ?assertEqual(ok, meck:delete(Mod, test, 2)), ?assertError(undef, Mod:test(a, b)), ?assert(meck:validate(Mod)). called_false_no_args_(Mod) -> Args = [], ok = meck:expect(Mod, test, length(Args), ok), assert_called(Mod, test, Args, false), ok. called_true_no_args_(Mod) -> Args = [], ok = meck:expect(Mod, test, length(Args), ok), ok = apply(Mod, test, Args), assert_called(Mod, test, Args, true), ok. called_true_two_functions_(Mod) -> Args = [], ok = meck:expect(Mod, test1, length(Args), ok), ok = meck:expect(Mod, test2, length(Args), ok), ok = apply(Mod, test1, Args), ok = apply(Mod, test2, Args), assert_called(Mod, test2, Args, true), ok. called_false_one_arg_(Mod) -> Args = ["hello"], ok = meck:expect(Mod, test, length(Args), ok), assert_called(Mod, test, Args, false), ok. called_true_one_arg_(Mod) -> Args = ["hello"], ok = meck:expect(Mod, test, length(Args), ok), ok = apply(Mod, test, Args), assert_called(Mod, test, Args, true), ok. called_false_few_args_(Mod) -> Args = [one, 2, {three, 3}, "four"], ok = meck:expect(Mod, test, length(Args), ok), assert_called(Mod, test, Args, false), ok. called_true_few_args_(Mod) -> Args = [one, 2, {three, 3}, "four"], ok = meck:expect(Mod, test, length(Args), ok), ok = apply(Mod, test, Args), assert_called(Mod, test, Args, true), ok. called_few_args_matchers_(Mod) -> Args = [one, 2, {three, 3}, "four"], ok = meck:expect(Mod, test, length(Args), ok), ok = apply(Mod, test, Args), assert_called(Mod, test, ['_', meck:is(equal_to(2)), {'_', 3}, "four"], true), assert_called(Mod, test, ['_', meck:is(equal_to(3)), {'_', 3}, "four"], false), ok. called_false_error_(Mod) -> Args = [one, "two", {3, 3}], TestFun = fun (_, _, _) -> meck:exception(error, my_error) end, ok = meck:expect(Mod, test, TestFun), assert_called(Mod, test, Args, false), ok. called_true_error_(Mod) -> Args = [one, "two", {3, 3}], expect_catch_apply(Mod, test, Args), assert_called(Mod, test, Args, true), ok. called_with_pid_no_args_(Mod) -> Args = [], ok = meck:expect(Mod, test, length(Args), ok), Pid = spawn_caller_and_sync(Mod, test, Args), assert_called(Mod, test, Args, self(), false), assert_called(Mod, test, Args, Pid, true), ok = apply(Mod, test, Args), assert_called(Mod, test, Args, self(), true), ?assertEqual(meck:called(Mod, test, Args, '_'), meck:called(Mod, test, Args)). spawn_caller_and_sync(Mod, Func, Args) -> TestPid = self(), Fun = fun() -> catch apply(Mod, Func, Args), TestPid ! {self(), done} end, Pid = spawn(Fun), receive {Pid, done} -> ok end, % sync with the spawned process Pid. num_calls_(Mod) -> Args = [], IncorrectArgs = [foo], ok = meck:expect(Mod, test1, length(Args), ok), ?assertEqual(0, meck:num_calls(Mod, test1, Args)), ok = apply(Mod, test1, Args), ?assertEqual(1, meck:num_calls(Mod, test1, Args)), ?assertEqual(0, meck:num_calls(Mod, test1, IncorrectArgs)). num_calls_error_(Mod) -> Args = [one, "two", {3, 3}], expect_catch_apply(Mod, test, Args), ?assertEqual(1, meck:num_calls(Mod, test, Args)). num_calls_with_pid_no_args_(Mod) -> Args = [], ok = meck:expect(Mod, test, length(Args), ok), Pid = spawn_caller_and_sync(Mod, test, Args), ?assertEqual(0, meck:num_calls(Mod, test, Args, self())), ?assertEqual(1, meck:num_calls(Mod, test, Args, Pid)), ok = apply(Mod, test, Args), ?assertEqual(1, meck:num_calls(Mod, test, Args, self())), ?assertEqual(meck:num_calls(Mod, test, Args, '_'), meck:num_calls(Mod, test, Args)). expect_apply(Mod, Func, Args) -> ok = meck:expect(Mod, Func, length(Args), ok), ok = apply(Mod, Func, Args). expect_catch_apply(Mod, Func, Args) -> TestFun = fun (_, _, _) -> meck:exception(error, my_error) end, ok = meck:expect(Mod, Func, TestFun), catch apply(Mod, Func, Args). called_wildcard_(Mod) -> Args = [one, 2, {three, 3}, "four"], ok = meck:expect(Mod, test, length(Args), ok), ok = apply(Mod, test, Args), assert_called(Mod, test, [one, '_', {three, '_'}, "four"], true), ok. sequence_(Mod) -> Sequence = [a, b, c, d, e], ?assertEqual(ok, meck:sequence(Mod, s, 2, Sequence)), ?assertEqual(Sequence, [Mod:s(a, b) || _ <- lists:seq(1, length(Sequence))]), ?assertEqual([e, e, e, e, e], [Mod:s(a, b) || _ <- lists:seq(1, 5)]), ?assert(meck:validate(Mod)). sequence_multi_(Mod) -> meck:new(mymod2, [non_strict]), Mods = [Mod, mymod2], Sequence = [a, b, c, d, e], ?assertEqual(ok, meck:sequence(Mods, s, 2, Sequence)), ?assertEqual(Sequence, [Mod:s(a, b) || _ <- lists:seq(1, length(Sequence))]), ?assertEqual([e, e, e, e, e], [Mod:s(a, b) || _ <- lists:seq(1, 5)]), ?assertEqual(Sequence, [mymod2:s(a, b) || _ <- lists:seq(1, length(Sequence))]), ?assertEqual([e, e, e, e, e], [mymod2:s(a, b) || _ <- lists:seq(1, 5)]), ?assert(meck:validate(Mods)). expect_empty_clause_list_(Mod) -> ?assertError(empty_clause_list, meck:expect(Mod, dummy, [])). expect_args_value_(Mod) -> %% When meck:expect(Mod, val, [1001], meck:val(a)), %% Then ?assertEqual(a, Mod:val(1001)), ?assertEqual(a, Mod:val(1001)). expect_args_invalid_call_(Mod) -> %% When meck:expect(Mod, val, [1001], meck:val(a)), %% Then ?assertError(function_clause, Mod:val(1002)). expect_arity_value_(Mod) -> %% When meck:expect(Mod, val, 1, meck:val(a)), %% Then ?assertEqual(a, Mod:val(1001)), ?assertEqual(a, Mod:val(1001)). expect_args_sequence_(Mod) -> %% When meck:expect(Mod, seq, [1001], meck:seq([a, b, c])), %% Then ?assertEqual(a, Mod:seq(1001)), ?assertEqual(b, Mod:seq(1001)), ?assertEqual(c, Mod:seq(1001)), ?assertEqual(c, Mod:seq(1001)), ?assertEqual(c, Mod:seq(1001)). expect_arity_sequence_(Mod) -> %% When meck:expect(Mod, seq, 1, meck:seq([a, b, c])), %% Then ?assertEqual(a, Mod:seq(1001)), ?assertEqual(b, Mod:seq(1001)), ?assertEqual(c, Mod:seq(1001)), ?assertEqual(c, Mod:seq(1001)), ?assertEqual(c, Mod:seq(1001)). expect_complex_sequence_(Mod) -> %% When meck:expect(Mod, seq, 1, meck:seq([meck:val(a), meck:seq([b, c]), meck:seq([meck:raise(error, d), meck:seq([e, f, g]), h, meck:val(i)])])), %% Then ?assertEqual(a, Mod:seq(1001)), ?assertEqual(b, Mod:seq(1001)), ?assertEqual(c, Mod:seq(1001)), ?assertException(error, d, Mod:seq(1001)), ?assertEqual(e, Mod:seq(1001)), ?assertEqual(f, Mod:seq(1001)), ?assertEqual(g, Mod:seq(1001)), ?assertEqual(h, Mod:seq(1001)), ?assertEqual(i, Mod:seq(1001)), ?assertEqual(i, Mod:seq(1001)), ?assertEqual(i, Mod:seq(1001)). 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)). expect_args_loop_(Mod) -> %% When meck:expect(Mod, loop, [1001], meck:loop([a, b, c])), %% Then ?assertEqual(a, Mod:loop(1001)), ?assertEqual(b, Mod:loop(1001)), ?assertEqual(c, Mod:loop(1001)), ?assertEqual(a, Mod:loop(1001)), ?assertEqual(b, Mod:loop(1001)), ?assertEqual(c, Mod:loop(1001)), ?assertEqual(a, Mod:loop(1001)). expect_arity_loop_(Mod) -> %% When meck:expect(Mod, loop, 1, meck:loop([a, b, c])), %% Then ?assertEqual(a, Mod:loop(1001)), ?assertEqual(b, Mod:loop(1001)), ?assertEqual(c, Mod:loop(1001)), ?assertEqual(a, Mod:loop(1001)), ?assertEqual(b, Mod:loop(1001)), ?assertEqual(c, Mod:loop(1001)), ?assertEqual(a, Mod:loop(1001)). expect_complex_loop_(Mod) -> %% When meck:expect(Mod, loop, 1, meck:loop([meck:val(a), meck:seq([b, c]), meck:seq([meck:raise(error, d), meck:seq([e, f, g]), h, meck:val(i)])])), %% Then ?assertEqual(a, Mod:loop(1001)), ?assertEqual(b, Mod:loop(1001)), ?assertEqual(c, Mod:loop(1001)), ?assertException(error, d, Mod:loop(1001)), ?assertEqual(e, Mod:loop(1001)), ?assertEqual(f, Mod:loop(1001)), ?assertEqual(g, Mod:loop(1001)), ?assertEqual(h, Mod:loop(1001)), ?assertEqual(i, Mod:loop(1001)), %% The second round ?assertEqual(a, Mod:loop(1001)), ?assertEqual(b, Mod:loop(1001)), ?assertEqual(c, Mod:loop(1001)), ?assertException(error, d, Mod:loop(1001)), ?assertEqual(e, Mod:loop(1001)), ?assertEqual(f, Mod:loop(1001)), ?assertEqual(g, Mod:loop(1001)), ?assertEqual(h, Mod:loop(1001)), ?assertEqual(i, Mod:loop(1001)), %% The third round ?assertEqual(a, Mod:loop(1001)). expect_loop_in_seq_(Mod) -> %% When meck:expect(Mod, seq, 1, meck:seq([meck:val(a), meck:loop([b, meck:raise(throw, c), d]), meck:val(e), % Never returned meck:raise(exit, f)])), %% Then ?assertEqual(a, Mod:seq(1001)), ?assertEqual(b, Mod:seq(1001)), ?assertException(throw, c, Mod:seq(1001)), ?assertEqual(d, Mod:seq(1001)), %% The second round ?assertEqual(b, Mod:seq(1001)), ?assertException(throw, c, Mod:seq(1001)), ?assertEqual(d, Mod:seq(1001)), %% The third round ?assertEqual(b, Mod:seq(1001)). expect_args_exception_(Mod) -> %% Given meck:expect(Mod, f, [{[1001], meck:raise(error, a)}, {[1002], meck:raise(throw, b)}, {[1003], meck:raise(exit, c)}, {[1004], meck:val(d)}]), %% When/Then ?assertException(error, a, Mod:f(1001)), ?assertException(throw, b, Mod:f(1002)), ?assertException(exit, c, Mod:f(1003)), ?assertMatch(d, Mod:f(1004)). expect_arity_exception_(Mod) -> %% Given meck:expect(Mod, f, 1, meck:raise(error, a)), %% When/Then ?assertError(a, Mod:f(1001)). expect_arity_clause_(Mod) -> %% Given meck:expect(Mod, foo, [{2, blah}]), %% When/Then ?assertMatch(blah, Mod:foo(1, 2)), ?assertError(_, Mod:foo(1, 2, 3)). loop_multi_(Mod) -> meck:new(mymod2, [non_strict]), 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)). expect_args_pattern_override_(Mod) -> %% When meck:expect(Mod, f, [{[1, 1], a}, {[1, '_'], b}, {['_', '_'], c}]), %% Then ?assertEqual(a, Mod:f(1, 1)), ?assertEqual(b, Mod:f(1, 2)), ?assertEqual(c, Mod:f(2, 2)). expect_args_pattern_shadow_(Mod) -> %% When meck:expect(Mod, f, [{[1, 1], a}, {['_', '_'], c}, {[1, '_'], b}]), %% Then ?assertEqual(a, Mod:f(1, 1)), ?assertEqual(c, Mod:f(1, 2)), ?assertEqual(c, Mod:f(2, 2)). expect_args_pattern_missing_(Mod) -> %% When meck:expect(Mod, f, [{[1, 1], a}, {[1, '_'], b}]), %% Then ?assertError(function_clause, Mod:f(2, 2)), ?assertEqual(a, Mod:f(1, 1)), ?assertEqual(b, Mod:f(1, 2)). expect_args_pattern_invalid_(Mod) -> %% When/Then ?assertError({invalid_arity, {{expected, 2}, {actual, 3}, {clause, {[1, 2, 3], b}}}}, meck:expect(Mod, f, [{[1, 2], a}, {[1, 2, 3], b}])). expect_args_matchers_(Mod) -> %% When meck:expect(Mod, f, [{[1, meck:is(fun(X) -> X == 1 end)], a}, {[1, meck:is(less_than(3))], b}, {['_', '_'], c}]), %% Then ?assertEqual(a, Mod:f(1, 1)), ?assertEqual(b, Mod:f(1, 2)), ?assertEqual(c, Mod:f(2, 2)). expect_ret_specs_(Mod) -> %% When meck:expect(Mod, f, [{[1, 1], meck:seq([a, b, c])}, {[1, '_'], meck:loop([d, e])}, {['_', '_'], meck:val(f)}]), %% Then ?assertEqual(d, Mod:f(1, 2)), ?assertEqual(f, Mod:f(2, 2)), ?assertEqual(e, Mod:f(1, 2)), ?assertEqual(a, Mod:f(1, 1)), ?assertEqual(d, Mod:f(1, 2)), ?assertEqual(b, Mod:f(1, 1)), ?assertEqual(c, Mod:f(1, 1)), ?assertEqual(f, Mod:f(2, 2)), ?assertEqual(c, Mod:f(1, 1)), ?assertEqual(e, Mod:f(1, 2)), ?assertEqual(c, Mod:f(1, 1)). %% --- Tests with own setup ---------------------------------------------------- undefined_module_test() -> %% When/Then ?assertError({{undefined_module, blah}, _}, meck:new(blah, [no_link])). undefined_function_test() -> %% Given meck:new(meck_test_module), %% When/Then meck:expect(meck_test_module, b, 0, ok), ?assertError({undefined_function, {meck_test_module, b, 1}}, meck:expect(meck_test_module, b, 1, ok)), meck:unload(meck_test_module). call_original_test() -> false = code:purge(meck_test_module), ?assertEqual({module, meck_test_module}, code:load_file(meck_test_module)), ok = meck:new(meck_test_module, [no_passthrough_cover]), ?assertEqual({file, ""}, code:is_loaded(meck_test_module_meck_original)), ok = meck:expect(meck_test_module, a, fun() -> c end), ok = meck:expect(meck_test_module, b, fun() -> meck:passthrough([]) end), ?assertEqual(c, meck_test_module:a()), ?assertEqual(b, meck_test_module:b()), ok = meck:unload(meck_test_module). unload_renamed_original_test() -> ok = meck:new(meck_test_module), ok = meck:unload(meck_test_module), ?assertEqual(false, code:is_loaded(meck_test_module_meck_original)). unload_all_test() -> Mods = [test_a, test_b, test_c, test_d, test_e], ok = meck:new(Mods, [non_strict]), ?assertEqual(lists:sort(Mods), lists:sort(meck:unload())), [?assertEqual(false, code:is_loaded(M)) || M <- Mods]. original_no_file_test() -> {ok, Mod, Beam} = compile:forms([{attribute, 1, module, meck_not_on_disk}]), {module, Mod} = code:load_binary(Mod, "", Beam), ?assertEqual(ok, meck:new(meck_not_on_disk)), ok = meck:unload(meck_not_on_disk). original_has_no_object_code_test() -> {ok, Mod, Beam} = compile:forms([{attribute, 1, module, meck_on_disk}]), ok = file:write_file("meck_on_disk.beam", Beam), {module, Mod} = code:load_binary(Mod, "meck_on_disk.beam", Beam), ?assertEqual(ok, meck:new(meck_on_disk)), ok = file:delete("meck_on_disk.beam"), ok = meck:unload(meck_on_disk). passthrough_nonexisting_module_test() -> ok = meck:new(mymod, [passthrough, non_strict]), ok = meck:expect(mymod, test, fun() -> ok end), ?assertEqual(ok, mymod:test()), ok = meck:unload(mymod). passthrough_test() -> passthrough_test([]). passthrough_test(Opts) -> ok = meck:new(meck_test_module, [passthrough|Opts]), ok = meck:expect(meck_test_module, a, fun() -> c end), ?assertEqual(c, meck_test_module:a()), ?assertEqual(b, meck_test_module:b()), ?assertEqual({1, 2}, meck_test_module:c(1, 2)), ok = meck:unload(meck_test_module). passthrough_different_arg_test() -> ok = meck:new(meck_test_module), ok = meck:expect(meck_test_module, c, fun(_, _) -> meck:passthrough([x, y]) end), ?assertEqual({x, y}, meck_test_module:c(1, 2)), ok = meck:unload(meck_test_module). passthrough_bif_test() -> ?assertEqual(ok, meck:new(file, [unstick, passthrough])), ?assertEqual(ok, meck:unload(file)). stub_all_test() -> ok = meck:new(meck_test_module, [{stub_all, meck:seq([a, b])}]), ok = meck:expect(meck_test_module, a, [], c), ?assertEqual(c, meck_test_module:a()), ?assertEqual(a, meck_test_module:b()), ?assertEqual(b, meck_test_module:b()), ?assertEqual(b, meck_test_module:b()), ?assertEqual(a, meck_test_module:c(1, 2)), ?assertEqual(b, meck_test_module:c(1, 2)), ?assertEqual(b, meck_test_module:c(1, 2)), ok = meck:unload(meck_test_module). stub_all_default_test() -> ok = meck:new(meck_test_module, [stub_all]), ?assertEqual(ok, meck_test_module:c(1, 2)), ok = meck:unload(meck_test_module). stub_all_undefined_test() -> ok = meck:new(meck_test_module, [{stub_all, undefined}]), ?assertEqual(undefined, meck_test_module:c(1, 2)), ok = meck:unload(meck_test_module). stub_all_true_test() -> ok = meck:new(meck_test_module, [{stub_all, true}]), ?assertEqual(true, meck_test_module:c(1, 2)), ok = meck:unload(meck_test_module). stub_all_overridden_by_passthrough_test() -> ok = meck:new(meck_test_module, [stub_all, passthrough]), ?assertEqual(a, meck_test_module:a()), ok = meck:unload(meck_test_module). mock_file_existing_test() -> %% Given ExistingFile = atom_to_list(?MODULE) ++ ".erl", {ok, ExistsInfo} = file:read_file_info(ExistingFile), meck:new(file, [unstick, passthrough]), %% When meck:expect(file, read_file_info, fun(Path) -> meck:passthrough([Path]) end), %% Then ?assertEqual({ok, ExistsInfo}, file:read_file_info(ExistingFile)), %% Cleanup meck:unload(file). mock_file_missing_test() -> %% Given MissingFile = "blah.erl", {error, enoent} = file:read_file_info(MissingFile), meck:new(file, [unstick, passthrough]), %% When meck:expect(file, read_file_info, 1, {ok, no_info}), %% Then ?assertEqual({ok, no_info}, file:read_file_info(MissingFile)), %% Cleanup meck:unload(file). cover_test() -> {ok, _} = cover:compile("../test/meck_test_module.erl"), a = meck_test_module:a(), b = meck_test_module:b(), {1, 2} = meck_test_module:c(1, 2), {ok, {meck_test_module, {3,0}}} = cover:analyze(meck_test_module, module), run_mock_no_cover_file(meck_test_module), {ok, {meck_test_module, {3,0}}} = cover:analyze(meck_test_module, module). cover_options_test_() -> {foreach, fun compile_options_setup/0, fun compile_options_teardown/1, [{with, [T]} || T <- [fun ?MODULE:cover_options_/1, fun ?MODULE:cover_options_fail_/1 ]]}. compile_options_setup() -> Module = cover_test_module, % Our test module won't compile without compiler options that % rebar won't give it, thus the rename dance. Src = join("../test/", Module, ".erl"), ok = file:rename(join("../test/", Module, ".dontcompile"), Src), OldPath = code:get_path(), code:add_path("../test"), {OldPath, Src, Module}. compile_options_teardown({OldPath, Src, Module}) -> file:rename(Src, join("../test/", Module, ".dontcompile")), code:purge(Module), code:delete(Module), code:set_path(OldPath). cover_options_({_OldPath, Src, Module}) -> % Test that compilation options (include paths and preprocessor % definitions) are used when un-mecking previously cover compiled % modules. CompilerOptions = [{i, "../test/include"}, {d, 'TEST', true}], % The option recover feature depends on having the BEAM file % available. {ok, _} = compile:file(Src, [{outdir, "../test"}|CompilerOptions]), {ok, _} = cover:compile(Src, CompilerOptions), a = Module:a(), b = Module:b(), {1, 2} = Module:c(1, 2), % We get 2 instead of 3 as expected. Maybe because cover doesn't % count include files? ?assertEqual({ok, {Module, {2,0}}}, cover:analyze(Module, module)), run_mock_no_cover_file(Module), % 2 instead of 3, as above ?assertEqual({ok, {Module, {2,0}}}, cover:analyze(Module, module)). cover_options_fail_({_OldPath, Src, Module}) -> %% This may look like the test above but there is a subtle %% difference. When `cover:compile_beam' is called it squashes %% compile options. This test verifies that function `b/0', which %% relies on the `TEST' directive being set can still be called %% after the module is meck'ed. CompilerOptions = [{i, "../test/include"}, {d, 'TEST', true}, {outdir, "../test"}, debug_info], {ok, _} = compile:file(Src, CompilerOptions), ?assertEqual(CompilerOptions, meck_code:compile_options(Module)), {ok, _} = cover:compile_beam(Module), ?assertEqual([], meck_code:compile_options(Module)), a = Module:a(), b = Module:b(), {1, 2} = Module:c(1, 2), ?assertEqual({ok, {Module, {2,0}}}, cover:analyze(Module, module)), ok = meck:new(Module, [passthrough]), ok = meck:expect(Module, a, fun () -> c end), ?assertEqual(c, Module:a()), ?assertEqual(b, Module:b()), ?assertEqual({1, 2}, Module:c(1, 2)), ok = meck:unload(Module), %% Verify passthru calls went to cover ?assertEqual({ok, {Module, 4}}, cover:analyze(Module, calls, module)). join(Path, Module, Ext) -> filename:join(Path, atom_to_list(Module) ++ Ext). run_mock_no_cover_file(Module) -> ok = meck:new(Module), ok = meck:expect(Module, a, fun () -> c end), ?assertEqual(c, Module:a()), ok = meck:unload(Module), ?assert(not filelib:is_file(atom_to_list(Module) ++ ".coverdata")). %% @doc Verify that passthrough calls _don't_ appear in cover %% analysis. no_cover_passthrough_test() -> {ok, _} = cover:compile("../test/meck_test_module.erl"), {ok, {meck_test_module, {0,3}}} = cover:analyze(meck_test_module, module), passthrough_test([no_passthrough_cover]), {ok, {meck_test_module, {0,3}}} = cover:analyze(meck_test_module, module). %% @doc Verify that passthrough calls appear in cover analysis. cover_passthrough_test() -> {ok, _} = cover:compile("../test/meck_test_module.erl"), ?assertEqual({ok, {meck_test_module, {0,3}}}, cover:analyze(meck_test_module, module)), passthrough_test([]), ?assertEqual({ok, {meck_test_module, {2,1}}}, cover:analyze(meck_test_module, module)). % @doc The mocked module is unloaded if the meck process crashes. unload_when_crashed_test() -> ok = meck:new(mymod, [non_strict]), ?assertMatch({file, _}, code:is_loaded(mymod)), SaltedName = mymod_meck, Pid = whereis(SaltedName), ?assertEqual(true, is_pid(Pid)), unlink(Pid), exit(Pid, expected_test_exit), timer:sleep(100), ?assertEqual(undefined, whereis(SaltedName)), ?assertEqual(false, code:is_loaded(mymod)). % @doc The mocked module is unloaded if the meck process crashes. unlink_test() -> ok = meck:new(mymod, [no_link, non_strict]), SaltedName = mymod_meck, {links, Links} = process_info(whereis(SaltedName), links), ?assert(not lists:member(self(), Links)), ok = meck:unload(mymod). %% @doc Exception is thrown when you run expect on a non-existing (and not yet %% mocked) module. expect_without_new_test() -> ?assertError({not_mocked, othermod}, meck:expect(othermod, test, fun() -> ok end)). history_passthrough_test() -> ok = meck:new(meck_test_module, [passthrough]), ok = meck:expect(meck_test_module, a, fun() -> c end), c = meck_test_module:a(), b = meck_test_module:b(), ?assertEqual([{self(), {meck_test_module, a, []}, c}, {self(), {meck_test_module, b, []}, b}], meck:history(meck_test_module)), ok = meck:unload(meck_test_module). multi_test() -> Mods = [mod1, mod2, mod3], ok = meck:new(Mods, [non_strict]), ok = meck:expect(Mods, test, fun() -> ok end), ok = meck:expect(Mods, test2, 0, ok), [?assertEqual(ok, M:test()) || M <- Mods], ?assert(meck:validate(Mods)), ok = meck:unload(Mods). multi_invalid_test() -> Mods = [mod1, mod2, mod3], ok = meck:new(Mods, [non_strict]), ok = meck:expect(Mods, test, fun(1) -> ok end), ?assertError(function_clause, mod2:test(2)), ?assert(not meck:validate(Mods)), ok = meck:unload(Mods). multi_option_test() -> Mods = [mod1, mod2, mod3], ok = meck:new(Mods, [passthrough, non_strict]), ok = meck:expect(Mods, test, fun() -> ok end), [?assertEqual(ok, M:test()) || M <- Mods], ?assert(meck:validate(Mods)), ok = meck:unload(Mods). multi_shortcut_test() -> Mods = [mod1, mod2, mod3], ok = meck:new(Mods, [non_strict]), ok = meck:expect(Mods, test, 0, ok), [?assertEqual(ok, M:test()) || M <- Mods], ?assert(meck:validate(Mods)), ok = meck:unload(Mods). multi_delete_test() -> Mods = [mod1, mod2, mod3], ok = meck:new(Mods, [non_strict]), ok = meck:expect(Mods, test, 0, ok), ?assertEqual(ok, meck:delete(Mods, test, 0)), [?assertError(undef, M:test()) || M <- Mods], ?assert(meck:validate(Mods)), ok = meck:unload(Mods). multi_reset_test() -> % Given Mods = [mod1, mod2, mod3], meck:new(Mods, [non_strict]), meck:expect(Mods, test1, 0, ok), meck:expect(Mods, test2, 0, ok), mod1:test1(), mod1:test2(), mod2:test1(), mod3:test2(), % When meck:reset(Mods), mod1:test1(), mod1:test1(), % Then ?assertMatch([{_Pid, {mod1, test1, []}, ok}, {_Pid, {mod1, test1, []}, ok}], meck:history(mod1)), ?assertMatch([], meck:history(mod2)), ?assertMatch([], meck:history(mod3)). handle_cast_unmodified_state_test() -> S = dummy_state, ?assertEqual({noreply, S}, meck_proc:handle_cast(dummy_msg, S)). code_change_unmodified_state_test() -> S = dummy_state, ?assertEqual({ok, S}, meck_proc:code_change(old_version, S, [])). remote_meck_test_() -> {foreach, fun remote_setup/0, fun remote_teardown/1, [{with, [T]} || T <- [fun remote_meck_/1, fun remote_meck_cover_/1]]}. remote_setup() -> [] = os:cmd("epmd -daemon"), case node() of 'nonode@nohost' -> Hostname = "localhost"; _ -> Hostname = test_server_sup:hoststr() end, Myself = list_to_atom("meck_eunit_test@" ++ Hostname), net_kernel:start([Myself, shortnames]), {ok, Node} = slave:start_link(list_to_atom(Hostname), meck_remote_test, "-pa test"), {Mod, Bin, File} = code:get_object_code(meck), true = rpc:call(Node, code, add_path, [filename:dirname(File)]), {module, Mod} = rpc:call(Node, code, load_binary, [Mod, File, Bin]), {module, meck_test_module} = rpc:call(Node, code, load_file, [meck_test_module]), {Node, meck_test_module}. remote_teardown({Node, _Mod}) -> ok = slave:stop(Node), ok = net_kernel:stop(). remote_meck_({Node, Mod}) -> ?assertEqual(ok, rpc:call(Node, meck, new, [Mod, [no_link, non_strict]])), ?assertEqual(ok, rpc:call(Node, meck, expect, [Mod, test, 0, true])), ?assertEqual(true, rpc:call(Node, Mod, test, [])). remote_meck_cover_({Node, Mod}) -> {ok, Mod} = cover:compile(Mod), {ok, _Nodes} = cover:start([Node]), ?assertEqual(ok, rpc:call(Node, meck, new, [Mod])). can_mock_sticky_modules_test() -> code:stick_mod(meck_test_module), meck:new(meck_test_module, [unstick]), ?assertNot(code:is_sticky(meck_test_module)), meck:unload(meck_test_module), ?assert(code:is_sticky(meck_test_module)), code:unstick_mod(meck_test_module). sticky_directory_test_() -> {foreach, fun sticky_setup/0, fun sticky_teardown/1, [{with, [T]} || T <- [fun ?MODULE:can_mock_sticky_module_not_yet_loaded_/1, fun ?MODULE:cannot_mock_sticky_module_without_unstick_/1]]}. sticky_setup() -> % Find out where the beam file is (purge because it is cover compiled) Module = meck_test_module, false = code:purge(Module), {module, Module} = code:load_file(Module), Beam = code:which(Module), % Unload module so it's not loaded when running meck false = code:purge(Module), true = code:delete(Module), % Create new sticky dir and copy beam file Dir = "sticky_test", ok = filelib:ensure_dir(filename:join(Dir, "dummy")), Dest = filename:join(Dir, filename:basename(Beam)), {ok, _BytesCopied} = file:copy(Beam, Dest), true = code:add_patha(Dir), ok = code:stick_dir(Dir), code:load_file(Module), {Module, {Dir, Dest}}. sticky_teardown({Module, {Dir, Dest}}) -> % Clean up ok = code:unstick_dir(Dir), false = code:purge(Module), true = code:del_path(Dir), ok = file:delete(Dest), ok = file:del_dir(Dir). can_mock_sticky_module_not_yet_loaded_({Mod, _}) -> ?assertEqual(ok, meck:new(Mod, [unstick])), ?assertNot(code:is_sticky(Mod)), ?assertEqual(ok, meck:unload(Mod)), ?assert(code:is_sticky(Mod)). cannot_mock_sticky_module_without_unstick_({Mod, _}) -> ?assertError(module_is_sticky, meck:new(Mod, [no_link])). can_mock_non_sticky_module_test() -> ?assertNot(code:is_sticky(meck_test_module)), ?assertEqual(ok, meck:new(meck_test_module, [unstick])), ?assertNot(code:is_sticky(meck_test_module)), ?assertEqual(ok, meck:unload(meck_test_module)), ?assertNot(code:is_sticky(meck_test_module)). cannot_expect_bif_or_autogenerated_test() -> ?assertEqual(ok, meck:new(unicode, [unstick, passthrough])), ?assertError({cannot_mock_builtin, {unicode, characters_to_binary, 2}}, meck:expect(unicode, characters_to_binary, 2, doh)), ?assertError({cannot_mock_autogenerated, {unicode, module_info, 0}}, meck:expect(unicode, module_info, 0, doh)), ?assertEqual(ok, meck:unload(unicode)). meck_module_attributes_test() -> ?assertEqual(ok, meck:new(meck_test_module)), ?assertEqual([foobar], proplists:get_value(tag, proplists:get_value(attributes, meck_test_module:module_info()))), ?assertEqual(ok, meck:unload(meck_test_module)). meck_implicit_new_test()-> meck:expect(meck_test_module, c, [{[1, 1], foo}, {['_', '_'], bar}]), ?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]), meck:expect(test, foo, 2, ok), %% When test:foo(1, 2), test:foo(1, 2), %% Then ?assertMatch(ok, meck:wait(2, test, foo, [1, '_'], 100)), %% Clean meck:unload(). wait_not_called_zero_timeout_test() -> %% Given meck:new(test, [non_strict]), meck:expect(test, foo, 2, ok), %% When test:foo(1, 2), test:foo(1, 2), %% Then ?assertError(timeout, meck:wait(3, test, foo, [1, '_'], 0)), %% Clean meck:unload(). wait_not_called_another_proc_test() -> %% Given meck:new(test, [non_strict]), meck:expect(test, foo, 2, ok), %% When test:foo(1, 2), % Called, but not by the expected proc. Pid = erlang:spawn(fun() -> test:foo(2, 2) % Unexpected first argument end), %% Then ?assertError(timeout, meck:wait(1, test, foo, [1, '_'], Pid, 100)), %% Clean meck:unload(). wait_called_another_proc_test() -> %% Given meck:new(test, [non_strict]), meck:expect(test, foo, 2, ok), %% When Pid = erlang:spawn(fun() -> timer:sleep(50), test:foo(1, 2), test:foo(2, 2), % Unexpected first argument test:foo(1, 2) end), %% Then ?assertMatch(ok, meck:wait(2, test, foo, [1, '_'], Pid, 500)), %% Clean meck:unload(). wait_timeout_test() -> %% Given meck:new(test, [non_strict]), meck:expect(test, foo, 2, ok), %% When test:foo(1, 2), %% Then ?assertError(timeout, meck:wait(2, test, foo, [1, '_'], '_', 10)), %% 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 %%============================================================================= assert_called(Mod, Function, Args, WasCalled) -> ?assertEqual(WasCalled, meck:called(Mod, Function, Args)), ?assert(meck:validate(Mod)). assert_called(Mod, Function, Args, Pid, WasCalled) -> ?assertEqual(WasCalled, meck:called(Mod, Function, Args, Pid)), ?assert(meck:validate(Mod)).