Skip to content

Commit

Permalink
Refactor the solution
Browse files Browse the repository at this point in the history
- get rid of the process dictionary;
- make tests run on any OS;
  • Loading branch information
horkhe committed Oct 24, 2012
1 parent 3d6fcdf commit 5e63c2f
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 47 deletions.
76 changes: 46 additions & 30 deletions src/meck.erl
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@
valid = true :: boolean(),
history = [] :: history() | undefined,
original :: term(),
was_sticky :: boolean()}).
was_sticky = false :: boolean(),
reload :: {CompilerPid::pid(), From::{pid(), Tag::any()}} |
undefined}).

%% Includes
-include("meck_abstract.hrl").
Expand Down Expand Up @@ -515,16 +517,22 @@ handle_call({get_expect, FuncAri, Args}, _From, S) ->
{reply, Expect, S#state{expects = NewExpects}};
handle_call({expect, FuncAri = {Func, Ari}, Clauses}, From,
S = #state{mod = Mod, expects = Expects}) ->
check_if_being_reloaded(S),
case validate_expect(Mod, Func, Ari, S#state.can_expect) of
ok ->
NewExpects = store_expect(Mod, FuncAri, Clauses, Expects, From),
{noreply, S#state{expects = NewExpects}};
{NewExpects, CompilerPid} = store_expect(Mod, FuncAri, Clauses,
Expects),
{noreply, S#state{expects = NewExpects,
reload = {CompilerPid, From}}};
{error, Reason} ->
{reply, {error, Reason}, S}
end;
handle_call({delete, Func, Ari}, From, S) ->
NewExpects = delete_expect(S#state.mod, {Func, Ari}, S#state.expects, From),
{noreply, S#state{expects = NewExpects}};
handle_call({delete, Func, Ari}, From,
S = #state{mod = Mod, expects = Expects}) ->
check_if_being_reloaded(S),
{NewExpects, CompilerPid} = delete_expect(Mod, {Func, Ari}, Expects),
{noreply, S#state{expects = NewExpects,
reload = {CompilerPid, From}}};
handle_call(history, _From, S = #state{history = undefined}) ->
{reply, [], S};
handle_call(history, _From, S) ->
Expand All @@ -541,21 +549,26 @@ handle_call(stop, _From, S) ->
%% @hidden
handle_cast({add_history, _Item}, S = #state{history = undefined}) ->
{noreply, S};
handle_cast({add_history, Item}, S) ->
NewHistory = case get(meck_reloading) of
undefined -> [Item|S#state.history];
_Pid when is_pid(_Pid) -> S#state.history
end,
{noreply, S#state{history=NewHistory}};
handle_cast({add_history, Item}, S = #state{reload = Reload}) ->
case Reload of
undefined ->
{noreply, S#state{history = [Item | S#state.history]}};
_ ->
% 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) ->
%% XXX: don't do this
Running = get(meck_reloading),
Pid =:= Running andalso put(meck_reloading, undefined),
{noreply, S};
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}.

Expand Down Expand Up @@ -764,28 +777,31 @@ unwind_stack(NewInnerRs, [{meck_loop, [_InnerRs | Rest], Loop} | Stack],
unwind_stack({meck_loop, [NewInnerRs | Rest], Loop}, Stack, true).


store_expect(Mod, FuncAri, Clauses, Expects, From) ->
check_if_being_reloaded(#state{reload = undefined}) ->
ok;
check_if_being_reloaded(_S) ->
erlang:error(concurrent_reload).


store_expect(Mod, FuncAri, Clauses, Expects) ->
NewExpects = dict:store(FuncAri, Clauses, Expects),
compile_expects(Mod, NewExpects, From).
compile_expects(Mod, NewExpects).


delete_expect(Mod, FuncAri, Expects, From) ->
delete_expect(Mod, FuncAri, Expects) ->
NewExpects = dict:erase(FuncAri, Expects),
compile_expects(Mod, NewExpects, From).
compile_expects(Mod, NewExpects).


compile_expects(Mod, Expects, From) ->
compile_expects(Mod, Expects) ->
%% If the recompilation is made by the server that executes a module
%% no module that is called from meck_mod:compile_and_load_forms/2
%% can be mocked by meck.
Pid = spawn_link(fun() ->
Forms = to_forms(Mod, Expects),
_Bin = meck_mod:compile_and_load_forms(Forms),
gen_server:reply(From, ok)
end),
Running = put(meck_reloading, Pid), %% XXX: don't do this.
Running =:= undefined orelse erlang:error(concurrent_reload),
Expects.
CompilerPid = spawn_link(fun() ->
Forms = to_forms(Mod, Expects),
meck_mod:compile_and_load_forms(Forms)
end),
{Expects, CompilerPid}.


update_clause(Expects, FuncAri, ArgsMatcher, RetSpec) ->
Expand Down
40 changes: 23 additions & 17 deletions test/meck_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -857,23 +857,29 @@ stub_all_overridden_by_passthrough_test() ->
?assertEqual(a, meck_test_module:a()),
ok = meck:unload(meck_test_module).

passthrough_file_bif_test() ->
NeverExists = "/proc/0", %% 0 is invalid
AlwaysExists = "/proc/1", %% 1 is init
?assertEqual({error, enoent}, file:read_file_info(NeverExists)),
?assertMatch({ok, _}, file:read_file_info(AlwaysExists)),
{ok, ExistsInfo} = file:read_file_info(AlwaysExists),
{setup,local,
fun() -> ok = meck:new(file, [unstick, passthrough]) end,
fun(_) -> ok = meck:unload(file) end,
?_test(begin
?assertEqual(ok, meck:expect(file, read_file_info, fun
(Path) when Path =:= NeverExists -> {ok, no_info};
(Path) when Path =:= AlwaysExists -> meck:passthrough([Path]) end)),
?assertEqual([], meck:history(file)),
?assertEqual({ok, no_info}, file:read_file_info(NeverExists)),
?assertEqual({ok, ExistsInfo}, file:read_file_info(AlwaysExists))
end)}.
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"),
Expand Down

0 comments on commit 5e63c2f

Please sign in to comment.