podman

Форк
0
231 строка · 6.9 Кб
1
// untested sections: 6
2

3
package gstruct
4

5
import (
6
	"errors"
7
	"fmt"
8
	"reflect"
9
	"runtime/debug"
10
	"strconv"
11

12
	"github.com/onsi/gomega/format"
13
	errorsutil "github.com/onsi/gomega/gstruct/errors"
14
	"github.com/onsi/gomega/types"
15
)
16

17
//MatchAllElements succeeds if every element of a slice matches the element matcher it maps to
18
//through the id function, and every element matcher is matched.
19
//    idFn := func(element interface{}) string {
20
//        return fmt.Sprintf("%v", element)
21
//    }
22
//
23
//    Expect([]string{"a", "b"}).To(MatchAllElements(idFn, Elements{
24
//        "a": Equal("a"),
25
//        "b": Equal("b"),
26
//    }))
27
func MatchAllElements(identifier Identifier, elements Elements) types.GomegaMatcher {
28
	return &ElementsMatcher{
29
		Identifier: identifier,
30
		Elements:   elements,
31
	}
32
}
33

34
//MatchAllElementsWithIndex succeeds if every element of a slice matches the element matcher it maps to
35
//through the id with index function, and every element matcher is matched.
36
//    idFn := func(index int, element interface{}) string {
37
//        return strconv.Itoa(index)
38
//    }
39
//
40
//    Expect([]string{"a", "b"}).To(MatchAllElements(idFn, Elements{
41
//        "0": Equal("a"),
42
//        "1": Equal("b"),
43
//    }))
44
func MatchAllElementsWithIndex(identifier IdentifierWithIndex, elements Elements) types.GomegaMatcher {
45
	return &ElementsMatcher{
46
		Identifier: identifier,
47
		Elements:   elements,
48
	}
49
}
50

51
//MatchElements succeeds if each element of a slice matches the element matcher it maps to
52
//through the id function. It can ignore extra elements and/or missing elements.
53
//    idFn := func(element interface{}) string {
54
//        return fmt.Sprintf("%v", element)
55
//    }
56
//
57
//    Expect([]string{"a", "b", "c"}).To(MatchElements(idFn, IgnoreExtras, Elements{
58
//        "a": Equal("a"),
59
//        "b": Equal("b"),
60
//    }))
61
//    Expect([]string{"a", "c"}).To(MatchElements(idFn, IgnoreMissing, Elements{
62
//        "a": Equal("a"),
63
//        "b": Equal("b"),
64
//        "c": Equal("c"),
65
//        "d": Equal("d"),
66
//    }))
67
func MatchElements(identifier Identifier, options Options, elements Elements) types.GomegaMatcher {
68
	return &ElementsMatcher{
69
		Identifier:      identifier,
70
		Elements:        elements,
71
		IgnoreExtras:    options&IgnoreExtras != 0,
72
		IgnoreMissing:   options&IgnoreMissing != 0,
73
		AllowDuplicates: options&AllowDuplicates != 0,
74
	}
75
}
76

77
//MatchElementsWithIndex succeeds if each element of a slice matches the element matcher it maps to
78
//through the id with index function. It can ignore extra elements and/or missing elements.
79
//    idFn := func(index int, element interface{}) string {
80
//        return strconv.Itoa(index)
81
//    }
82
//
83
//    Expect([]string{"a", "b", "c"}).To(MatchElements(idFn, IgnoreExtras, Elements{
84
//        "0": Equal("a"),
85
//        "1": Equal("b"),
86
//    }))
87
//    Expect([]string{"a", "c"}).To(MatchElements(idFn, IgnoreMissing, Elements{
88
//        "0": Equal("a"),
89
//        "1": Equal("b"),
90
//        "2": Equal("c"),
91
//        "3": Equal("d"),
92
//    }))
93
func MatchElementsWithIndex(identifier IdentifierWithIndex, options Options, elements Elements) types.GomegaMatcher {
94
	return &ElementsMatcher{
95
		Identifier:      identifier,
96
		Elements:        elements,
97
		IgnoreExtras:    options&IgnoreExtras != 0,
98
		IgnoreMissing:   options&IgnoreMissing != 0,
99
		AllowDuplicates: options&AllowDuplicates != 0,
100
	}
101
}
102

103
// ElementsMatcher is a NestingMatcher that applies custom matchers to each element of a slice mapped
104
// by the Identifier function.
105
// TODO: Extend this to work with arrays & maps (map the key) as well.
106
type ElementsMatcher struct {
107
	// Matchers for each element.
108
	Elements Elements
109
	// Function mapping an element to the string key identifying its matcher.
110
	Identifier Identify
111

112
	// Whether to ignore extra elements or consider it an error.
113
	IgnoreExtras bool
114
	// Whether to ignore missing elements or consider it an error.
115
	IgnoreMissing bool
116
	// Whether to key duplicates when matching IDs.
117
	AllowDuplicates bool
118

119
	// State.
120
	failures []error
121
}
122

123
// Element ID to matcher.
124
type Elements map[string]types.GomegaMatcher
125

126
// Function for identifying (mapping) elements.
127
type Identifier func(element interface{}) string
128

129
// Calls the underlying fucntion with the provided params.
130
// Identifier drops the index.
131
func (i Identifier) WithIndexAndElement(index int, element interface{}) string {
132
	return i(element)
133
}
134

135
// Uses the index and element to generate an element name
136
type IdentifierWithIndex func(index int, element interface{}) string
137

138
// Calls the underlying fucntion with the provided params.
139
// IdentifierWithIndex uses the index.
140
func (i IdentifierWithIndex) WithIndexAndElement(index int, element interface{}) string {
141
	return i(index, element)
142
}
143

144
// Interface for identifing the element
145
type Identify interface {
146
	WithIndexAndElement(i int, element interface{}) string
147
}
148

149
// IndexIdentity is a helper function for using an index as
150
// the key in the element map
151
func IndexIdentity(index int, _ interface{}) string {
152
	return strconv.Itoa(index)
153
}
154

155
func (m *ElementsMatcher) Match(actual interface{}) (success bool, err error) {
156
	if reflect.TypeOf(actual).Kind() != reflect.Slice {
157
		return false, fmt.Errorf("%v is type %T, expected slice", actual, actual)
158
	}
159

160
	m.failures = m.matchElements(actual)
161
	if len(m.failures) > 0 {
162
		return false, nil
163
	}
164
	return true, nil
165
}
166

167
func (m *ElementsMatcher) matchElements(actual interface{}) (errs []error) {
168
	// Provide more useful error messages in the case of a panic.
169
	defer func() {
170
		if err := recover(); err != nil {
171
			errs = append(errs, fmt.Errorf("panic checking %+v: %v\n%s", actual, err, debug.Stack()))
172
		}
173
	}()
174

175
	val := reflect.ValueOf(actual)
176
	elements := map[string]bool{}
177
	for i := 0; i < val.Len(); i++ {
178
		element := val.Index(i).Interface()
179
		id := m.Identifier.WithIndexAndElement(i, element)
180
		if elements[id] {
181
			if !m.AllowDuplicates {
182
				errs = append(errs, fmt.Errorf("found duplicate element ID %s", id))
183
				continue
184
			}
185
		}
186
		elements[id] = true
187

188
		matcher, expected := m.Elements[id]
189
		if !expected {
190
			if !m.IgnoreExtras {
191
				errs = append(errs, fmt.Errorf("unexpected element %s", id))
192
			}
193
			continue
194
		}
195

196
		match, err := matcher.Match(element)
197
		if match {
198
			continue
199
		}
200

201
		if err == nil {
202
			if nesting, ok := matcher.(errorsutil.NestingMatcher); ok {
203
				err = errorsutil.AggregateError(nesting.Failures())
204
			} else {
205
				err = errors.New(matcher.FailureMessage(element))
206
			}
207
		}
208
		errs = append(errs, errorsutil.Nest(fmt.Sprintf("[%s]", id), err))
209
	}
210

211
	for id := range m.Elements {
212
		if !elements[id] && !m.IgnoreMissing {
213
			errs = append(errs, fmt.Errorf("missing expected element %s", id))
214
		}
215
	}
216

217
	return errs
218
}
219

220
func (m *ElementsMatcher) FailureMessage(actual interface{}) (message string) {
221
	failure := errorsutil.AggregateError(m.failures)
222
	return format.Message(actual, fmt.Sprintf("to match elements: %v", failure))
223
}
224

225
func (m *ElementsMatcher) NegatedFailureMessage(actual interface{}) (message string) {
226
	return format.Message(actual, "not to match elements")
227
}
228

229
func (m *ElementsMatcher) Failures() []error {
230
	return m.failures
231
}
232

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.