summaryrefslogtreecommitdiff
path: root/test/mock.erl
diff options
context:
space:
mode:
Diffstat (limited to 'test/mock.erl')
-rw-r--r--test/mock.erl322
1 files changed, 0 insertions, 322 deletions
diff --git a/test/mock.erl b/test/mock.erl
deleted file mode 100644
index 2ecbf4f7..00000000
--- a/test/mock.erl
+++ /dev/null
@@ -1,322 +0,0 @@
-%%% -*- erlang-indent-level:2 -*-
-%%%-------------------------------------------------------------------
-%%% File: mock.erl
-%%% @author Cliff Moon <> []
-%%% @copyright 2009 Cliff Moon
-%%% @doc
-%%%
-%%% @end
-%%%
-%%% @since 2009-01-04 by Cliff Moon
-%%%-------------------------------------------------------------------
--module(mock).
--author('cliff@powerset.com').
-
-%% API
--export([mock/1, proxy_call/2, proxy_call/3, expects/4, expects/5,
- verify_and_stop/1, verify/1, stub_proxy_call/3, stop/1]).
-
--include_lib("eunit/include/eunit.hrl").
--include("../include/common.hrl").
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
--record(mockstate, {old_code, module, expectations=[]}).
-
-%%====================================================================
-%% API
-%%====================================================================
-%%--------------------------------------------------------------------
-%% @spec mock(Module::atom()) -> {ok,Mock::record()} | ignore | {error,Error}
-%% @doc Starts the server
-%% @end
-%%--------------------------------------------------------------------
-mock(Module) ->
- case gen_server:start_link({local, mod_to_name(Module)}, mock, Module, []) of
- {ok, Pid} -> {ok, Pid};
- {error, Reason} -> {error, Reason}
- end.
-
-%% @spec proxy_call(Module::atom(), Function::atom()) -> term()
-%% @doc Proxies a call to the mock server for Module without arguments
-%% @end
-proxy_call(Module, Function) ->
- gen_server:call(mod_to_name(Module), {proxy_call, Function, {}}).
-
-%% @spec proxy_call(Module::atom(), Function::atom(), Args::tuple()) -> term()
-%% @doc Proxies a call to the mock server for Module with arguments
-%% @end
-proxy_call(Module, Function, Args) ->
- gen_server:call(mod_to_name(Module), {proxy_call, Function, Args}).
-
-stub_proxy_call(Module, Function, Args) ->
- RegName = list_to_atom(lists:concat([Module, "_", Function, "_stub"])),
- Ref = make_ref(),
- RegName ! {Ref, self(), Args},
- ?debugFmt("sending {~p,~p,~p}", [Ref, self(), Args]),
- receive
- {Ref, Answer} -> Answer
- end.
-
-%% @spec expects(Module::atom(),
-%% Function::atom(),
-%% Args::function(),
-%% Ret::function() | term() ) -> term()
-
-%% Times:: {at_least, integer()} | never | {no_more_than, integer()} | integer()) -> term()
-
-%% @doc Sets the expectation that Function of Module will be called during a
-%% test with Args. Args should be a fun predicate that will return true or
-%% false whether or not the argument list matches. The argument list of the
-%% function is passed in as a tuple. Ret is either a value to return or a fun
-%% of arity 2 to be evaluated in response to a proxied call. The first argument
-%% is the actual args from the call, the second is the call count starting
-%% with 1.
-expects(Module, Function, Args, Ret) ->
- gen_server:call(mod_to_name(Module), {expects, Function, Args, Ret, 1}).
-
-expects(Module, Function, Args, Ret, Times) ->
- gen_server:call(mod_to_name(Module), {expects, Function, Args, Ret, Times}).
-
-%% stub(Module, Function, Args, Ret) ->
-%% gen_server:call(mod_to_name(Module), {stub, Function, Args, Ret}).
-
-verify_and_stop(Module) ->
- verify(Module),
- stop(Module).
-
-verify(Module) ->
- ?assertEqual(ok, gen_server:call(mod_to_name(Module), verify)).
-
-stop(Module) ->
- gen_server:cast(mod_to_name(Module), stop),
- timer:sleep(10).
-
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% @spec init(Args) -> {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
-%% @doc Initiates the server
-%% @end
-%%--------------------------------------------------------------------
-init(Module) ->
- case code:get_object_code(Module) of
- {Module, Bin, Filename} ->
- case replace_code(Module) of
- ok -> {ok, #mockstate{module=Module,old_code={Module, Bin, Filename}}};
- {error, Reason} -> {stop, Reason}
- end;
- error -> {stop, ?fmt("Could not get object code for module ~p", [Module])}
- end.
-
-%%--------------------------------------------------------------------
-%% @spec
-%% handle_call(Request, From, State) -> {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} |
-%% {stop, Reason, State}
-%% @doc Handling call messages
-%% @end
-%%--------------------------------------------------------------------
-handle_call({proxy_call, Function, Args}, _From,
- State = #mockstate{module=Mod,expectations=Expects}) ->
- case match_expectation(Function, Args, Expects) of
- {matched, ReturnTerm, NewExpects} ->
- {reply, ReturnTerm, State#mockstate{expectations=NewExpects}};
- unmatched ->
- {stop, ?fmt("got unexpected call to ~p:~p", [Mod,Function])}
- end;
-
-handle_call({expects, Function, Args, Ret, Times}, _From,
- State = #mockstate{expectations=Expects}) ->
- {reply, ok, State#mockstate{
- expectations=add_expectation(Function, Args, Ret, Times, Expects)}};
-
-handle_call(verify, _From, State = #mockstate{expectations=Expects,module=Mod}) ->
- ?infoFmt("verifying ~p~n", [Mod]),
- if
- length(Expects) > 0 ->
- {reply, {mismatch, format_missing_expectations(Expects, Mod)}, State};
- true -> {reply, ok, State}
- end.
-
-%%--------------------------------------------------------------------
-%% @spec handle_cast(Msg, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% @doc Handling cast messages
-%% @end
-%%--------------------------------------------------------------------
-handle_cast(stop, State) ->
- timer:sleep(10),
- {stop, normal, State}.
-
-%%--------------------------------------------------------------------
-%% @spec handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% @doc Handling all non call/cast messages
-%% @end
-%%--------------------------------------------------------------------
-handle_info(_Info, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% @spec terminate(Reason, State) -> void()
-%% @doc This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%% @end
-%%--------------------------------------------------------------------
-terminate(_Reason, #mockstate{old_code={Module, Binary, Filename}}) ->
- code:purge(Module),
- code:delete(Module),
- code:load_binary(Module, Filename, Binary),
- timer:sleep(10).
-
-%%--------------------------------------------------------------------
-%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% @doc Convert process state when code is changed
-%% @end
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-format_missing_expectations(Expects, Mod) ->
- format_missing_expectations(Expects, Mod, []).
-
-format_missing_expectations([], _, Msgs) ->
- lists:reverse(Msgs);
-
-format_missing_expectations([{Function, _Args, _Ret, Times, Called}|Expects], Mod, Msgs) ->
- Msgs1 = [?fmt("expected ~p:~p to be called ~p times but was called ~p", [Mod,Function,Times,Called])|Msgs],
- format_missing_expectations(Expects, Mod, Msgs1).
-
-add_expectation(Function, Args, Ret, Times, Expects) ->
- Expects ++ [{Function, Args, Ret, Times, 0}].
-
-match_expectation(Function, Args, Expectations) ->
- match_expectation(Function, Args, Expectations, []).
-
-match_expectation(_Function, _Args, [], _Rest) ->
- unmatched;
-
-match_expectation(Function, Args, [{Function, Matcher, Ret, MaxTimes, Invoked}|Expects], Rest) ->
- case Matcher(Args) of
- true ->
- ReturnTerm = prepare_return(Args, Ret, Invoked+1),
- if
- Invoked + 1 >= MaxTimes -> {matched, ReturnTerm, lists:reverse(Rest) ++ Expects};
- true -> {matched, ReturnTerm, lists:reverse(Rest) ++ [{Function, Matcher, Ret, MaxTimes, Invoked+1}] ++ Expects}
- end;
- false -> match_expectation(Function, Args, Expects, [{Function,Matcher,Ret,MaxTimes,Invoked}|Rest])
- end;
-
-match_expectation(Function, Args, [Expect|Expects], Rest) ->
- match_expectation(Function, Args, Expects, [Expect|Rest]).
-
-prepare_return(Args, Ret, Invoked) when is_function(Ret) ->
- Ret(Args, Invoked);
-
-prepare_return(_Args, Ret, _Invoked) ->
- Ret.
-
-replace_code(Module) ->
- Info = Module:module_info(),
- Exports = get_exports(Info),
- unload_code(Module),
- NewFunctions = generate_functions(Module, Exports),
- Forms = [
- {attribute,1,module,Module},
- {attribute,2,export,Exports}
- ] ++ NewFunctions,
- case compile:forms(Forms, [binary]) of
- {ok, Module, Binary} -> case code:load_binary(Module, atom_to_list(Module) ++ ".erl", Binary) of
- {module, Module} -> ok;
- {error, Reason} -> {error, Reason}
- end;
- error -> {error, "An undefined error happened when compiling."};
- {error, Errors, Warnings} -> {error, Errors ++ Warnings}
- end.
-
-unload_code(Module) ->
- code:purge(Module),
- code:delete(Module).
-
-get_exports(Info) ->
- get_exports(Info, []).
-
-get_exports(Info, Acc) ->
- case lists:keytake(exports, 1, Info) of
- {value, {exports, Exports}, ModInfo} ->
- get_exports(ModInfo, Acc ++ lists:filter(fun({module_info, _}) -> false; (_) -> true end, Exports));
- _ -> Acc
- end.
-
-%% stub_function_loop(Fun) ->
-%% receive
-%% {Ref, Pid, Args} ->
-%% ?debugFmt("received {~p,~p,~p}", [Ref, Pid, Args]),
-%% Ret = (catch Fun(Args) ),
-%% ?debugFmt("sending {~p,~p}", [Ref,Ret]),
-%% Pid ! {Ref, Ret},
-%% stub_function_loop(Fun)
-%% end.
-
-% Function -> {function, Lineno, Name, Arity, [Clauses]}
-% Clause -> {clause, Lineno, [Variables], [Guards], [Expressions]}
-% Variable -> {var, Line, Name}
-%
-generate_functions(Module, Exports) ->
- generate_functions(Module, Exports, []).
-
-generate_functions(_Module, [], FunctionForms) ->
- lists:reverse(FunctionForms);
-
-generate_functions(Module, [{Name,Arity}|Exports], FunctionForms) ->
- generate_functions(Module, Exports, [generate_function(Module, Name, Arity)|FunctionForms]).
-
-generate_function(Module, Name, Arity) ->
- {function, 1, Name, Arity, [{clause, 1, generate_variables(Arity), [], generate_expression(mock, proxy_call, Module, Name, Arity)}]}.
-
-generate_variables(0) -> [];
-generate_variables(Arity) ->
- lists:map(fun(N) ->
- {var, 1, list_to_atom(lists:concat(['Arg', N]))}
- end, lists:seq(1, Arity)).
-
-generate_expression(M, F, Module, Name, 0) ->
- [{call,1,{remote,1,{atom,1,M},{atom,1,F}}, [{atom,1,Module}, {atom,1,Name}]}];
-generate_expression(M, F, Module, Name, Arity) ->
- [{call,1,{remote,1,{atom,1,M},{atom,1,F}}, [{atom,1,Module}, {atom,1,Name}, {tuple,1,lists:map(fun(N) ->
- {var, 1, list_to_atom(lists:concat(['Arg', N]))}
- end, lists:seq(1, Arity))}]}].
-
-mod_to_name(Module) ->
- list_to_atom(lists:concat([mock_, Module])).
-
-%% replace_function(FF, Forms) ->
-%% replace_function(FF, Forms, []).
-
-%% replace_function(FF, [], Ret) ->
-%% [FF|lists:reverse(Ret)];
-
-%% replace_function({function,_,Name,Arity,Clauses}, [{function,Line,Name,Arity,_}|Forms], Ret) ->
-%% lists:reverse(Ret) ++ [{function,Line,Name,Arity,Clauses}|Forms];
-
-%% replace_function(FF, [FD|Forms], Ret) ->
-%% replace_function(FF, Forms, [FD|Ret]).