1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
15 // matcher sanitizes, uniques, and filters names of subtests and subbenchmarks.
18 matchFunc
func(pat
, str
string) (bool, error
)
22 // subNames is used to deduplicate subtest names.
23 // Each key is the subtest name joined to the deduplicated name of the parent test.
24 // Each value is the count of the number of occurrences of the given subtest name
26 subNames
map[string]int32
29 type filterMatch
interface {
30 // matches checks the name against the receiver's pattern strings using the
31 // given match function.
32 matches(name
[]string, matchString
func(pat
, str
string) (bool, error
)) (ok
, partial
bool)
34 // verify checks that the receiver's pattern strings are valid filters by
35 // calling the given match function.
36 verify(name
string, matchString
func(pat
, str
string) (bool, error
)) error
39 // simpleMatch matches a test name if all of the pattern strings match in
41 type simpleMatch
[]string
43 // alternationMatch matches a test name if one of the alternations match.
44 type alternationMatch
[]filterMatch
46 // TODO: fix test_main to avoid race and improve caching, also allowing to
47 // eliminate this Mutex.
48 var matchMutex sync
.Mutex
50 func newMatcher(matchString
func(pat
, str
string) (bool, error
), patterns
, name
string) *matcher
{
53 impl
= splitRegexp(patterns
)
54 if err
:= impl
.verify(name
, matchString
); err
!= nil {
55 fmt
.Fprintf(os
.Stderr
, "testing: invalid regexp for %s\n", err
)
61 matchFunc
: matchString
,
62 subNames
: map[string]int32{},
66 func (m
*matcher
) fullName(c
*common
, subname
string) (name
string, ok
, partial
bool) {
72 if c
!= nil && c
.level
> 0 {
73 name
= m
.unique(c
.name
, rewrite(subname
))
77 defer matchMutex
.Unlock()
80 return name
, true, false
83 // We check the full array of paths each time to allow for the case that
84 // a pattern contains a '/'.
85 elem
:= strings
.Split(name
, "/")
86 ok
, partial
= m
.filter
.matches(elem
, m
.matchFunc
)
87 return name
, ok
, partial
90 // clearSubNames clears the matcher's internal state, potentially freeing
91 // memory. After this is called, T.Name may return the same strings as it did
92 // for earlier subtests.
93 func (m
*matcher
) clearSubNames() {
96 for key
:= range m
.subNames
{
97 delete(m
.subNames
, key
)
101 func (m simpleMatch
) matches(name
[]string, matchString
func(pat
, str
string) (bool, error
)) (ok
, partial
bool) {
102 for i
, s
:= range name
{
106 if ok
, _
:= matchString(m
[i
], s
); !ok
{
110 return true, len(name
) < len(m
)
113 func (m simpleMatch
) verify(name
string, matchString
func(pat
, str
string) (bool, error
)) error
{
114 for i
, s
:= range m
{
117 // Verify filters before doing any processing.
118 for i
, s
:= range m
{
119 if _
, err
:= matchString(s
, "non-empty"); err
!= nil {
120 return fmt
.Errorf("element %d of %s (%q): %s", i
, name
, s
, err
)
126 func (m alternationMatch
) matches(name
[]string, matchString
func(pat
, str
string) (bool, error
)) (ok
, partial
bool) {
127 for _
, m
:= range m
{
128 if ok
, partial
= m
.matches(name
, matchString
); ok
{
135 func (m alternationMatch
) verify(name
string, matchString
func(pat
, str
string) (bool, error
)) error
{
136 for i
, m
:= range m
{
137 if err
:= m
.verify(name
, matchString
); err
!= nil {
138 return fmt
.Errorf("alternation %d of %s", i
, err
)
144 func splitRegexp(s
string) filterMatch
{
145 a
:= make(simpleMatch
, 0, strings
.Count(s
, "/"))
146 b
:= make(alternationMatch
, 0, strings
.Count(s
, "|"))
149 for i
:= 0; i
< len(s
); {
154 if cs
--; cs
< 0 { // An unmatched ']' is legal.
168 if cs
== 0 && cp
== 0 {
175 if cs
== 0 && cp
== 0 {
180 a
= make(simpleMatch
, 0, len(a
))
194 // unique creates a unique name for the given parent and subname by affixing it
195 // with one or more counts, if necessary.
196 func (m
*matcher
) unique(parent
, subname
string) string {
197 base
:= parent
+ "/" + subname
200 n
:= m
.subNames
[base
]
202 panic("subtest count overflow")
204 m
.subNames
[base
] = n
+ 1
206 if n
== 0 && subname
!= "" {
207 prefix
, nn
:= parseSubtestNumber(base
)
208 if len(prefix
) < len(base
) && nn
< m
.subNames
[prefix
] {
209 // This test is explicitly named like "parent/subname#NN",
210 // and #NN was already used for the NNth occurrence of "parent/subname".
211 // Loop to add a disambiguating suffix.
217 name
:= fmt
.Sprintf("%s#%02d", base
, n
)
218 if m
.subNames
[name
] != 0 {
219 // This is the nth occurrence of base, but the name "parent/subname#NN"
220 // collides with the first occurrence of a subtest *explicitly* named
221 // "parent/subname#NN". Try the next number.
229 // parseSubtestNumber splits a subtest name into a "#%02d"-formatted int32
230 // suffix (if present), and a prefix preceding that suffix (always).
231 func parseSubtestNumber(s
string) (prefix
string, nn
int32) {
232 i
:= strings
.LastIndex(s
, "#")
237 prefix
, suffix
:= s
[:i
], s
[i
+1:]
238 if len(suffix
) < 2 ||
(len(suffix
) > 2 && suffix
[0] == '0') {
239 // Even if suffix is numeric, it is not a possible output of a "%02" format
240 // string: it has either too few digits or too many leading zeroes.
244 if !strings
.HasSuffix(prefix
, "/") {
245 // We only use "#00" as a suffix for subtests named with the empty
246 // string — it isn't a valid suffix if the subtest name is non-empty.
251 n
, err
:= strconv
.ParseInt(suffix
, 10, 32)
252 if err
!= nil || n
< 0 {
255 return prefix
, int32(n
)
258 // rewrite rewrites a subname to having only printable characters and no white
260 func rewrite(s
string) string {
262 for _
, r
:= range s
{
266 case !strconv
.IsPrint(r
):
267 s
:= strconv
.QuoteRune(r
)
268 b
= append(b
, s
[1:len(s
)-1]...)
270 b
= append(b
, string(r
)...)
276 func isSpace(r rune
) bool {
279 // Note: not the same as Unicode Z class.
280 case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680:
288 case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000: