github.com/onsi/gomega@v1.32.0/matchers/consist_of.go (about) 1 // untested sections: 3 2 3 package matchers 4 5 import ( 6 "fmt" 7 "reflect" 8 9 "github.com/onsi/gomega/format" 10 "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph" 11 ) 12 13 type ConsistOfMatcher struct { 14 Elements []interface{} 15 missingElements []interface{} 16 extraElements []interface{} 17 } 18 19 func (matcher *ConsistOfMatcher) Match(actual interface{}) (success bool, err error) { 20 if !isArrayOrSlice(actual) && !isMap(actual) { 21 return false, fmt.Errorf("ConsistOf matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1)) 22 } 23 24 matchers := matchers(matcher.Elements) 25 values := valuesOf(actual) 26 27 bipartiteGraph, err := bipartitegraph.NewBipartiteGraph(values, matchers, neighbours) 28 if err != nil { 29 return false, err 30 } 31 32 edges := bipartiteGraph.LargestMatching() 33 if len(edges) == len(values) && len(edges) == len(matchers) { 34 return true, nil 35 } 36 37 var missingMatchers []interface{} 38 matcher.extraElements, missingMatchers = bipartiteGraph.FreeLeftRight(edges) 39 matcher.missingElements = equalMatchersToElements(missingMatchers) 40 41 return false, nil 42 } 43 44 func neighbours(value, matcher interface{}) (bool, error) { 45 match, err := matcher.(omegaMatcher).Match(value) 46 return match && err == nil, nil 47 } 48 49 func equalMatchersToElements(matchers []interface{}) (elements []interface{}) { 50 for _, matcher := range matchers { 51 if equalMatcher, ok := matcher.(*EqualMatcher); ok { 52 elements = append(elements, equalMatcher.Expected) 53 } else if _, ok := matcher.(*BeNilMatcher); ok { 54 elements = append(elements, nil) 55 } else { 56 elements = append(elements, matcher) 57 } 58 } 59 return 60 } 61 62 func flatten(elems []interface{}) []interface{} { 63 if len(elems) != 1 || !isArrayOrSlice(elems[0]) { 64 return elems 65 } 66 67 value := reflect.ValueOf(elems[0]) 68 flattened := make([]interface{}, value.Len()) 69 for i := 0; i < value.Len(); i++ { 70 flattened[i] = value.Index(i).Interface() 71 } 72 return flattened 73 } 74 75 func matchers(expectedElems []interface{}) (matchers []interface{}) { 76 for _, e := range flatten(expectedElems) { 77 if e == nil { 78 matchers = append(matchers, &BeNilMatcher{}) 79 } else if matcher, isMatcher := e.(omegaMatcher); isMatcher { 80 matchers = append(matchers, matcher) 81 } else { 82 matchers = append(matchers, &EqualMatcher{Expected: e}) 83 } 84 } 85 return 86 } 87 88 func presentable(elems []interface{}) interface{} { 89 elems = flatten(elems) 90 91 if len(elems) == 0 { 92 return []interface{}{} 93 } 94 95 sv := reflect.ValueOf(elems) 96 firstEl := sv.Index(0) 97 if firstEl.IsNil() { 98 return elems 99 } 100 tt := firstEl.Elem().Type() 101 for i := 1; i < sv.Len(); i++ { 102 el := sv.Index(i) 103 if el.IsNil() || (sv.Index(i).Elem().Type() != tt) { 104 return elems 105 } 106 } 107 108 ss := reflect.MakeSlice(reflect.SliceOf(tt), sv.Len(), sv.Len()) 109 for i := 0; i < sv.Len(); i++ { 110 ss.Index(i).Set(sv.Index(i).Elem()) 111 } 112 113 return ss.Interface() 114 } 115 116 func valuesOf(actual interface{}) []interface{} { 117 value := reflect.ValueOf(actual) 118 values := []interface{}{} 119 if isMap(actual) { 120 keys := value.MapKeys() 121 for i := 0; i < value.Len(); i++ { 122 values = append(values, value.MapIndex(keys[i]).Interface()) 123 } 124 } else { 125 for i := 0; i < value.Len(); i++ { 126 values = append(values, value.Index(i).Interface()) 127 } 128 } 129 130 return values 131 } 132 133 func (matcher *ConsistOfMatcher) FailureMessage(actual interface{}) (message string) { 134 message = format.Message(actual, "to consist of", presentable(matcher.Elements)) 135 message = appendMissingElements(message, matcher.missingElements) 136 if len(matcher.extraElements) > 0 { 137 message = fmt.Sprintf("%s\nthe extra elements were\n%s", message, 138 format.Object(presentable(matcher.extraElements), 1)) 139 } 140 return 141 } 142 143 func appendMissingElements(message string, missingElements []interface{}) string { 144 if len(missingElements) == 0 { 145 return message 146 } 147 return fmt.Sprintf("%s\nthe missing elements were\n%s", message, 148 format.Object(presentable(missingElements), 1)) 149 } 150 151 func (matcher *ConsistOfMatcher) NegatedFailureMessage(actual interface{}) (message string) { 152 return format.Message(actual, "not to consist of", presentable(matcher.Elements)) 153 }