func (m *FieldsMatcher) matchFields(actual interface{}) (errs []error) { val := reflect.ValueOf(actual) typ := val.Type() fields := map[string]bool{} for i := 0; i < val.NumField(); i++ { fieldName := typ.Field(i).Name fields[fieldName] = true err := func() (err error) { // This test relies heavily on reflect, which tends to panic. // Recover here to provide more useful error messages in that case. defer func() { if r := recover(); r != nil { err = fmt.Errorf("panic checking %+v: %v\n%s", actual, r, debug.Stack()) } }() matcher, expected := m.Fields[fieldName] if !expected { if !m.IgnoreExtras { return fmt.Errorf("unexpected field %s: %+v", fieldName, actual) } return nil } var field interface{} if val.Field(i).IsValid() { field = val.Field(i).Interface() } else { field = reflect.Zero(typ.Field(i).Type) } match, err := matcher.Match(field) if err != nil { return err } else if !match { if nesting, ok := matcher.(errorsutil.NestingMatcher); ok { return errorsutil.AggregateError(nesting.Failures()) } return errors.New(matcher.FailureMessage(field)) } return nil }() if err != nil { errs = append(errs, errorsutil.Nest("."+fieldName, err)) } } for field := range m.Fields { if !fields[field] && !m.IgnoreMissing { errs = append(errs, fmt.Errorf("missing expected field %s", field)) } } return errs }
func (m *ElementsMatcher) matchElements(actual interface{}) (errs []error) { // Provide more useful error messages in the case of a panic. defer func() { if err := recover(); err != nil { errs = append(errs, fmt.Errorf("panic checking %+v: %v\n%s", actual, err, debug.Stack())) } }() val := reflect.ValueOf(actual) elements := map[string]bool{} for i := 0; i < val.Len(); i++ { element := val.Index(i).Interface() id := m.Identifier(element) if elements[id] { if !m.AllowDuplicates { errs = append(errs, fmt.Errorf("found duplicate element ID %s", id)) continue } } elements[id] = true matcher, expected := m.Elements[id] if !expected { if !m.IgnoreExtras { errs = append(errs, fmt.Errorf("unexpected element %s", id)) } continue } match, err := matcher.Match(element) if match { continue } if err == nil { if nesting, ok := matcher.(errorsutil.NestingMatcher); ok { err = errorsutil.AggregateError(nesting.Failures()) } else { err = errors.New(matcher.FailureMessage(element)) } } errs = append(errs, errorsutil.Nest(fmt.Sprintf("[%s]", id), err)) } for id := range m.Elements { if !elements[id] && !m.IgnoreMissing { errs = append(errs, fmt.Errorf("missing expected element %s", id)) } } return errs }