podman

Форк
0
165 строк · 4.0 Кб
1
// untested sections: 6
2

3
package gstruct
4

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

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

17
//MatchAllFields succeeds if every field of a struct matches the field matcher associated with
18
//it, and every element matcher is matched.
19
//    actual := struct{
20
//      A int
21
//      B []bool
22
//      C string
23
//    }{
24
//      A: 5,
25
//      B: []bool{true, false},
26
//      C: "foo",
27
//    }
28
//
29
//    Expect(actual).To(MatchAllFields(Fields{
30
//      "A": Equal(5),
31
//      "B": ConsistOf(true, false),
32
//      "C": Equal("foo"),
33
//    }))
34
func MatchAllFields(fields Fields) types.GomegaMatcher {
35
	return &FieldsMatcher{
36
		Fields: fields,
37
	}
38
}
39

40
//MatchFields succeeds if each element of a struct matches the field matcher associated with
41
//it. It can ignore extra fields and/or missing fields.
42
//    actual := struct{
43
//      A int
44
//      B []bool
45
//      C string
46
//    }{
47
//      A: 5,
48
//      B: []bool{true, false},
49
//      C: "foo",
50
//    }
51
//
52
//    Expect(actual).To(MatchFields(IgnoreExtras, Fields{
53
//      "A": Equal(5),
54
//      "B": ConsistOf(true, false),
55
//    }))
56
//    Expect(actual).To(MatchFields(IgnoreMissing, Fields{
57
//      "A": Equal(5),
58
//      "B": ConsistOf(true, false),
59
//      "C": Equal("foo"),
60
//      "D": Equal("extra"),
61
//    }))
62
func MatchFields(options Options, fields Fields) types.GomegaMatcher {
63
	return &FieldsMatcher{
64
		Fields:        fields,
65
		IgnoreExtras:  options&IgnoreExtras != 0,
66
		IgnoreMissing: options&IgnoreMissing != 0,
67
	}
68
}
69

70
type FieldsMatcher struct {
71
	// Matchers for each field.
72
	Fields Fields
73

74
	// Whether to ignore extra elements or consider it an error.
75
	IgnoreExtras bool
76
	// Whether to ignore missing elements or consider it an error.
77
	IgnoreMissing bool
78

79
	// State.
80
	failures []error
81
}
82

83
// Field name to matcher.
84
type Fields map[string]types.GomegaMatcher
85

86
func (m *FieldsMatcher) Match(actual interface{}) (success bool, err error) {
87
	if reflect.TypeOf(actual).Kind() != reflect.Struct {
88
		return false, fmt.Errorf("%v is type %T, expected struct", actual, actual)
89
	}
90

91
	m.failures = m.matchFields(actual)
92
	if len(m.failures) > 0 {
93
		return false, nil
94
	}
95
	return true, nil
96
}
97

98
func (m *FieldsMatcher) matchFields(actual interface{}) (errs []error) {
99
	val := reflect.ValueOf(actual)
100
	typ := val.Type()
101
	fields := map[string]bool{}
102
	for i := 0; i < val.NumField(); i++ {
103
		fieldName := typ.Field(i).Name
104
		fields[fieldName] = true
105

106
		err := func() (err error) {
107
			// This test relies heavily on reflect, which tends to panic.
108
			// Recover here to provide more useful error messages in that case.
109
			defer func() {
110
				if r := recover(); r != nil {
111
					err = fmt.Errorf("panic checking %+v: %v\n%s", actual, r, debug.Stack())
112
				}
113
			}()
114

115
			matcher, expected := m.Fields[fieldName]
116
			if !expected {
117
				if !m.IgnoreExtras {
118
					return fmt.Errorf("unexpected field %s: %+v", fieldName, actual)
119
				}
120
				return nil
121
			}
122

123
			field := val.Field(i).Interface()
124

125
			match, err := matcher.Match(field)
126
			if err != nil {
127
				return err
128
			} else if !match {
129
				if nesting, ok := matcher.(errorsutil.NestingMatcher); ok {
130
					return errorsutil.AggregateError(nesting.Failures())
131
				}
132
				return errors.New(matcher.FailureMessage(field))
133
			}
134
			return nil
135
		}()
136
		if err != nil {
137
			errs = append(errs, errorsutil.Nest("."+fieldName, err))
138
		}
139
	}
140

141
	for field := range m.Fields {
142
		if !fields[field] && !m.IgnoreMissing {
143
			errs = append(errs, fmt.Errorf("missing expected field %s", field))
144
		}
145
	}
146

147
	return errs
148
}
149

150
func (m *FieldsMatcher) FailureMessage(actual interface{}) (message string) {
151
	failures := make([]string, len(m.failures))
152
	for i := range m.failures {
153
		failures[i] = m.failures[i].Error()
154
	}
155
	return format.Message(reflect.TypeOf(actual).Name(),
156
		fmt.Sprintf("to match fields: {\n%v\n}\n", strings.Join(failures, "\n")))
157
}
158

159
func (m *FieldsMatcher) NegatedFailureMessage(actual interface{}) (message string) {
160
	return format.Message(actual, "not to match fields")
161
}
162

163
func (m *FieldsMatcher) Failures() []error {
164
	return m.failures
165
}
166

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

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

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

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