You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

66 lines
1.9 KiB

  1. package matchers
  2. import (
  3. "fmt"
  4. "reflect"
  5. "github.com/onsi/gomega/format"
  6. )
  7. type SatisfyMatcher struct {
  8. Predicate interface{}
  9. // cached type
  10. predicateArgType reflect.Type
  11. }
  12. func NewSatisfyMatcher(predicate interface{}) *SatisfyMatcher {
  13. if predicate == nil {
  14. panic("predicate cannot be nil")
  15. }
  16. predicateType := reflect.TypeOf(predicate)
  17. if predicateType.Kind() != reflect.Func {
  18. panic("predicate must be a function")
  19. }
  20. if predicateType.NumIn() != 1 {
  21. panic("predicate must have 1 argument")
  22. }
  23. if predicateType.NumOut() != 1 || predicateType.Out(0).Kind() != reflect.Bool {
  24. panic("predicate must return bool")
  25. }
  26. return &SatisfyMatcher{
  27. Predicate: predicate,
  28. predicateArgType: predicateType.In(0),
  29. }
  30. }
  31. func (m *SatisfyMatcher) Match(actual interface{}) (success bool, err error) {
  32. // prepare a parameter to pass to the predicate
  33. var param reflect.Value
  34. if actual != nil && reflect.TypeOf(actual).AssignableTo(m.predicateArgType) {
  35. // The dynamic type of actual is compatible with the predicate argument.
  36. param = reflect.ValueOf(actual)
  37. } else if actual == nil && m.predicateArgType.Kind() == reflect.Interface {
  38. // The dynamic type of actual is unknown, so there's no way to make its
  39. // reflect.Value. Create a nil of the predicate argument, which is known.
  40. param = reflect.Zero(m.predicateArgType)
  41. } else {
  42. return false, fmt.Errorf("predicate expects '%s' but we have '%T'", m.predicateArgType, actual)
  43. }
  44. // call the predicate with `actual`
  45. fn := reflect.ValueOf(m.Predicate)
  46. result := fn.Call([]reflect.Value{param})
  47. return result[0].Bool(), nil
  48. }
  49. func (m *SatisfyMatcher) FailureMessage(actual interface{}) (message string) {
  50. return format.Message(actual, "to satisfy predicate", m.Predicate)
  51. }
  52. func (m *SatisfyMatcher) NegatedFailureMessage(actual interface{}) (message string) {
  53. return format.Message(actual, "to not satisfy predicate", m.Predicate)
  54. }