initial commit
[govpp.git] / vendor / github.com / onsi / gomega / gstruct / fields.go
1 package gstruct
2
3 import (
4         "errors"
5         "fmt"
6         "reflect"
7         "runtime/debug"
8         "strings"
9
10         "github.com/onsi/gomega/format"
11         errorsutil "github.com/onsi/gomega/gstruct/errors"
12         "github.com/onsi/gomega/types"
13 )
14
15 //MatchAllFields succeeds if every field of a struct matches the field matcher associated with
16 //it, and every element matcher is matched.
17 //  Expect([]string{"a", "b"}).To(MatchAllFields(idFn, gstruct.Fields{
18 //      "a": BeEqual("a"),
19 //      "b": BeEqual("b"),
20 //  })
21 func MatchAllFields(fields Fields) types.GomegaMatcher {
22         return &FieldsMatcher{
23                 Fields: fields,
24         }
25 }
26
27 //MatchFields succeeds if each element of a struct matches the field matcher associated with
28 //it. It can ignore extra fields and/or missing fields.
29 //  Expect([]string{"a", "c"}).To(MatchFields(idFn, IgnoreMissing|IgnoreExtra, gstruct.Fields{
30 //      "a": BeEqual("a")
31 //      "b": BeEqual("b"),
32 //  })
33 func MatchFields(options Options, fields Fields) types.GomegaMatcher {
34         return &FieldsMatcher{
35                 Fields:        fields,
36                 IgnoreExtras:  options&IgnoreExtras != 0,
37                 IgnoreMissing: options&IgnoreMissing != 0,
38         }
39 }
40
41 type FieldsMatcher struct {
42         // Matchers for each field.
43         Fields Fields
44
45         // Whether to ignore extra elements or consider it an error.
46         IgnoreExtras bool
47         // Whether to ignore missing elements or consider it an error.
48         IgnoreMissing bool
49
50         // State.
51         failures []error
52 }
53
54 // Field name to matcher.
55 type Fields map[string]types.GomegaMatcher
56
57 func (m *FieldsMatcher) Match(actual interface{}) (success bool, err error) {
58         if reflect.TypeOf(actual).Kind() != reflect.Struct {
59                 return false, fmt.Errorf("%v is type %T, expected struct", actual, actual)
60         }
61
62         m.failures = m.matchFields(actual)
63         if len(m.failures) > 0 {
64                 return false, nil
65         }
66         return true, nil
67 }
68
69 func (m *FieldsMatcher) matchFields(actual interface{}) (errs []error) {
70         val := reflect.ValueOf(actual)
71         typ := val.Type()
72         fields := map[string]bool{}
73         for i := 0; i < val.NumField(); i++ {
74                 fieldName := typ.Field(i).Name
75                 fields[fieldName] = true
76
77                 err := func() (err error) {
78                         // This test relies heavily on reflect, which tends to panic.
79                         // Recover here to provide more useful error messages in that case.
80                         defer func() {
81                                 if r := recover(); r != nil {
82                                         err = fmt.Errorf("panic checking %+v: %v\n%s", actual, r, debug.Stack())
83                                 }
84                         }()
85
86                         matcher, expected := m.Fields[fieldName]
87                         if !expected {
88                                 if !m.IgnoreExtras {
89                                         return fmt.Errorf("unexpected field %s: %+v", fieldName, actual)
90                                 }
91                                 return nil
92                         }
93
94                         var field interface{}
95                         if val.Field(i).IsValid() {
96                                 field = val.Field(i).Interface()
97                         } else {
98                                 field = reflect.Zero(typ.Field(i).Type)
99                         }
100
101                         match, err := matcher.Match(field)
102                         if err != nil {
103                                 return err
104                         } else if !match {
105                                 if nesting, ok := matcher.(errorsutil.NestingMatcher); ok {
106                                         return errorsutil.AggregateError(nesting.Failures())
107                                 }
108                                 return errors.New(matcher.FailureMessage(field))
109                         }
110                         return nil
111                 }()
112                 if err != nil {
113                         errs = append(errs, errorsutil.Nest("."+fieldName, err))
114                 }
115         }
116
117         for field := range m.Fields {
118                 if !fields[field] && !m.IgnoreMissing {
119                         errs = append(errs, fmt.Errorf("missing expected field %s", field))
120                 }
121         }
122
123         return errs
124 }
125
126 func (m *FieldsMatcher) FailureMessage(actual interface{}) (message string) {
127         failures := make([]string, len(m.failures))
128         for i := range m.failures {
129                 failures[i] = m.failures[i].Error()
130         }
131         return format.Message(reflect.TypeOf(actual).Name(),
132                 fmt.Sprintf("to match fields: {\n%v\n}\n", strings.Join(failures, "\n")))
133 }
134
135 func (m *FieldsMatcher) NegatedFailureMessage(actual interface{}) (message string) {
136         return format.Message(actual, "not to match fields")
137 }
138
139 func (m *FieldsMatcher) Failures() []error {
140         return m.failures
141 }