Skip to content

Commit

Permalink
Experimental first pass at ameliorating #15
Browse files Browse the repository at this point in the history
Compares and reports on different arguments at top level.
Could be nice in future to recursively descend non-scalar types to
find which sub-elements differ.
  • Loading branch information
MattVonVielen committed Jan 21, 2016
1 parent 166e711 commit c802dae
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 9 deletions.
23 changes: 22 additions & 1 deletion src/hoax_invocation.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ handle(M, F, Args) ->
Records ->
case find_matching_args(Args, Records) of
false ->
erlang:error({unexpected_arguments, hoax_fmt:fmt({M, F, Args})});
unexpected_arguments(M, F, Args, Records);
#expectation{call_count=X,expected_count=X,expected_args=ExpectedArgs} ->
erlang:error({too_many_invocations, X+1, hoax_fmt:fmt({M, F, ExpectedArgs})});
#expectation{action = Action} = Record ->
Expand All @@ -19,6 +19,24 @@ handle(M, F, Args) ->
end
end.

% As the most-common case, handle a single expectation separately to provide better error detail
unexpected_arguments(M, F, Args, [#expectation{expected_args = Expected}]) ->
Arity = length(Args),
FuncRep = flatfmt("~s:~s/~b", [M, F, Arity]),
ArgsNotMatched = lists:foldl(
fun ({_, ExpectedArg, ActualArg}, Acc) when ExpectedArg == ActualArg ->
Acc;
({Seq, ExpectedArg, ActualArg}, Acc) ->
Info = flatfmt("parameter ~b expected ~p but got ~p", [Seq, ExpectedArg, ActualArg]),
[Info | Acc]
end,
[],
lists:zip3(lists:seq(1, Arity), Expected, Args)
),
erlang:error({unexpected_arguments, [FuncRep | lists:reverse(ArgsNotMatched)]});
unexpected_arguments(M, F, Args, Records) when length(Records) > 1 ->
erlang:error({unexpected_arguments, hoax_fmt:fmt({M, F, Args})}).

find_matching_args(Args, Records) ->
keyfind(Args, Records).

Expand All @@ -41,3 +59,6 @@ replace_wildcards(ActualArgs, ExpectedArgs) ->

replace_wildcard(Actual, '_') -> Actual;
replace_wildcard(_, Expected) -> Expected.

flatfmt(Fmt, Args) ->
lists:flatten(io_lib:format(Fmt, Args)).
23 changes: 15 additions & 8 deletions test/hoax_invocation_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
-compile([export_all]).

-include_lib("eunit/include/eunit.hrl").
-include_lib("hoax/include/hoax.hrl").
-include("../src/hoax_int.hrl").

-define(EXPORTS, [{f,2},{g,1},{h,0}]).
Expand Down Expand Up @@ -70,16 +69,24 @@ should_exit_with_expected_error_when_args_match() ->

?assertExit(an_error, hoax_invocation:handle(m, f, [1, 2])).

should_throw_when_args_do_not_match() ->
hoax_tab:insert(?EXPECT(f, [1,2], {return, a_result})),
should_throw_when_args_do_not_match_and_exactly_1_expectation() ->
hoax_tab:insert(?EXPECT(f, [1,2,3,4], {return, a_result})),

ExpectedError = {unexpected_arguments, "m:f(1,a)"},
?assertError(ExpectedError, hoax_invocation:handle(m, f, [1, a])).
ExpectedError = {unexpected_arguments, ["m:f/4", "parameter 2 expected 2 but got a", "parameter 4 expected 4 but got z"]},
% try
%
% catch error:ActualError ->
% ?debugFmt("ExpectedError:: ~p", [ExpectedError]),
% ?debugFmt("ActualError:: ~p", [ActualError]),
% ?assert(false)
% end.
?assertError(ExpectedError, hoax_invocation:handle(m, f, [1, a, 3, z])).

should_throw_when_args_do_not_match_wildcard_pattern() ->
hoax_tab:insert(?EXPECT(f, ['_',2], {return, a_result})),
should_throw_when_args_do_not_match_and_multiple_expectations() ->
hoax_tab:insert(?EXPECT(f, [1,2], {return, a_result})),
hoax_tab:insert(?EXPECT(f, [1,7], {return, a_result})),

ExpectedError = {unexpected_arguments, "m:f(1,a)"},
ExpectedError = {unexpected_arguments, "m:f(1,a)"},
?assertError(ExpectedError, hoax_invocation:handle(m, f, [1, a])).

should_throw_when_call_count_equals_expected_count() ->
Expand Down

0 comments on commit c802dae

Please sign in to comment.