Source file
src/testing/match.go
1
2
3
4
5 package testing
6
7 import (
8 "fmt"
9 "os"
10 "strconv"
11 "strings"
12 "sync"
13 )
14
15
16 type matcher struct {
17 filter filterMatch
18 skip filterMatch
19 matchFunc func(pat, str string) (bool, error)
20
21 mu sync.Mutex
22
23
24
25
26
27 subNames map[string]int32
28 }
29
30 type filterMatch interface {
31
32
33 matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool)
34
35
36
37 verify(name string, matchString func(pat, str string) (bool, error)) error
38 }
39
40
41
42 type simpleMatch []string
43
44
45 type alternationMatch []filterMatch
46
47
48
49 var matchMutex sync.Mutex
50
51 func allMatcher() *matcher {
52 return newMatcher(nil, "", "", "")
53 }
54
55 func newMatcher(matchString func(pat, str string) (bool, error), patterns, name, skips string) *matcher {
56 var filter, skip filterMatch
57 if patterns == "" {
58 filter = simpleMatch{}
59 } else {
60 filter = splitRegexp(patterns)
61 if err := filter.verify(name, matchString); err != nil {
62 fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s\n", err)
63 os.Exit(1)
64 }
65 }
66 if skips == "" {
67 skip = alternationMatch{}
68 } else {
69 skip = splitRegexp(skips)
70 if err := skip.verify("-test.skip", matchString); err != nil {
71 fmt.Fprintf(os.Stderr, "testing: invalid regexp for %v\n", err)
72 os.Exit(1)
73 }
74 }
75 return &matcher{
76 filter: filter,
77 skip: skip,
78 matchFunc: matchString,
79 subNames: map[string]int32{},
80 }
81 }
82
83 func (m *matcher) fullName(c *common, subname string) (name string, ok, partial bool) {
84 name = subname
85
86 m.mu.Lock()
87 defer m.mu.Unlock()
88
89 if c != nil && c.level > 0 {
90 name = m.unique(c.name, rewrite(subname))
91 }
92
93 matchMutex.Lock()
94 defer matchMutex.Unlock()
95
96
97 elem := strings.Split(name, "/")
98
99
100
101 ok, partial = m.filter.matches(elem, m.matchFunc)
102 if !ok {
103 return name, false, false
104 }
105
106
107
108 skip, partialSkip := m.skip.matches(elem, m.matchFunc)
109 if skip && !partialSkip {
110 return name, false, false
111 }
112
113 return name, ok, partial
114 }
115
116
117
118
119 func (m *matcher) clearSubNames() {
120 m.mu.Lock()
121 defer m.mu.Unlock()
122 clear(m.subNames)
123 }
124
125 func (m simpleMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
126 for i, s := range name {
127 if i >= len(m) {
128 break
129 }
130 if ok, _ := matchString(m[i], s); !ok {
131 return false, false
132 }
133 }
134 return true, len(name) < len(m)
135 }
136
137 func (m simpleMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
138 for i, s := range m {
139 m[i] = rewrite(s)
140 }
141
142 for i, s := range m {
143 if _, err := matchString(s, "non-empty"); err != nil {
144 return fmt.Errorf("element %d of %s (%q): %s", i, name, s, err)
145 }
146 }
147 return nil
148 }
149
150 func (m alternationMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
151 for _, m := range m {
152 if ok, partial = m.matches(name, matchString); ok {
153 return ok, partial
154 }
155 }
156 return false, false
157 }
158
159 func (m alternationMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
160 for i, m := range m {
161 if err := m.verify(name, matchString); err != nil {
162 return fmt.Errorf("alternation %d of %s", i, err)
163 }
164 }
165 return nil
166 }
167
168 func splitRegexp(s string) filterMatch {
169 a := make(simpleMatch, 0, strings.Count(s, "/"))
170 b := make(alternationMatch, 0, strings.Count(s, "|"))
171 cs := 0
172 cp := 0
173 for i := 0; i < len(s); {
174 switch s[i] {
175 case '[':
176 cs++
177 case ']':
178 if cs--; cs < 0 {
179 cs = 0
180 }
181 case '(':
182 if cs == 0 {
183 cp++
184 }
185 case ')':
186 if cs == 0 {
187 cp--
188 }
189 case '\\':
190 i++
191 case '/':
192 if cs == 0 && cp == 0 {
193 a = append(a, s[:i])
194 s = s[i+1:]
195 i = 0
196 continue
197 }
198 case '|':
199 if cs == 0 && cp == 0 {
200 a = append(a, s[:i])
201 s = s[i+1:]
202 i = 0
203 b = append(b, a)
204 a = make(simpleMatch, 0, len(a))
205 continue
206 }
207 }
208 i++
209 }
210
211 a = append(a, s)
212 if len(b) == 0 {
213 return a
214 }
215 return append(b, a)
216 }
217
218
219
220 func (m *matcher) unique(parent, subname string) string {
221 base := parent + "/" + subname
222
223 for {
224 n := m.subNames[base]
225 if n < 0 {
226 panic("subtest count overflow")
227 }
228 m.subNames[base] = n + 1
229
230 if n == 0 && subname != "" {
231 prefix, nn := parseSubtestNumber(base)
232 if len(prefix) < len(base) && nn < m.subNames[prefix] {
233
234
235
236 continue
237 }
238 return base
239 }
240
241 name := fmt.Sprintf("%s#%02d", base, n)
242 if m.subNames[name] != 0 {
243
244
245
246 continue
247 }
248
249 return name
250 }
251 }
252
253
254
255 func parseSubtestNumber(s string) (prefix string, nn int32) {
256 i := strings.LastIndex(s, "#")
257 if i < 0 {
258 return s, 0
259 }
260
261 prefix, suffix := s[:i], s[i+1:]
262 if len(suffix) < 2 || (len(suffix) > 2 && suffix[0] == '0') {
263
264
265 return s, 0
266 }
267 if suffix == "00" {
268 if !strings.HasSuffix(prefix, "/") {
269
270
271 return s, 0
272 }
273 }
274
275 n, err := strconv.ParseInt(suffix, 10, 32)
276 if err != nil || n < 0 {
277 return s, 0
278 }
279 return prefix, int32(n)
280 }
281
282
283
284 func rewrite(s string) string {
285 b := []byte{}
286 for _, r := range s {
287 switch {
288 case isSpace(r):
289 b = append(b, '_')
290 case !strconv.IsPrint(r):
291 s := strconv.QuoteRune(r)
292 b = append(b, s[1:len(s)-1]...)
293 default:
294 b = append(b, string(r)...)
295 }
296 }
297 return string(b)
298 }
299
300 func isSpace(r rune) bool {
301 if r < 0x2000 {
302 switch r {
303
304 case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680:
305 return true
306 }
307 } else {
308 if r <= 0x200a {
309 return true
310 }
311 switch r {
312 case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
313 return true
314 }
315 }
316 return false
317 }
318
View as plain text