blob: 7b0e0886842a16da02bb1f332c3f4abee93beed4 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001package matchers
2
3import (
4 "fmt"
5 "reflect"
6
7 "github.com/onsi/gomega/format"
8 "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph"
9)
10
11type ConsistOfMatcher struct {
12 Elements []interface{}
13}
14
15func (matcher *ConsistOfMatcher) Match(actual interface{}) (success bool, err error) {
16 if !isArrayOrSlice(actual) && !isMap(actual) {
17 return false, fmt.Errorf("ConsistOf matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1))
18 }
19
20 elements := matcher.Elements
21 if len(matcher.Elements) == 1 && isArrayOrSlice(matcher.Elements[0]) {
22 elements = []interface{}{}
23 value := reflect.ValueOf(matcher.Elements[0])
24 for i := 0; i < value.Len(); i++ {
25 elements = append(elements, value.Index(i).Interface())
26 }
27 }
28
29 matchers := []interface{}{}
30 for _, element := range elements {
31 matcher, isMatcher := element.(omegaMatcher)
32 if !isMatcher {
33 matcher = &EqualMatcher{Expected: element}
34 }
35 matchers = append(matchers, matcher)
36 }
37
38 values := matcher.valuesOf(actual)
39
40 if len(values) != len(matchers) {
41 return false, nil
42 }
43
44 neighbours := func(v, m interface{}) (bool, error) {
45 match, err := m.(omegaMatcher).Match(v)
46 return match && err == nil, nil
47 }
48
49 bipartiteGraph, err := bipartitegraph.NewBipartiteGraph(values, matchers, neighbours)
50 if err != nil {
51 return false, err
52 }
53
54 return len(bipartiteGraph.LargestMatching()) == len(values), nil
55}
56
57func (matcher *ConsistOfMatcher) valuesOf(actual interface{}) []interface{} {
58 value := reflect.ValueOf(actual)
59 values := []interface{}{}
60 if isMap(actual) {
61 keys := value.MapKeys()
62 for i := 0; i < value.Len(); i++ {
63 values = append(values, value.MapIndex(keys[i]).Interface())
64 }
65 } else {
66 for i := 0; i < value.Len(); i++ {
67 values = append(values, value.Index(i).Interface())
68 }
69 }
70
71 return values
72}
73
74func (matcher *ConsistOfMatcher) FailureMessage(actual interface{}) (message string) {
75 return format.Message(actual, "to consist of", matcher.Elements)
76}
77
78func (matcher *ConsistOfMatcher) NegatedFailureMessage(actual interface{}) (message string) {
79 return format.Message(actual, "not to consist of", matcher.Elements)
80}