Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/onsi/gomega/matchers/contain_element_matcher.go
2880 views
1
// untested sections: 2
2
3
package matchers
4
5
import (
6
"errors"
7
"fmt"
8
"reflect"
9
10
"github.com/onsi/gomega/format"
11
"github.com/onsi/gomega/matchers/internal/miter"
12
)
13
14
type ContainElementMatcher struct {
15
Element any
16
Result []any
17
}
18
19
func (matcher *ContainElementMatcher) Match(actual any) (success bool, err error) {
20
if !isArrayOrSlice(actual) && !isMap(actual) && !miter.IsIter(actual) {
21
return false, fmt.Errorf("ContainElement matcher expects an array/slice/map/iterator. Got:\n%s", format.Object(actual, 1))
22
}
23
24
var actualT reflect.Type
25
var result reflect.Value
26
switch numResultArgs := len(matcher.Result); {
27
case numResultArgs > 1:
28
return false, errors.New("ContainElement matcher expects at most a single optional pointer to store its findings at")
29
case numResultArgs == 1:
30
// Check the optional result arg to point to a single value/array/slice/map
31
// of a type compatible with the actual value.
32
if reflect.ValueOf(matcher.Result[0]).Kind() != reflect.Ptr {
33
return false, fmt.Errorf("ContainElement matcher expects a non-nil pointer to store its findings at. Got\n%s",
34
format.Object(matcher.Result[0], 1))
35
}
36
actualT = reflect.TypeOf(actual)
37
resultReference := matcher.Result[0]
38
result = reflect.ValueOf(resultReference).Elem() // what ResultReference points to, to stash away our findings
39
switch result.Kind() {
40
case reflect.Array: // result arrays are not supported, as they cannot be dynamically sized.
41
if miter.IsIter(actual) {
42
_, actualvT := miter.IterKVTypes(actual)
43
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
44
reflect.SliceOf(actualvT), result.Type().String())
45
}
46
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
47
reflect.SliceOf(actualT.Elem()).String(), result.Type().String())
48
49
case reflect.Slice: // result slice
50
// can we assign elements in actual to elements in what the result
51
// arg points to?
52
// - ✔ actual is an array or slice
53
// - ✔ actual is an iter.Seq producing "v" elements
54
// - ✔ actual is an iter.Seq2 producing "v" elements, ignoring
55
// the "k" elements.
56
switch {
57
case isArrayOrSlice(actual):
58
if !actualT.Elem().AssignableTo(result.Type().Elem()) {
59
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
60
actualT.String(), result.Type().String())
61
}
62
63
case miter.IsIter(actual):
64
_, actualvT := miter.IterKVTypes(actual)
65
if !actualvT.AssignableTo(result.Type().Elem()) {
66
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
67
actualvT.String(), result.Type().String())
68
}
69
70
default: // incompatible result reference
71
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
72
reflect.MapOf(actualT.Key(), actualT.Elem()).String(), result.Type().String())
73
}
74
75
case reflect.Map: // result map
76
// can we assign elements in actual to elements in what the result
77
// arg points to?
78
// - ✔ actual is a map
79
// - ✔ actual is an iter.Seq2 (iter.Seq doesn't fit though)
80
switch {
81
case isMap(actual):
82
if !actualT.AssignableTo(result.Type()) {
83
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
84
actualT.String(), result.Type().String())
85
}
86
87
case miter.IsIter(actual):
88
actualkT, actualvT := miter.IterKVTypes(actual)
89
if actualkT == nil {
90
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
91
reflect.SliceOf(actualvT).String(), result.Type().String())
92
}
93
if !reflect.MapOf(actualkT, actualvT).AssignableTo(result.Type()) {
94
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
95
reflect.MapOf(actualkT, actualvT), result.Type().String())
96
}
97
98
default: // incompatible result reference
99
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
100
actualT.String(), result.Type().String())
101
}
102
103
default:
104
// can we assign a (single) element in actual to what the result arg
105
// points to?
106
switch {
107
case miter.IsIter(actual):
108
_, actualvT := miter.IterKVTypes(actual)
109
if !actualvT.AssignableTo(result.Type()) {
110
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
111
actualvT.String(), result.Type().String())
112
}
113
default:
114
if !actualT.Elem().AssignableTo(result.Type()) {
115
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
116
actualT.Elem().String(), result.Type().String())
117
}
118
}
119
}
120
}
121
122
// If the supplied matcher isn't an Omega matcher, default to the Equal
123
// matcher.
124
elemMatcher, elementIsMatcher := matcher.Element.(omegaMatcher)
125
if !elementIsMatcher {
126
elemMatcher = &EqualMatcher{Expected: matcher.Element}
127
}
128
129
value := reflect.ValueOf(actual)
130
131
var getFindings func() reflect.Value // abstracts how the findings are collected and stored
132
var lastError error
133
134
if !miter.IsIter(actual) {
135
var valueAt func(int) any
136
var foundAt func(int)
137
// We're dealing with an array/slice/map, so in all cases we can iterate
138
// over the elements in actual using indices (that can be considered
139
// keys in case of maps).
140
if isMap(actual) {
141
keys := value.MapKeys()
142
valueAt = func(i int) any {
143
return value.MapIndex(keys[i]).Interface()
144
}
145
if result.Kind() != reflect.Invalid {
146
fm := reflect.MakeMap(actualT)
147
getFindings = func() reflect.Value { return fm }
148
foundAt = func(i int) {
149
fm.SetMapIndex(keys[i], value.MapIndex(keys[i]))
150
}
151
}
152
} else {
153
valueAt = func(i int) any {
154
return value.Index(i).Interface()
155
}
156
if result.Kind() != reflect.Invalid {
157
var fsl reflect.Value
158
if result.Kind() == reflect.Slice {
159
fsl = reflect.MakeSlice(result.Type(), 0, 0)
160
} else {
161
fsl = reflect.MakeSlice(reflect.SliceOf(result.Type()), 0, 0)
162
}
163
getFindings = func() reflect.Value { return fsl }
164
foundAt = func(i int) {
165
fsl = reflect.Append(fsl, value.Index(i))
166
}
167
}
168
}
169
170
for i := 0; i < value.Len(); i++ {
171
elem := valueAt(i)
172
success, err := elemMatcher.Match(elem)
173
if err != nil {
174
lastError = err
175
continue
176
}
177
if success {
178
if result.Kind() == reflect.Invalid {
179
return true, nil
180
}
181
foundAt(i)
182
}
183
}
184
} else {
185
// We're dealing with an iterator as a first-class construct, so things
186
// are slightly different: there is no index defined as in case of
187
// arrays/slices/maps, just "ooooorder"
188
var found func(k, v reflect.Value)
189
if result.Kind() != reflect.Invalid {
190
if result.Kind() == reflect.Map {
191
fm := reflect.MakeMap(result.Type())
192
getFindings = func() reflect.Value { return fm }
193
found = func(k, v reflect.Value) { fm.SetMapIndex(k, v) }
194
} else {
195
var fsl reflect.Value
196
if result.Kind() == reflect.Slice {
197
fsl = reflect.MakeSlice(result.Type(), 0, 0)
198
} else {
199
fsl = reflect.MakeSlice(reflect.SliceOf(result.Type()), 0, 0)
200
}
201
getFindings = func() reflect.Value { return fsl }
202
found = func(_, v reflect.Value) { fsl = reflect.Append(fsl, v) }
203
}
204
}
205
206
success := false
207
actualkT, _ := miter.IterKVTypes(actual)
208
if actualkT == nil {
209
miter.IterateV(actual, func(v reflect.Value) bool {
210
var err error
211
success, err = elemMatcher.Match(v.Interface())
212
if err != nil {
213
lastError = err
214
return true // iterate on...
215
}
216
if success {
217
if result.Kind() == reflect.Invalid {
218
return false // a match and no result needed, so we're done
219
}
220
found(reflect.Value{}, v)
221
}
222
return true // iterate on...
223
})
224
} else {
225
miter.IterateKV(actual, func(k, v reflect.Value) bool {
226
var err error
227
success, err = elemMatcher.Match(v.Interface())
228
if err != nil {
229
lastError = err
230
return true // iterate on...
231
}
232
if success {
233
if result.Kind() == reflect.Invalid {
234
return false // a match and no result needed, so we're done
235
}
236
found(k, v)
237
}
238
return true // iterate on...
239
})
240
}
241
if success && result.Kind() == reflect.Invalid {
242
return true, nil
243
}
244
}
245
246
// when the expectation isn't interested in the findings except for success
247
// or non-success, then we're done here and return the last matcher error
248
// seen, if any, as well as non-success.
249
if result.Kind() == reflect.Invalid {
250
return false, lastError
251
}
252
253
// pick up any findings the test is interested in as it specified a non-nil
254
// result reference. However, the expectation always is that there are at
255
// least one or multiple findings. So, if a result is expected, but we had
256
// no findings, then this is an error.
257
findings := getFindings()
258
if findings.Len() == 0 {
259
return false, lastError
260
}
261
262
// there's just a single finding and the result is neither a slice nor a map
263
// (so it's a scalar): pick the one and only finding and return it in the
264
// place the reference points to.
265
if findings.Len() == 1 && !isArrayOrSlice(result.Interface()) && !isMap(result.Interface()) {
266
if isMap(actual) {
267
miter := findings.MapRange()
268
miter.Next()
269
result.Set(miter.Value())
270
} else {
271
result.Set(findings.Index(0))
272
}
273
return true, nil
274
}
275
276
// at least one or even multiple findings and a the result references a
277
// slice or a map, so all we need to do is to store our findings where the
278
// reference points to.
279
if !findings.Type().AssignableTo(result.Type()) {
280
return false, fmt.Errorf("ContainElement cannot return multiple findings. Need *%s, got *%s",
281
findings.Type().String(), result.Type().String())
282
}
283
result.Set(findings)
284
return true, nil
285
}
286
287
func (matcher *ContainElementMatcher) FailureMessage(actual any) (message string) {
288
return format.Message(actual, "to contain element matching", matcher.Element)
289
}
290
291
func (matcher *ContainElementMatcher) NegatedFailureMessage(actual any) (message string) {
292
return format.Message(actual, "not to contain element matching", matcher.Element)
293
}
294
295