blob: d52495f39e5e85959823ecc0c2d123a0713386c1 [file] [log] [blame]
vinokumaf7605fc2023-06-02 18:08:01 +05301// Copyright 2010 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package gomock
16
17import (
18 "fmt"
19 "reflect"
bseenivadd66c362026-02-12 19:13:26 +053020 "regexp"
vinokumaf7605fc2023-06-02 18:08:01 +053021 "strings"
22)
23
24// A Matcher is a representation of a class of values.
25// It is used to represent the valid or expected arguments to a mocked method.
26type Matcher interface {
27 // Matches returns whether x is a match.
bseenivadd66c362026-02-12 19:13:26 +053028 Matches(x any) bool
vinokumaf7605fc2023-06-02 18:08:01 +053029
30 // String describes what the matcher matches.
31 String() string
32}
33
34// WantFormatter modifies the given Matcher's String() method to the given
35// Stringer. This allows for control on how the "Want" is formatted when
36// printing .
37func WantFormatter(s fmt.Stringer, m Matcher) Matcher {
38 type matcher interface {
bseenivadd66c362026-02-12 19:13:26 +053039 Matches(x any) bool
vinokumaf7605fc2023-06-02 18:08:01 +053040 }
41
42 return struct {
43 matcher
44 fmt.Stringer
45 }{
46 matcher: m,
47 Stringer: s,
48 }
49}
50
51// StringerFunc type is an adapter to allow the use of ordinary functions as
52// a Stringer. If f is a function with the appropriate signature,
53// StringerFunc(f) is a Stringer that calls f.
54type StringerFunc func() string
55
56// String implements fmt.Stringer.
57func (f StringerFunc) String() string {
58 return f()
59}
60
61// GotFormatter is used to better print failure messages. If a matcher
62// implements GotFormatter, it will use the result from Got when printing
63// the failure message.
64type GotFormatter interface {
65 // Got is invoked with the received value. The result is used when
66 // printing the failure message.
bseenivadd66c362026-02-12 19:13:26 +053067 Got(got any) string
vinokumaf7605fc2023-06-02 18:08:01 +053068}
69
70// GotFormatterFunc type is an adapter to allow the use of ordinary
71// functions as a GotFormatter. If f is a function with the appropriate
72// signature, GotFormatterFunc(f) is a GotFormatter that calls f.
bseenivadd66c362026-02-12 19:13:26 +053073type GotFormatterFunc func(got any) string
vinokumaf7605fc2023-06-02 18:08:01 +053074
75// Got implements GotFormatter.
bseenivadd66c362026-02-12 19:13:26 +053076func (f GotFormatterFunc) Got(got any) string {
vinokumaf7605fc2023-06-02 18:08:01 +053077 return f(got)
78}
79
80// GotFormatterAdapter attaches a GotFormatter to a Matcher.
81func GotFormatterAdapter(s GotFormatter, m Matcher) Matcher {
82 return struct {
83 GotFormatter
84 Matcher
85 }{
86 GotFormatter: s,
87 Matcher: m,
88 }
89}
90
91type anyMatcher struct{}
92
bseenivadd66c362026-02-12 19:13:26 +053093func (anyMatcher) Matches(any) bool {
vinokumaf7605fc2023-06-02 18:08:01 +053094 return true
95}
96
97func (anyMatcher) String() string {
98 return "is anything"
99}
100
bseenivadd66c362026-02-12 19:13:26 +0530101type condMatcher[T any] struct {
102 fn func(x T) bool
vinokumaf7605fc2023-06-02 18:08:01 +0530103}
104
bseenivadd66c362026-02-12 19:13:26 +0530105func (c condMatcher[T]) Matches(x any) bool {
106 typed, ok := x.(T)
107 if !ok {
108 return false
109 }
110 return c.fn(typed)
111}
112
113func (c condMatcher[T]) String() string {
114 return "adheres to a custom condition"
115}
116
117type eqMatcher struct {
118 x any
119}
120
121func (e eqMatcher) Matches(x any) bool {
Abhay Kumarfe505f22025-11-10 14:16:31 +0000122 // In case, some value is nil
123 if e.x == nil || x == nil {
124 return reflect.DeepEqual(e.x, x)
125 }
126
127 // Check if types assignable and convert them to common type
128 x1Val := reflect.ValueOf(e.x)
129 x2Val := reflect.ValueOf(x)
130
131 if x1Val.Type().AssignableTo(x2Val.Type()) {
132 x1ValConverted := x1Val.Convert(x2Val.Type())
133 return reflect.DeepEqual(x1ValConverted.Interface(), x2Val.Interface())
134 }
135
136 return false
vinokumaf7605fc2023-06-02 18:08:01 +0530137}
138
139func (e eqMatcher) String() string {
bseenivadd66c362026-02-12 19:13:26 +0530140 return fmt.Sprintf("is equal to %s (%T)", getString(e.x), e.x)
vinokumaf7605fc2023-06-02 18:08:01 +0530141}
142
143type nilMatcher struct{}
144
bseenivadd66c362026-02-12 19:13:26 +0530145func (nilMatcher) Matches(x any) bool {
vinokumaf7605fc2023-06-02 18:08:01 +0530146 if x == nil {
147 return true
148 }
149
150 v := reflect.ValueOf(x)
151 switch v.Kind() {
152 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map,
153 reflect.Ptr, reflect.Slice:
154 return v.IsNil()
155 }
156
157 return false
158}
159
160func (nilMatcher) String() string {
161 return "is nil"
162}
163
164type notMatcher struct {
165 m Matcher
166}
167
bseenivadd66c362026-02-12 19:13:26 +0530168func (n notMatcher) Matches(x any) bool {
vinokumaf7605fc2023-06-02 18:08:01 +0530169 return !n.m.Matches(x)
170}
171
172func (n notMatcher) String() string {
vinokumaf7605fc2023-06-02 18:08:01 +0530173 return "not(" + n.m.String() + ")"
174}
175
bseenivadd66c362026-02-12 19:13:26 +0530176type regexMatcher struct {
177 regex *regexp.Regexp
178}
179
180func (m regexMatcher) Matches(x any) bool {
181 switch t := x.(type) {
182 case string:
183 return m.regex.MatchString(t)
184 case []byte:
185 return m.regex.Match(t)
186 default:
187 return false
188 }
189}
190
191func (m regexMatcher) String() string {
192 return "matches regex " + m.regex.String()
193}
194
vinokumaf7605fc2023-06-02 18:08:01 +0530195type assignableToTypeOfMatcher struct {
196 targetType reflect.Type
197}
198
bseenivadd66c362026-02-12 19:13:26 +0530199func (m assignableToTypeOfMatcher) Matches(x any) bool {
vinokumaf7605fc2023-06-02 18:08:01 +0530200 return reflect.TypeOf(x).AssignableTo(m.targetType)
201}
202
203func (m assignableToTypeOfMatcher) String() string {
204 return "is assignable to " + m.targetType.Name()
205}
206
bseenivadd66c362026-02-12 19:13:26 +0530207type anyOfMatcher struct {
208 matchers []Matcher
209}
210
211func (am anyOfMatcher) Matches(x any) bool {
212 for _, m := range am.matchers {
213 if m.Matches(x) {
214 return true
215 }
216 }
217 return false
218}
219
220func (am anyOfMatcher) String() string {
221 ss := make([]string, 0, len(am.matchers))
222 for _, matcher := range am.matchers {
223 ss = append(ss, matcher.String())
224 }
225 return strings.Join(ss, " | ")
226}
227
vinokumaf7605fc2023-06-02 18:08:01 +0530228type allMatcher struct {
229 matchers []Matcher
230}
231
bseenivadd66c362026-02-12 19:13:26 +0530232func (am allMatcher) Matches(x any) bool {
vinokumaf7605fc2023-06-02 18:08:01 +0530233 for _, m := range am.matchers {
234 if !m.Matches(x) {
235 return false
236 }
237 }
238 return true
239}
240
241func (am allMatcher) String() string {
242 ss := make([]string, 0, len(am.matchers))
243 for _, matcher := range am.matchers {
244 ss = append(ss, matcher.String())
245 }
246 return strings.Join(ss, "; ")
247}
248
249type lenMatcher struct {
250 i int
251}
252
bseenivadd66c362026-02-12 19:13:26 +0530253func (m lenMatcher) Matches(x any) bool {
vinokumaf7605fc2023-06-02 18:08:01 +0530254 v := reflect.ValueOf(x)
255 switch v.Kind() {
256 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
257 return v.Len() == m.i
258 default:
259 return false
260 }
261}
262
263func (m lenMatcher) String() string {
264 return fmt.Sprintf("has length %d", m.i)
265}
266
Abhay Kumarfe505f22025-11-10 14:16:31 +0000267type inAnyOrderMatcher struct {
bseenivadd66c362026-02-12 19:13:26 +0530268 x any
Abhay Kumarfe505f22025-11-10 14:16:31 +0000269}
270
bseenivadd66c362026-02-12 19:13:26 +0530271func (m inAnyOrderMatcher) Matches(x any) bool {
Abhay Kumarfe505f22025-11-10 14:16:31 +0000272 given, ok := m.prepareValue(x)
273 if !ok {
274 return false
275 }
276 wanted, ok := m.prepareValue(m.x)
277 if !ok {
278 return false
279 }
280
281 if given.Len() != wanted.Len() {
282 return false
283 }
284
285 usedFromGiven := make([]bool, given.Len())
286 foundFromWanted := make([]bool, wanted.Len())
287 for i := 0; i < wanted.Len(); i++ {
288 wantedMatcher := Eq(wanted.Index(i).Interface())
289 for j := 0; j < given.Len(); j++ {
290 if usedFromGiven[j] {
291 continue
292 }
293 if wantedMatcher.Matches(given.Index(j).Interface()) {
294 foundFromWanted[i] = true
295 usedFromGiven[j] = true
296 break
297 }
298 }
299 }
300
301 missingFromWanted := 0
302 for _, found := range foundFromWanted {
303 if !found {
304 missingFromWanted++
305 }
306 }
307 extraInGiven := 0
308 for _, used := range usedFromGiven {
309 if !used {
310 extraInGiven++
311 }
312 }
313
314 return extraInGiven == 0 && missingFromWanted == 0
315}
316
bseenivadd66c362026-02-12 19:13:26 +0530317func (m inAnyOrderMatcher) prepareValue(x any) (reflect.Value, bool) {
Abhay Kumarfe505f22025-11-10 14:16:31 +0000318 xValue := reflect.ValueOf(x)
319 switch xValue.Kind() {
320 case reflect.Slice, reflect.Array:
321 return xValue, true
322 default:
323 return reflect.Value{}, false
324 }
325}
326
327func (m inAnyOrderMatcher) String() string {
328 return fmt.Sprintf("has the same elements as %v", m.x)
329}
330
vinokumaf7605fc2023-06-02 18:08:01 +0530331// Constructors
332
333// All returns a composite Matcher that returns true if and only all of the
334// matchers return true.
335func All(ms ...Matcher) Matcher { return allMatcher{ms} }
336
337// Any returns a matcher that always matches.
338func Any() Matcher { return anyMatcher{} }
339
bseenivadd66c362026-02-12 19:13:26 +0530340// Cond returns a matcher that matches when the given function returns true
341// after passing it the parameter to the mock function.
342// This is particularly useful in case you want to match over a field of a custom struct, or dynamic logic.
343//
344// Example usage:
345//
346// Cond(func(x int){return x == 1}).Matches(1) // returns true
347// Cond(func(x int){return x == 2}).Matches(1) // returns false
348func Cond[T any](fn func(x T) bool) Matcher { return condMatcher[T]{fn} }
349
350// AnyOf returns a composite Matcher that returns true if at least one of the
351// matchers returns true.
352//
353// Example usage:
354//
355// AnyOf(1, 2, 3).Matches(2) // returns true
356// AnyOf(1, 2, 3).Matches(10) // returns false
357// AnyOf(Nil(), Len(2)).Matches(nil) // returns true
358// AnyOf(Nil(), Len(2)).Matches("hi") // returns true
359// AnyOf(Nil(), Len(2)).Matches("hello") // returns false
360func AnyOf(xs ...any) Matcher {
361 ms := make([]Matcher, 0, len(xs))
362 for _, x := range xs {
363 if m, ok := x.(Matcher); ok {
364 ms = append(ms, m)
365 } else {
366 ms = append(ms, Eq(x))
367 }
368 }
369 return anyOfMatcher{ms}
370}
371
vinokumaf7605fc2023-06-02 18:08:01 +0530372// Eq returns a matcher that matches on equality.
373//
374// Example usage:
bseenivadd66c362026-02-12 19:13:26 +0530375//
376// Eq(5).Matches(5) // returns true
377// Eq(5).Matches(4) // returns false
378func Eq(x any) Matcher { return eqMatcher{x} }
vinokumaf7605fc2023-06-02 18:08:01 +0530379
380// Len returns a matcher that matches on length. This matcher returns false if
381// is compared to a type that is not an array, chan, map, slice, or string.
382func Len(i int) Matcher {
383 return lenMatcher{i}
384}
385
386// Nil returns a matcher that matches if the received value is nil.
387//
388// Example usage:
bseenivadd66c362026-02-12 19:13:26 +0530389//
390// var x *bytes.Buffer
391// Nil().Matches(x) // returns true
392// x = &bytes.Buffer{}
393// Nil().Matches(x) // returns false
vinokumaf7605fc2023-06-02 18:08:01 +0530394func Nil() Matcher { return nilMatcher{} }
395
396// Not reverses the results of its given child matcher.
397//
398// Example usage:
bseenivadd66c362026-02-12 19:13:26 +0530399//
400// Not(Eq(5)).Matches(4) // returns true
401// Not(Eq(5)).Matches(5) // returns false
402func Not(x any) Matcher {
vinokumaf7605fc2023-06-02 18:08:01 +0530403 if m, ok := x.(Matcher); ok {
404 return notMatcher{m}
405 }
406 return notMatcher{Eq(x)}
407}
408
bseenivadd66c362026-02-12 19:13:26 +0530409// Regex checks whether parameter matches the associated regex.
410//
411// Example usage:
412//
413// Regex("[0-9]{2}:[0-9]{2}").Matches("23:02") // returns true
414// Regex("[0-9]{2}:[0-9]{2}").Matches([]byte{'2', '3', ':', '0', '2'}) // returns true
415// Regex("[0-9]{2}:[0-9]{2}").Matches("hello world") // returns false
416// Regex("[0-9]{2}").Matches(21) // returns false as it's not a valid type
417func Regex(regexStr string) Matcher {
418 return regexMatcher{regex: regexp.MustCompile(regexStr)}
419}
420
vinokumaf7605fc2023-06-02 18:08:01 +0530421// AssignableToTypeOf is a Matcher that matches if the parameter to the mock
422// function is assignable to the type of the parameter to this function.
423//
424// Example usage:
vinokumaf7605fc2023-06-02 18:08:01 +0530425//
bseenivadd66c362026-02-12 19:13:26 +0530426// var s fmt.Stringer = &bytes.Buffer{}
427// AssignableToTypeOf(s).Matches(time.Second) // returns true
428// AssignableToTypeOf(s).Matches(99) // returns false
429//
430// var ctx = reflect.TypeOf((*context.Context)(nil)).Elem()
431// AssignableToTypeOf(ctx).Matches(context.Background()) // returns true
432func AssignableToTypeOf(x any) Matcher {
vinokumaf7605fc2023-06-02 18:08:01 +0530433 if xt, ok := x.(reflect.Type); ok {
434 return assignableToTypeOfMatcher{xt}
435 }
436 return assignableToTypeOfMatcher{reflect.TypeOf(x)}
437}
Abhay Kumarfe505f22025-11-10 14:16:31 +0000438
439// InAnyOrder is a Matcher that returns true for collections of the same elements ignoring the order.
440//
441// Example usage:
bseenivadd66c362026-02-12 19:13:26 +0530442//
443// InAnyOrder([]int{1, 2, 3}).Matches([]int{1, 3, 2}) // returns true
444// InAnyOrder([]int{1, 2, 3}).Matches([]int{1, 2}) // returns false
445func InAnyOrder(x any) Matcher {
Abhay Kumarfe505f22025-11-10 14:16:31 +0000446 return inAnyOrderMatcher{x}
447}