-
Notifications
You must be signed in to change notification settings - Fork 75
/
golden.go
189 lines (178 loc) · 5.27 KB
/
golden.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package util
import (
"bufio"
"fmt"
"regexp"
"strings"
)
// TestYAMLEqual judges whether golden and result yaml are the same and return the diff if they are different.
// If this function returns error, it means that golden file or result file could not be formatted.
// eg:
//
// golden: line
// result: line
// return: true, ""
//
// golden: lineG
// result: lineR
// return: false, line 1 diff:
// --golden--
// lineG
// --result--
// lineR
//
// golden: line
// result: line
// lineAdd
// return: false, line 2 to 2:
// --result addition--
// lineAdd
func TestYAMLEqual(golden, result string) (bool, string, error) {
golden = ApplyFilters(golden, LicenseFilter, SpaceLineFilter)
result = ApplyFilters(result, LicenseFilter, SpaceLineFilter)
// do not use FormatterFilter here because we need to know whether manifest could be formatted
var err error
golden, err = formatTestYAML(golden)
if err != nil {
return false, "", fmt.Errorf("golden file format err: %s", err)
}
result, err = formatTestYAML(result)
if err != nil {
return false, "", fmt.Errorf("result file format err: %s", err)
}
var diffBuilder strings.Builder
var diffFlag bool
var line int
scannerG := bufio.NewScanner(strings.NewReader(strings.TrimSpace(golden)))
scannerR := bufio.NewScanner(strings.NewReader(strings.TrimSpace(result)))
for scannerG.Scan() && scannerR.Scan() {
line += 1
lineG := scannerG.Text()
lineR := scannerR.Text()
// judge whether lineG and lindR are the same. if not, add this diff line to diffBuilder.
if !isTestYAMLLineEqual(lineG, lineR) {
diffFlag = true
diffBuilder.WriteString(
fmt.Sprintf("line %d diff:\n--golden--\n%s\n--result--\n%s\n", line, lineG, lineR),
)
}
}
// additional lines processing
// [lineStart, lineEnd] represents additional lines
// scan golden ends.
if len(scannerG.Text()) == 0 {
var addBuilder strings.Builder
lineStart, lineEnd := line+1, line
for scannerR.Scan() {
lineEnd += 1
addBuilder.WriteString(scannerR.Text() + "\n")
}
// length of result is equal to length of golden
if lineStart > lineEnd {
// result is equal to golden
if !diffFlag {
return true, "", nil
}
return false, diffBuilder.String(), nil
}
// result is longer than golden, we add these additional lines to diffBuilder
diffBuilder.WriteString(
fmt.Sprintf("line %d to %d:\n--result addition--\n%s", lineStart, lineEnd, addBuilder.String()),
)
return false, diffBuilder.String(), nil
}
// scan result ends, we know that golden is longer than result.
// due to scannerG.Scan has been invoked before, we add the first additional line.
var addBuilder strings.Builder
lineStart, lineEnd := line+1, line+1
addBuilder.WriteString(scannerG.Text() + "\n")
for scannerG.Scan() {
lineEnd += 1
addBuilder.WriteString(scannerG.Text() + "\n")
}
diffBuilder.WriteString(
fmt.Sprintf("line %d to %d:\n--golden addition--\n%s", lineStart, lineEnd, addBuilder.String()),
)
return false, diffBuilder.String(), nil
}
func formatTestYAML(original string) (string, error) {
resBytes, err := formatter.Format([]byte(original))
if err != nil {
return "", err
}
return string(resBytes), nil
}
func isTestYAMLLineEqual(golden, result string) bool {
keyG, valG, typG := splitKeyVal(golden)
keyR, valR, typR := splitKeyVal(result)
if typG != typR {
return false
}
if keyG != keyR {
return false
}
// golden and result strings could not be split by ":", compare them directly
if typG == complete {
return true
}
if valG == valR {
return true
}
// valG may contain regular expression
reg, err := regexp.Compile(valG)
if err != nil {
return false
}
if !reg.MatchString(result) {
return false
}
return true
}
type keyValType uint8
const (
// eg: line
complete keyValType = iota + 1
// eg: key:val
singlePair
// eg: key:val:val
multiPairs
)
// split key:val format str and return keyValType.
// eg:
//
// key, it returns key, "", complete.
// key:val, it returns key, val, singlePair
// key:val:val1, it returns key, val:val1, multiPairs;
func splitKeyVal(str string) (string, string, keyValType) {
var key, val string
var typ keyValType
elems := strings.Split(str, ":")
switch len(elems) {
case 1:
key = elems[0]
typ = complete
case 2:
key = elems[0]
val = elems[1]
typ = singlePair
default:
key = elems[0]
val = strings.Join(elems[1:], "")
typ = multiPairs
}
return strings.TrimSpace(key), strings.TrimSpace(val), typ
}