1%%%============================================================================ 2%%% Licensed under the Apache License, Version 2.0 (the "License"); 3%%% you may not use this file except in compliance with the License. 4%%% You may obtain a copy of the License at 5%%% 6%%% http://www.apache.org/licenses/LICENSE-2.0 7%%% 8%%% Unless required by applicable law or agreed to in writing, software 9%%% distributed under the License is distributed on an "AS IS" BASIS, 10%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11%%% See the License for the specific language governing permissions and 12%%% limitations under the License. 13%%%============================================================================ 14 15%%% @private 16%%% @doc Provides expectation processing functions. 17-module(meck_expect). 18 19%% API 20-export_type([func_ari/0]). 21-export_type([expect/0]). 22 23-export([new/2]). 24-export([new/3]). 25-export([new_passthrough/1]). 26-export([new_dummy/2]). 27-export([func_ari/1]). 28-export([is_passthrough/1]). 29-export([fetch_result/2]). 30 31%%%============================================================================ 32%%% Types 33%%%============================================================================ 34 35-type func_clause_spec() :: {meck_args_matcher:args_spec(), 36 meck_ret_spec:ret_spec()}. 37 38-type func_clause() :: {meck_args_matcher:args_matcher(), 39 meck_ret_spec:ret_spec()}. 40 41-type func_ari() :: {Func::atom(), Ari::byte()}. 42-opaque expect() :: {func_ari(), [func_clause()]}. 43 44%%%============================================================================ 45%%% API 46%%%============================================================================ 47 48-spec new(Func::atom(), fun() | func_clause_spec()) -> expect(). 49new(Func, StubFun) when is_function(StubFun) -> 50 {arity, Arity} = erlang:fun_info(StubFun, arity), 51 Clause = {meck_args_matcher:new(Arity), meck_ret_spec:exec(StubFun)}, 52 {{Func, Arity}, [Clause]}; 53new(Func, ClauseSpecs) when is_list(ClauseSpecs) -> 54 {Arity, Clauses} = parse_clause_specs(ClauseSpecs), 55 {{Func, Arity}, Clauses}. 56 57-spec new(Func::atom(), 58 meck_args_matcher:args_spec(), 59 meck_ret_spec:ret_spec()) -> 60 expect(). 61new(Func, ArgsSpec, RetSpec) -> 62 {Ari, Clause} = parse_clause_spec({ArgsSpec, RetSpec}), 63 {{Func, Ari}, [Clause]}. 64 65-spec new_passthrough(func_ari()) -> expect(). 66new_passthrough({Func, Ari}) -> 67 {{Func, Ari}, [{meck_args_matcher:new(Ari), meck_ret_spec:passthrough()}]}. 68 69-spec new_dummy(func_ari(), meck_ret_spec:ret_spec()) -> expect(). 70new_dummy({Func, Ari}, RetSpec) -> 71 {{Func, Ari}, [{meck_args_matcher:new(Ari), RetSpec}]}. 72 73-spec func_ari(expect()) -> func_ari(). 74func_ari({FuncAri, _Clauses}) -> 75 FuncAri. 76 77-spec is_passthrough(expect()) -> boolean(). 78is_passthrough({_FuncAri, [{_Matcher, RetSpec}]}) -> 79 meck_ret_spec:is_passthrough(RetSpec); 80is_passthrough(_) -> 81 false. 82 83-spec fetch_result(Args::[any()], expect()) -> 84 {undefined, unchanged} | 85 {meck_ret_spec:result_spec(), unchanged} | 86 {meck_ret_spec:result_spec(), NewExpect::expect()}. 87fetch_result(Args, {FuncAri, Clauses}) -> 88 case find_matching_clause(Args, Clauses) of 89 not_found -> 90 {undefined, unchanged}; 91 {ArgsMatcher, RetSpec} -> 92 case meck_ret_spec:retrieve_result(RetSpec) of 93 {ResultSpec, unchanged} -> 94 {ResultSpec, unchanged}; 95 {ResultSpec, NewRetSpec} -> 96 NewClauses = lists:keyreplace(ArgsMatcher, 1, Clauses, 97 {ArgsMatcher, NewRetSpec}), 98 {ResultSpec, {FuncAri, NewClauses}} 99 end 100 end. 101 102%%%============================================================================ 103%%% Internal functions 104%%%============================================================================ 105 106-spec parse_clause_specs([func_clause_spec()]) -> {Ari::byte(), [func_clause()]}. 107parse_clause_specs([ClauseSpec | Rest]) -> 108 {Ari, Clause} = parse_clause_spec(ClauseSpec), 109 parse_clause_specs(Rest, Ari, [Clause]). 110 111-spec parse_clause_specs([func_clause_spec()], 112 FirstClauseAri::byte(), 113 Clauses::[func_clause()]) -> 114 {Ari::byte(), [func_clause()]}. 115parse_clause_specs([ClauseSpec | Rest], FirstClauseAri, Clauses) -> 116 {Ari, Clause} = parse_clause_spec(ClauseSpec), 117 case Ari of 118 FirstClauseAri -> 119 parse_clause_specs(Rest, FirstClauseAri, [Clause | Clauses]); 120 _ -> 121 erlang:error({invalid_arity, {{expected, FirstClauseAri}, 122 {actual, Ari}, 123 {clause, ClauseSpec}}}) 124 end; 125parse_clause_specs([], FirstClauseAri, Clauses) -> 126 {FirstClauseAri, lists:reverse(Clauses)}. 127 128-spec parse_clause_spec(func_clause_spec()) -> 129 {Ari::byte(), func_clause()}. 130parse_clause_spec({ArgsSpec, RetSpec}) -> 131 ArgsMatcher = meck_args_matcher:new(ArgsSpec), 132 Ari = meck_args_matcher:arity(ArgsMatcher), 133 Clause = {ArgsMatcher, RetSpec}, 134 {Ari, Clause}. 135 136-spec find_matching_clause(Args::[any()], Defined::[func_clause()]) -> 137 Matching::func_clause() | not_found. 138find_matching_clause(Args, [{ArgsMatcher, RetSpec} | Rest]) -> 139 case meck_args_matcher:match(Args, ArgsMatcher) of 140 true -> 141 {ArgsMatcher, RetSpec}; 142 _Else -> 143 find_matching_clause(Args, Rest) 144 end; 145find_matching_clause(_Args, []) -> 146 not_found. 147