-
Notifications
You must be signed in to change notification settings - Fork 26
/
contain_lines.go
120 lines (96 loc) · 3.16 KB
/
contain_lines.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package matchers
import (
"fmt"
"reflect"
"regexp"
"strings"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)
func ContainLines(expected ...interface{}) types.GomegaMatcher {
return &containLinesMatcher{
expected: expected,
}
}
type containLinesMatcher struct {
expected []interface{}
}
func (matcher *containLinesMatcher) Match(actual interface{}) (success bool, err error) {
_, ok := actual.(string)
if !ok {
_, ok := actual.(fmt.Stringer)
if !ok {
return false, fmt.Errorf("ContainLinesMatcher requires a string or fmt.Stringer. Got actual: %s", format.Object(actual, 1))
}
}
actualLines := matcher.lines(actual)
for currentActualLineIndex := 0; currentActualLineIndex < len(actualLines); currentActualLineIndex++ {
currentActualLine := actualLines[currentActualLineIndex]
currentExpectedLine := matcher.expected[currentActualLineIndex]
match, err := matcher.compare(currentActualLine, currentExpectedLine)
if err != nil {
return false, err
}
if match {
if currentActualLineIndex+1 == len(matcher.expected) {
return true, nil
}
} else {
if len(actualLines) > 1 {
actualLines = actualLines[1:]
currentActualLineIndex = -1
}
}
}
return false, nil
}
func (matcher *containLinesMatcher) compare(actual string, expected interface{}) (bool, error) {
if m, ok := expected.(types.GomegaMatcher); ok {
match, err := m.Match(actual)
if err != nil {
return false, err
}
return match, nil
}
return reflect.DeepEqual(actual, expected), nil
}
func (matcher *containLinesMatcher) lines(actual interface{}) []string {
raw, ok := actual.(string)
if !ok {
raw = actual.(fmt.Stringer).String()
}
re := regexp.MustCompile(`^\[[a-z]+\]\s`)
var lines []string
for _, line := range strings.Split(raw, "\n") {
lines = append(lines, re.ReplaceAllString(line, ""))
}
return lines
}
func (matcher *containLinesMatcher) FailureMessage(actual interface{}) (message string) {
actualLines := "\n" + strings.Join(matcher.lines(actual), "\n")
missing := matcher.linesMatching(actual, false)
if len(missing) > 0 {
return fmt.Sprintf("Expected\n%s\nto contain lines\n%s\nbut missing\n%s", format.Object(actualLines, 1), format.Object(matcher.expected, 1), format.Object(missing, 1))
}
return fmt.Sprintf("Expected\n%s\nto contain lines\n%s\nall lines appear, but may be misordered", format.Object(actualLines, 1), format.Object(matcher.expected, 1))
}
func (matcher *containLinesMatcher) NegatedFailureMessage(actual interface{}) (message string) {
actualLines := "\n" + strings.Join(matcher.lines(actual), "\n")
missing := matcher.linesMatching(actual, true)
return fmt.Sprintf("Expected\n%s\nnot to contain lines\n%s\nbut includes\n%s", format.Object(actualLines, 1), format.Object(matcher.expected, 1), format.Object(missing, 1))
}
func (matcher *containLinesMatcher) linesMatching(actual interface{}, matching bool) []interface{} {
var set []interface{}
for _, expected := range matcher.expected {
var match bool
for _, line := range matcher.lines(actual) {
if ok, _ := matcher.compare(line, expected); ok {
match = true
}
}
if match == matching {
set = append(set, expected)
}
}
return set
}